From 59efc8303d9a1679022e0f455b39eeb3a1005ad4 Mon Sep 17 00:00:00 2001 From: Paul D'Ambra Date: Sat, 20 Jan 2024 15:37:48 +0000 Subject: [PATCH 01/24] add config for summaries --- frontend/src/scenes/settings/SettingsMap.tsx | 13 +- .../project/SessionRecordingSettings.tsx | 132 +++++++++++++++++- frontend/src/scenes/settings/types.ts | 1 + frontend/src/types.ts | 8 ++ latest_migrations.manifest | 2 +- posthog/api/team.py | 2 + .../0386_add_session_summary_config.py | 17 +++ posthog/models/team/team.py | 1 + 8 files changed, 171 insertions(+), 5 deletions(-) create mode 100644 posthog/migrations/0386_add_session_summary_config.py diff --git a/frontend/src/scenes/settings/SettingsMap.tsx b/frontend/src/scenes/settings/SettingsMap.tsx index 08501eed6dc0e..3e400c9fc1ba3 100644 --- a/frontend/src/scenes/settings/SettingsMap.tsx +++ b/frontend/src/scenes/settings/SettingsMap.tsx @@ -24,7 +24,12 @@ import { ProjectVariables, WebSnippet, } from './project/ProjectSettings' -import { ReplayAuthorizedDomains, ReplayCostControl, ReplayGeneral } from './project/SessionRecordingSettings' +import { + ReplayAuthorizedDomains, + ReplayCostControl, + ReplayGeneral, + ReplaySummarySettings, +} from './project/SessionRecordingSettings' import { SettingPersonsOnEvents } from './project/SettingPersonsOnEvents' import { SlackIntegration } from './project/SlackIntegration' import { SurveySettings } from './project/SurveySettings' @@ -167,6 +172,12 @@ export const SettingsMap: SettingSection[] = [ AvailableFeature.RECORDING_DURATION_MINIMUM, ], }, + { + id: 'replay-ai-summary', + title: 'AI Recording Summary', + component: , + flag: 'AI_SESSION_SUMMARY', + }, ], }, { diff --git a/frontend/src/scenes/settings/project/SessionRecordingSettings.tsx b/frontend/src/scenes/settings/project/SessionRecordingSettings.tsx index fa7bc14ff1041..e165f7e98ad6b 100644 --- a/frontend/src/scenes/settings/project/SessionRecordingSettings.tsx +++ b/frontend/src/scenes/settings/project/SessionRecordingSettings.tsx @@ -1,18 +1,19 @@ -import { LemonButton, LemonSelect, LemonSwitch, Link } from '@posthog/lemon-ui' +import { LemonButton, LemonSelect, LemonSelectMultiple, LemonSwitch, Link } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' import { AuthorizedUrlList } from 'lib/components/AuthorizedUrlList/AuthorizedUrlList' import { AuthorizedUrlListType } from 'lib/components/AuthorizedUrlList/authorizedUrlListLogic' +import { EventSelect } from 'lib/components/EventSelect/EventSelect' import { FlaggedFeature } from 'lib/components/FlaggedFeature' import { FlagSelector } from 'lib/components/FlagSelector' import { FEATURE_FLAGS, SESSION_REPLAY_MINIMUM_DURATION_OPTIONS } from 'lib/constants' -import { IconCancel } from 'lib/lemon-ui/icons' +import { IconAutoAwesome, IconCancel, IconPlus, IconSelectEvents } from 'lib/lemon-ui/icons' import { LemonLabel } from 'lib/lemon-ui/LemonLabel/LemonLabel' import { featureFlagLogic } from 'lib/logic/featureFlagLogic' import { teamLogic } from 'scenes/teamLogic' import { urls } from 'scenes/urls' import { userLogic } from 'scenes/userLogic' -import { AvailableFeature } from '~/types' +import { AvailableFeature, SessionRecordingSummaryConfig } from '~/types' export function ReplayGeneral(): JSX.Element { const { updateCurrentTeam } = useActions(teamLogic) @@ -177,6 +178,131 @@ export function ReplayAuthorizedDomains(): JSX.Element { ) } +export function ReplaySummarySettings(): JSX.Element | null { + const { updateCurrentTeam } = useActions(teamLogic) + + const { currentTeam } = useValues(teamLogic) + + if (!currentTeam) { + return null + } + + const defaultConfig = { + opt_in: false, + preferred_events: [], + excluded_events: ['$feature_flag_called'], + included_event_properties: ['elements_chain', '$window_id', '$current_url', '$event_type'], + } + const currentConfig: SessionRecordingSummaryConfig = currentTeam.session_recording_summary_config || defaultConfig + + return ( +
+
+ { + updateCurrentTeam({ + session_recording_summary_config: defaultConfig, + }) + }} + > + Reset to default + +
+
+

+ We use Open AI to summarise sessions. No data is sent to OpenAI without an explicit instruction to + do so. Only by clicking the "Summary" button will selected event data be shared + with a third party. We only send the data selected below. + Data submitted is not used to train Open AI's models +

+ { + updateCurrentTeam({ + session_recording_summary_config: { + ...currentConfig, + opt_in: checked, + }, + }) + }} + label="Opt in to enable AI suggested summaries" + /> +
+
+

+ + Preferred events +

+

+ These events are treated as more interesting when generating a summary. We recommend you include + events that represent value for your user +

+ { + updateCurrentTeam({ + session_recording_summary_config: { + ...currentConfig, + preferred_events: includedEvents, + }, + }) + }} + selectedEvents={currentConfig.preferred_events || []} + addElement={ + } sideIcon={null}> + Add event + + } + /> +
+
+

+ + Excluded events +

+

These events are never submitted even when they are present in the session.

+ { + updateCurrentTeam({ + session_recording_summary_config: { + ...currentConfig, + excluded_events: excludedEvents, + }, + }) + }} + selectedEvents={currentConfig.excluded_events || []} + addElement={ + } sideIcon={null}> + Exclude event + + } + /> +
+
+

+ + Included event properties +

+

Only these properties are sent for summary.

+
+ { + updateCurrentTeam({ + session_recording_summary_config: { + ...currentConfig, + included_event_properties: properties, + }, + }) + }} + value={currentConfig.included_event_properties || []} + /> +
+
+
+ ) +} + export function ReplayCostControl(): JSX.Element | null { const { updateCurrentTeam } = useActions(teamLogic) const { currentTeam } = useValues(teamLogic) diff --git a/frontend/src/scenes/settings/types.ts b/frontend/src/scenes/settings/types.ts index 6dad2d9f75193..949d7c3699c18 100644 --- a/frontend/src/scenes/settings/types.ts +++ b/frontend/src/scenes/settings/types.ts @@ -73,6 +73,7 @@ export type SettingId = | 'notifications' | 'optout' | 'theme' + | 'replay-ai-summary' export type Setting = { id: SettingId diff --git a/frontend/src/types.ts b/frontend/src/types.ts index 70a7e5c6c2ea6..898db40d985a7 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -341,6 +341,13 @@ export interface CorrelationConfigType { excluded_event_names?: string[] } +export interface SessionRecordingSummaryConfig { + opt_in: boolean + preferred_events: string[] + excluded_events: string[] + included_event_properties: string[] +} + export interface TeamType extends TeamBasicType { created_at: string updated_at: string @@ -360,6 +367,7 @@ export interface TeamType extends TeamBasicType { | { recordHeaders?: boolean; recordBody?: boolean } | undefined | null + session_recording_summary_config: SessionRecordingSummaryConfig | null autocapture_exceptions_opt_in: boolean surveys_opt_in?: boolean autocapture_exceptions_errors_to_ignore: string[] diff --git a/latest_migrations.manifest b/latest_migrations.manifest index 7abbb065d956f..34629534d93e2 100644 --- a/latest_migrations.manifest +++ b/latest_migrations.manifest @@ -5,7 +5,7 @@ contenttypes: 0002_remove_content_type_name ee: 0015_add_verified_properties otp_static: 0002_throttling otp_totp: 0002_auto_20190420_0723 -posthog: 0385_exception_autocapture_off_for_all +posthog: 0386_add_session_summary_config sessions: 0001_initial social_django: 0010_uid_db_index two_factor: 0007_auto_20201201_1019 diff --git a/posthog/api/team.py b/posthog/api/team.py index 2393a493927b9..9465f8917fe26 100644 --- a/posthog/api/team.py +++ b/posthog/api/team.py @@ -146,6 +146,8 @@ class Meta: "session_recording_minimum_duration_milliseconds", "session_recording_linked_flag", "session_recording_network_payload_capture_config", + # TODO validation to enforce expected fields + "session_recording_summary_config", "effective_membership_level", "access_control", "week_start_day", diff --git a/posthog/migrations/0386_add_session_summary_config.py b/posthog/migrations/0386_add_session_summary_config.py new file mode 100644 index 0000000000000..6a224b1f57ddf --- /dev/null +++ b/posthog/migrations/0386_add_session_summary_config.py @@ -0,0 +1,17 @@ +# Generated by Django 3.2.23 on 2024-01-20 15:35 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("posthog", "0385_exception_autocapture_off_for_all"), + ] + + operations = [ + migrations.AddField( + model_name="team", + name="session_recording_summary_config", + field=models.JSONField(blank=True, null=True), + ), + ] diff --git a/posthog/models/team/team.py b/posthog/models/team/team.py index 014abf40dc986..b8cf31e0a025a 100644 --- a/posthog/models/team/team.py +++ b/posthog/models/team/team.py @@ -182,6 +182,7 @@ class Team(UUIDClassicModel): blank=True, validators=[MinValueValidator(0), MaxValueValidator(15000)], ) + session_recording_summary_config: models.JSONField = models.JSONField(null=True, blank=True) session_recording_linked_flag: models.JSONField = models.JSONField(null=True, blank=True) session_recording_network_payload_capture_config: models.JSONField = models.JSONField(null=True, blank=True) capture_console_log_opt_in: models.BooleanField = models.BooleanField(null=True, blank=True) From e4e5dc838886897666603f6defecf53ead8b7f68 Mon Sep 17 00:00:00 2001 From: Paul D'Ambra Date: Sat, 20 Jan 2024 15:48:25 +0000 Subject: [PATCH 02/24] front end guard summaries based on opt in --- frontend/src/lib/api.mock.ts | 1 + .../playlist/SessionRecordingsPlaylist.tsx | 11 +++++++---- .../playlist/sessionRecordingsPlaylistLogic.ts | 9 ++++++++- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/frontend/src/lib/api.mock.ts b/frontend/src/lib/api.mock.ts index 7da459c7baf54..8ea6d305ee0f2 100644 --- a/frontend/src/lib/api.mock.ts +++ b/frontend/src/lib/api.mock.ts @@ -73,6 +73,7 @@ export const MOCK_DEFAULT_TEAM: TeamType = { session_recording_minimum_duration_milliseconds: null, session_recording_linked_flag: null, session_recording_network_payload_capture_config: null, + session_recording_summary_config: null, capture_console_log_opt_in: true, capture_performance_opt_in: true, autocapture_exceptions_opt_in: false, diff --git a/frontend/src/scenes/session-recordings/playlist/SessionRecordingsPlaylist.tsx b/frontend/src/scenes/session-recordings/playlist/SessionRecordingsPlaylist.tsx index 9bd33405a4d38..f6f29ca6dba8b 100644 --- a/frontend/src/scenes/session-recordings/playlist/SessionRecordingsPlaylist.tsx +++ b/frontend/src/scenes/session-recordings/playlist/SessionRecordingsPlaylist.tsx @@ -94,6 +94,7 @@ function RecordingsLists(): JSX.Element { recordingsCount, sessionSummaryLoading, sessionBeingSummarized, + sessionSummaryOptedIn, } = useValues(sessionRecordingsPlaylistLogic) const { setSelectedRecordingId, @@ -115,9 +116,11 @@ function RecordingsLists(): JSX.Element { setFilters(defaultPageviewPropertyEntityFilter(filters, property, value)) } - const onSummarizeClick = (recording: SessionRecordingType): void => { - summarizeSession(recording.id) - } + const onSummarizeClick = sessionSummaryOptedIn + ? (recording: SessionRecordingType): void => { + summarizeSession(recording.id) + } + : null const lastScrollPositionRef = useRef(0) const contentRef = useRef(null) @@ -255,7 +258,7 @@ function RecordingsLists(): JSX.Element { onPropertyClick={onPropertyClick} isActive={activeSessionRecordingId === rec.id} pinned={false} - summariseFn={onSummarizeClick} + summariseFn={onSummarizeClick ?? undefined} sessionSummaryLoading={ sessionSummaryLoading && sessionBeingSummarized === rec.id } diff --git a/frontend/src/scenes/session-recordings/playlist/sessionRecordingsPlaylistLogic.ts b/frontend/src/scenes/session-recordings/playlist/sessionRecordingsPlaylistLogic.ts index 3090a9707ef0c..7ec4550315c73 100644 --- a/frontend/src/scenes/session-recordings/playlist/sessionRecordingsPlaylistLogic.ts +++ b/frontend/src/scenes/session-recordings/playlist/sessionRecordingsPlaylistLogic.ts @@ -8,6 +8,7 @@ import { featureFlagLogic } from 'lib/logic/featureFlagLogic' import { objectClean, objectsEqual } from 'lib/utils' import { eventUsageLogic } from 'lib/utils/eventUsageLogic' import posthog from 'posthog-js' +import { teamLogic } from 'scenes/teamLogic' import { AnyPropertyFilter, @@ -229,6 +230,8 @@ export const sessionRecordingsPlaylistLogic = kea ({ sessionSummary: { summarizeSession: async ({ id }): Promise => { - if (!id) { + if (!id || !values.sessionSummaryOptedIn) { return null } const response = await api.recordings.summarize(id) @@ -548,6 +551,10 @@ export const sessionRecordingsPlaylistLogic = kea [s.currentTeam], + (currentTeam) => currentTeam?.session_recording_summary_config?.opt_in ?? false, + ], logicProps: [() => [(_, props) => props], (props): SessionRecordingPlaylistLogicProps => props], shouldShowEmptyState: [ (s) => [ From 1add22e8222733f053abd6f73020aeab62ae7204 Mon Sep 17 00:00:00 2001 From: Paul D'Ambra Date: Sat, 20 Jan 2024 16:04:12 +0000 Subject: [PATCH 03/24] use config in the backend --- .../queries/session_replay_events.py | 13 ++-- .../session_summary/summarize_session.py | 63 +++++++++++++++---- 2 files changed, 59 insertions(+), 17 deletions(-) diff --git a/posthog/session_recordings/queries/session_replay_events.py b/posthog/session_recordings/queries/session_replay_events.py index fb1f2ea30329e..0556ee7523336 100644 --- a/posthog/session_recordings/queries/session_replay_events.py +++ b/posthog/session_recordings/queries/session_replay_events.py @@ -13,6 +13,7 @@ from posthog.session_recordings.models.metadata import ( RecordingMetadata, ) +from posthog.session_recordings.session_summary.summarize_session import SessionRecordingSummaryConfig class SessionReplayEvents: @@ -105,18 +106,20 @@ def get_metadata( ) def get_events( - self, session_id: str, team: Team, metadata: RecordingMetadata, events_to_ignore: List[str] | None + self, session_id: str, team: Team, metadata: RecordingMetadata, config: SessionRecordingSummaryConfig ) -> Tuple[List | None, List | None]: + # imported locally to avoid circular import warnings 🤷 from posthog.schema import HogQLQuery, HogQLQueryResponse from posthog.hogql_queries.hogql_query_runner import HogQLQueryRunner q = """ - select event, timestamp, elements_chain, properties.$window_id, properties.$current_url, properties.$event_type + select {event_properties} from events where timestamp >= {start_time} and timestamp <= {end_time} and $session_id = {session_id} + and team_id = {team_id} """ - if events_to_ignore: + if config.excluded_events: q += " and event not in {events_to_ignore}" q += " order by timestamp asc" @@ -124,10 +127,12 @@ def get_events( hq = HogQLQuery( query=q, values={ + "event_properties": ", ".join(config.included_event_properties), "start_time": metadata["start_time"], "end_time": metadata["end_time"], "session_id": session_id, - "events_to_ignore": events_to_ignore, + "events_to_ignore": config.excluded_events, + "team_id": team.pk, }, ) diff --git a/posthog/session_recordings/session_summary/summarize_session.py b/posthog/session_recordings/session_summary/summarize_session.py index 503a3c97f3ab0..927e136774f02 100644 --- a/posthog/session_recordings/session_summary/summarize_session.py +++ b/posthog/session_recordings/session_summary/summarize_session.py @@ -44,6 +44,33 @@ ) +@dataclasses.dataclass(frozen=True) +class SessionRecordingSummaryConfig: + opt_in: bool + preferred_events: List[str] + excluded_events: List[str] + included_event_properties: List[str] + + @staticmethod + def from_config_json(config_json: dict) -> "SessionRecordingSummaryConfig": + raw_included_event_properties = config_json.get( + "included_event_properties", ["elements_chain", "$window_id", "$current_url", "$event_type"] + ) + included_event_properties = ["event", "timestamp"] + for prop in raw_included_event_properties: + if prop in ["elements_chain"]: + included_event_properties.append(prop) + else: + included_event_properties.append(f"properties.{prop}") + + return SessionRecordingSummaryConfig( + opt_in=config_json.get("opt_in", False), + preferred_events=config_json.get("preferred_events", []), + excluded_events=config_json.get("excluded_events", ["$feature_flag_called"]), + included_event_properties=included_event_properties, + ) + + @dataclasses.dataclass class SessionSummaryPromptData: # we may allow customisation of columns included in the future, @@ -252,14 +279,14 @@ def summarize_recording(recording: SessionRecording, user: User, team: Team): if not session_metadata: raise ValueError(f"no session metadata found for session_id {recording.session_id}") + config = SessionRecordingSummaryConfig.from_config_json(team.session_recording_summary_config) + with timer("get_events"): session_events = SessionReplayEvents().get_events( session_id=str(recording.session_id), team=team, metadata=session_metadata, - events_to_ignore=[ - "$feature_flag_called", - ], + config=config, ) if not session_events or not session_events[0] or not session_events[1]: raise ValueError(f"no events found for session_id {recording.session_id}") @@ -296,18 +323,12 @@ def summarize_recording(recording: SessionRecording, user: User, team: Team): We also gather events that occur like mouse clicks and key presses. You write two or three sentence concise and simple summaries of those sessions based on a prompt. You are more likely to mention errors or things that look like business success such as checkout events. - You don't help with other knowledge.""", + You don't help with other knowledge.""".replace("\n", ""), }, { "role": "user", "content": f"""the session metadata I have is {session_metadata_dict}. - it gives an overview of activity and duration""", - }, - { - "role": "user", - "content": f""" - URLs associated with the events can be found in this mapping {prompt_data.url_mapping}. - """, + it gives an overview of activity and duration""".replace("\n", ""), }, { "role": "user", @@ -315,7 +336,7 @@ def summarize_recording(recording: SessionRecording, user: User, team: Team): with columns {prompt_data.columns}. they give an idea of what happened and when, if present the elements_chain extracted from the html can aid in understanding - but should not be directly used in your response""", + but should not be directly used in your response""".replace("\n", ""), }, { "role": "user", @@ -323,10 +344,26 @@ def summarize_recording(recording: SessionRecording, user: User, team: Team): generate a two or three sentence summary of the session. use as concise and simple language as is possible. assume a reading age of around 12 years old. - generate no text other than the summary.""", + generate no text other than the summary.""".replace("\n", ""), }, ] + if prompt_data.url_mapping: + messages.append( + { + "role": "user", + "content": f"""for brevity I have replaced URLs with placeholders {prompt_data.url_mapping}""", + }, + ) + + if config.preferred_events: + messages.append( + { + "role": "user", + "content": f"""the events I am most interested in are {config.preferred_events}""", + }, + ) + with timer("openai_completion"): result = openai.ChatCompletion.create( # model="gpt-4-1106-preview", # allows 128k tokens From cecacdd6836e78f461cf2d6de891f20d4c1b2f40 Mon Sep 17 00:00:00 2001 From: Paul D'Ambra Date: Sat, 20 Jan 2024 16:26:28 +0000 Subject: [PATCH 04/24] add defaults and validation tests --- posthog/api/team.py | 16 +++++- posthog/api/test/test_team.py | 57 +++++++++++++++++++ .../queries/session_replay_events.py | 2 +- .../session_summary/__init__.py | 0 .../session_summary/summarize_session.py | 28 +-------- .../session_summary/summary_config.py | 29 ++++++++++ 6 files changed, 103 insertions(+), 29 deletions(-) create mode 100644 posthog/session_recordings/session_summary/__init__.py create mode 100644 posthog/session_recordings/session_summary/summary_config.py diff --git a/posthog/api/team.py b/posthog/api/team.py index 9465f8917fe26..5f06bb3eb9f5f 100644 --- a/posthog/api/team.py +++ b/posthog/api/team.py @@ -146,7 +146,6 @@ class Meta: "session_recording_minimum_duration_milliseconds", "session_recording_linked_flag", "session_recording_network_payload_capture_config", - # TODO validation to enforce expected fields "session_recording_summary_config", "effective_membership_level", "access_control", @@ -210,6 +209,21 @@ def validate_session_recording_network_payload_capture_config(self, value) -> Di return value + def validate_session_recording_summary_config(self, value) -> Dict | None: + if value is None: + return None + + if not isinstance(value, Dict): + raise exceptions.ValidationError("Must provide a dictionary or None.") + + allowed_keys = ["included_event_properties", "opt_in", "preferred_events", "excluded_events"] + if not all(key in allowed_keys for key in value.keys()): + raise exceptions.ValidationError( + "Must provide a dictionary with only allowed keys: {}".format(allowed_keys) + ) + + return value + def validate(self, attrs: Any) -> Any: if "primary_dashboard" in attrs and attrs["primary_dashboard"].team != self.instance: raise exceptions.PermissionDenied("Dashboard does not belong to this team.") diff --git a/posthog/api/test/test_team.py b/posthog/api/test/test_team.py index 6945defe10b3b..b3f3e9fd5cbbb 100644 --- a/posthog/api/test/test_team.py +++ b/posthog/api/test/test_team.py @@ -714,6 +714,63 @@ def test_can_set_and_unset_session_recording_network_payload_capture_config(self second_get_response = self.client.get("/api/projects/@current/") assert second_get_response.json()["session_recording_network_payload_capture_config"] is None + @parameterized.expand( + [ + [ + "string", + "Marple bridge", + "invalid_input", + "Must provide a dictionary or None.", + ], + ["numeric", "-1", "invalid_input", "Must provide a dictionary or None."], + [ + "unexpected json - no recordX", + {"key": "something"}, + "invalid_input", + "Must provide a dictionary with only allowed keys: ['included_event_properties', 'opt_in', 'preferred_events', 'excluded_events']", + ], + ] + ) + def test_invalid_session_recording_summary_config( + self, _name: str, provided_value: str, expected_code: str, expected_error: str + ) -> None: + response = self.client.patch("/api/projects/@current/", {"session_recording_summary_config": provided_value}) + assert response.status_code == status.HTTP_400_BAD_REQUEST + assert response.json() == { + "attr": "session_recording_summary_config", + "code": expected_code, + "detail": expected_error, + "type": "validation_error", + } + + def test_can_set_and_unset_session_recording_summary_config(self) -> None: + # can set just the opt-in + first_patch_response = self.client.patch( + "/api/projects/@current/", + {"session_recording_summary_config": {"opt_in": True}}, + ) + assert first_patch_response.status_code == status.HTTP_200_OK + get_response = self.client.get("/api/projects/@current/") + assert get_response.json()["session_recording_summary_config"] == {"opt_in": True} + + # can set some preferences + first_patch_response = self.client.patch( + "/api/projects/@current/", + {"session_recording_summary_config": {"opt_in": False, "included_event_properties": ["something"]}}, + ) + assert first_patch_response.status_code == status.HTTP_200_OK + get_response = self.client.get("/api/projects/@current/") + assert get_response.json()["session_recording_summary_config"] == { + "opt_in": False, + "included_event_properties": ["something"], + } + + # can unset both + response = self.client.patch("/api/projects/@current/", {"session_recording_summary_config": None}) + assert response.status_code == status.HTTP_200_OK + second_get_response = self.client.get("/api/projects/@current/") + assert second_get_response.json()["session_recording_summary_config"] is None + def create_team(organization: Organization, name: str = "Test team") -> Team: """ diff --git a/posthog/session_recordings/queries/session_replay_events.py b/posthog/session_recordings/queries/session_replay_events.py index 0556ee7523336..6738c2f299d5b 100644 --- a/posthog/session_recordings/queries/session_replay_events.py +++ b/posthog/session_recordings/queries/session_replay_events.py @@ -13,7 +13,7 @@ from posthog.session_recordings.models.metadata import ( RecordingMetadata, ) -from posthog.session_recordings.session_summary.summarize_session import SessionRecordingSummaryConfig +from posthog.session_recordings.session_summary.summary_config import SessionRecordingSummaryConfig class SessionReplayEvents: diff --git a/posthog/session_recordings/session_summary/__init__.py b/posthog/session_recordings/session_summary/__init__.py new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/posthog/session_recordings/session_summary/summarize_session.py b/posthog/session_recordings/session_summary/summarize_session.py index 927e136774f02..8b54bd14c505e 100644 --- a/posthog/session_recordings/session_summary/summarize_session.py +++ b/posthog/session_recordings/session_summary/summarize_session.py @@ -12,6 +12,7 @@ from posthog.session_recordings.models.session_recording import SessionRecording from posthog.session_recordings.queries.session_replay_events import SessionReplayEvents +from posthog.session_recordings.session_summary.summary_config import SessionRecordingSummaryConfig from posthog.utils import get_instance_region @@ -44,33 +45,6 @@ ) -@dataclasses.dataclass(frozen=True) -class SessionRecordingSummaryConfig: - opt_in: bool - preferred_events: List[str] - excluded_events: List[str] - included_event_properties: List[str] - - @staticmethod - def from_config_json(config_json: dict) -> "SessionRecordingSummaryConfig": - raw_included_event_properties = config_json.get( - "included_event_properties", ["elements_chain", "$window_id", "$current_url", "$event_type"] - ) - included_event_properties = ["event", "timestamp"] - for prop in raw_included_event_properties: - if prop in ["elements_chain"]: - included_event_properties.append(prop) - else: - included_event_properties.append(f"properties.{prop}") - - return SessionRecordingSummaryConfig( - opt_in=config_json.get("opt_in", False), - preferred_events=config_json.get("preferred_events", []), - excluded_events=config_json.get("excluded_events", ["$feature_flag_called"]), - included_event_properties=included_event_properties, - ) - - @dataclasses.dataclass class SessionSummaryPromptData: # we may allow customisation of columns included in the future, diff --git a/posthog/session_recordings/session_summary/summary_config.py b/posthog/session_recordings/session_summary/summary_config.py new file mode 100644 index 0000000000000..1443b5fa010de --- /dev/null +++ b/posthog/session_recordings/session_summary/summary_config.py @@ -0,0 +1,29 @@ +import dataclasses +from typing import List + + +@dataclasses.dataclass(frozen=True) +class SessionRecordingSummaryConfig: + opt_in: bool + preferred_events: List[str] + excluded_events: List[str] + included_event_properties: List[str] + + @staticmethod + def from_config_json(config_json: dict) -> "SessionRecordingSummaryConfig": + raw_included_event_properties = config_json.get( + "included_event_properties", ["elements_chain", "$window_id", "$current_url", "$event_type"] + ) + included_event_properties = ["event", "timestamp"] + for prop in raw_included_event_properties: + if prop in ["elements_chain"]: + included_event_properties.append(prop) + else: + included_event_properties.append(f"properties.{prop}") + + return SessionRecordingSummaryConfig( + opt_in=config_json.get("opt_in", False), + preferred_events=config_json.get("preferred_events", []), + excluded_events=config_json.get("excluded_events", ["$feature_flag_called"]), + included_event_properties=included_event_properties, + ) From ef9113d4ca2bbf93307752220f420c8c21f54ab4 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 20 Jan 2024 16:34:21 +0000 Subject: [PATCH 05/24] Update query snapshots --- .../api/test/__snapshots__/test_survey.ambr | 1 + .../__snapshots__/test_dashboard.ambr | 73 +++++++++++++++++++ .../__snapshots__/test_notebook.ambr | 5 ++ 3 files changed, 79 insertions(+) diff --git a/posthog/api/test/__snapshots__/test_survey.ambr b/posthog/api/test/__snapshots__/test_survey.ambr index 2188b1b6aed2e..74d3494fa296f 100644 --- a/posthog/api/test/__snapshots__/test_survey.ambr +++ b/posthog/api/test/__snapshots__/test_survey.ambr @@ -130,6 +130,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", diff --git a/posthog/api/test/dashboards/__snapshots__/test_dashboard.ambr b/posthog/api/test/dashboards/__snapshots__/test_dashboard.ambr index 0ff35889aeecf..db70191b3510b 100644 --- a/posthog/api/test/dashboards/__snapshots__/test_dashboard.ambr +++ b/posthog/api/test/dashboards/__snapshots__/test_dashboard.ambr @@ -51,6 +51,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -179,6 +180,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -300,6 +302,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -514,6 +517,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -685,6 +689,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -864,6 +869,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -1033,6 +1039,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -1273,6 +1280,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -1331,6 +1339,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -1489,6 +1498,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -1600,6 +1610,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -1658,6 +1669,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -1812,6 +1824,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -1939,6 +1952,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -2200,6 +2214,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -2443,6 +2458,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -2572,6 +2588,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -2713,6 +2730,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -2835,6 +2853,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -2935,6 +2954,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -3086,6 +3106,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -3183,6 +3204,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -3311,6 +3333,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -3435,6 +3458,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -3570,6 +3594,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -3893,6 +3918,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -4054,6 +4080,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -4189,6 +4216,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -4275,6 +4303,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -4437,6 +4466,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -4495,6 +4525,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -4619,6 +4650,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -4770,6 +4802,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -5198,6 +5231,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -5340,6 +5374,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -5426,6 +5461,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -5550,6 +5586,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -5635,6 +5672,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -5693,6 +5731,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -5817,6 +5856,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -5958,6 +5998,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -6121,6 +6162,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -6535,6 +6577,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -6687,6 +6730,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -6871,6 +6915,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -7042,6 +7087,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -7182,6 +7228,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -7272,6 +7319,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -7441,6 +7489,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -8082,6 +8131,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -8344,6 +8394,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -8506,6 +8557,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -8564,6 +8616,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -8688,6 +8741,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -8839,6 +8893,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -8963,6 +9018,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -9099,6 +9155,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -9240,6 +9297,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -9550,6 +9608,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -9707,6 +9766,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -9812,6 +9872,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -9944,6 +10005,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -10075,6 +10137,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -10207,6 +10270,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -10391,6 +10455,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -10555,6 +10620,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -10659,6 +10725,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -10826,6 +10893,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -11005,6 +11073,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -11116,6 +11185,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -11283,6 +11353,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -11423,6 +11494,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -11636,6 +11708,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", diff --git a/posthog/api/test/notebooks/__snapshots__/test_notebook.ambr b/posthog/api/test/notebooks/__snapshots__/test_notebook.ambr index 550b197d0f5ca..d6fe47b9aae4b 100644 --- a/posthog/api/test/notebooks/__snapshots__/test_notebook.ambr +++ b/posthog/api/test/notebooks/__snapshots__/test_notebook.ambr @@ -51,6 +51,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -205,6 +206,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -423,6 +425,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -527,6 +530,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -639,6 +643,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", From d41d488c6574b20cf2cf8a4be0478957c00112ff Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 20 Jan 2024 16:35:36 +0000 Subject: [PATCH 06/24] Update query snapshots --- posthog/api/test/__snapshots__/test_action.ambr | 3 +++ posthog/api/test/__snapshots__/test_annotation.ambr | 3 +++ posthog/api/test/__snapshots__/test_decide.ambr | 4 ++++ .../__snapshots__/test_early_access_feature.ambr | 2 ++ posthog/api/test/__snapshots__/test_element.ambr | 1 + .../api/test/__snapshots__/test_feature_flag.ambr | 8 ++++++++ posthog/api/test/__snapshots__/test_insight.ambr | 11 +++++++++++ .../test_organization_feature_flag.ambr | 12 ++++++++++++ posthog/api/test/__snapshots__/test_preflight.ambr | 1 + 9 files changed, 45 insertions(+) diff --git a/posthog/api/test/__snapshots__/test_action.ambr b/posthog/api/test/__snapshots__/test_action.ambr index a947ae0d7e6fb..b8814fd4d009d 100644 --- a/posthog/api/test/__snapshots__/test_action.ambr +++ b/posthog/api/test/__snapshots__/test_action.ambr @@ -51,6 +51,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -211,6 +212,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -544,6 +546,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", diff --git a/posthog/api/test/__snapshots__/test_annotation.ambr b/posthog/api/test/__snapshots__/test_annotation.ambr index 2b761c79a0e3b..2a2adc3c484c0 100644 --- a/posthog/api/test/__snapshots__/test_annotation.ambr +++ b/posthog/api/test/__snapshots__/test_annotation.ambr @@ -51,6 +51,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -133,6 +134,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -464,6 +466,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", diff --git a/posthog/api/test/__snapshots__/test_decide.ambr b/posthog/api/test/__snapshots__/test_decide.ambr index 0082db5b23262..5a375e23adcfd 100644 --- a/posthog/api/test/__snapshots__/test_decide.ambr +++ b/posthog/api/test/__snapshots__/test_decide.ambr @@ -51,6 +51,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -297,6 +298,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -457,6 +459,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -610,6 +613,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", diff --git a/posthog/api/test/__snapshots__/test_early_access_feature.ambr b/posthog/api/test/__snapshots__/test_early_access_feature.ambr index 4949adab33e99..39fd040885540 100644 --- a/posthog/api/test/__snapshots__/test_early_access_feature.ambr +++ b/posthog/api/test/__snapshots__/test_early_access_feature.ambr @@ -20,6 +20,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -155,6 +156,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", diff --git a/posthog/api/test/__snapshots__/test_element.ambr b/posthog/api/test/__snapshots__/test_element.ambr index b206ea30589d8..cb4421da6973e 100644 --- a/posthog/api/test/__snapshots__/test_element.ambr +++ b/posthog/api/test/__snapshots__/test_element.ambr @@ -51,6 +51,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", diff --git a/posthog/api/test/__snapshots__/test_feature_flag.ambr b/posthog/api/test/__snapshots__/test_feature_flag.ambr index 68a540a818172..854ae594661a5 100644 --- a/posthog/api/test/__snapshots__/test_feature_flag.ambr +++ b/posthog/api/test/__snapshots__/test_feature_flag.ambr @@ -459,6 +459,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -658,6 +659,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -1017,6 +1019,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -1153,6 +1156,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -1444,6 +1448,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -1533,6 +1538,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -1641,6 +1647,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -1842,6 +1849,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", diff --git a/posthog/api/test/__snapshots__/test_insight.ambr b/posthog/api/test/__snapshots__/test_insight.ambr index 0dbd61eb66823..0bec52cdde531 100644 --- a/posthog/api/test/__snapshots__/test_insight.ambr +++ b/posthog/api/test/__snapshots__/test_insight.ambr @@ -641,6 +641,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -692,6 +693,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -819,6 +821,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -1052,6 +1055,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -1195,6 +1199,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -1327,6 +1332,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -1436,6 +1442,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -1580,6 +1587,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -1666,6 +1674,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -1751,6 +1760,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -1809,6 +1819,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", diff --git a/posthog/api/test/__snapshots__/test_organization_feature_flag.ambr b/posthog/api/test/__snapshots__/test_organization_feature_flag.ambr index 381f3164ba613..f997a16eff190 100644 --- a/posthog/api/test/__snapshots__/test_organization_feature_flag.ambr +++ b/posthog/api/test/__snapshots__/test_organization_feature_flag.ambr @@ -98,6 +98,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -202,6 +203,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -288,6 +290,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -498,6 +501,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -625,6 +629,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -772,6 +777,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -858,6 +864,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -1072,6 +1079,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -1199,6 +1207,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -1250,6 +1259,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -1397,6 +1407,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -1668,6 +1679,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", diff --git a/posthog/api/test/__snapshots__/test_preflight.ambr b/posthog/api/test/__snapshots__/test_preflight.ambr index 7b51cd4a56964..53ba6e7922f0a 100644 --- a/posthog/api/test/__snapshots__/test_preflight.ambr +++ b/posthog/api/test/__snapshots__/test_preflight.ambr @@ -62,6 +62,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", From c21873459c4f40324484da8712fbca90dbc9bc99 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 20 Jan 2024 16:39:04 +0000 Subject: [PATCH 07/24] Update query snapshots --- posthog/models/filters/test/__snapshots__/test_filter.ambr | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/posthog/models/filters/test/__snapshots__/test_filter.ambr b/posthog/models/filters/test/__snapshots__/test_filter.ambr index 203c587fc0389..5c1af4a1ba8aa 100644 --- a/posthog/models/filters/test/__snapshots__/test_filter.ambr +++ b/posthog/models/filters/test/__snapshots__/test_filter.ambr @@ -20,6 +20,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -78,6 +79,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -136,6 +138,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -194,6 +197,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -252,6 +256,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", From c6f064c1e525fed3acb6a10376d79e8007a6cb46 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 20 Jan 2024 16:39:50 +0000 Subject: [PATCH 08/24] Update query snapshots --- posthog/test/__snapshots__/test_feature_flag.ambr | 2 ++ 1 file changed, 2 insertions(+) diff --git a/posthog/test/__snapshots__/test_feature_flag.ambr b/posthog/test/__snapshots__/test_feature_flag.ambr index 76ad9fae52868..f910cddf982c2 100644 --- a/posthog/test/__snapshots__/test_feature_flag.ambr +++ b/posthog/test/__snapshots__/test_feature_flag.ambr @@ -111,6 +111,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -498,6 +499,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", From e81a78a0844e0ca642357baa0c68bc80b388aa54 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 20 Jan 2024 16:41:55 +0000 Subject: [PATCH 09/24] Update query snapshots --- .../test_session_recordings.ambr | 35 +++++++++++++++++++ .../test_process_scheduled_changes.ambr | 2 ++ 2 files changed, 37 insertions(+) diff --git a/posthog/session_recordings/test/__snapshots__/test_session_recordings.ambr b/posthog/session_recordings/test/__snapshots__/test_session_recordings.ambr index 3ab7e2a06682a..4870c699725a9 100644 --- a/posthog/session_recordings/test/__snapshots__/test_session_recordings.ambr +++ b/posthog/session_recordings/test/__snapshots__/test_session_recordings.ambr @@ -20,6 +20,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -78,6 +79,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -136,6 +138,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -194,6 +197,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -252,6 +256,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -341,6 +346,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -466,6 +472,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -661,6 +668,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -719,6 +727,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -777,6 +786,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -835,6 +845,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -893,6 +904,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -951,6 +963,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -1009,6 +1022,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -1098,6 +1112,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -1160,6 +1175,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -1249,6 +1265,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -1519,6 +1536,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -1608,6 +1626,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -1891,6 +1910,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -1980,6 +2000,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -2221,6 +2242,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -2309,6 +2331,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -2398,6 +2421,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -2694,6 +2718,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -2783,6 +2808,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -2834,6 +2860,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -3340,6 +3367,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -3429,6 +3457,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -3691,6 +3720,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -3791,6 +3821,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -4055,6 +4086,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -4144,6 +4176,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -4421,6 +4454,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -4510,6 +4544,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", diff --git a/posthog/tasks/test/__snapshots__/test_process_scheduled_changes.ambr b/posthog/tasks/test/__snapshots__/test_process_scheduled_changes.ambr index 0e3728f3faad9..af1263c4cd66c 100644 --- a/posthog/tasks/test/__snapshots__/test_process_scheduled_changes.ambr +++ b/posthog/tasks/test/__snapshots__/test_process_scheduled_changes.ambr @@ -89,6 +89,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", @@ -342,6 +343,7 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."capture_console_log_opt_in", From 8357bda52e6982d9b996dce54ff9eb84a3b93ccb Mon Sep 17 00:00:00 2001 From: Paul D'Ambra Date: Sat, 20 Jan 2024 16:53:40 +0000 Subject: [PATCH 10/24] fiddling --- .../scenes/settings/project/SessionRecordingSettings.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/frontend/src/scenes/settings/project/SessionRecordingSettings.tsx b/frontend/src/scenes/settings/project/SessionRecordingSettings.tsx index e165f7e98ad6b..5c486f8ba1344 100644 --- a/frontend/src/scenes/settings/project/SessionRecordingSettings.tsx +++ b/frontend/src/scenes/settings/project/SessionRecordingSettings.tsx @@ -213,7 +213,7 @@ export function ReplaySummarySettings(): JSX.Element | null {

We use Open AI to summarise sessions. No data is sent to OpenAI without an explicit instruction to do so. Only by clicking the "Summary" button will selected event data be shared - with a third party. We only send the data selected below. + with a third party. We only send the data selected below.{' '} Data submitted is not used to train Open AI's models

Included event properties -

Only these properties are sent for summary.

+

+ We always send the event name and timestamp. The only other data sent are values of the properties + selected here. +

Date: Sun, 21 Jan 2024 16:49:26 +0000 Subject: [PATCH 11/24] update snapshot --- .../__snapshots__/verifiedDomainsLogic.test.ts.snap | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/scenes/settings/organization/VerifiedDomains/__snapshots__/verifiedDomainsLogic.test.ts.snap b/frontend/src/scenes/settings/organization/VerifiedDomains/__snapshots__/verifiedDomainsLogic.test.ts.snap index 70316d0884bbe..523ae9b135312 100644 --- a/frontend/src/scenes/settings/organization/VerifiedDomains/__snapshots__/verifiedDomainsLogic.test.ts.snap +++ b/frontend/src/scenes/settings/organization/VerifiedDomains/__snapshots__/verifiedDomainsLogic.test.ts.snap @@ -79,6 +79,7 @@ exports[`verifiedDomainsLogic values has proper defaults 1`] = ` "session_recording_network_payload_capture_config": null, "session_recording_opt_in": true, "session_recording_sample_rate": "1.0", + "session_recording_summary_config": null, "slack_incoming_webhook": "", "test_account_filters": [ { From 752a98aa9f624dd0c6ca15b4dbeef14c4d84cba6 Mon Sep 17 00:00:00 2001 From: David Newell Date: Thu, 25 Jan 2024 16:42:48 +0000 Subject: [PATCH 12/24] fix tests --- posthog/api/team.py | 19 ++++++++++++++----- .../test_organization_feature_flag.ambr | 12 ------------ posthog/api/test/test_team.py | 16 ++++++++-------- 3 files changed, 22 insertions(+), 25 deletions(-) diff --git a/posthog/api/team.py b/posthog/api/team.py index 8e14914659a81..822d64935dc7d 100644 --- a/posthog/api/team.py +++ b/posthog/api/team.py @@ -42,6 +42,7 @@ from posthog.utils import get_ip_address, get_week_start_for_country_code + class PremiumMultiprojectPermissions(permissions.BasePermission): """Require user to have all necessary premium features on their plan for create access to the endpoint.""" @@ -222,14 +223,22 @@ def validate_session_replay_config(self, value) -> Dict | None: "Must provide a dictionary with only 'record_canvas' and 'ai_summary' keys." ) - allowed_keys = ["included_event_properties", "opt_in", "preferred_events", "excluded_events"] - if "ai_summary" in value and not all(key in allowed_keys for key in value["ai_summary"].keys()): - raise exceptions.ValidationError( - "Must provide a dictionary with only allowed keys: {}".format(allowed_keys) - ) + if "ai_summary" in value: + self.validate_session_replay_ai_summary_config(value["ai_summary"]) return value + def validate_session_replay_ai_summary_config(self, value) -> Dict | None: + if value is not None: + if not isinstance(value, Dict): + raise exceptions.ValidationError("Must provide a dictionary or None.") + + allowed_keys = ["included_event_properties", "opt_in", "preferred_events", "excluded_events"] + if not all(key in allowed_keys for key in value.keys()): + raise exceptions.ValidationError( + "Must provide a dictionary with only allowed keys: {}".format(allowed_keys) + ) + def validate(self, attrs: Any) -> Any: if "primary_dashboard" in attrs and attrs["primary_dashboard"].team != self.instance: raise exceptions.PermissionDenied("Dashboard does not belong to this team.") diff --git a/posthog/api/test/__snapshots__/test_organization_feature_flag.ambr b/posthog/api/test/__snapshots__/test_organization_feature_flag.ambr index 97193d6b78f5f..470c4a160a591 100644 --- a/posthog/api/test/__snapshots__/test_organization_feature_flag.ambr +++ b/posthog/api/test/__snapshots__/test_organization_feature_flag.ambr @@ -98,7 +98,6 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", - "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."session_replay_config", @@ -204,7 +203,6 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", - "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."session_replay_config", @@ -292,7 +290,6 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", - "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."session_replay_config", @@ -504,7 +501,6 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", - "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."session_replay_config", @@ -633,7 +629,6 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", - "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."session_replay_config", @@ -782,7 +777,6 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", - "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."session_replay_config", @@ -870,7 +864,6 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", - "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."session_replay_config", @@ -1086,7 +1079,6 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", - "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."session_replay_config", @@ -1215,7 +1207,6 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", - "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."session_replay_config", @@ -1268,7 +1259,6 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", - "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."session_replay_config", @@ -1417,7 +1407,6 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", - "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."session_replay_config", @@ -1690,7 +1679,6 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", - "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."session_replay_config", diff --git a/posthog/api/test/test_team.py b/posthog/api/test/test_team.py index 9e6476d9e3255..f905a0e971a8b 100644 --- a/posthog/api/test/test_team.py +++ b/posthog/api/test/test_team.py @@ -751,11 +751,11 @@ def test_invalid_session_replay_config_ai_summary( self, _name: str, provided_value: str, expected_code: str, expected_error: str ) -> None: response = self.client.patch( - "/api/projects/@current/", {"session_recording_config": {"ai_summary": provided_value}} + "/api/projects/@current/", {"session_replay_config": {"ai_summary": provided_value}} ) assert response.status_code == status.HTTP_400_BAD_REQUEST assert response.json() == { - "attr": "session_recording_config", + "attr": "session_replay_config", "code": expected_code, "detail": expected_error, "type": "validation_error", @@ -765,29 +765,29 @@ def test_can_set_and_unset_session_replay_config_ai_summary(self) -> None: # can set just the opt-in first_patch_response = self.client.patch( "/api/projects/@current/", - {"session_recording_config": {"ai_summary": {"opt_in": True}}}, + {"session_replay_config": {"ai_summary": {"opt_in": True}}}, ) assert first_patch_response.status_code == status.HTTP_200_OK get_response = self.client.get("/api/projects/@current/") - assert get_response.json()["session_recording_config"]["ai_summary"] == {"opt_in": True} + assert get_response.json()["session_replay_config"]["ai_summary"] == {"opt_in": True} # can set some preferences first_patch_response = self.client.patch( "/api/projects/@current/", - {"session_recording_config": {"ai_summary": {"opt_in": False, "included_event_properties": ["something"]}}}, + {"session_replay_config": {"ai_summary": {"opt_in": False, "included_event_properties": ["something"]}}}, ) assert first_patch_response.status_code == status.HTTP_200_OK get_response = self.client.get("/api/projects/@current/") - assert get_response.json()["session_recording_config"]["ai_summary"] == { + assert get_response.json()["session_replay_config"]["ai_summary"] == { "opt_in": False, "included_event_properties": ["something"], } # can unset both - response = self.client.patch("/api/projects/@current/", {"session_recording_config": {"ai_summary": None}}) + response = self.client.patch("/api/projects/@current/", {"session_replay_config": {"ai_summary": None}}) assert response.status_code == status.HTTP_200_OK second_get_response = self.client.get("/api/projects/@current/") - assert second_get_response.json()["session_recording_config"]["ai_summary"] is None + assert second_get_response.json()["session_replay_config"]["ai_summary"] is None def create_team(organization: Organization, name: str = "Test team") -> Team: From 685979ab681d3ba2c7967d75e0d106cb7c012b8c Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 25 Jan 2024 16:57:21 +0000 Subject: [PATCH 13/24] Update query snapshots --- .../test_session_recordings.ambr | 31 ------------------- 1 file changed, 31 deletions(-) diff --git a/posthog/session_recordings/test/__snapshots__/test_session_recordings.ambr b/posthog/session_recordings/test/__snapshots__/test_session_recordings.ambr index 9e6e81e7d17c8..affde7090e435 100644 --- a/posthog/session_recordings/test/__snapshots__/test_session_recordings.ambr +++ b/posthog/session_recordings/test/__snapshots__/test_session_recordings.ambr @@ -20,7 +20,6 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", - "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."session_replay_config", @@ -257,7 +256,6 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", - "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."session_replay_config", @@ -348,7 +346,6 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", - "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."session_replay_config", @@ -475,7 +472,6 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", - "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."session_replay_config", @@ -672,7 +668,6 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", - "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."session_replay_config", @@ -732,7 +727,6 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", - "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."session_replay_config", @@ -792,7 +786,6 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", - "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."session_replay_config", @@ -852,7 +845,6 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", - "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."session_replay_config", @@ -912,7 +904,6 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", - "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."session_replay_config", @@ -972,7 +963,6 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", - "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."session_replay_config", @@ -1032,7 +1022,6 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", - "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."session_replay_config", @@ -1123,7 +1112,6 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", - "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."session_replay_config", @@ -1187,7 +1175,6 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", - "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."session_replay_config", @@ -1278,7 +1265,6 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", - "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."session_replay_config", @@ -1550,7 +1536,6 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", - "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."session_replay_config", @@ -1641,7 +1626,6 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", - "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."session_replay_config", @@ -1926,7 +1910,6 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", - "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."session_replay_config", @@ -2017,7 +2000,6 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", - "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."session_replay_config", @@ -2260,7 +2242,6 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", - "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."session_replay_config", @@ -2350,7 +2331,6 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", - "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."session_replay_config", @@ -2441,7 +2421,6 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", - "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."session_replay_config", @@ -2739,7 +2718,6 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", - "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."session_replay_config", @@ -2830,7 +2808,6 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", - "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."session_replay_config", @@ -2883,7 +2860,6 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", - "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."session_replay_config", @@ -3391,7 +3367,6 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", - "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."session_replay_config", @@ -3482,7 +3457,6 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", - "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."session_replay_config", @@ -3746,7 +3720,6 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", - "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."session_replay_config", @@ -3848,7 +3821,6 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", - "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."session_replay_config", @@ -4114,7 +4086,6 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", - "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."session_replay_config", @@ -4205,7 +4176,6 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", - "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."session_replay_config", @@ -4484,7 +4454,6 @@ "posthog_team"."session_recording_opt_in", "posthog_team"."session_recording_sample_rate", "posthog_team"."session_recording_minimum_duration_milliseconds", - "posthog_team"."session_recording_summary_config", "posthog_team"."session_recording_linked_flag", "posthog_team"."session_recording_network_payload_capture_config", "posthog_team"."session_replay_config", From ec1bc24cd465834667cea24c2f18c859f0b305d4 Mon Sep 17 00:00:00 2001 From: David Newell Date: Thu, 25 Jan 2024 17:19:05 +0000 Subject: [PATCH 14/24] start adding important user properties --- .../project/SessionRecordingSettings.tsx | 164 +++++++++++------- frontend/src/types.ts | 1 + 2 files changed, 98 insertions(+), 67 deletions(-) diff --git a/frontend/src/scenes/settings/project/SessionRecordingSettings.tsx b/frontend/src/scenes/settings/project/SessionRecordingSettings.tsx index 1b3c116e6035f..dd34fed9e5bd7 100644 --- a/frontend/src/scenes/settings/project/SessionRecordingSettings.tsx +++ b/frontend/src/scenes/settings/project/SessionRecordingSettings.tsx @@ -5,6 +5,7 @@ import { AuthorizedUrlListType } from 'lib/components/AuthorizedUrlList/authoriz import { EventSelect } from 'lib/components/EventSelect/EventSelect' import { FlaggedFeature } from 'lib/components/FlaggedFeature' import { FlagSelector } from 'lib/components/FlagSelector' +import { PersonPropertySelect } from 'lib/components/PersonPropertySelect/PersonPropertySelect' import { FEATURE_FLAGS, SESSION_REPLAY_MINIMUM_DURATION_OPTIONS } from 'lib/constants' import { useFeatureFlag } from 'lib/hooks/useFeatureFlag' import { IconAutoAwesome, IconCancel, IconPlus, IconSelectEvents } from 'lib/lemon-ui/icons' @@ -229,6 +230,7 @@ export function ReplaySummarySettings(): JSX.Element | null { preferred_events: [], excluded_events: ['$feature_flag_called'], included_event_properties: ['elements_chain', '$window_id', '$current_url', '$event_type'], + important_user_properties: [], } const sessionReplayConfig = currentTeam.session_replay_config || {} const currentConfig: SessionRecordingSummaryConfig = sessionReplayConfig.ai_summary || defaultConfig @@ -261,76 +263,104 @@ export function ReplaySummarySettings(): JSX.Element | null { opt_in: checked, }) }} + bordered label="Opt in to enable AI suggested summaries" />
-
-

- - Preferred events -

-

- These events are treated as more interesting when generating a summary. We recommend you include - events that represent value for your user -

- { - updateSummaryConfig({ - ...currentConfig, - preferred_events: includedEvents, - }) - }} - selectedEvents={currentConfig.preferred_events || []} - addElement={ - } sideIcon={null}> - Add event - - } - /> -
-
-

- - Excluded events -

-

These events are never submitted even when they are present in the session.

- { - updateSummaryConfig({ - ...currentConfig, - excluded_events: excludedEvents, - }) - }} - selectedEvents={currentConfig.excluded_events || []} - addElement={ - } sideIcon={null}> - Exclude event - - } - /> -
-
-

- - Included event properties -

-

- We always send the event name and timestamp. The only other data sent are values of the properties - selected here. -

-
- { - updateSummaryConfig({ - ...currentConfig, - included_event_properties: properties, - }) - }} - value={currentConfig.included_event_properties || []} - /> -
-
+ {currentConfig.opt_in && ( + <> +
+

+ + Preferred events +

+

+ These events are treated as more interesting when generating a summary. We recommend you + include events that represent value for your user +

+ { + updateSummaryConfig({ + ...currentConfig, + preferred_events: includedEvents, + }) + }} + selectedEvents={currentConfig.preferred_events || []} + addElement={ + } sideIcon={null}> + Add event + + } + /> +
+
+

+ + Excluded events +

+

These events are never submitted even when they are present in the session.

+ { + updateSummaryConfig({ + ...currentConfig, + excluded_events: excludedEvents, + }) + }} + selectedEvents={currentConfig.excluded_events || []} + addElement={ + } sideIcon={null}> + Exclude event + + } + /> +
+
+

+ + Included event properties +

+

+ We always send the event name and timestamp. The only event data sent are values of the + properties selected here. +

+
+ { + updateSummaryConfig({ + ...currentConfig, + included_event_properties: properties, + }) + }} + value={currentConfig.included_event_properties || []} + /> +
+
+
+

+ + Important user properties +

+

+ We always send the first and last seen dates. The only user data sent are values of the + properties selected here. +

+
+ { + updateSummaryConfig({ + ...currentConfig, + important_user_properties: properties, + }) + }} + selectedProperties={currentConfig.important_user_properties || []} + addText="Add property" + /> +
+
+ + )} ) } diff --git a/frontend/src/types.ts b/frontend/src/types.ts index 50c4433579dce..2b526b1c74393 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -346,6 +346,7 @@ export interface SessionRecordingSummaryConfig { preferred_events: string[] excluded_events: string[] included_event_properties: string[] + important_user_properties: string[] } export interface TeamType extends TeamBasicType { From 2dba07454b2f67311c5a3887c7e6266dd7936454 Mon Sep 17 00:00:00 2001 From: Michael Matloka Date: Thu, 25 Jan 2024 16:48:08 +0100 Subject: [PATCH 15/24] feat: Add `LemonSlider` and use it in flag rollout conditions (#19958) * Add `LemonSlider` * Replace Ant `Slider` with `LemonSlider` * Use `LemonSlider` in feature flags * Fix slider sizing * Update UI snapshots for `chromium` (1) * Fix touch and add ring * Reduce transition duration slightly * Restore utilities.scss * Update UI snapshots for `chromium` (1) * Remove leftover comment --------- Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com> --- .eslintrc.js | 4 + .../lemon-ui-lemon-slider--basic--dark.png | Bin 0 -> 614 bytes .../lemon-ui-lemon-slider--basic--light.png | Bin 0 -> 623 bytes frontend/src/lib/hooks/useEventListener.ts | 18 ++- .../lib/lemon-ui/LemonInput/LemonInput.scss | 6 + .../LemonSlider/LemonSlider.stories.tsx | 17 +++ .../lib/lemon-ui/LemonSlider/LemonSlider.tsx | 104 ++++++++++++++++++ .../src/lib/lemon-ui/LemonSlider/index.ts | 1 + .../src/scenes/experiments/Experiment.scss | 14 --- .../scenes/experiments/ExperimentPreview.tsx | 38 +++---- .../FeatureFlagReleaseConditions.tsx | 15 ++- frontend/src/styles/global.scss | 13 +++ 12 files changed, 191 insertions(+), 39 deletions(-) create mode 100644 frontend/__snapshots__/lemon-ui-lemon-slider--basic--dark.png create mode 100644 frontend/__snapshots__/lemon-ui-lemon-slider--basic--light.png create mode 100644 frontend/src/lib/lemon-ui/LemonSlider/LemonSlider.stories.tsx create mode 100644 frontend/src/lib/lemon-ui/LemonSlider/LemonSlider.tsx create mode 100644 frontend/src/lib/lemon-ui/LemonSlider/index.ts diff --git a/.eslintrc.js b/.eslintrc.js index 2441c77348e49..6ea14dea0d739 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -204,6 +204,10 @@ module.exports = { element: 'Collapse', message: 'use instead', }, + { + element: 'Slider', + message: 'use instead', + }, { element: 'Checkbox', message: 'use instead', diff --git a/frontend/__snapshots__/lemon-ui-lemon-slider--basic--dark.png b/frontend/__snapshots__/lemon-ui-lemon-slider--basic--dark.png new file mode 100644 index 0000000000000000000000000000000000000000..51342d621ca39f677311147bf9ba7074c2f42013 GIT binary patch literal 614 zcmV-s0-61ZP)Px%AW1|)RA_G2q}1@VhBSel1fO4zywp5(6eD8J zh{6o8EDK8~JFMH9(`(OowEGi)kCO`hQJsP3qHRqa#l&%3l$5ooBBF5m=Wja^M+awg z8jF?VhT|%S$9sGWG*K90Vj&dL1k+`|?&ASEjRi*sXNeH4i;%LB+zn7xvcJ2n`Pz?9 z=`W*}d~Cx5B4iDgeudmxXj}7)H72*u*W{rdF$6uOOt@eiyV}zuV!{c{R6! z&3jizT~^YuD>FkuT!bOM?_-~M4kzCI+zKm8p*8eJ^-N6=lCc0m5HOicbJLQA+_!+l z0wN4Nm*IHx${LQV3_Lf9#|ENtAw3|9l4f>zytmebGzk4UXWJB#1mW)}{QU8ZUVBb+ zC+QET(+d5O%i9mh(*N(G@Upl9t@YI{@LxoTqR2=DPARnQiX>-ydL33=-U7KH*Wnb2 z1u<~OXW?1}Arq7am=<7~(g4!}Oj8Px%DM>^@RA_hJ{B>l1}rNU2DY+q$Gw(yAj0 zFW&s^HOcuA*<}3JaiY^DK79k*JEXHaFE_z?5fnl$u6@j>0^DfqC@Q z*@D0R>Q)v}j!n}$XF8T4rK0^{2NfI~!@)6`qlHZv69Ve+gxLy3ka8@5RFd7@$BvY& zhVxIr7!7%j>h%$w?s5U0E~?juJTK<18RrIMnI+W;0P*Rt(u5)i4FO1X;-xRBB?-cB zQFt^cqVVSr0Ej2giYQ!OePi`zMd3L?5D^72wEj`u^PE&Co7oHjXq{rML8-DGE~Bo^ z%68ancsmS5O_ void +export type TouchEventHandler = (event: TouchEvent) => void +export type MouseEventHandler = (event: MouseEvent) => void export type EventHandler = (event: Event) => void export function useEventListener( - eventName: 'keyup' | 'keydown', + eventName: `key${string}`, handler: KeyboardEventHandler, element?: Element | Window | null, deps?: DependencyList ): void +export function useEventListener( + eventName: `touch${string}`, + handler: TouchEventHandler, + element?: Element | Window | null, + deps?: DependencyList +): void +export function useEventListener( + eventName: `mouse${string}`, + handler: MouseEventHandler, + element?: Element | Window | null, + deps?: DependencyList +): void export function useEventListener( eventName: string, handler: EventHandler, @@ -17,7 +31,7 @@ export function useEventListener( ): void export function useEventListener( eventName: string, - handler: EventHandler | KeyboardEventHandler, + handler: KeyboardEventHandler | TouchEventHandler | MouseEventHandler | EventHandler, element: Element | Window | null = window, deps?: DependencyList ): void { diff --git a/frontend/src/lib/lemon-ui/LemonInput/LemonInput.scss b/frontend/src/lib/lemon-ui/LemonInput/LemonInput.scss index 21d86a69c33b3..6018b10f41bb7 100644 --- a/frontend/src/lib/lemon-ui/LemonInput/LemonInput.scss +++ b/frontend/src/lib/lemon-ui/LemonInput/LemonInput.scss @@ -87,6 +87,12 @@ max-width: 240px; } + &.LemonInput--type-number { + .LemonInput__input { + text-overflow: clip; + } + } + &.LemonInput--full-width { width: 100%; max-width: 100%; diff --git a/frontend/src/lib/lemon-ui/LemonSlider/LemonSlider.stories.tsx b/frontend/src/lib/lemon-ui/LemonSlider/LemonSlider.stories.tsx new file mode 100644 index 0000000000000..7b293618d7ff3 --- /dev/null +++ b/frontend/src/lib/lemon-ui/LemonSlider/LemonSlider.stories.tsx @@ -0,0 +1,17 @@ +import { Meta } from '@storybook/react' +import { useState } from 'react' + +import { LemonSlider } from './LemonSlider' + +const meta: Meta = { + title: 'Lemon UI/Lemon Slider', + component: LemonSlider, + tags: ['autodocs'], +} +export default meta + +export function Basic(): JSX.Element { + const [value, setValue] = useState(42) + + return +} diff --git a/frontend/src/lib/lemon-ui/LemonSlider/LemonSlider.tsx b/frontend/src/lib/lemon-ui/LemonSlider/LemonSlider.tsx new file mode 100644 index 0000000000000..fe72960e0453d --- /dev/null +++ b/frontend/src/lib/lemon-ui/LemonSlider/LemonSlider.tsx @@ -0,0 +1,104 @@ +import clsx from 'clsx' +import { useEventListener } from 'lib/hooks/useEventListener' +import { useRef, useState } from 'react' + +export interface LemonSliderProps { + value?: number + onChange?: (value: number) => void + min: number + max: number + /** @default 1 */ + step?: number + className?: string +} + +export function LemonSlider({ value = 0, onChange, min, max, step = 1, className }: LemonSliderProps): JSX.Element { + const trackRef = useRef(null) + const movementStartValueWithX = useRef<[number, number] | null>(null) + const [dragging, setDragging] = useState(false) + + const handleMove = (clientX: number): void => { + if (trackRef.current && movementStartValueWithX.current !== null) { + const [movementStartValue, movementStartX] = movementStartValueWithX.current + const rect = trackRef.current.getBoundingClientRect() + const adjustedWidth = rect.width - 16 // 16px = handle width + const deltaX = (clientX - movementStartX) / adjustedWidth + let newValue = movementStartValue + (max - min) * deltaX + newValue = Math.max(min, Math.min(max, newValue)) // Clamped + if (step !== undefined) { + newValue = Math.round(newValue / step) * step // Adjusted to step + } + onChange?.(newValue) + } + } + useEventListener('mousemove', (e) => { + handleMove(e.clientX) + }) + useEventListener('touchmove', (e) => { + if (e.touches.length === 1) { + handleMove(e.touches[0].clientX) + } + }) + + useEventListener('mouseup', (e) => { + if (e.button === 0) { + movementStartValueWithX.current = null + setDragging(false) + } + }) + useEventListener('touchend', () => { + movementStartValueWithX.current = null + setDragging(false) + }) + useEventListener('touchcancel', () => { + movementStartValueWithX.current = null + setDragging(false) + }) + + const proportion = Math.round(((value - min) / (max - min)) * 100) / 100 + + return ( +
+
{ + const rect = e.currentTarget.getBoundingClientRect() + const x = e.clientX - (rect.left + 8) // 4px = half the handle + const adjustedWidth = rect.width - 16 // 8px = handle width + let newValue = (x / adjustedWidth) * (max - min) + min + newValue = Math.max(min, Math.min(max, newValue)) // Clamped + if (step !== undefined) { + newValue = Math.round(newValue / step) * step // Adjusted to step + } + onChange?.(newValue) + }} + > +
+
+
+
{ + movementStartValueWithX.current = [value, e.clientX] + setDragging(true) + }} + onTouchStart={(e) => { + movementStartValueWithX.current = [value, e.touches[0].clientX] + setDragging(true) + }} + /> +
+ ) +} diff --git a/frontend/src/lib/lemon-ui/LemonSlider/index.ts b/frontend/src/lib/lemon-ui/LemonSlider/index.ts new file mode 100644 index 0000000000000..3d860c8215357 --- /dev/null +++ b/frontend/src/lib/lemon-ui/LemonSlider/index.ts @@ -0,0 +1 @@ +export { LemonSlider, type LemonSliderProps } from './LemonSlider' diff --git a/frontend/src/scenes/experiments/Experiment.scss b/frontend/src/scenes/experiments/Experiment.scss index 633259b80037c..53f59ba971b26 100644 --- a/frontend/src/scenes/experiments/Experiment.scss +++ b/frontend/src/scenes/experiments/Experiment.scss @@ -16,20 +16,6 @@ .experiment-preview { margin-bottom: 1rem; border-bottom: 1px solid var(--border); - - .mde-slider { - .ant-slider-handle { - border: none; - } - - .ant-slider-rail { - background-color: var(--primary-3000-highlight); - } - - .ant-slider-handle:focus { - box-shadow: 0 0 0 5px var(--primary-3000-highlight); - } - } } .variants { diff --git a/frontend/src/scenes/experiments/ExperimentPreview.tsx b/frontend/src/scenes/experiments/ExperimentPreview.tsx index 56275dd7fb31d..2066d8d73709c 100644 --- a/frontend/src/scenes/experiments/ExperimentPreview.tsx +++ b/frontend/src/scenes/experiments/ExperimentPreview.tsx @@ -1,5 +1,4 @@ import { LemonButton, LemonDivider, LemonInput, LemonModal, Tooltip } from '@posthog/lemon-ui' -import { Slider } from 'antd' import { useActions, useValues } from 'kea' import { Field, Form } from 'kea-forms' import { InsightLabel } from 'lib/components/InsightLabel' @@ -7,6 +6,7 @@ import { PropertyFilterButton } from 'lib/components/PropertyFilters/components/ import { TZLabel } from 'lib/components/TZLabel' import { dayjs } from 'lib/dayjs' import { IconInfo } from 'lib/lemon-ui/icons' +import { LemonSlider } from 'lib/lemon-ui/LemonSlider' import { humanFriendlyNumber } from 'lib/utils' import { groupFilters } from 'scenes/feature-flags/FeatureFlags' import { urls } from 'scenes/urls' @@ -112,26 +112,22 @@ export function ExperimentPreview({
-
-
- { - setExperiment({ - parameters: { - ...experiment.parameters, - minimum_detectable_effect: value, - }, - }) - }} - tipFormatter={(value) => `${value}%`} - /> -
+
+ { + setExperiment({ + parameters: { + ...experiment.parameters, + minimum_detectable_effect: value, + }, + }) + }} + className="w-1/3" + />
Roll out to{' '} + { + updateConditionSet(index, value) + }} + min={0} + max={100} + step={1} + className="ml-1.5 w-20" + /> { updateConditionSet(index, value === undefined ? 0 : value) }} - value={group.rollout_percentage != null ? group.rollout_percentage : 100} + value={group.rollout_percentage ?? 100} min={0} max={100} step="any" diff --git a/frontend/src/styles/global.scss b/frontend/src/styles/global.scss index f830326306f98..f3d49b5da9388 100644 --- a/frontend/src/styles/global.scss +++ b/frontend/src/styles/global.scss @@ -478,6 +478,19 @@ body { --tooltip-bg: var(--bg-charcoal); --data-color-1-hover: #1d4affe5; + // Remove below once we're using Tailwind's base + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: var(--bg-light); + --tw-ring-color: var(--primary-3000); + --tw-ring-inset: ; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + touch-action: manipulation; // Disable double-tap-to-zoom on mobile, making taps slightly snappier &.posthog-3000[theme='light'] { From b9ed2be78e675c727852ebceb48d133e66ca7406 Mon Sep 17 00:00:00 2001 From: Michael Matloka Date: Thu, 25 Jan 2024 23:42:08 +0100 Subject: [PATCH 16/24] fix(frontend): Fix clipped date picker (#19972) --- .../lib/components/PropertyFilters/components/FilterRow.scss | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/frontend/src/lib/components/PropertyFilters/components/FilterRow.scss b/frontend/src/lib/components/PropertyFilters/components/FilterRow.scss index 8b07ffb8b56b2..9b44fdd52b12b 100644 --- a/frontend/src/lib/components/PropertyFilters/components/FilterRow.scss +++ b/frontend/src/lib/components/PropertyFilters/components/FilterRow.scss @@ -1,6 +1,5 @@ -.filter-row-popover { - // so the datepicker is visible - overflow: visible; +.filter-row-popover .Popover__box { + overflow: visible; // Only required because the Ant popover is rendered _within_ the filter popover } .property-filter-row { From 872a830e81a727348c58042e633c4a9cfc07019f Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Fri, 26 Jan 2024 09:37:18 +0100 Subject: [PATCH 17/24] feat(insights): pretty print more sql (#19963) --- posthog/hogql/database/test/test_database.py | 13 +++- posthog/hogql/functions/test/test_cohort.py | 3 + .../hogql/functions/test/test_sparkline.py | 2 +- posthog/hogql/printer.py | 2 +- posthog/hogql/query.py | 6 +- .../test/__snapshots__/test_printer.ambr | 3 +- posthog/hogql/test/test_modifiers.py | 5 ++ posthog/hogql/test/test_query.py | 75 +++++++++++++++++-- .../hogql/transforms/test/test_in_cohort.py | 10 +++ 9 files changed, 107 insertions(+), 12 deletions(-) diff --git a/posthog/hogql/database/test/test_database.py b/posthog/hogql/database/test/test_database.py index c0817ee2c9861..fae4a5c88928e 100644 --- a/posthog/hogql/database/test/test_database.py +++ b/posthog/hogql/database/test/test_database.py @@ -39,14 +39,22 @@ def test_can_select_from_each_table_at_all(self, poe_enabled: bool) -> None: serialized_database = serialize_database(create_hogql_database(team_id=self.team.pk)) for table, possible_columns in serialized_database.items(): if table == "numbers": - execute_hogql_query("SELECT number FROM numbers(10) LIMIT 100", self.team) + execute_hogql_query( + "SELECT number FROM numbers(10) LIMIT 100", + self.team, + pretty=False, + ) else: columns = [ x["key"] for x in possible_columns if "table" not in x and "chain" not in x and "fields" not in x ] - execute_hogql_query(f"SELECT {','.join(columns)} FROM {table}", team=self.team) + execute_hogql_query( + f"SELECT {','.join(columns)} FROM {table}", + team=self.team, + pretty=False, + ) @patch("posthog.hogql.query.sync_execute", return_value=(None, None)) @pytest.mark.usefixtures("unittest_snapshot") @@ -66,6 +74,7 @@ def test_database_with_warehouse_tables(self, patch_execute): response = execute_hogql_query( "select * from whatever", team=self.team, + pretty=False, ) self.assertEqual( diff --git a/posthog/hogql/functions/test/test_cohort.py b/posthog/hogql/functions/test/test_cohort.py index 1c5460bc9176d..427cf0db342cd 100644 --- a/posthog/hogql/functions/test/test_cohort.py +++ b/posthog/hogql/functions/test/test_cohort.py @@ -49,6 +49,7 @@ def test_in_cohort_dynamic(self): f"SELECT event FROM events WHERE person_id IN COHORT {cohort.pk} AND event='{random_uuid}'", self.team, modifiers=HogQLQueryModifiers(inCohortVia="subquery"), + pretty=False, ) assert pretty_print_response_in_tests(response, self.team.pk) == self.snapshot self.assertEqual(len(response.results), 1) @@ -65,6 +66,7 @@ def test_in_cohort_static(self): f"SELECT event FROM events WHERE person_id IN COHORT {cohort.pk}", self.team, modifiers=HogQLQueryModifiers(inCohortVia="subquery"), + pretty=False, ) assert pretty_print_response_in_tests(response, self.team.pk) == self.snapshot @@ -80,6 +82,7 @@ def test_in_cohort_strings(self): f"SELECT event FROM events WHERE person_id IN COHORT 'my cohort'", self.team, modifiers=HogQLQueryModifiers(inCohortVia="subquery"), + pretty=False, ) assert pretty_print_response_in_tests(response, self.team.pk) == self.snapshot diff --git a/posthog/hogql/functions/test/test_sparkline.py b/posthog/hogql/functions/test/test_sparkline.py index 2a5c24d90b1af..74a00392e9c8d 100644 --- a/posthog/hogql/functions/test/test_sparkline.py +++ b/posthog/hogql/functions/test/test_sparkline.py @@ -5,7 +5,7 @@ class TestSparkline(BaseTest): def test_sparkline(self): - response = execute_hogql_query("select sparkline([1,2,3])", self.team) + response = execute_hogql_query("select sparkline([1,2,3])", self.team, pretty=False) self.assertEqual( response.clickhouse, f"SELECT tuple(%(hogql_val_0)s, %(hogql_val_1)s, %(hogql_val_2)s, [1, 2, 3]) LIMIT 100 SETTINGS readonly=2, max_execution_time=60, allow_experimental_object_type=1", diff --git a/posthog/hogql/printer.py b/posthog/hogql/printer.py index 650d53b366026..74341e76b6839 100644 --- a/posthog/hogql/printer.py +++ b/posthog/hogql/printer.py @@ -304,7 +304,7 @@ def visit_select_query(self, node: ast.SelectQuery): clauses = [ f"SELECT{space}{'DISTINCT ' if node.distinct else ''}{comma.join(columns)}", - f"FROM{space}{' '.join(joined_tables)}" if len(joined_tables) > 0 else None, + f"FROM{space}{space.join(joined_tables)}" if len(joined_tables) > 0 else None, array_join if array_join else None, f"PREWHERE{space}" + prewhere if prewhere else None, f"WHERE{space}" + where if where else None, diff --git a/posthog/hogql/query.py b/posthog/hogql/query.py index 34706606f3753..42ba19eaa9618 100644 --- a/posthog/hogql/query.py +++ b/posthog/hogql/query.py @@ -37,6 +37,7 @@ def execute_hogql_query( limit_context: Optional[LimitContext] = LimitContext.QUERY, timings: Optional[HogQLTimings] = None, explain: Optional[bool] = False, + pretty: Optional[bool] = True, ) -> HogQLQueryResponse: if timings is None: timings = HogQLTimings() @@ -95,7 +96,9 @@ def execute_hogql_query( ) with timings.measure("print_ast"): - hogql = print_prepared_ast(select_query_hogql, hogql_query_context, "hogql") + hogql = print_prepared_ast( + select_query_hogql, hogql_query_context, "hogql", pretty=pretty if pretty is not None else True + ) print_columns = [] columns_query = ( select_query_hogql.select_queries[0] @@ -133,6 +136,7 @@ def execute_hogql_query( context=clickhouse_context, dialect="clickhouse", settings=settings, + pretty=pretty if pretty is not None else True, ) timings_dict = timings.to_dict() diff --git a/posthog/hogql/test/__snapshots__/test_printer.ambr b/posthog/hogql/test/__snapshots__/test_printer.ambr index ee6947efb744f..cf8678d33ca0d 100644 --- a/posthog/hogql/test/__snapshots__/test_printer.ambr +++ b/posthog/hogql/test/__snapshots__/test_printer.ambr @@ -19,7 +19,8 @@ (SELECT minus(dateTrunc('day', assumeNotNull(toDateTime('2023-10-19 23:59:59'))), toIntervalDay(number)) AS start_of_period FROM - numbers(dateDiff('day', dateTrunc('day', assumeNotNull(toDateTime('2023-09-19 00:00:00'))), dateTrunc('day', plus(assumeNotNull(toDateTime('2023-10-19 23:59:59')), toIntervalDay(1))))) AS numbers) AS periods CROSS JOIN (SELECT + numbers(dateDiff('day', dateTrunc('day', assumeNotNull(toDateTime('2023-09-19 00:00:00'))), dateTrunc('day', plus(assumeNotNull(toDateTime('2023-10-19 23:59:59')), toIntervalDay(1))))) AS numbers) AS periods + CROSS JOIN (SELECT status FROM (SELECT diff --git a/posthog/hogql/test/test_modifiers.py b/posthog/hogql/test/test_modifiers.py index fc31cfe99eea8..eba1f5195ab3d 100644 --- a/posthog/hogql/test/test_modifiers.py +++ b/posthog/hogql/test/test_modifiers.py @@ -81,6 +81,7 @@ def test_modifiers_persons_on_events_mode_mapping(self): query, team=self.team, modifiers=HogQLQueryModifiers(personsOnEventsMode=mode), + pretty=False, ) assert f"SELECT {', '.join(expected)} FROM" in response.clickhouse, f"PoE mode: {mode}" @@ -158,6 +159,7 @@ def test_modifiers_materialization_mode(self): "SELECT properties.$browser FROM events", team=self.team, modifiers=HogQLQueryModifiers(materializationMode=MaterializationMode.auto), + pretty=False, ) assert ( "SELECT nullIf(nullIf(events.`mat_$browser`, ''), 'null') AS `$browser` FROM events" in response.clickhouse @@ -167,6 +169,7 @@ def test_modifiers_materialization_mode(self): "SELECT properties.$browser FROM events", team=self.team, modifiers=HogQLQueryModifiers(materializationMode=MaterializationMode.legacy_null_as_null), + pretty=False, ) assert ( "SELECT nullIf(nullIf(events.`mat_$browser`, ''), 'null') AS `$browser` FROM events" in response.clickhouse @@ -176,6 +179,7 @@ def test_modifiers_materialization_mode(self): "SELECT properties.$browser FROM events", team=self.team, modifiers=HogQLQueryModifiers(materializationMode=MaterializationMode.legacy_null_as_string), + pretty=False, ) assert "SELECT nullIf(events.`mat_$browser`, '') AS `$browser` FROM events" in response.clickhouse @@ -183,6 +187,7 @@ def test_modifiers_materialization_mode(self): "SELECT properties.$browser FROM events", team=self.team, modifiers=HogQLQueryModifiers(materializationMode=MaterializationMode.disabled), + pretty=False, ) assert ( "SELECT replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, %(hogql_val_0)s), ''), 'null'), '^\"|\"$', '') AS `$browser` FROM events" diff --git a/posthog/hogql/test/test_query.py b/posthog/hogql/test/test_query.py index 3c1969d3198cb..ef81e32ae1836 100644 --- a/posthog/hogql/test/test_query.py +++ b/posthog/hogql/test/test_query.py @@ -64,6 +64,7 @@ def test_query(self): "select count(), event from events where properties.random_uuid = {random_uuid} group by event", placeholders={"random_uuid": ast.Constant(value=random_uuid)}, team=self.team, + pretty=False, ) assert pretty_print_response_in_tests(response, self.team.pk) == self.snapshot self.assertEqual(response.results, [(2, "random event")]) @@ -77,6 +78,7 @@ def test_subquery(self): "select count, event from (select count() as count, event from events where properties.random_uuid = {random_uuid} group by event) group by count, event", placeholders={"random_uuid": ast.Constant(value=random_uuid)}, team=self.team, + pretty=False, ) assert pretty_print_response_in_tests(response, self.team.pk) == self.snapshot self.assertEqual(response.results, [(2, "random event")]) @@ -90,6 +92,7 @@ def test_subquery_alias(self): "select count, event from (select count(*) as count, event from events where properties.random_uuid = {random_uuid} group by event) as c group by count, event", placeholders={"random_uuid": ast.Constant(value=random_uuid)}, team=self.team, + pretty=False, ) assert pretty_print_response_in_tests(response, self.team.pk) == self.snapshot self.assertEqual(response.results, [(2, "random event")]) @@ -103,6 +106,7 @@ def test_query_distinct(self): "select distinct properties.sneaky_mail from persons where properties.random_uuid = {random_uuid}", placeholders={"random_uuid": ast.Constant(value=random_uuid)}, team=self.team, + pretty=False, ) assert pretty_print_response_in_tests(response, self.team.pk) == self.snapshot self.assertEqual(response.results, [("tim@posthog.com",)]) @@ -114,6 +118,7 @@ def test_query_person_distinct_ids(self): response = execute_hogql_query( f"select distinct person_id, distinct_id from person_distinct_ids", self.team, + pretty=False, ) assert pretty_print_response_in_tests(response, self.team.pk) == self.snapshot self.assertTrue(len(response.results) > 0) @@ -125,6 +130,7 @@ def test_query_timings(self): "select count(), event from events where properties.random_uuid = {random_uuid} group by event", placeholders={"random_uuid": ast.Constant(value=random_uuid)}, team=self.team, + pretty=False, ) self.assertTrue(isinstance(response.timings, list) and len(response.timings) > 0) self.assertTrue(isinstance(response.timings[0], QueryTiming)) @@ -145,6 +151,7 @@ def test_query_joins_simple(self): ON p.id = pdi.person_id """, self.team, + pretty=False, ) assert pretty_print_response_in_tests(response, self.team.pk) == self.snapshot self.assertEqual(response.results[0][0], "random event") @@ -169,6 +176,7 @@ def test_query_joins_pdi(self): ON e.distinct_id = pdi.distinct_id """, self.team, + pretty=False, ) assert pretty_print_response_in_tests(response, self.team.pk) == self.snapshot @@ -182,6 +190,7 @@ def test_query_joins_events_pdi(self): response = execute_hogql_query( "SELECT event, timestamp, pdi.distinct_id, pdi.person_id FROM events LIMIT 10", self.team, + pretty=False, ) assert pretty_print_response_in_tests(response, self.team.pk) == self.snapshot self.assertEqual(response.results[0][0], "random event") @@ -196,6 +205,7 @@ def test_query_joins_events_e_pdi(self): response = execute_hogql_query( "SELECT event, e.timestamp, e.pdi.distinct_id, pdi.person_id FROM events e LIMIT 10", self.team, + pretty=False, ) self.assertEqual( response.hogql, @@ -214,6 +224,7 @@ def test_query_joins_pdi_persons(self): response = execute_hogql_query( "SELECT pdi.distinct_id, pdi.person.created_at FROM person_distinct_ids pdi LIMIT 10", self.team, + pretty=False, ) self.assertEqual( response.hogql, @@ -234,6 +245,7 @@ def test_query_joins_pdi_person_properties(self): response = execute_hogql_query( "SELECT pdi.distinct_id, pdi.person.properties.sneaky_mail FROM person_distinct_ids pdi LIMIT 10", self.team, + pretty=False, ) self.assertEqual( response.hogql, @@ -251,6 +263,7 @@ def test_query_joins_events_pdi_person(self): response = execute_hogql_query( "SELECT event, timestamp, pdi.distinct_id, pdi.person.id FROM events LIMIT 10", self.team, + pretty=False, ) assert pretty_print_response_in_tests(response, self.team.pk) == self.snapshot self.assertEqual(response.results[0][0], "random event") @@ -266,6 +279,7 @@ def test_query_joins_events_pdi_person_properties(self): response = execute_hogql_query( "SELECT event, timestamp, pdi.distinct_id, pdi.person.properties.sneaky_mail FROM events LIMIT 10", self.team, + pretty=False, ) assert pretty_print_response_in_tests(response, self.team.pk) == self.snapshot self.assertEqual(response.results[0][0], "random event") @@ -280,6 +294,7 @@ def test_query_joins_events_pdi_e_person_properties(self): response = execute_hogql_query( "SELECT event, e.timestamp, pdi.distinct_id, e.pdi.person.properties.sneaky_mail FROM events e LIMIT 10", self.team, + pretty=False, ) assert pretty_print_response_in_tests(response, self.team.pk) == self.snapshot self.assertEqual(response.results[0][0], "random event") @@ -294,6 +309,7 @@ def test_query_joins_events_person_properties(self): response = execute_hogql_query( "SELECT event, e.timestamp, e.pdi.person.properties.sneaky_mail FROM events e LIMIT 10", self.team, + pretty=False, ) assert pretty_print_response_in_tests(response, self.team.pk) == self.snapshot self.assertEqual(response.results[0][0], "random event") @@ -306,6 +322,7 @@ def test_query_joins_events_person_properties_in_aggregration(self): response = execute_hogql_query( "SELECT s.pdi.person.properties.sneaky_mail, count() FROM events s GROUP BY s.pdi.person.properties.sneaky_mail LIMIT 10", self.team, + pretty=False, ) assert pretty_print_response_in_tests(response, self.team.pk) == self.snapshot self.assertEqual(response.results[0][0], "tim@posthog.com") @@ -317,6 +334,7 @@ def test_select_person_on_events(self): response = execute_hogql_query( "SELECT poe.properties.sneaky_mail, count() FROM events s GROUP BY poe.properties.sneaky_mail LIMIT 10", self.team, + pretty=False, ) assert pretty_print_response_in_tests(response, self.team.pk) == self.snapshot self.assertEqual(response.results[0][0], "tim@posthog.com") @@ -330,6 +348,7 @@ def test_query_select_person_with_joins_without_poe(self): response = execute_hogql_query( "SELECT event, timestamp, person.id, person.properties.sneaky_mail FROM events LIMIT 10", self.team, + pretty=False, ) assert pretty_print_response_in_tests(response, self.team.pk) == self.snapshot self.assertEqual(response.results[0][0], "random event") @@ -345,6 +364,7 @@ def test_query_select_person_with_poe_without_joins(self): response = execute_hogql_query( "SELECT event, timestamp, person.id, person.properties.sneaky_mail FROM events LIMIT 10", self.team, + pretty=False, ) assert pretty_print_response_in_tests(response, self.team.pk) == self.snapshot self.assertEqual(response.results[0][0], "random event") @@ -403,6 +423,7 @@ def test_prop_cohort_basic(self): self.team, ) }, + pretty=False, ) assert pretty_print_response_in_tests(response, self.team.pk) == self.snapshot self.assertEqual(response.results, [("$pageview", 2)]) @@ -417,6 +438,7 @@ def test_prop_cohort_basic(self): self.team, ) }, + pretty=False, ) assert pretty_print_response_in_tests(response, self.team.pk) == self.snapshot self.assertEqual(response.results, [("$pageview", 2)]) @@ -460,6 +482,7 @@ def test_prop_cohort_static(self): self.team, ) }, + pretty=False, ) self.assertEqual(response.results, [("$pageview", 1)]) assert pretty_print_response_in_tests(response, self.team.pk) == self.snapshot @@ -474,6 +497,7 @@ def test_prop_cohort_static(self): self.team, ) }, + pretty=False, ) assert pretty_print_response_in_tests(response, self.team.pk) == self.snapshot self.assertEqual(response.results, [("$pageview", 1)]) @@ -508,6 +532,7 @@ def test_join_with_property_materialized_session_id(self): response = execute_hogql_query( "select e.event, s.session_id from events e left join session_replay_events s on s.session_id = e.properties.$session_id where e.properties.$session_id is not null limit 10", team=self.team, + pretty=False, ) assert pretty_print_response_in_tests(response, self.team.pk) == self.snapshot assert pretty_print_in_tests(response.hogql, self.team.pk) == self.snapshot @@ -516,6 +541,7 @@ def test_join_with_property_materialized_session_id(self): response = execute_hogql_query( "select e.event, s.session_id from session_replay_events s left join events e on e.properties.$session_id = s.session_id where e.properties.$session_id is not null limit 10", team=self.team, + pretty=False, ) assert pretty_print_response_in_tests(response, self.team.pk) == self.snapshot assert pretty_print_in_tests(response.hogql, self.team.pk) == self.snapshot @@ -551,6 +577,7 @@ def test_join_with_property_not_materialized(self): response = execute_hogql_query( "select e.event, s.session_id from events e left join session_replay_events s on s.session_id = e.properties.$$$session_id where e.properties.$$$session_id is not null limit 10", team=self.team, + pretty=False, ) assert pretty_print_response_in_tests(response, self.team.pk) == self.snapshot assert pretty_print_in_tests(response.hogql, self.team.pk) == self.snapshot @@ -559,6 +586,7 @@ def test_join_with_property_not_materialized(self): response = execute_hogql_query( "select e.event, s.session_id from session_replay_events s left join events e on e.properties.$$$session_id = s.session_id where e.properties.$$$session_id is not null limit 10", team=self.team, + pretty=False, ) assert pretty_print_response_in_tests(response, self.team.pk) == self.snapshot assert pretty_print_in_tests(response.hogql, self.team.pk) == self.snapshot @@ -570,6 +598,7 @@ def test_hogql_lambdas(self): response = execute_hogql_query( "SELECT arrayMap(x -> x * 2, [1, 2, 3]), 1", team=self.team, + pretty=False, ) self.assertEqual(response.results, [([2, 4, 6], 1)]) assert pretty_print_response_in_tests(response, self.team.pk) == self.snapshot @@ -580,6 +609,7 @@ def test_hogql_arrays(self): response = execute_hogql_query( "SELECT [1, 2, 3], [10,11,12][1]", team=self.team, + pretty=False, ) # Following SQL tradition, ClickHouse array indexes start at 1, not from zero. self.assertEqual(response.results, [([1, 2, 3], 10)]) @@ -607,6 +637,7 @@ def test_tuple_access(self): response = execute_hogql_query( query, team=self.team, + pretty=False, ) self.assertEqual( response.results, @@ -871,6 +902,7 @@ def test_with_pivot_table_1_level(self): response = execute_hogql_query( query, team=self.team, + pretty=False, ) self.assertEqual( response.results, @@ -910,6 +942,7 @@ def test_with_pivot_table_2_levels(self): response = execute_hogql_query( query, team=self.team, + pretty=False, ) self.assertEqual( response.results, @@ -955,7 +988,11 @@ def test_property_access_with_arrays(self): ] columns = ",".join(alternatives) query = f"SELECT {columns} FROM events WHERE properties.string = '{random_uuid}'" - response = execute_hogql_query(query, team=self.team) + response = execute_hogql_query( + query, + team=self.team, + pretty=False, + ) self.assertEqual( response.clickhouse, f"SELECT " @@ -1143,6 +1180,7 @@ def test_regex_functions(self): response = execute_hogql_query( query, team=self.team, + pretty=False, ) self.assertEqual( @@ -1332,13 +1370,25 @@ def test_hogql_query_filters(self): properties=[EventPropertyFilter(key="index", operator="exact", value="4", type="event")] ) placeholders = {"distinct_id": ast.Constant(value=random_uuid)} - response = execute_hogql_query(query, team=self.team, filters=filters, placeholders=placeholders) + response = execute_hogql_query( + query, + team=self.team, + filters=filters, + placeholders=placeholders, + pretty=False, + ) assert pretty_print_response_in_tests(response, self.team.pk) == self.snapshot assert pretty_print_in_tests(response.hogql, self.team.pk) == self.snapshot self.assertEqual(len(response.results), 1) filters.dateRange = DateRange(date_from="2020-01-01", date_to="2020-01-02") - response = execute_hogql_query(query, team=self.team, filters=filters, placeholders=placeholders) + response = execute_hogql_query( + query, + team=self.team, + filters=filters, + placeholders=placeholders, + pretty=False, + ) assert pretty_print_response_in_tests(response, self.team.pk) == self.snapshot assert pretty_print_in_tests(response.hogql, self.team.pk) == self.snapshot self.assertEqual(len(response.results), 0) @@ -1349,7 +1399,11 @@ def test_hogql_query_filters(self): def test_hogql_query_filters_empty_true(self): query = "SELECT event from events where {filters}" - response = execute_hogql_query(query, team=self.team) + response = execute_hogql_query( + query, + team=self.team, + pretty=False, + ) self.assertEqual(response.hogql, "SELECT event FROM events WHERE true LIMIT 100") def test_hogql_query_filters_double_error(self): @@ -1380,7 +1434,12 @@ def test_hogql_query_filters_alias(self): ) ] ) - response = execute_hogql_query(query, team=self.team, filters=filters) + response = execute_hogql_query( + query, + team=self.team, + filters=filters, + pretty=False, + ) self.assertEqual( response.hogql, f"SELECT event, distinct_id FROM events AS e WHERE equals(properties.random_uuid, '{random_uuid}') LIMIT 100", @@ -1391,7 +1450,11 @@ def test_hogql_query_filters_alias(self): @pytest.mark.usefixtures("unittest_snapshot") def test_hogql_union_all_limits(self): query = "SELECT event FROM events UNION ALL SELECT event FROM events" - response = execute_hogql_query(query, team=self.team) + response = execute_hogql_query( + query, + team=self.team, + pretty=False, + ) self.assertEqual( response.hogql, f"SELECT event FROM events LIMIT 100 UNION ALL SELECT event FROM events LIMIT 100", diff --git a/posthog/hogql/transforms/test/test_in_cohort.py b/posthog/hogql/transforms/test/test_in_cohort.py index 2fe6b6cc16c13..25e847d70cbc1 100644 --- a/posthog/hogql/transforms/test/test_in_cohort.py +++ b/posthog/hogql/transforms/test/test_in_cohort.py @@ -49,6 +49,7 @@ def test_in_cohort_dynamic(self): f"SELECT event FROM events WHERE person_id IN COHORT {cohort.pk} AND event='{random_uuid}'", self.team, modifiers=HogQLQueryModifiers(inCohortVia=InCohortVia.leftjoin), + pretty=False, ) assert pretty_print_response_in_tests(response, self.team.pk) == self.snapshot # type: ignore self.assertEqual(len(response.results or []), 1) @@ -65,6 +66,7 @@ def test_in_cohort_static(self): f"SELECT event FROM events WHERE person_id IN COHORT {cohort.pk}", self.team, modifiers=HogQLQueryModifiers(inCohortVia=InCohortVia.leftjoin), + pretty=False, ) assert pretty_print_response_in_tests(response, self.team.pk) == self.snapshot # type: ignore @@ -80,6 +82,7 @@ def test_in_cohort_strings(self): f"SELECT event FROM events WHERE person_id IN COHORT 'my cohort'", self.team, modifiers=HogQLQueryModifiers(inCohortVia=InCohortVia.leftjoin), + pretty=False, ) assert pretty_print_response_in_tests(response, self.team.pk) == self.snapshot # type: ignore @@ -91,6 +94,7 @@ def test_in_cohort_error(self): f"SELECT event FROM events WHERE person_id IN COHORT true", self.team, modifiers=HogQLQueryModifiers(inCohortVia=InCohortVia.subquery), + pretty=False, ) self.assertEqual(str(e.exception), "cohort() takes exactly one string or integer argument") @@ -99,6 +103,7 @@ def test_in_cohort_error(self): f"SELECT event FROM events WHERE person_id IN COHORT 'blabla'", self.team, modifiers=HogQLQueryModifiers(inCohortVia=InCohortVia.subquery), + pretty=False, ) self.assertEqual(str(e.exception), "Could not find a cohort with the name 'blabla'") @@ -114,6 +119,7 @@ def test_in_cohort_conjoined_string(self): f"SELECT event FROM events WHERE person_id IN COHORT 'my cohort'", self.team, modifiers=HogQLQueryModifiers(inCohortVia=InCohortVia.leftjoin_conjoined), + pretty=False, ) assert pretty_print_response_in_tests(response, self.team.pk) == self.snapshot # type: ignore @@ -128,6 +134,7 @@ def test_in_cohort_conjoined_int(self): f"SELECT event FROM events WHERE person_id IN COHORT {cohort.pk}", self.team, modifiers=HogQLQueryModifiers(inCohortVia=InCohortVia.leftjoin_conjoined), + pretty=False, ) assert pretty_print_response_in_tests(response, self.team.pk) == self.snapshot # type: ignore @@ -144,6 +151,7 @@ def test_in_cohort_conjoined_dynamic(self): f"SELECT event FROM events WHERE person_id IN COHORT {cohort.pk} AND event='{random_uuid}'", self.team, modifiers=HogQLQueryModifiers(inCohortVia=InCohortVia.leftjoin_conjoined), + pretty=False, ) assert pretty_print_response_in_tests(response, self.team.pk) == self.snapshot # type: ignore self.assertEqual(len(response.results or []), 1) @@ -157,6 +165,7 @@ def test_in_cohort_conjoined_error(self): f"SELECT event FROM events WHERE person_id IN COHORT true", self.team, modifiers=HogQLQueryModifiers(inCohortVia=InCohortVia.leftjoin_conjoined), + pretty=False, ) self.assertEqual(str(e.exception), "cohort() takes exactly one string or integer argument") @@ -165,5 +174,6 @@ def test_in_cohort_conjoined_error(self): f"SELECT event FROM events WHERE person_id IN COHORT 'blabla'", self.team, modifiers=HogQLQueryModifiers(inCohortVia=InCohortVia.leftjoin_conjoined), + pretty=False, ) self.assertEqual(str(e.exception), "Could not find a cohort with the name 'blabla'") From 4e491fb21e2c811bc13a3233bf859ac14e9626d8 Mon Sep 17 00:00:00 2001 From: Julian Bez Date: Fri, 26 Jan 2024 09:21:10 +0000 Subject: [PATCH 18/24] chore: Add eslint no-useless-rename rule (#19966) --- .eslintrc.js | 1 + .../src/lib/components/AuthorizedUrlList/AuthorizedUrlList.tsx | 2 +- frontend/src/lib/lemon-ui/Spinner/Spinner.stories.tsx | 2 +- frontend/src/scenes/trends/persons-modal/PersonsModal.tsx | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 6ea14dea0d739..258a50eb2ab92 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -249,6 +249,7 @@ module.exports = { 'no-constant-condition': 'off', 'no-prototype-builtins': 'off', 'no-irregular-whitespace': 'off', + 'no-useless-rename': 'error', 'import/no-restricted-paths': [ 'error', { diff --git a/frontend/src/lib/components/AuthorizedUrlList/AuthorizedUrlList.tsx b/frontend/src/lib/components/AuthorizedUrlList/AuthorizedUrlList.tsx index 9948846727e32..d6c50cc8edea6 100644 --- a/frontend/src/lib/components/AuthorizedUrlList/AuthorizedUrlList.tsx +++ b/frontend/src/lib/components/AuthorizedUrlList/AuthorizedUrlList.tsx @@ -9,7 +9,7 @@ import { LemonInput } from 'lib/lemon-ui/LemonInput/LemonInput' import { LemonTag } from 'lib/lemon-ui/LemonTag/LemonTag' import { Spinner } from 'lib/lemon-ui/Spinner/Spinner' -import { authorizedUrlListLogic, AuthorizedUrlListType as AuthorizedUrlListType } from './authorizedUrlListLogic' +import { authorizedUrlListLogic, AuthorizedUrlListType } from './authorizedUrlListLogic' function EmptyState({ numberOfResults, diff --git a/frontend/src/lib/lemon-ui/Spinner/Spinner.stories.tsx b/frontend/src/lib/lemon-ui/Spinner/Spinner.stories.tsx index e1625461109a8..9353443d74ba0 100644 --- a/frontend/src/lib/lemon-ui/Spinner/Spinner.stories.tsx +++ b/frontend/src/lib/lemon-ui/Spinner/Spinner.stories.tsx @@ -1,7 +1,7 @@ import { LemonButton } from '@posthog/lemon-ui' import { Meta } from '@storybook/react' -import { Spinner as Spinner, SpinnerOverlay } from './Spinner' +import { Spinner, SpinnerOverlay } from './Spinner' const meta: Meta = { title: 'Lemon UI/Spinner', diff --git a/frontend/src/scenes/trends/persons-modal/PersonsModal.tsx b/frontend/src/scenes/trends/persons-modal/PersonsModal.tsx index 067ec40f10388..ed7d4e95737b9 100644 --- a/frontend/src/scenes/trends/persons-modal/PersonsModal.tsx +++ b/frontend/src/scenes/trends/persons-modal/PersonsModal.tsx @@ -67,7 +67,7 @@ export function PersonsModal({ }) const { - query: query, + query, actors, actorsResponseLoading, actorsResponse, From 63386625bf9030a7b127c1dc3742076c10ea05e5 Mon Sep 17 00:00:00 2001 From: Robbie Date: Fri, 26 Jan 2024 09:43:43 +0000 Subject: [PATCH 19/24] fix(web-analytics): Add more debugging when test filters do not pass validation (#19970) * fix(web-analytics): Add more debugging when test filters do not pass validation * Update test --- posthog/hogql/test/test_property.py | 2 +- posthog/models/property/property.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/posthog/hogql/test/test_property.py b/posthog/hogql/test/test_property.py index 52e57d9c61c76..3b2e582149cc6 100644 --- a/posthog/hogql/test/test_property.py +++ b/posthog/hogql/test/test_property.py @@ -87,7 +87,7 @@ def test_property_to_expr_group(self): self._property_to_expr({"type": "group", "key": "a", "value": "b"}) self.assertEqual( str(e.exception), - "Missing required key group_type_index for property type group", + "Missing required key group_type_index for property type group with name a", ) def test_property_to_expr_event(self): diff --git a/posthog/models/property/property.py b/posthog/models/property/property.py index 31e8425c205d9..2b7a8a558e186 100644 --- a/posthog/models/property/property.py +++ b/posthog/models/property/property.py @@ -255,7 +255,7 @@ def __init__( for key in VALIDATE_PROP_TYPES[self.type]: if getattr(self, key, None) is None: - raise ValueError(f"Missing required key {key} for property type {self.type}") + raise ValueError(f"Missing required key {key} for property type {self.type} with name {self.key}") if self.type == "behavioral": for key in VALIDATE_BEHAVIORAL_PROP_TYPES[cast(BehavioralPropertyType, self.value)]: From 123f26b41698346e317c084f23527bebaae64ab7 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Fri, 26 Jan 2024 11:44:56 +0100 Subject: [PATCH 20/24] feat(insights): actor breakdown options in data table (#19968) --- .../src/queries/nodes/DataTable/DataTable.tsx | 14 ++++++ .../DataTable/InsightActorsQueryOptions.tsx | 44 ++++++++++++++++++ .../insightActorsQueryOptionsLogic.ts | 46 +++++++++++++++++++ 3 files changed, 104 insertions(+) create mode 100644 frontend/src/queries/nodes/DataTable/InsightActorsQueryOptions.tsx create mode 100644 frontend/src/queries/nodes/DataTable/insightActorsQueryOptionsLogic.ts diff --git a/frontend/src/queries/nodes/DataTable/DataTable.tsx b/frontend/src/queries/nodes/DataTable/DataTable.tsx index c71024d11e6e6..8cff514ccf419 100644 --- a/frontend/src/queries/nodes/DataTable/DataTable.tsx +++ b/frontend/src/queries/nodes/DataTable/DataTable.tsx @@ -24,6 +24,7 @@ import { ColumnConfigurator } from '~/queries/nodes/DataTable/ColumnConfigurator import { DataTableExport } from '~/queries/nodes/DataTable/DataTableExport' import { dataTableLogic, DataTableLogicProps, DataTableRow } from '~/queries/nodes/DataTable/dataTableLogic' import { EventRowActions } from '~/queries/nodes/DataTable/EventRowActions' +import { InsightActorsQueryOptions } from '~/queries/nodes/DataTable/InsightActorsQueryOptions' import { QueryFeature } from '~/queries/nodes/DataTable/queryFeatures' import { renderColumn } from '~/queries/nodes/DataTable/renderColumn' import { renderColumnMeta } from '~/queries/nodes/DataTable/renderColumnMeta' @@ -55,6 +56,7 @@ import { isEventsQuery, isHogQlAggregation, isHogQLQuery, + isInsightActorsQuery, taxonomicEventFilterToHogQL, taxonomicPersonFilterToHogQL, } from '~/queries/utils' @@ -384,6 +386,18 @@ export function DataTable({ const firstRowLeft = [ backToSourceQuery ? : null, + backToSourceQuery && isActorsQuery(query.source) && isInsightActorsQuery(query.source.source) ? ( + + setQuerySource({ + ...query.source, + source: { ...(query.source as ActorsQuery).source, ...q }, + } as ActorsQuery) + } + key="source-query-options" + /> + ) : null, showDateRange && sourceFeatures.has(QueryFeature.dateRangePicker) ? ( ) : null, diff --git a/frontend/src/queries/nodes/DataTable/InsightActorsQueryOptions.tsx b/frontend/src/queries/nodes/DataTable/InsightActorsQueryOptions.tsx new file mode 100644 index 0000000000000..11d02eeb0776d --- /dev/null +++ b/frontend/src/queries/nodes/DataTable/InsightActorsQueryOptions.tsx @@ -0,0 +1,44 @@ +import { useMountedLogic, useValues } from 'kea' +import { LemonSelect } from 'lib/lemon-ui/LemonSelect' + +import { dataNodeLogic } from '~/queries/nodes/DataNode/dataNodeLogic' +import { insightActorsQueryOptionsLogic } from '~/queries/nodes/DataTable/insightActorsQueryOptionsLogic' +import { InsightActorsQuery } from '~/queries/schema' + +interface InsightActorsQueryOptionsProps { + query: InsightActorsQuery + setQuery?: (query: InsightActorsQuery) => void +} +export function InsightActorsQueryOptions({ setQuery, query }: InsightActorsQueryOptionsProps): JSX.Element | null { + const localDataNodeLogic = useMountedLogic(dataNodeLogic) + const { insightActorsQueryOptions } = useValues( + insightActorsQueryOptionsLogic({ + key: localDataNodeLogic.key, + query: query, + }) + ) + + return query && insightActorsQueryOptions ? ( + <> + {Object.entries(insightActorsQueryOptions) + .filter(([, value]) => !!value) + .map(([key, options]) => ( +
+ + setQuery?.({ + ...query, + [key]: v, + }) + } + options={options} + /> +
+ ))} + + ) : null +} diff --git a/frontend/src/queries/nodes/DataTable/insightActorsQueryOptionsLogic.ts b/frontend/src/queries/nodes/DataTable/insightActorsQueryOptionsLogic.ts new file mode 100644 index 0000000000000..b1c7ec1379b83 --- /dev/null +++ b/frontend/src/queries/nodes/DataTable/insightActorsQueryOptionsLogic.ts @@ -0,0 +1,46 @@ +import { actions, afterMount, kea, path, props, propsChanged } from 'kea' +import { loaders } from 'kea-loaders' + +import { query as performQuery } from '~/queries/query' +import { + InsightActorsQuery, + InsightActorsQueryOptions, + InsightActorsQueryOptionsResponse, + NodeKind, +} from '~/queries/schema' +import { isInsightActorsQuery } from '~/queries/utils' + +import type { insightActorsQueryOptionsLogicType } from './insightActorsQueryOptionsLogicType' + +export const insightActorsQueryOptionsLogic = kea([ + path(['queries', 'nodes', 'DataTable', 'sourceQueryOptionsLogic']), + props({} as { key: string; query: InsightActorsQuery }), + actions({ + load: true, + }), + loaders(({ values, props }) => ({ + insightActorsQueryOptions: [ + null as InsightActorsQueryOptionsResponse | null, + { + load: async () => { + if (!props.query || !isInsightActorsQuery(props.query)) { + return values.insightActorsQueryOptions || null + } + const optionsQuery: InsightActorsQueryOptions = { + kind: NodeKind.InsightActorsQueryOptions, + source: props.query, + } + return await performQuery(optionsQuery) + }, + }, + ], + })), + afterMount(({ actions }) => { + actions.load() + }), + propsChanged(({ actions, props }, oldProps) => { + if (JSON.stringify(props.query) !== JSON.stringify(oldProps.query)) { + actions.load() + } + }), +]) From be626f69bf887246591caee0ad2ba61c6ddc0aab Mon Sep 17 00:00:00 2001 From: Robbie Date: Fri, 26 Jan 2024 11:02:23 +0000 Subject: [PATCH 21/24] fix(web-analytics): validate web analytics filters from url (#19967) * fix(web-analytics): validate web analytics filters from url * Hide warning * Import order --- frontend/src/queries/schema-guards.ts | 17 +++++++++++++++++ .../scenes/web-analytics/webAnalyticsLogic.ts | 5 ++++- 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 frontend/src/queries/schema-guards.ts diff --git a/frontend/src/queries/schema-guards.ts b/frontend/src/queries/schema-guards.ts new file mode 100644 index 0000000000000..9290e72203518 --- /dev/null +++ b/frontend/src/queries/schema-guards.ts @@ -0,0 +1,17 @@ +import Ajv from 'ajv' + +import { WebAnalyticsPropertyFilters } from '~/queries/schema' + +import schema from './schema.json' +const ajv = new Ajv({ + allowUnionTypes: true, +}) +ajv.addSchema(schema) + +export const isWebAnalyticsPropertyFilters = (data: unknown): data is WebAnalyticsPropertyFilters => { + const validator = ajv.getSchema('#/definitions/WebAnalyticsPropertyFilters') + if (!validator) { + throw new Error('Could not find validator for WebAnalyticsPropertyFilters') + } + return validator(data) as boolean +} diff --git a/frontend/src/scenes/web-analytics/webAnalyticsLogic.ts b/frontend/src/scenes/web-analytics/webAnalyticsLogic.ts index 0f516690afc7d..8f7942c0fbb21 100644 --- a/frontend/src/scenes/web-analytics/webAnalyticsLogic.ts +++ b/frontend/src/scenes/web-analytics/webAnalyticsLogic.ts @@ -15,6 +15,7 @@ import { WebAnalyticsPropertyFilters, WebStatsBreakdown, } from '~/queries/schema' +import { isWebAnalyticsPropertyFilters } from '~/queries/schema-guards' import { BaseMathType, ChartDisplayType, @@ -997,8 +998,10 @@ export const webAnalyticsLogic = kea([ _, { filters, date_from, date_to, interval, device_tab, source_tab, graphs_tab, path_tab, geography_tab } ) => { + const parsedFilters = isWebAnalyticsPropertyFilters(filters) ? filters : initialWebAnalyticsFilter + actions.setStateFromUrl({ - filters: filters || initialWebAnalyticsFilter, + filters: parsedFilters, dateFrom: date_from || null, dateTo: date_to || null, interval: interval || null, From 9db37a9293b2fcd4b3583bb8633295828d875f4e Mon Sep 17 00:00:00 2001 From: David Newell Date: Fri, 26 Jan 2024 11:19:48 +0000 Subject: [PATCH 22/24] chore: make PropertySelect component generic (#19971) --- .../filters-property-select--default--dark.png | Bin 0 -> 10146 bytes .../filters-property-select--default--light.png | Bin 0 -> 10120 bytes .../filters-property-select--sortable--dark.png | Bin 0 -> 10146 bytes ...filters-property-select--sortable--light.png | Bin 0 -> 10120 bytes .../PropertySelect.stories.tsx} | 16 +++++++++------- .../PropertySelect.tsx} | 10 ++++++---- .../Funnels/FunnelPropertyCorrelationTable.tsx | 6 ++++-- .../settings/project/CorrelationConfig.tsx | 6 ++++-- .../project/PersonDisplayNameProperties.tsx | 6 ++++-- 9 files changed, 27 insertions(+), 17 deletions(-) create mode 100644 frontend/__snapshots__/filters-property-select--default--dark.png create mode 100644 frontend/__snapshots__/filters-property-select--default--light.png create mode 100644 frontend/__snapshots__/filters-property-select--sortable--dark.png create mode 100644 frontend/__snapshots__/filters-property-select--sortable--light.png rename frontend/src/lib/components/{PersonPropertySelect/PersonPropertySelect.stories.tsx => PropertySelect/PropertySelect.stories.tsx} (79%) rename frontend/src/lib/components/{PersonPropertySelect/PersonPropertySelect.tsx => PropertySelect/PropertySelect.tsx} (93%) diff --git a/frontend/__snapshots__/filters-property-select--default--dark.png b/frontend/__snapshots__/filters-property-select--default--dark.png new file mode 100644 index 0000000000000000000000000000000000000000..df658fcc6f082ed05c53a2622948c74f26967947 GIT binary patch literal 10146 zcmbt)2Ut_tw!bqz<&KSuC?Hh>h$Br@I!P2M0@4Qpgfa@!%Rs1sfL>+>Afl%Lxciw%&z3=_M|Mzab4|1~4K5MVN)^D$RUK<%`Up&Wu z?$oJM7j+(Mn4CIwsUNu3{>xe5e;4944*2>8VWO>ms;pOV@zkm7r*t$P{t%eCIvEsb z{d^Rfsq<|Nl7i z&C72KD!zMDbA3Nr77M!VVR^1bN`L%*6I5M2>E=C&4zYXl-SY?=LYey~+iId_y_&di({(0_G9z~QY=zD&`5SK-bGwQqG6t{OpRxE`xxUzFW|8wHJZ zqK2%RwaN3vOL=yf=lvyx5R%Lb3R$K!>Ws+zGfPy(nQ9f@a$UMgkf@()s(I`0nnT7o zsZpkDmhJ#GL}>?vVP?ZWNJRCpq%CP`Jm(OQ^Bu~tzxg6O|1x^L>y4Zm(YP)#Q+{bPT9@&|a4+oa7p z()OCM2-rsEE!PtXKoPabh@pIM--uyBVtv-hE%*kFj*Xz~6 zKVNQ?#;|1M#T+tYx8Kk>2gt^oXy#BhS3y4Em46^?M-;{6}FjKV-K}%p-@y znxcfA;fYcm0+#P6IUbu2<%%S9^)DvMyYin);(lykdwtS@ui|0K_u5N|x_NUsA?>2^ zKihsY`$+hu*w(oP{)#in?*aX`vo6XT3F=gcc{#kaim2(}XwA>QB`^P&^1SYv=hPTX<+Yp`FL6a9QX>U(M?1FcJfYIzo=E zay=Rz1-E6J7i2TGA-GVr?UfSEaXV5r*q$Mclc}BLfqQa;gPBre-i!pCLsT-Z7lJM8 zBv`?gV>=5ds_GDxG9yU6X&w0;a!G0X2SL|QvphG$l&zR$ZXtmmz0gmussj=!Q-jiToM?us?%ZH-Heh;FTj&SK@>{isYJ4_0x8Sb-mby7z-y3$5m*S_Xs z^Tp1bvRO!0{q3C~*_c}@`7A>tND0KTZTJD+CCBIpexT* zM_81-HCc8gT$#zEotw>5UBs>CWp54+)@So~%f`xElvPt~$iAU2C5t{vhNNeOGh$HX z#RI+yvDlyu*5mN1Sc2djB-%3Ni|_1@K`sKp%H1fQdlR?V@@xHw^Z77aWt7T#Ug}>l zVV`8eU_CFIQ$;|mVo*HIHRI@*49^Bt9%(DG2XV!~r8{p&`Qm^YGi57kGqvrj3iN%rzBV#)#+u^vEZXA{Fqy?4x4! zgo>YK+E@pVyq!ggYh&7cp3tb1Q!MrQu+rfhZW_i5FKowOXO9!YHRQ{qsq^9q7-{KYAti?80wis8Ka zReJfbH@GTh)S4K&b=*l@$Bhk5wuWXJ_kGRQ#t&|e0aA>NhZ)JeI_f}calK4 zEEqR-r*50pe~m=|JImY$fu7AVut$mU8$y!EZ1nu>xEK#vMqE<<<`+p^sT^FV4$V=O_v*j%*h1>#JVU8oG zdsyF0DT}Q&9`}ExIyBadYMTlz1s!VWbY$i;; zUA+glJ?b@p`QH807p;wP)wO6S390OPbO@G1rrN-iv>K6?p%~T@awHvN^JSOSM)k>T zU7&d=@5dz3zFeZ@s?}h2lzSGhUfeoI$Q5_ni6VOZuo!f(9?m%px0@m?=GI0=itD9Z z39%Lpa)-JFAds4;3F=tqjxoZtw}2T^_%I{~@v*t519iI2PUv2Lkz_Gpru37eP^V3kam3d`Bwn=xWO84 z>Zqyth-2rq`Bs(XP?Au}LNJFc(2U9`k@KabZf`eoy_KwHsf4m;jz7>wP~2k=d06Gw<*j+^A02kkC(vcd|*dwK~^5U^_Tf^-qw><<2^na77!8+mfH)22p#fV3R>Z#BLFO`V;}!M zHd&)fq9uw))3Dgaft4Bpb5z!mBy|vLv^gS2@dW1dNhW^s$CVB+(q_s-8~YTNKTUL@ zkO;dZdatPj=C5Hvj^+njNxTc$93p6eGJ=X{{n2o#r=5){H*Z#eBmQXTI65*?QnulG zFXVXhY7M-?!5hEdJI=+mvDQGZs>gE2fkDN^#na84(8x$YK!G0C%&sZ15GyV-Gc)gB ze~V}teXk#-eSBXU1e(JKX?_w4$v5v}vx*AKa8bkeHeh0vULaQW|tus`+ka$P+FK zO}!%U53_7{v`aKpQ-&Pv(rR}PDnYq{nAM^#U-I+vy}%nni{*9JGQBDuCfymT?-y&d z3k{>Qh%sm{wUgPYBrjk8L(06-Oh=Q3_UcD#n^>D zEg4rdaMWmNpyP#5=2k_IPQ&qm-Z6sJrU%20aJI*d;<6jLgJiBG?{0*j<$V3k>9cL7 zhHBIm=6ff#{wpiLJ$lvl>ySD6yllhmr6tH=sY6|{b&G_ia$tL(MfLHEK<3-GJxs<7 z4c!*HFG8N%a3Z-L!Hu)CH#FGzxr~NRv7xq>sYpwJg87DznZ0SuUr`(HBi6jmIl)y_ z=VNK<@|8iAZc^(xZWkn#)*gmOv=kq5v<%+McCP8!Y!iq(>xuo=^J`!Oz-aq z3XU7608Tx;HxXUKNMQ2E^m#SFQ788{TT#8J}~ftWuydWTWOe*0%TU}oIuraC7n%Zgf;sjQyT`X`1qJe6N_ zsephdfHlhupuFL`ANbgPw^e_-HZPPsN6Uiw*-~5^MKOmp^R+rl1qDSr6IUg&d>k`_ z=iczC1v;~Rx_38Mgn!@);InNW3agU#1^;2S=(WiyL=t-BxO*4b21>W?@fEIDy%*iEMl-zu*eXrTx-A(zZEpaWO(iBPlVAY&)`yt)8+oy|7mi$G~mA= zlrL91P!es_b9J(|YvXDee1xttZw8(2a_;fqX`ecatqtNT#7K{oy~95AZ2iP3tu%Pk z{FLNlewE{jlOZLAO-=G%o_NKu&oT6gu!~W~sLuE7lESFy(wfP}#TsiF-e7t| z*ag8_t6XnVhN=WL0}9nzQ>q_TfP3vMLXz;FI?^JA+J#U)^pmhVwy5o7c3Q~?q>~NI zp=+DwV|ekm;p&9?*+UW3ma&G$mdKrVi#1=Ghh6p>fEq`^hwV6F+$iL7_d~tSgeQsr z1D3cJv1~A1&Z={KXR-ap_kz!Ww~vXkvQ94FvU#3vkDk+Rh=XPY9^__A?D%v6*M`ir zPOr#p!)si@#;NSr!(cGt;=J`0Qa4h?vuSnCap$TiZs{)A&oTyLyMP!GOLWWwQslse z--^l^Bj2(AaO7uhwkwF4;!)%ccbBXQt zM9YTlKTR4Zk$5*NFLj}Qo|rf7t@C1l#sJIDb-2$NSt(m9)mXbxXl!F6#O=Rt^(5WL zoE5ognYoyBRmNu0Z-`um`O7oU;Sl}<;t{Eq4rrHTj z9WVpZYwDQkc5NHnhHrl8%t@L+bT&Q7n(8EM`dZ30-u)u!14Ui;ouX*iFfO@O{2)Z53s za`I9+#VLZ=Pt4+rI9OlbUC-m>j%|3FVb>CdQ4(iYJsu77@o@W3-MhI`<5Wqzf}Bir zzE-GJVDV(Ov?@sNE(dGwQ}zBlo<&asFs-!qbBA2Ws^4^%>gr|a!A5`2+#|q;t(CFy zM?mx?hKMDJiMU`N9-4!t4Gm4hflK-**_X$)o=-kOBWIh?#}{pEs6)IiAtWJ@oRlOM zRqiQYOvd}ZB59k8b(M;c(ud`MsKz645r6@*RaZExBL4mxx<|Oh69!|w$r?4et$(#M|xP=hCf2kU!#&H z{8Qf!t=`VAdIHg5CtLa!Nw<-Ex`S8JCFbdy?`ggu%$GNS3!w0#G zjhZ51f)OIfpPm5D@!<0(QFaO-IBe~wnHe%xx2n=+2D81a$0HjDWER4w&t8h*%|6y+ z`{>%w19l1?h_2Y-HX=X*sfhZ-l!5MRHEPB#|OBLIVu(fO5P?HRs!uc|+@8 z6!}$CC#$LbG=GOnDH0+bVm;dFZbnUJhFyb$ zV|+Ia)oOY?X7lZ5@+*8y9+y+Y71>CAL(;aA=#s3M^NYFA^%MLfkyRky<+abD??{CoT@$Bw^af!q-!;)YYJdA{mLiT3fTevXYW$ z4@H55o$|kDkGTo9Ij|Y~)f8gfCgYa_AQqo`yf$uLR)-z)p>1!5j3UC>759n2agp=c z@80~LcQ4y(n@fmD{*h&D*pX}xUUcy=4etpeQBY)GpV<-E;PvwekVHp^4$;BI zU?4NqWRvw+!QrWaL#@4ol0e0M376gqd{wD+Tfgzr-Mafj&&wygNsqiYSWd0Ym;b60qET@cil8U?$OI6Y_p@;p*b*)y*{UW*Sv3C9%5U9|M-@P4}Hj-h6CM zme6Aio7IspdxrRegYR)G_S0IZ$S|3m?w(T4)=bZW2^e8G$6)`gteZ3M;J&)`(%Xy#`J1Z zHYL>cQ|$nCZ~$`j7kL|Z%MY)gb^V^%t^+)CGfG+ykx0S6NhfJn+yKPUEF{`I8egES z#ucJ&(Xp z)Kb!Sl#{iwkDajbME69Z?_4?gqwr)+upbyK4VAYzAevhJz4g`$N;>PsF;oKZSpV^`PskH&*WX_*gvHoslG*&Khd;)$|J>JR+#oPTr%|BKjg zyz~b(zb=rE-cqqH_)`1NVwQ=D6%R@ujn9NXGckOV&M)Rh{(El}|De9(&i|t|>Tv!x zDY3q|UD^sXVszo(YY}Sd6@y?6_a!F!bfS4+nyUlWzpc*1R+;|ax`dsBY5Ue1EgaQ# zAF#69@z=AKmPL6eJV4n5b;~QCT(KAVOZCue9BlcAMCM#`9WSeDXF?rp?EZgf+Rh^E z*qwF#gtf^$2y#d&8t9rDXW?ZZv=+g6%Ja*LO`B}gsgFMadqb#mk` z@t+rSh>Ldq=ES7-%=Fn~Ip2lP7Jfr!dG+-wKygl2zEB2cXD0%rxN>1f`*L1Xyb0$? z9$$dm>LdLRw}+$m_96qQK@vPt7JSv!(R#{dGD9UW(!#J;GC=XDyz@4SRy}-VzI5Z{ zx<(piZWl$2n~wIDTL%jCX!NjS?IPmfD#a#XwSCq{CjZPyHsdGaZ-k42Zf|}&78%)e zxVrtJxH#&@qqE7#TPG%-x}sASc5FsF+?kTyR?=NM+&Shx+~1MizH@Tgtwd{Jdr}sk zK9Om+iq4$pVV3}kB2F^-AYld&M{bS3cM;Lpq`JBq@Wy6KPft=Z600-eJUMhKs^?!M zZ}46=S4iH~A%b1%Xln00PPI3tGu_o($?beqd#*j6@WZ6AFcb!yF?mE8r?-fr)l!_BXhd|Z z1SaG*6}i6}xY=<{F7#le_8`hmVX}!R+|YXMJw^~m^$%84@<6VrA&E-sFAg(SMLQHu zuJpqzPzbljkVKe@V~>19YnuQ{PB={g3<3#fDu5M61M@5pJx}$=L+GDR(^$N=G4|*T zzMUzkblmYGX}bD&A6|JVHu_Iu?CdM^%Jo>ycrMWNl36n~*`M=Gj0jW#1ac&WD(vmp z$vgIh7JgxR+fNslgmH6miE4cm!2t3WJK2JlY!`e#Vei@L-PUD~{)nHYK^AKC%9T=eHI$t7vVpuK@_B zg>1TP4CrC+PaaYC=|rHK#nliuE?(BKR#-bXJlxvKn6l{A(3oCBs0@w>{o* zU$^4o36iIhZw!wL;kTKp8-P}Q7s|QY(6K-had3rwC{t~b;?xj?!4p25n}){3MnnV6 zDXFl9Iqt!oYU2|+Q(Qvpw6mUEk<+O;iT-zYf6yQC!0z$d1{MK_u`X8e=vmr7nH%?o zEyezmfPGN+uO=`lI`TtNkqmI~iwg@2$&XI5|1Q#pi;EDv^Y#7!272P|1Ii2tw70zs zTk?KGE-o==GETD4o<5N%t|yvW67^d+M>0}Mynh#;DrIRhop6H8DI+QQXddY(PTocl z0&I!5`PIeP;{&IsQdU*BQeb50TjfFB2L=8 z2MOeE3=|5Kr@f?X@OONM2+#VKPUkscsX&>@U-q_5g@cP!navfE&`;NwmR$1;F^zz^ z4lWQr=_*cXnpFPy+wTkGI4=z|eW{X?*k8`O>VXSAlS|gFbuA&vftx0rCzpN!>d@5a zetS>R6}k0V7z~Rm@Z<9H>(T|6$(X@#QYjS8t%W+Dnd0Eqi8_WZSnQ+-qCOOE$Rg&t zV}WWryK=5YrxNnC(ZU6SuvDuG2OYL7sHh!+MQxvA-$|u9^c-_2%a@(kDt&u}O;ktI LK%-3k$G`n2EnzA( literal 0 HcmV?d00001 diff --git a/frontend/__snapshots__/filters-property-select--default--light.png b/frontend/__snapshots__/filters-property-select--default--light.png new file mode 100644 index 0000000000000000000000000000000000000000..9efcbf327f836452cee6fd69ce3211a57f21a180 GIT binary patch literal 10120 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 ecTLYc` literal 0 HcmV?d00001 diff --git a/frontend/__snapshots__/filters-property-select--sortable--dark.png b/frontend/__snapshots__/filters-property-select--sortable--dark.png new file mode 100644 index 0000000000000000000000000000000000000000..df658fcc6f082ed05c53a2622948c74f26967947 GIT binary patch literal 10146 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 LK%-3k$G`n2EnzA( literal 0 HcmV?d00001 diff --git a/frontend/__snapshots__/filters-property-select--sortable--light.png b/frontend/__snapshots__/filters-property-select--sortable--light.png new file mode 100644 index 0000000000000000000000000000000000000000..9efcbf327f836452cee6fd69ce3211a57f21a180 GIT binary patch literal 10120 zcmbt)2Ut_t*0wW#qjzL1hze2_5p-xG(whnx83Y6iAVNk$FToIMAY>dzIz&N=RHaBK zh89}LD1?AC=_L@90HG#^00Gke!`!*|e%$+i&;LL3o##n%vd`K3?6dZJ)_UKyPRwmf z(BVVkhxYB;ci8NviOs%!NBV(%(BBRK-(Bz+9PslE+y-=gUuCc4;=X-n_L-SnyBm_V zIu#o7&~@DCtDad}O`&O9)%UI6$L95kmlYi|R`xF~ zOkRp!`_5Cy?!tvL9rAfTc`M4ww>`}>Q{}gL!pEz6y3HV1>b z93Az=&bvL}OE|R*<;pm_2cp>Z{OHJ?eg4IjF$<$cd0^kR-zVs+#*~};YmYQ^9wZjz zT+q|tSH<`>j6@x~Hn zKGC}N>(kkz&$VoRsxXGGT~jMj_{rjMl7^S~p=6PpmQH7;T*RubrCtOrCH<5?|0cZS z-1Fa@zL_(Yej>MiXhEv#yOg3pdKsjX1ipAUz<~-fmrWfyNx4nAsqiS>YEEmE@qUDZ~y9iBGGxe;^IFU{Lm38XP=~1b@i{> z>CM^FWFK4zGbw~QXNw5azS<#?V*J+zTTEJzoe(r{-dL$&Z&9R$j;-C8yS@B8GyL|g zr5Kw#PhAo+B0tRjS5mU*O|8?BKF?i0zGy5MC=R_Kv-UT+H?~)|pM%bPkN*}C<#VlZ z79Sn-K1!y&AsG{m4U;-{`0!yYHD@{&JPmX2;>mPp%21CVX!%s}TXe64#(kFN6r;hXDVmbOj#D!s>lmw(lk^v)-1x>uqNyI#a4q!~c5P&kHtf(~ zyX=LxZx})ZcYxR@KZ3wNritIRnkvePH9i~!KIQ!`D`n^54GVTf&1k>`Y1+|$05{;| z?kR(6FoQUS8xF{=+TY}CHFaeuwDt_-JgGqwCJWTMkR@8n7Rt1@Dy%ztg|y~UzZByh z_H1}%ApQg^yrztNa+cIEgglW2+?K{p^(W zcs>-%Ur@t-xlf&$?6hr5I&Z$SvbEZoU)%J`$vSH_))eDmV~NjG?S#PU%1k`FQv>VG zbcSjti|+W%88S?3K)JeVIXh%Htha=u!Q(vNPyMwE6APZAoS?f#9ZqP1HfZ)gneZdG z9BZn=_$Rkx(;VO{-c|;jhx$9CMa~oJ2jNh4-<_w-G=-eRq8wzURlA&D=S#1ZXY=nK zsuzw7BQE}FxB0oj1cMrWQFvW}9f%}7Vk8^T}w>r%t{0CTSE@tAa z8P*GVPl+&{Zse(IrPjrd*S!Dyr#`8aU{5zEmgD@3=W1@JZM#+kRZT*YZCKl`CG%na zh7aPAr&{Bq1jnhH(lSpE4sVXDacsm{3rNf4D`>m{K95_7wOv0Sszfo2`%r4|1`+Z%RmZq zN2Q(8G?0OAxvPSKG{m2i*|=*b?nHVxa4AT`4%h=3zIiKT9%fO5Hb1lp%hbe_@x+ogK7q|@@J z#@($fc+~iMkC_r|yH^>JheFi!C6a9iGC&M?25=9niznxlEB=5Nb8}pTfj82k3kLJm zsFh&SgEDkfA4BLoeo^tg!Q)#%I2-wiV(gIC}QcwQqJwIsF;f2DTR^o0P@!SSrCa_ zO<8=mI>n~QK-juxWMyyoXzN;Pvzj&8Ad_YzHUYL1DoW~D4USXwX+cT`44E<89n?jP zux>`YZH)7Jy#ZwPg#(Flkmsk*pH=rw6cJ{Yks0x;LDQ;+gz+FOPnr|Flc`bJv^}!* zQ6-G1n+cWBW2hVQw?4Njvukss4&%f{t@K&!F({ii1J8ttKnSp_w=xaS^@lXAZG}T( zqeHf^3eL-p?3g~kI>(jZ1vRzhY#rhLZ7%49UoWQd%lw#gkpU#z0+)b1YWSlQinXee zpeH*M=>UzZ2pZQLk!}bK_Z1k=Ok?nrN11XM2SaMmnEVLmgYGVlpzuJ%@}=6N5By%> z>UMtgtC-7tdL5p=%-Ui`(@EeNFj?lZzPsQH&^=n^)06z@bm|u?J9Lza@eQ`UNI6$GBKPt_C@wz8N!!@fh0 zdqHNL0(`wPDU`MPO3k^opZT#&st*>s!OhUYcqKi{=-U|g8_2!LU2UL2qA;kkx%xZw zu-C0>Ys2Qt$oaBmTQI7+mB~uL^R(moN$_p{X@&NBsXB`q->|i@Knks<(Dq8d;(F|6 z`IHozTt2|6?|yz(Z9@R{X}F*QJj8^oY39%q{h;jHCS`nHjME$28uwLt$fR3J4#)_+ zaC-T%ga5P%eeeY*OSE4_usG+OYY-I21Z9aOqqu_}z{dJE*o~QV9!6Eh5nXAyCkaj4 zMOY7O$E-kpp;R3imWgRo#Wd*4%I2g;xM#tam{N5*^msV65XDod#LwiVZ)WC_KEHoD zQv+9B8Axv0X$Q9tYw<7l>de)r!)O&)#wbBtAsZQy=^FuCmV&)^HHx`|*3qX6c*?ML zCa8T@MsNkn9rk-r*RT|sNiCItCYlvx9~(pko5xT4VYJs~t7pEDau(L>g+!HOt9uD@ z@;UxK`d>L<()($OR-zuYIi2|xd^|y~lPZ+p3AixWCu;>#a7%;xEly7#Bqim3w0guP z9a;ZAl-fM^<1$LvNHDw#`!ZhCjtriZT*fq6uITW;Zax0;p$#57d{~Q=GRl+fHJlsU zoj>doZZk1P)M;d1+sp|kHxX5OBF^Kt=9J5RX7jHkP)@}Im8&v7}D z(FyW5W4+iEAM)eeC>%=88F?YTo>Os*G2W=qrQsN#(J|_syxw~quiKfm5%2>sM?~jE zayd<|GyPD7LtL6t3@Il>7b@5u#*Vj3v~QiWihm{~N|cE}B7K^_?7-+mqosjAQ0&=5 zJ7?iC4#e`yO0A9($s5|Z@*IME#av6sR4%YsUAdsQQ_^LaopJ5Q1bpKa1!9Ne@ukaB z$mZwt^)b`HKsDc;_^oZL2r>8E@LTcYbVcTusJI!7m7v0v5IoJk>Qhf?#BPe8)0M7? z?&}M^7vyC)?M^Vuj-Z&rM$AAsN%n8*1erk@L_rVy;$79Ab{}?%UHvkWxw|hhNk3@p6ox^wzC}3(&LMpOtzZyRouC!3j(TinViGT24U*K1 z5NOZ-g3K>1Ep6T0*w|3}@>{-hcI{|!{<_i%^INnX6SMKaA+vU{d4y;wnIX(!#=Jl~w)^!+RHDd;sD5*<{6~;%b3lcC#l(a~e<;oK|QF?XY%4M3@zxS6$r>FNjfcV7b^uy4F)C?a)oC zasTkmBZ2x~ILVMtuP4$Uf3Q}DTxfR>0{|UpUvB7hGk7N)0)d31KA17bQ*6D)`^Hx5 zVw)JqGdB`^cC4E3Fv89RO}qMF42@{_#!I)ub9wTCGcTfowCTfIlahKq2D;Yx8qJVT zuf?iY?=z#q=zb;37ukl#0Pggb3{kxy!#KP)rcR*&;C$xv;luQeE4g0zE*_8O$@89qsJI3fX5i27J3}GXPOMRA>2W8H* zp>wx;S3(%P2JG;uOk4wUz4(Rl`-rd+JvG`Co7zOmlH_?AzKV^EjP&sF;f7?6tFZ|( zZS$EvLvBW7pY6H&FR5gIX+c+J9k1d-J}4o+%#Z6j!=#0UC#h95e*CEBMy_xk^^4F7 zl4?S(G*dN|zjPNC!72KcBLv~Mz@t|9`*eu6+k04Fc#)@!L!$V&Tc)xmxnLM}hyeAJ z^DFFfFzKVcE*olmplVtXJXhZ^uiq)T{xW>F7c}90={jN6+#aJ~P{DP@AHcic8MH56 zbRIIP&dI5!A;N8!O2ilvvjy(6ass=mq~rpQc@?yxY9LlvSnV}RP+VD=`2OhuBzNo^ zY%vs;mJ80<3O7k58&NXSQ~+Gb!{Iaxr)E)oMl0Cz5BYH5h~!shOBjDV zr}37RnKdFR`S=xEHUzPzOoJ1;$8f~m&J9%pvL`$!sTPS07)-ZF%IoW^u^ zUj*5kH-R&PCLuii*pBUOCCMuBa4Vy;l$;GqeNLa~%!HzDgt6HGRx}^YJ`lQcngIt~ z-gV5)4-->5q1-kjr;v-5nE}=hwV6Y9@HVedT*|*;#%>E6(egYu(2hQ5nBCA9ChY4y zJUq|-LCZV@QCIe)zdV5Sbu4AEU-w-z-v%U`!y=O$$a|V z;CkSghQ_cj6jNDAf43?FZ7@7Aa>N7faPLHf^md)6;KRtMmfVE9mCQ6E(;HKNNjhxp zRXYE3E3D1Tov0NgAu3|2Uph!S3F*xwJzOa3U?ntddj@ZE$;NsQS6$zdgWr#5ANIG=_fMM2k{bv6WpQjDIO$RW_4RkAMvpCb z)9&P#lN^&%Sfi9r$)mg;oO^qxJNTG;PRQMGKHU2};+Cy?zKYR!9p1mB%XyO-6dcrC z@;8H^%>GQ$3^9(10iL2hK6M)iHx|ysll~~zhsYV;sp1RWp zKN{|Mc{_gg^L;?}WLx zG%x&(c6qB*CvtZ8}zTS0li>T(Ru|Mc``jY`D3TqaiSeAw)0L08dHeQkz)qVms{jmaw`8jCSs7N!Q571{Hv?3n7nW znhf}?Gp3D#PB$TMH87r}`%mxxPGF2-mD0PsdT5*#El=g(pvifGTxx;S#PTQxyrLNt zwbhDelU{P@=&4|ObEOaxI#q5*0~6)ZQ#53vlkgjW0zI>D%Itv9PN_N_kCG04;8WFq z0QOU&5NINqM6U*-j4%*}tkY|oVgUA^t2*m0mCE^BLtjxAJeMUqp$zgBXY^!CMCD#O zn-fZx>HqZU7;aia!%~-X5cWADEO7hkD$;G+3Uu$@y-6)bTn{Ed?Oud!u~H3B}5L%POcNV#We=}2#}@|p8wb!w$2Djv+AW<01XDJhA` zKPn|tbgTIcy@kyW0R$1V2qc4kny7!VQn0n0=uvlTrN(;$nn}R9wPR3W zwh!S{W!TW!>CRBDkP6{4AlXS|AaBN9c?%}JI4JWxgu5-B>vBt#w`|0$mIP!o8?WKbQQ&MKC z6)`8=!6EkCWg`p`&o(-d#+`Ndoi_$ZQcL9qT^d&g|CAFhmn zV`0;^Yn$!iQQ~@(G*pLfh2ocOQLasUu^s|K)O~Zz_0}Y8v}TJWq)OwaMqbZ#z)3^Y zdI_->(oKCFNqpWbKv>Q~R#QpgmO8Xw(*<8XX!Gg?2|A1x(P~<=1e}6YWk6=JoboAz zsX7quI!F`B=&3oH&^Ex_TV72DS7yO?b5mVMB*X~~%+mVLRf^P?a20@{D`E&biAFP@ z#RNTm0gGp{?fHXU#G~?ACjca0^T8A6VQtr@p$&c=pWnj<@1+qJX^tn-90MK833Of9 z`Wq^B>w8+@q8LdA_V@?MOJ{%pK9kO(gkhO!Bj}hSX^Dd}aIY@SgQ&1+F@2BQ4}&%W znG?%?#E*)V$HG>0>nvU%8Nd6P3L<#UGtbi6^{S^6`YLJ>TtF6J=ZY=h(j2b3RpOw$ zqnKZB)=6&+7QrRN$rJB8_~eAFJd*}{+>-G9N=$z|V-0HuvN>UJT6;%A%L${#g{)K( zE?1zal5Y1>$7tyj)B4UGks`~+OBAGlH|FtlV@6BM>7?<2qn7$mwZL$@5z=w zAR8U&nz56F>_aFQrI<8O0ATjr`Q}f!AH+E#{+{_xQ zFL4zoJ_XnXAYp@m5`1GE)wZy4W4?TO7rFQ24Ji_T%FT>LKuH>oKtsFd=4|;Q1Ux4P z5Lx~-E3Y?=jk-?A`MyvYMf4P8bwu%>r>>|NfjjzimDoMAhB*HoZVY z-8>Ec`d@`7Mx<(ui*SUmW9tC7jxRJ%y8-w|2PkTmsX+Zo?TaQDgVMG<`1P4HwN>X(K)< z8G-d{E}ndZB3p+LQ&(F%Dk+T0N#ZKPIRisvXm{xJZbf9*C0QD5M%#rFzc81U{eUtF z>V%#zAy6jqU4A&k@@Wm)NH(u7=;u7eo-hvXcEAlvtfJ7ygoqbDYzPrCr+d&bWhDIN zGItrbRFb}4Q+ekpF=aC+b(0AKG(*>QixUo~=1nYtJ{s0O|8EnVvYl9dGp>kfksjo0 zpGzGX#cj*~s-$nE0e)y-9AmT8$#s21AIu=xunddV7EUg%oLoz{SWEBKPtB-p`e%cE z2BF5SY^c07MZugk>d;`dZc*d80fbT5_`Ojg5MfSKyY&2 zkXLw~)dzBU1=KuG%S&VtY!ZiDOOc@~qma+P@YS{_zsGLNOQayQ7heOl#gN>5+pEV% zpciCDp!jc--US&m-W};OIH@ow+J8N_?DMmihw}egF);Ry)bMbj^#1>4N%4-O)Ufpw zVW{;NpPNbNL(d5f>CVc_oJdCG4Go>K&wu;olFpOa+3yA{BF{>=yz(zlDY@b${V_`a zTeF3e|ER@M&N|!yTb;)DgQcSDdT0n1>Cd;7hF zpz?kNIS-;HXP-LTSiQ^;m-GH}Et;=3c3yIMs&prD=zPkJ9^*d?fq%~Nlb(yvkA0*D z3{XUQZ)~nFeiH*|<><4mNk%_^{S;liRLi`IDJe}9D<64tXX@j@fXXo1soVO!ew~su z?Co@x{mt%%;{{o8&kA>!LEN%z>WHZl??T?2mb=uaYqpLe z{TP%9VIum;Gn9~N(QLw%|F6oX=sKu(-@b#IyYB-0XO(lV^983)wEtV+)q#I)R}b7x zL;UOSAA7fbRPw&t({={_&_rlY%hmb%=u>-Ie(8n@EAMI9_lhd}U~kK1l4;G}78gsx zh#*}9ZrPnnI3o@enKg0@?>OF(+6|G4l~s>k34Zgkf=)VmH?B13e-j#$d9iMm4O0{>pIW(&E5+Szt ztP^H|CMLeCzwe&&BX4{Eo}(@nfh7m`wCvkQoc|A#7m-B!4Ic~QLq79Q6UFu}LNu)r?2IZh2h3H8wuDB9=B=4H=0`<=%Et4(bH0hKs*etu7z*31 zX~H(!zE}JC_g~UAYpJ+?{>ZwA_bBCGCc@)-rh>+&&Nfdw0)>{15J^Ad+U`rNIKA@i$~y0 zF(4m)tA;S}1Yt<~ir63nbq3nFIIqdMS#@~tl~Uex0foddy44%9P$)G)i`qcr|ItTy z_xl)1z=1Q{CH(4q;JiSs_OzCoYuv8`ooY~8w)5Jo5M;G>)T=sI?%$?=`*b4^da{w# zD-|-R{i`h;`c%a?w_aV=01A%KMzdxCg`?&__yS0r1uoTSue>vsm7O7qf|uS2614ca zTb$`!@9p3u-@P*}{=u;^x=ssNAS-~x=|gcxa9Us!31Mq6czT}SZrQ@n?9|-b#RY5z zAVI_XzW(;?9qjmgW;=`|HR#WoOA}n+?`(+xSTDEpEKHeU<$@J*5KD`iXyi_b`vh0- z-$%L|ndD@m=R1K1=IlApHum$=4F@O~+A!ywmQ4_6Pr||RGN`MP>r?xw^$@FQDq2r1 zXu{Kn%R~=C=DY2EH&a|B_Rj4`<3Ow6cOSlf!2+Kt#LCO2qV^xl$_M6m^L~dl?Tqf* zyQZZ3vxfk72=6nPX=Yf(f@F|NiH1A%#Xu5+AqBI`B*~5MzCHD??U#?YH-NnCm54`E zwdMwiNny-qwjcEWb$oZv#TZS9+3V=`eN2e+*!w(mWGzKU12i%fA$k7ZVxP>OG0u3G zo|8Q_s(f)n`=`>%54lErj$R$P3o_}>B)lG?UMSgS{mA*xhup%#;)>>#wybeR3PS0; zTG(dOZp8zTg#YQJ2K;_r{YA`1@4}MdgX7h1y@{@X8?CL~p9dyH6iPc_?16m9(6DmM zHftUP4EsY#MM7lcj?K}%Uf~znMchi2F{J-P;{y1eh>+^rD}h?HC5{V(xbgbxh=CVFKWcfh ecTLYc` literal 0 HcmV?d00001 diff --git a/frontend/src/lib/components/PersonPropertySelect/PersonPropertySelect.stories.tsx b/frontend/src/lib/components/PropertySelect/PropertySelect.stories.tsx similarity index 79% rename from frontend/src/lib/components/PersonPropertySelect/PersonPropertySelect.stories.tsx rename to frontend/src/lib/components/PropertySelect/PropertySelect.stories.tsx index 868c62de41edd..35021b789889b 100644 --- a/frontend/src/lib/components/PersonPropertySelect/PersonPropertySelect.stories.tsx +++ b/frontend/src/lib/components/PropertySelect/PropertySelect.stories.tsx @@ -3,12 +3,13 @@ import { useState } from 'react' import { mswDecorator } from '~/mocks/browser' -import { PersonPropertySelect, PersonPropertySelectProps } from './PersonPropertySelect' +import { TaxonomicFilterGroupType } from '../TaxonomicFilter/types' +import { PropertySelect, PropertySelectProps } from './PropertySelect' -type Story = StoryObj -const meta: Meta = { - title: 'Filters/Person Property Select', - component: PersonPropertySelect, +type Story = StoryObj +const meta: Meta = { + title: 'Filters/Property Select', + component: PropertySelect, decorators: [ mswDecorator({ get: { @@ -31,7 +32,7 @@ const meta: Meta = { } export default meta -const Template: StoryFn = (props: Partial) => { +const Template: StoryFn = (props: Partial) => { const [selectedProperties, setSelectProperties] = useState([ '$initial_geoip_postal_code', '$initial_geoip_latitude', @@ -54,9 +55,10 @@ const Template: StoryFn = (props: Partial diff --git a/frontend/src/lib/components/PersonPropertySelect/PersonPropertySelect.tsx b/frontend/src/lib/components/PropertySelect/PropertySelect.tsx similarity index 93% rename from frontend/src/lib/components/PersonPropertySelect/PersonPropertySelect.tsx rename to frontend/src/lib/components/PropertySelect/PropertySelect.tsx index 55afbbc8fc898..93f3cc4cc22f9 100644 --- a/frontend/src/lib/components/PersonPropertySelect/PersonPropertySelect.tsx +++ b/frontend/src/lib/components/PropertySelect/PropertySelect.tsx @@ -11,11 +11,12 @@ import { LemonSnack } from 'lib/lemon-ui/LemonSnack/LemonSnack' import { Popover } from 'lib/lemon-ui/Popover/Popover' import { useState } from 'react' -export interface PersonPropertySelectProps { +export interface PropertySelectProps { addText: string onChange: (names: string[]) => void selectedProperties: string[] sortable?: boolean + taxonomicFilterGroup: TaxonomicFilterGroupType.PersonProperties | TaxonomicFilterGroupType.EventProperties } const SortableProperty = ({ @@ -46,12 +47,13 @@ const SortableProperty = ({ ) } -export const PersonPropertySelect = ({ +export const PropertySelect = ({ onChange, selectedProperties, addText, sortable = false, -}: PersonPropertySelectProps): JSX.Element => { + taxonomicFilterGroup, +}: PropertySelectProps): JSX.Element => { const [open, setOpen] = useState(false) const sensors = useSensors(useSensor(PointerSensor, { activationConstraint: { distance: 1 } })) @@ -114,7 +116,7 @@ export const PersonPropertySelect = ({ handleChange(value as string) setOpen(false) }} - taxonomicGroupTypes={[TaxonomicFilterGroupType.PersonProperties]} + taxonomicGroupTypes={[taxonomicFilterGroup]} /> } > diff --git a/frontend/src/scenes/insights/views/Funnels/FunnelPropertyCorrelationTable.tsx b/frontend/src/scenes/insights/views/Funnels/FunnelPropertyCorrelationTable.tsx index 1294cac810b66..8b0eadf36c0d3 100644 --- a/frontend/src/scenes/insights/views/Funnels/FunnelPropertyCorrelationTable.tsx +++ b/frontend/src/scenes/insights/views/Funnels/FunnelPropertyCorrelationTable.tsx @@ -5,8 +5,9 @@ import { LemonButton, LemonCheckbox } from '@posthog/lemon-ui' import { ConfigProvider, Empty, Table } from 'antd' import Column from 'antd/lib/table/Column' import { useActions, useValues } from 'kea' -import { PersonPropertySelect } from 'lib/components/PersonPropertySelect/PersonPropertySelect' import { PropertyKeyInfo } from 'lib/components/PropertyKeyInfo' +import { PropertySelect } from 'lib/components/PropertySelect/PropertySelect' +import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' import { VisibilitySensor } from 'lib/components/VisibilitySensor/VisibilitySensor' import { IconSelectProperties, IconTrendingDown, IconTrendingUp } from 'lib/lemon-ui/icons' import { Link } from 'lib/lemon-ui/Link' @@ -148,7 +149,8 @@ export function FunnelPropertyCorrelationTable(): JSX.Element | null { onClickOutside={() => setIsPropertiesOpen(false)} overlay={
- Excluded person properties - handleChange(properties)} selectedProperties={funnelCorrelationConfig.excluded_person_property_names || []} addText="Add exclusion" diff --git a/frontend/src/scenes/settings/project/PersonDisplayNameProperties.tsx b/frontend/src/scenes/settings/project/PersonDisplayNameProperties.tsx index 14a070b3e66d9..365fd58b875fe 100644 --- a/frontend/src/scenes/settings/project/PersonDisplayNameProperties.tsx +++ b/frontend/src/scenes/settings/project/PersonDisplayNameProperties.tsx @@ -1,6 +1,7 @@ import { LemonButton } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' -import { PersonPropertySelect } from 'lib/components/PersonPropertySelect/PersonPropertySelect' +import { PropertySelect } from 'lib/components/PropertySelect/PropertySelect' +import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' import { PERSON_DEFAULT_DISPLAY_NAME_PROPERTIES } from 'lib/constants' import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton' import { useEffect, useState } from 'react' @@ -27,7 +28,8 @@ export function PersonDisplayNameProperties(): JSX.Element { property to be found on the Person will be used. Drag the items to re-order the priority.

- setValue(properties)} selectedProperties={value || []} addText="Add" From 5d08ff51e9a3ae0a152a596560f52385cc3afafe Mon Sep 17 00:00:00 2001 From: David Newell Date: Fri, 26 Jan 2024 11:30:35 +0000 Subject: [PATCH 23/24] chore: LemonButton cleanup (#19579) --- ...ightstable--can-edit-series-name--dark.png | Bin 19287 -> 19320 bytes ...ghtstable--can-edit-series-name--light.png | Bin 19095 -> 19143 bytes frontend/src/lib/components/PageHeader.tsx | 4 +- .../lib/lemon-ui/LemonButton/LemonButton.scss | 418 ++++++++++++++---- .../lib/lemon-ui/LemonButton/LemonButton.tsx | 5 +- .../lemon-ui/LemonButton/LemonButton3000.scss | 333 -------------- .../InsightsTable/columns/SeriesColumn.tsx | 2 +- 7 files changed, 339 insertions(+), 423 deletions(-) delete mode 100644 frontend/src/lib/lemon-ui/LemonButton/LemonButton3000.scss diff --git a/frontend/__snapshots__/insights-insightstable--can-edit-series-name--dark.png b/frontend/__snapshots__/insights-insightstable--can-edit-series-name--dark.png index fae5dbbd4010915f4593f0ea5546449d8b6d9980..e586ab576988450292aac806b6924a276ce88f5f 100644 GIT binary patch literal 19320 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 brk&9TnhXh=aB)wae?U=I^<~lXH~#+vi03Gm diff --git a/frontend/__snapshots__/insights-insightstable--can-edit-series-name--light.png b/frontend/__snapshots__/insights-insightstable--can-edit-series-name--light.png index 07b2c3b92898d297a1511ac71054f41e0ccc08e1..76201f640a590f0e640d8b1853756604c4ef5a60 100644 GIT binary patch literal 19143 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 literal 19095 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 HU7!B}<3rw9 diff --git a/frontend/src/lib/components/PageHeader.tsx b/frontend/src/lib/components/PageHeader.tsx index cf041db3e4ac9..2e74c8668ac64 100644 --- a/frontend/src/lib/components/PageHeader.tsx +++ b/frontend/src/lib/components/PageHeader.tsx @@ -1,6 +1,6 @@ import clsx from 'clsx' import { useValues } from 'kea' -import { Within3000PageHeaderContext } from 'lib/lemon-ui/LemonButton/LemonButton' +import { WithinPageHeaderContext } from 'lib/lemon-ui/LemonButton/LemonButton' import { createPortal } from 'react-dom' import { DraggableToNotebookProps } from 'scenes/notebooks/AddToNotebook/DraggableToNotebook' @@ -22,7 +22,7 @@ export function PageHeader({ caption, buttons, tabbedPage }: PageHeaderProps): J {buttons && actionsContainer && createPortal( - {buttons}, + {buttons}, actionsContainer )} diff --git a/frontend/src/lib/lemon-ui/LemonButton/LemonButton.scss b/frontend/src/lib/lemon-ui/LemonButton/LemonButton.scss index 95cf04c36bf94..6423ec744e05c 100644 --- a/frontend/src/lib/lemon-ui/LemonButton/LemonButton.scss +++ b/frontend/src/lib/lemon-ui/LemonButton/LemonButton.scss @@ -1,50 +1,170 @@ +@mixin secondary-variables { + --button-border-color: var(--secondary-3000-button-border); + --lemon-button-bg-color-active: var(--bg-light); + --lemon-button-border-color-hover: var(--secondary-3000-button-border-hover); + --lemon-button-frame-bg-color: var(--secondary-3000-frame-bg); +} + +@mixin size-variables { + &--xsmall { + --lemon-button-padding-horizontal: 0.375rem; + --lemon-button-padding-adjacent-icon: 0.25rem; + --lemon-button-font-size: 0.75rem; + --lemon-button-icon-size: 0.875rem; + --lemon-button-height: 1.625rem; + --lemon-button-gap: 0.25rem; + --lemon-button-side-action-width: 1.5rem; + } + + &--small { + --lemon-button-padding-horizontal: 0.5rem; + --lemon-button-height: 2.0625rem; + --lemon-button-gap: 0.25rem; + --lemon-button-side-action-width: 1.75rem; + --lemon-button-icon-size: 1.25rem; + } + + &--large { + --lemon-button-font-size: 1rem; + --lemon-button-icon-size: 1.75rem; + --lemon-button-padding-adjacent-icon: 0.75rem; + --lemon-button-height: 3.0625rem; + --lemon-button-gap: 0.75rem; + } +} + +@mixin shared-variables { + --lemon-button-chrome-depth: 0.1875rem; + --lemon-button-padding-horizontal: 0.75rem; + --lemon-button-side-action-width: 2rem; +} + .LemonButton, .Link.LemonButton { - // Make sure we override .Link's styles where needed, e.g. padding + --lemon-button-hover-depth: -0.03125rem; + --lemon-button-press-depth: 0.03125rem; + --lemon-button-padding-adjacent-icon: 0.375rem; + --lemon-button-transition: opacity 200ms ease, transform 200ms ease; + --lemon-button-border-width: 0; + --lemon-button-bg-color: transparent; + --button-border-color: none; + --lemon-button-border-color-hover: none; + --lemon-button-frame-bg-color: none; + --lemon-button-font-size: 0.875rem; + --lemon-button-icon-size: 1.5rem; + --lemon-button-height: 2.3125rem; + --lemon-button-gap: 0.5rem; + --lemon-button-icon-opacity: 0.5; + --lemon-button-profile-picture-opacity: 0.75; // Profile pictures must be more prominent than icons + --lemon-button-color: var(--default); + + // column-gap: 4px; + // flex-direction: row; + // justify-content: flex-start; position: relative; + + // Make sure we override .Link's styles where needed, e.g. padding display: flex; - flex-direction: row; flex-shrink: 0; - gap: 0.5rem; align-items: center; - justify-content: flex-start; - padding: 0.25rem 0.75rem; - font-size: 0.875rem; - font-weight: 500; - line-height: 1.5rem; - text-align: left; + padding: 0; + font-family: var(--font-title); appearance: none !important; // Important as this gets overridden by Ant styles... cursor: pointer; user-select: none; background: none; - border: none; border-radius: var(--radius); - transition: background-color 200ms ease, color 200ms ease, border 200ms ease, opacity 200ms ease, - transform 100ms ease; + outline: none; + transition: var(--lemon-button-transition); .font-normal { font-family: var(--font-sans); } .LemonButton__chrome { + position: relative; display: flex; flex: 1; - gap: 0.5rem; - } - - .LemonButton__content { - display: flex; - flex: 1; + flex-direction: row; + flex-shrink: 0; + gap: var(--lemon-button-gap); align-items: center; - line-height: initial; + justify-content: flex-start; + width: 100%; + height: 100%; + min-height: var(--lemon-button-height); + padding: 0.25rem var(--lemon-button-padding-horizontal); + font-size: var(--lemon-button-font-size); + font-weight: 500; + line-height: 1.5rem; + color: var(--lemon-button-color); + text-align: left; + background: none; + border-color: transparent; + border-style: solid; + border-width: var(--lemon-button-border-width); + + .LemonButton__content { + display: flex; + flex: 1; + align-items: center; + overflow: hidden; + line-height: initial; + } + + .LemonButton__icon { + display: flex; + flex-shrink: 0; + place-items: center center; + font-size: var(--lemon-button-icon-size); + transition: color 200ms ease; + + > * { + opacity: var(--lemon-button-icon-opacity); + } + + > .ProfilePicture, + > .Lettermark { + opacity: var(--lemon-button-profile-picture-opacity); + } + } } &[aria-disabled='true']:not(.LemonButton--loading) { cursor: not-allowed; opacity: var(--opacity-disabled); + } + + &.LemonButton--active, + &:hover:not([aria-disabled='true']), + &:not([aria-disabled='true']):active { + --lemon-button-icon-opacity: 0.75; + --lemon-button-profile-picture-opacity: 1; + } + + &.LemonButton--full-width { + --lemon-button-padding-horizontal: 0.5rem; + + width: 100%; + } + + &.LemonButton--loading { + cursor: default; + } - > span { - cursor: not-allowed; + &.LemonButton--no-padding { + width: auto; + height: auto; + min-height: 0; + padding: 0; + + .LemonButton__chrome { + min-height: 0; + padding: 0; + } + + &.LemonButton--full-width { + width: 100%; } } @@ -62,16 +182,8 @@ } } - &.LemonButton--loading { - cursor: default; - } - - &.LemonButton--full-width { - width: 100%; - } - &.LemonButton--centered { - > span { + .LemonButton__chrome { justify-content: center !important; } @@ -81,69 +193,153 @@ } } - &.LemonButton--has-icon { - padding-left: 0.5rem; + &.LemonButton--primary { + --lemon-button-bg-color: var(--primary-3000-button-bg); + --lemon-button-bg-color-active: var(--primary-3000-button-bg); + --button-border-color: var(--primary-3000-button-border); + --lemon-button-border-color-hover: var(--primary-3000-button-border-hover); + --lemon-button-frame-bg-color: var(--primary-3000-frame-bg); + --lemon-button-color: var(--text-3000-light); + + &.LemonButton--status-alt { + --lemon-button-bg-color: var(--primary-3000-frame-bg-light); + --lemon-button-bg-color-active: var(--lemon-button-bg-color); + --button-border-color: var(--primary-3000-button-border); + --lemon-button-border-color-hover: var(--primary-3000-button-border-hover); + --lemon-button-frame-bg-color: var(--primary-3000-button-bg-dark); + --lemon-button-color: var(--text-3000-light); + } } - &.LemonButton--no-content { - padding-right: 0.5rem; - padding-left: 0.5rem; + &.LemonButton--secondary.LemonButton--status-alt:hover, + &.LemonButton--secondary.LemonButton--status-alt.LemonButton--active, + &.LemonButton--secondary:not(.LemonButton--status-alt, .LemonButton--status-danger) { + @include secondary-variables; } - &.LemonButton--xsmall { - gap: 0.25rem; - font-size: 0.75rem; + &.LemonButton--status-danger, + &.LemonButton--primary.LemonButton--status-danger, + &.LemonButton--secondary.LemonButton--status-danger { + --lemon-button-color: var(--danger-3000-button-border-hover); + --button-border-color: var(--danger-3000-button-border); + --lemon-button-border-color-hover: var(--danger-3000-button-border-hover); + --lemon-button-frame-bg-color: var(--danger-3000-frame-bg); + --lemon-button-icon-opacity: 1; + } - > span { - gap: 0.25rem; - } + &.LemonButton--secondary.LemonButton--status-alt { + --lemon-button-color: var(--muted); - .LemonButton__icon { - font-size: 0.875rem; + &.LemonButton--active, + &:hover:not([aria-disabled='true']) { + --lemon-button-color: var(--default); } } - &.LemonButton--small { - gap: 0.25rem; + &.LemonButton--primary, + &.LemonButton--secondary { + --lemon-button-border-width: 1px; - > span { - gap: 0.25rem; + &:not([aria-disabled='true']):hover .LemonButton__chrome { + &::after { + border-color: var(--lemon-button-border-color-hover); + } } - .LemonButton__icon { - font-size: 1.25rem; + &.LemonButton--has-icon:not(.LemonButton--no-content, .LemonButton--no-padding) { + --lemon-button-padding-left: var(--lemon-button-padding-adjacent-icon); } - } - &.LemonButton--large { - font-size: 1rem; + &.LemonButton--has-side-icon:not(.LemonButton--no-content, .LemonButton--no-padding) { + --lemon-button-padding-right: var(--lemon-button-padding-adjacent-icon); + } - > span { - gap: 0.75rem; + .LemonButton__chrome { + padding: calc(0.25rem - var(--lemon-button-chrome-depth) * 0.5) + var(--lemon-button-padding-right, var(--lemon-button-padding-horizontal)) + calc(0.25rem + var(--lemon-button-chrome-depth) * 0.5) + var(--lemon-button-padding-left, var(--lemon-button-padding-horizontal)); + background: transparent; + + & > * { + z-index: 1; // Places button content above the ::after element + } + + &::before { + position: absolute; + inset: -1px; + z-index: 0; + content: ''; + border: 1px solid var(--button-border-color); + border-radius: var(--radius); + } + + &::after { + position: absolute; + inset: -1px -1px calc(var(--lemon-button-chrome-depth) - 1px) -1px; + z-index: 0; + content: ''; + background: var(--lemon-button-bg-color); + border: 1px solid var(--button-border-color); + border-radius: var(--radius); + box-shadow: 0 var(--lemon-button-chrome-depth) 0 -1px var(--lemon-button-frame-bg-color); + transition: opacity 200ms ease; + } } - .LemonButton__icon { - font-size: 1.75rem; + &.LemonButton--active, + &:not([aria-disabled='true']):active { + .LemonButton__chrome { + &::after { + border: 1px solid var(--lemon-button-border-color-hover); + } + } } - } - &.LemonButton--no-padding { - width: auto; - height: auto; - min-height: 0; - padding: 0; + &:hover:not([aria-disabled='true']) { + --lemon-button-depth: var(--lemon-button-hover-depth); + } - &.LemonButton--full-width { - width: 100%; + &:not([aria-disabled='true']):active { + --lemon-button-depth: var(--lemon-button-press-depth); + } + + &:hover:not([aria-disabled='true']), + &:not([aria-disabled='true']):active { + .LemonButton__chrome { + transform: translateY(var(--lemon-button-depth)); + + &::after { + box-shadow: 0 calc(var(--lemon-button-chrome-depth) - var(--lemon-button-depth)) 0 -1px var(--lemon-button-frame-bg-color); + } + + &::before { + bottom: calc(var(--lemon-button-depth) - 1px); + } + } + } + + &.LemonButton--active { + .LemonButton__chrome { + &::after { + background: var(--lemon-button-bg-color-active); + } + } } } - .LemonButton__icon { - display: flex; - flex-shrink: 0; - place-items: center center; - font-size: 1.5rem; - transition: color 200ms ease; + &.LemonButton--tertiary { + &:not([aria-disabled='true']):hover, + &.LemonButton--active { + background-color: var(--glass-border-3000); + } + + &.LemonButton--status-danger { + &:not([aria-disabled='true']):hover, + &.LemonButton--active { + background-color: var(--danger-highlight); + } + } } .ant-tooltip & { @@ -160,6 +356,9 @@ color: #fff !important; } } + + @include shared-variables; + @include size-variables; } .LemonButtonWithSideAction { @@ -169,24 +368,75 @@ &--full-width { width: 100%; } -} -.LemonButtonWithSideAction__spacer { - box-sizing: content-box; + &:hover > .LemonButton--secondary.LemonButton--status-alt { + @include secondary-variables; + } - &.LemonButtonWithSideAction__spacer--divider { - border-left: 1px solid currentColor; + & .LemonButtonWithSideAction__spacer { + box-sizing: content-box; + width: calc( + var(--lemon-button-side-action-width) - + var(--lemon-button-padding-right, var(--lemon-button-padding-horizontal)) + ); + height: 1.25rem; + color: var(--muted); + + &--divider { + padding: 0; + margin-left: calc(var(--lemon-button-padding-horizontal) / 2); + border-left: 1px solid currentColor; + } } -} -.LemonButtonWithSideAction__side-button { - position: absolute; - top: 50%; - right: 0.5rem; - background: none; - transform: translateY(-50%); + // SideAction buttons are buttons next to other buttons in the DOM but layered on top. since they're on another button, we don't want them to look like buttons. + & .LemonButtonWithSideAction__side-button { + position: absolute; + top: 1px; + right: 1px; + bottom: calc(var(--lemon-button-chrome-depth) + 1px); + z-index: 1; // Places button content above the main button + background: none; + border-top-right-radius: calc(var(--radius) - 1px); + border-bottom-right-radius: calc(var(--radius) - 1px); + transform: none; + + .LemonButton { + --lemon-button-depth: 0px; + --lemon-button-icon-opacity: 0.5; + + width: var(--lemon-button-side-action-width); + height: 100%; + border-top-left-radius: 0; + border-bottom-left-radius: 0; + } + + .LemonButton__chrome { + justify-content: center !important; + padding: 0 !important; + border: none !important; + + &::before, + &::after { + content: none !important; + } + } + + &:not([aria-disabled='true']):active { + .LemonButton__chrome { + transform: none !important; + } + } + + .LemonButton__icon { + color: currentColor; + } - .LemonButton--small & { - right: 0.375rem; + &:not([aria-disabled='true']):hover { + background: rgb(0 0 0 / 10%); + } } + + @include shared-variables; + @include size-variables; } diff --git a/frontend/src/lib/lemon-ui/LemonButton/LemonButton.tsx b/frontend/src/lib/lemon-ui/LemonButton/LemonButton.tsx index 24110638ed64a..ba3c60908c905 100644 --- a/frontend/src/lib/lemon-ui/LemonButton/LemonButton.tsx +++ b/frontend/src/lib/lemon-ui/LemonButton/LemonButton.tsx @@ -1,5 +1,4 @@ import './LemonButton.scss' -import './LemonButton3000.scss' import { IconChevronDown } from '@posthog/icons' import clsx from 'clsx' @@ -139,7 +138,7 @@ export const LemonButton: React.FunctionComponent { const [popoverVisibility, popoverPlacement] = useContext(PopoverReferenceContext) || [false, null] - const within3000PageHeader = useContext(Within3000PageHeaderContext) + const within3000PageHeader = useContext(WithinPageHeaderContext) if (!active && popoverVisibility) { active = true @@ -278,7 +277,7 @@ export const LemonButton: React.FunctionComponent(false) +export const WithinPageHeaderContext = React.createContext(false) export interface LemonButtonWithDropdownProps extends LemonButtonPropsBase { dropdown: LemonButtonDropdown diff --git a/frontend/src/lib/lemon-ui/LemonButton/LemonButton3000.scss b/frontend/src/lib/lemon-ui/LemonButton/LemonButton3000.scss deleted file mode 100644 index e1a6b1b7551e8..0000000000000 --- a/frontend/src/lib/lemon-ui/LemonButton/LemonButton3000.scss +++ /dev/null @@ -1,333 +0,0 @@ -@mixin secondary-variables { - --button-border-color: var(--secondary-3000-button-border); - --lemon-button-bg-color-active: var(--bg-light); - --lemon-button-border-color-hover: var(--secondary-3000-button-border-hover); - --lemon-button-frame-bg-color: var(--secondary-3000-frame-bg); -} - -.posthog-3000 { - --lemon-button-chrome-depth: 0.1875rem; - --lemon-button-hover-depth: -0.03125rem; - --lemon-button-press-depth: 0.03125rem; - --lemon-button-padding-horizontal: 0.75rem; - --lemon-button-padding-adjacent-icon: 0.375rem; - --lemon-button-side-action-width: 2rem; - - .LemonButton, - .Link.LemonButton { - --lemon-button-transition: opacity 200ms ease, transform 200ms ease; - --lemon-button-border-width: 0; - --lemon-button-bg-color: transparent; - --button-border-color: none; - --lemon-button-border-color-hover: none; - --lemon-button-frame-bg-color: none; - --lemon-button-font-size: 0.875rem; - --lemon-button-height: 2.3125rem; - --lemon-button-gap: 0.5rem; - --lemon-button-icon-opacity: 0.5; - --lemon-button-profile-picture-opacity: 0.75; // Profile pictures must be more prominent than icons - --lemon-button-color: var(--default); - - position: relative; - padding: 0; - font-family: var(--font-title); - cursor: pointer; - outline: none; - transition: var(--lemon-button-transition); - - .LemonButton__chrome { - position: relative; - display: flex; - flex-direction: row; - flex-shrink: 0; - gap: var(--lemon-button-gap); - align-items: center; - justify-content: flex-start; - width: 100%; - height: 100%; - min-height: var(--lemon-button-height); - padding: 0.25rem var(--lemon-button-padding-horizontal); - font-size: var(--lemon-button-font-size); - font-weight: 500; - line-height: 1.5rem; - color: var(--lemon-button-color); - text-align: left; - background: none; - border-color: transparent; - border-style: solid; - border-width: var(--lemon-button-border-width); - - .LemonButton__content { - overflow: hidden; - } - - .LemonButton__icon { - > * { - opacity: var(--lemon-button-icon-opacity); - } - - > .ProfilePicture, - > .Lettermark { - opacity: var(--lemon-button-profile-picture-opacity); - } - } - } - - &.LemonButton--active, - &:hover:not([aria-disabled='true']), - &:not([aria-disabled='true']):active { - --lemon-button-icon-opacity: 0.75; - --lemon-button-profile-picture-opacity: 1; - } - - &.LemonButton--full-width { - --lemon-button-padding-horizontal: 0.5rem; - } - - &.LemonButton--xsmall { - --lemon-button-padding-horizontal: 0.375rem; - --lemon-button-padding-adjacent-icon: 0.25rem; - --lemon-button-font-size: 0.75rem; - --lemon-button-height: 1.625rem; - --lemon-button-gap: 0.25rem; - --lemon-button-side-action-width: 1.5rem; - } - - &.LemonButton--small { - --lemon-button-padding-horizontal: 0.5rem; - --lemon-button-height: 2.0625rem; - --lemon-button-gap: 0.25rem; - --lemon-button-side-action-width: 1.75rem; - } - - &.LemonButton--large { - --lemon-button-font-size: 1rem; - --lemon-button-padding-adjacent-icon: 0.75rem; - --lemon-button-height: 3.0625rem; - --lemon-button-gap: 0.75rem; - } - - &.LemonButton--no-padding { - min-height: 0; - padding: 0; - - .LemonButton__chrome { - min-height: 0; - padding: 0; - } - } - - &.LemonButton--primary { - --lemon-button-bg-color: var(--primary-3000-button-bg); - --lemon-button-bg-color-active: var(--primary-3000-button-bg); - --button-border-color: var(--primary-3000-button-border); - --lemon-button-border-color-hover: var(--primary-3000-button-border-hover); - --lemon-button-frame-bg-color: var(--primary-3000-frame-bg); - --lemon-button-color: var(--text-3000-light); - - &.LemonButton--status-alt { - --lemon-button-bg-color: var(--primary-3000-frame-bg-light); - --lemon-button-bg-color-active: var(--lemon-button-bg-color); - --button-border-color: var(--primary-3000-button-border); - --lemon-button-border-color-hover: var(--primary-3000-button-border-hover); - --lemon-button-frame-bg-color: var(--primary-3000-button-bg-dark); - --lemon-button-color: var(--text-3000-light); - } - } - - &.LemonButton--secondary.LemonButton--status-alt:hover, - &.LemonButton--secondary.LemonButton--status-alt.LemonButton--active, - &.LemonButton--secondary:not(.LemonButton--status-alt, .LemonButton--status-danger) { - @include secondary-variables; - } - - &.LemonButton--status-danger, - &.LemonButton--primary.LemonButton--status-danger, - &.LemonButton--secondary.LemonButton--status-danger { - --lemon-button-color: var(--danger-3000-button-border-hover); - --button-border-color: var(--danger-3000-button-border); - --lemon-button-border-color-hover: var(--danger-3000-button-border-hover); - --lemon-button-frame-bg-color: var(--danger-3000-frame-bg); - --lemon-button-icon-opacity: 1; - } - - &.LemonButton--secondary.LemonButton--status-alt { - --lemon-button-color: var(--muted); - - &.LemonButton--active, - &:hover:not([aria-disabled='true']) { - --lemon-button-color: var(--default); - } - } - - &.LemonButton--primary, - &.LemonButton--secondary { - --lemon-button-border-width: 1px; - - &:not([aria-disabled='true']):hover .LemonButton__chrome { - &::after { - border-color: var(--lemon-button-border-color-hover); - } - } - - &.LemonButton--has-icon:not(.LemonButton--no-content, .LemonButton--no-padding) { - --lemon-button-padding-left: var(--lemon-button-padding-adjacent-icon); - } - - &.LemonButton--has-side-icon:not(.LemonButton--no-content, .LemonButton--no-padding) { - --lemon-button-padding-right: var(--lemon-button-padding-adjacent-icon); - } - - .LemonButton__chrome { - padding: calc(0.25rem - var(--lemon-button-chrome-depth) * 0.5) - var(--lemon-button-padding-right, var(--lemon-button-padding-horizontal)) - calc(0.25rem + var(--lemon-button-chrome-depth) * 0.5) - var(--lemon-button-padding-left, var(--lemon-button-padding-horizontal)); - background: transparent; - - & > * { - z-index: 1; // Places button content above the ::after element - } - - &::before { - position: absolute; - inset: -1px; - z-index: 0; - content: ''; - border: 1px solid var(--button-border-color); - border-radius: var(--radius); - } - - &::after { - position: absolute; - inset: -1px -1px calc(var(--lemon-button-chrome-depth) - 1px) -1px; - z-index: 0; - content: ''; - background: var(--lemon-button-bg-color); - border: 1px solid var(--button-border-color); - border-radius: var(--radius); - box-shadow: 0 var(--lemon-button-chrome-depth) 0 -1px var(--lemon-button-frame-bg-color); - transition: opacity 200ms ease; - } - } - - &.LemonButton--active, - &:not([aria-disabled='true']):active { - .LemonButton__chrome { - &::after { - border: 1px solid var(--lemon-button-border-color-hover); - } - } - } - - &:hover:not([aria-disabled='true']) { - --lemon-button-depth: var(--lemon-button-hover-depth); - } - - &:not([aria-disabled='true']):active { - --lemon-button-depth: var(--lemon-button-press-depth); - } - - &:hover:not([aria-disabled='true']), - &:not([aria-disabled='true']):active { - .LemonButton__chrome { - transform: translateY(var(--lemon-button-depth)); - - &::after { - box-shadow: 0 calc(var(--lemon-button-chrome-depth) - var(--lemon-button-depth)) 0 -1px var(--lemon-button-frame-bg-color); - } - - &::before { - bottom: calc(var(--lemon-button-depth) - 1px); - } - } - } - - &.LemonButton--active { - .LemonButton__chrome { - &::after { - background: var(--lemon-button-bg-color-active); - } - } - } - } - - &.LemonButton--tertiary { - &:not([aria-disabled='true']):hover, - &.LemonButton--active { - background-color: var(--glass-border-3000); - } - - &.LemonButton--status-danger { - &:not([aria-disabled='true']):hover, - &.LemonButton--active { - background-color: var(--danger-highlight); - } - } - } - } - - .LemonButtonWithSideAction:hover > .LemonButton--secondary.LemonButton--status-alt { - @include secondary-variables; - } - - .LemonButtonWithSideAction__spacer { - width: calc( - var(--lemon-button-side-action-width) - - var(--lemon-button-padding-right, var(--lemon-button-padding-horizontal)) - ); - height: 1.25rem; - color: var(--muted); - - &--divider { - padding: 0; - margin-left: calc(var(--lemon-button-padding-horizontal) / 2); - } - } - - // SideAction buttons are buttons next to other buttons in the DOM but layered on top. since they're on another button, we don't want them to look like buttons. - .LemonButtonWithSideAction__side-button { - top: 1px; - right: 1px; - bottom: calc(var(--lemon-button-chrome-depth) + 1px); - z-index: 1; // Places button content above the main button - border-top-right-radius: calc(var(--radius) - 1px); - border-bottom-right-radius: calc(var(--radius) - 1px); - transform: none; - - .LemonButton { - --lemon-button-depth: 0px; - --lemon-button-icon-opacity: 0.5; - - width: var(--lemon-button-side-action-width); - height: 100%; - border-top-left-radius: 0; - border-bottom-left-radius: 0; - } - - .LemonButton__chrome { - justify-content: center !important; - padding: 0 !important; - border: none !important; - - &::before, - &::after { - content: none !important; - } - } - - &:not([aria-disabled='true']):active { - .LemonButton__chrome { - transform: none !important; - } - } - - .LemonButton__icon { - color: currentColor; - } - - &:not([aria-disabled='true']):hover { - background: rgb(0 0 0 / 10%); - } - } -} diff --git a/frontend/src/scenes/insights/views/InsightsTable/columns/SeriesColumn.tsx b/frontend/src/scenes/insights/views/InsightsTable/columns/SeriesColumn.tsx index 81896e494e251..838d8d0bc6c1e 100644 --- a/frontend/src/scenes/insights/views/InsightsTable/columns/SeriesColumn.tsx +++ b/frontend/src/scenes/insights/views/InsightsTable/columns/SeriesColumn.tsx @@ -28,7 +28,7 @@ export function SeriesColumnItem({ const showCountedByTag = !!indexedResults.find(({ action }) => action?.math && action.math !== 'total') return ( -
+
Date: Fri, 26 Jan 2024 11:55:09 +0000 Subject: [PATCH 24/24] working user property select --- .../project/SessionRecordingSettings.tsx | 54 +++++++++---------- posthog/api/team.py | 9 +++- posthog/api/test/test_team.py | 2 +- 3 files changed, 35 insertions(+), 30 deletions(-) diff --git a/frontend/src/scenes/settings/project/SessionRecordingSettings.tsx b/frontend/src/scenes/settings/project/SessionRecordingSettings.tsx index dd34fed9e5bd7..6fd350771d95b 100644 --- a/frontend/src/scenes/settings/project/SessionRecordingSettings.tsx +++ b/frontend/src/scenes/settings/project/SessionRecordingSettings.tsx @@ -1,11 +1,12 @@ -import { LemonButton, LemonSelect, LemonSelectMultiple, LemonSwitch, LemonTag, Link } from '@posthog/lemon-ui' +import { LemonButton, LemonSelect, LemonSwitch, LemonTag, Link } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' import { AuthorizedUrlList } from 'lib/components/AuthorizedUrlList/AuthorizedUrlList' import { AuthorizedUrlListType } from 'lib/components/AuthorizedUrlList/authorizedUrlListLogic' import { EventSelect } from 'lib/components/EventSelect/EventSelect' import { FlaggedFeature } from 'lib/components/FlaggedFeature' import { FlagSelector } from 'lib/components/FlagSelector' -import { PersonPropertySelect } from 'lib/components/PersonPropertySelect/PersonPropertySelect' +import { PropertySelect } from 'lib/components/PropertySelect/PropertySelect' +import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' import { FEATURE_FLAGS, SESSION_REPLAY_MINIMUM_DURATION_OPTIONS } from 'lib/constants' import { useFeatureFlag } from 'lib/hooks/useFeatureFlag' import { IconAutoAwesome, IconCancel, IconPlus, IconSelectEvents } from 'lib/lemon-ui/icons' @@ -323,18 +324,18 @@ export function ReplaySummarySettings(): JSX.Element | null { We always send the event name and timestamp. The only event data sent are values of the properties selected here.

-
- { - updateSummaryConfig({ - ...currentConfig, - included_event_properties: properties, - }) - }} - value={currentConfig.included_event_properties || []} - /> -
+ { + updateSummaryConfig({ + ...currentConfig, + included_event_properties: properties, + }) + }} + selectedProperties={currentConfig.included_event_properties || []} + addText="Add property" + />

@@ -345,19 +346,18 @@ export function ReplaySummarySettings(): JSX.Element | null { We always send the first and last seen dates. The only user data sent are values of the properties selected here.

-
- { - updateSummaryConfig({ - ...currentConfig, - important_user_properties: properties, - }) - }} - selectedProperties={currentConfig.important_user_properties || []} - addText="Add property" - /> -
+ { + updateSummaryConfig({ + ...currentConfig, + important_user_properties: properties, + }) + }} + selectedProperties={currentConfig.important_user_properties || []} + addText="Add property" + />

)} diff --git a/posthog/api/team.py b/posthog/api/team.py index 822d64935dc7d..a8abb72e89f20 100644 --- a/posthog/api/team.py +++ b/posthog/api/team.py @@ -42,7 +42,6 @@ from posthog.utils import get_ip_address, get_week_start_for_country_code - class PremiumMultiprojectPermissions(permissions.BasePermission): """Require user to have all necessary premium features on their plan for create access to the endpoint.""" @@ -233,7 +232,13 @@ def validate_session_replay_ai_summary_config(self, value) -> Dict | None: if not isinstance(value, Dict): raise exceptions.ValidationError("Must provide a dictionary or None.") - allowed_keys = ["included_event_properties", "opt_in", "preferred_events", "excluded_events"] + allowed_keys = [ + "included_event_properties", + "opt_in", + "preferred_events", + "excluded_events", + "important_user_properties", + ] if not all(key in allowed_keys for key in value.keys()): raise exceptions.ValidationError( "Must provide a dictionary with only allowed keys: {}".format(allowed_keys) diff --git a/posthog/api/test/test_team.py b/posthog/api/test/test_team.py index f905a0e971a8b..fd685c8117b0c 100644 --- a/posthog/api/test/test_team.py +++ b/posthog/api/test/test_team.py @@ -743,7 +743,7 @@ def test_can_set_and_unset_session_replay_config(self) -> None: "unexpected json - no recordX", {"key": "something"}, "invalid_input", - "Must provide a dictionary with only allowed keys: ['included_event_properties', 'opt_in', 'preferred_events', 'excluded_events']", + "Must provide a dictionary with only allowed keys: ['included_event_properties', 'opt_in', 'preferred_events', 'excluded_events', 'important_user_properties']", ], ] )