0
0
mirror of https://github.com/PostHog/posthog.git synced 2024-11-21 13:39:22 +01:00

feat: record non-onboarding product intents for data warehouse (#25859)

Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
This commit is contained in:
Raquel Smith 2024-10-29 09:52:29 -07:00 committed by GitHub
parent 30cfe5322e
commit e6877d3436
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 73 additions and 11 deletions

View File

@ -6,6 +6,7 @@ import api from 'lib/api'
import posthog from 'posthog-js'
import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic'
import { Scene } from 'scenes/sceneTypes'
import { teamLogic } from 'scenes/teamLogic'
import { urls } from 'scenes/urls'
import {
@ -16,6 +17,7 @@ import {
manualLinkSources,
ManualLinkSourceType,
PipelineTab,
ProductKey,
SourceConfig,
SourceFieldConfig,
} from '~/types'
@ -731,6 +733,8 @@ export const sourceWizardLogic = kea<sourceWizardLogicType>([
['resetTable', 'createTableSuccess'],
dataWarehouseSettingsLogic,
['loadSources'],
teamLogic,
['addProductIntent'],
],
}),
reducers({
@ -1129,6 +1133,9 @@ export const sourceWizardLogic = kea<sourceWizardLogicType>([
setManualLinkingProvider: () => {
actions.onNext()
},
selectConnector: () => {
actions.addProductIntent({ product_type: ProductKey.DATA_WAREHOUSE, intent_context: 'selected connector' })
},
})),
urlToAction(({ actions }) => ({
'/data-warehouse/:kind/redirect': ({ kind = '' }, searchParams) => {

View File

@ -347,6 +347,7 @@ const teamActionsMapping: Record<
updated_at: () => null,
uuid: () => null,
live_events_token: () => null,
product_intents: () => null,
}
function nameAndLink(logItem?: ActivityLogItem): JSX.Element {

View File

@ -145,13 +145,14 @@ export const teamLogic = kea<teamLogicType>([
resetToken: async () => await api.update(`api/environments/${values.currentTeamId}/reset_token`, {}),
addProductIntent: async ({
product_type,
intent_context,
}: {
product_type: ProductKey
intent_context?: string | null
}) =>
await api.update(`api/environments/${values.currentTeamId}/add_product_intent`, {
product_type,
intent_context: null,
intent_context: intent_context ?? undefined,
}),
recordProductIntentOnboardingComplete: async ({ product_type }: { product_type: ProductKey }) =>
await api.update(`api/environments/${values.currentTeamId}/complete_product_onboarding`, {

View File

@ -545,6 +545,13 @@ export interface TeamType extends TeamBasicType {
extra_settings?: Record<string, string | number | boolean | undefined>
modifiers?: HogQLQueryModifiers
default_modifiers?: HogQLQueryModifiers
product_intents?: ProductIntentType[]
}
export interface ProductIntentType {
product_type: string
created_at: string
onboarding_completed_at?: string
}
// This type would be more correct without `Partial<TeamType>`, but it's only used in the shared dashboard/insight

View File

@ -44,7 +44,11 @@ from posthog.permissions import (
TeamMemberStrictManagementPermission,
)
from posthog.user_permissions import UserPermissions, UserPermissionsSerializerMixin
from posthog.utils import get_ip_address, get_week_start_for_country_code
from posthog.utils import (
get_instance_realm,
get_ip_address,
get_week_start_for_country_code,
)
class ProjectSerializer(serializers.ModelSerializer):
@ -195,7 +199,9 @@ class ProjectBackwardCompatSerializer(ProjectBackwardCompatBasicSerializer, User
def get_product_intents(self, obj):
project = obj
team = project.passthrough_team
return ProductIntent.objects.filter(team=team).values("product_type", "created_at", "onboarding_completed_at")
return ProductIntent.objects.filter(team=team).values(
"product_type", "created_at", "onboarding_completed_at", "updated_at"
)
@staticmethod
def validate_session_recording_linked_flag(value) -> dict | None:
@ -572,7 +578,7 @@ class ProjectViewSet(TeamAndOrgViewSetMixin, viewsets.ModelViewSet):
product_intent.updated_at = datetime.now(tz=UTC)
product_intent.save()
if created and isinstance(user, User):
if isinstance(user, User):
report_user_action(
user,
"user showed product intent",
@ -582,6 +588,10 @@ class ProjectViewSet(TeamAndOrgViewSetMixin, viewsets.ModelViewSet):
"$current_url": current_url,
"$session_id": session_id,
"intent_context": request.data.get("intent_context"),
"is_first_intent_for_product": created,
"intent_created_at": product_intent.created_at,
"intent_updated_at": product_intent.updated_at,
"realm": get_instance_realm(),
},
team=team,
)
@ -612,6 +622,10 @@ class ProjectViewSet(TeamAndOrgViewSetMixin, viewsets.ModelViewSet):
"$current_url": current_url,
"$session_id": session_id,
"intent_context": request.data.get("intent_context"),
"is_first_intent_for_product": created,
"intent_created_at": product_intent.created_at,
"intent_updated_at": product_intent.updated_at,
"realm": get_instance_realm(),
},
team=team,
)
@ -626,6 +640,10 @@ class ProjectViewSet(TeamAndOrgViewSetMixin, viewsets.ModelViewSet):
"product_key": product_type,
"$current_url": current_url,
"$session_id": session_id,
"intent_context": request.data.get("intent_context"),
"intent_created_at": product_intent.created_at,
"intent_updated_at": product_intent.updated_at,
"realm": get_instance_realm(),
},
team=team,
)

View File

@ -6,17 +6,17 @@ from uuid import UUID
from django.shortcuts import get_object_or_404
from loginas.utils import is_impersonated_session
from posthog.auth import PersonalAPIKeyAuthentication
from posthog.jwt import PosthogJwtAudience, encode_jwt
from rest_framework import exceptions, request, response, serializers, viewsets
from rest_framework.permissions import BasePermission, IsAuthenticated
from posthog.api.routing import TeamAndOrgViewSetMixin
from posthog.api.shared import TeamBasicSerializer
from posthog.api.utils import action
from posthog.auth import PersonalAPIKeyAuthentication
from posthog.constants import AvailableFeature
from posthog.event_usage import report_user_action
from posthog.geoip import get_geoip_properties
from posthog.jwt import PosthogJwtAudience, encode_jwt
from posthog.models import ProductIntent, Team, User
from posthog.models.activity_logging.activity_log import (
Detail,
@ -43,7 +43,11 @@ from posthog.permissions import (
get_organization_from_view,
)
from posthog.user_permissions import UserPermissions, UserPermissionsSerializerMixin
from posthog.utils import get_ip_address, get_week_start_for_country_code
from posthog.utils import (
get_instance_realm,
get_ip_address,
get_week_start_for_country_code,
)
class PremiumMultiProjectPermissions(BasePermission): # TODO: Rename to include "Env" in name
@ -211,7 +215,9 @@ class TeamSerializer(serializers.ModelSerializer, UserPermissionsSerializerMixin
)
def get_product_intents(self, obj):
return ProductIntent.objects.filter(team=obj).values("product_type", "created_at", "onboarding_completed_at")
return ProductIntent.objects.filter(team=obj).values(
"product_type", "created_at", "onboarding_completed_at", "updated_at"
)
@staticmethod
def validate_session_recording_linked_flag(value) -> dict | None:
@ -582,7 +588,7 @@ class TeamViewSet(TeamAndOrgViewSetMixin, viewsets.ModelViewSet):
product_intent.updated_at = datetime.now(tz=UTC)
product_intent.save()
if created and isinstance(user, User):
if isinstance(user, User):
report_user_action(
user,
"user showed product intent",
@ -592,6 +598,10 @@ class TeamViewSet(TeamAndOrgViewSetMixin, viewsets.ModelViewSet):
"$current_url": current_url,
"$session_id": session_id,
"intent_context": request.data.get("intent_context"),
"is_first_intent_for_product": created,
"intent_created_at": product_intent.created_at,
"intent_updated_at": product_intent.updated_at,
"realm": get_instance_realm(),
},
team=team,
)
@ -621,6 +631,10 @@ class TeamViewSet(TeamAndOrgViewSetMixin, viewsets.ModelViewSet):
"$current_url": current_url,
"$session_id": session_id,
"intent_context": request.data.get("intent_context"),
"is_first_intent_for_product": created,
"intent_created_at": product_intent.created_at,
"intent_updated_at": product_intent.updated_at,
"realm": get_instance_realm(),
},
team=team,
)
@ -635,6 +649,10 @@ class TeamViewSet(TeamAndOrgViewSetMixin, viewsets.ModelViewSet):
"product_key": product_type,
"$current_url": current_url,
"$session_id": session_id,
"intent_context": request.data.get("intent_context"),
"intent_created_at": product_intent.created_at,
"intent_updated_at": product_intent.updated_at,
"realm": get_instance_realm(),
},
team=team,
)

View File

@ -97,8 +97,8 @@
'/home/runner/work/posthog/posthog/posthog/api/survey.py: Warning [SurveyViewSet > SurveySerializer]: unable to resolve type hint for function "get_conditions". Consider using a type hint or @extend_schema_field. Defaulting to string.',
'/home/runner/work/posthog/posthog/posthog/api/web_experiment.py: Warning [WebExperimentViewSet]: could not derive type of path parameter "project_id" because model "posthog.models.web_experiment.WebExperiment" contained no such field. Consider annotating parameter with @extend_schema. Defaulting to "string".',
'Warning: encountered multiple names for the same choice set (HrefMatchingEnum). This may be unwanted even though the generated schema is technically correct. Add an entry to ENUM_NAME_OVERRIDES to fix the naming.',
'Warning: enum naming encountered a non-optimally resolvable collision for fields named "kind". The same name has been used for multiple choice sets in multiple components. The collision was resolved with "KindCfaEnum". add an entry to ENUM_NAME_OVERRIDES to fix the naming.',
'Warning: enum naming encountered a non-optimally resolvable collision for fields named "kind". The same name has been used for multiple choice sets in multiple components. The collision was resolved with "Kind069Enum". add an entry to ENUM_NAME_OVERRIDES to fix the naming.',
'Warning: enum naming encountered a non-optimally resolvable collision for fields named "kind". The same name has been used for multiple choice sets in multiple components. The collision was resolved with "KindCfaEnum". add an entry to ENUM_NAME_OVERRIDES to fix the naming.',
'Warning: enum naming encountered a non-optimally resolvable collision for fields named "type". The same name has been used for multiple choice sets in multiple components. The collision was resolved with "TypeF73Enum". add an entry to ENUM_NAME_OVERRIDES to fix the naming.',
'Warning: encountered multiple names for the same choice set (EffectivePrivilegeLevelEnum). This may be unwanted even though the generated schema is technically correct. Add an entry to ENUM_NAME_OVERRIDES to fix the naming.',
'Warning: encountered multiple names for the same choice set (MembershipLevelEnum). This may be unwanted even though the generated schema is technically correct. Add an entry to ENUM_NAME_OVERRIDES to fix the naming.',

View File

@ -523,7 +523,8 @@
'''
SELECT "posthog_productintent"."product_type",
"posthog_productintent"."created_at",
"posthog_productintent"."onboarding_completed_at"
"posthog_productintent"."onboarding_completed_at",
"posthog_productintent"."updated_at"
FROM "posthog_productintent"
WHERE "posthog_productintent"."team_id" = 2
'''

View File

@ -26,6 +26,7 @@ from posthog.models.utils import generate_random_token_personal
from posthog.temporal.common.client import sync_connect
from posthog.temporal.common.schedule import describe_schedule
from posthog.test.base import APIBaseTest
from posthog.utils import get_instance_realm
def team_api_test_factory():
@ -1042,6 +1043,10 @@ def team_api_test_factory():
"$session_id": "test_session_id",
"intent_context": "onboarding product selected",
"$set_once": {"first_onboarding_product_selected": "product_analytics"},
"is_first_intent_for_product": True,
"intent_created_at": datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC),
"intent_updated_at": datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC),
"realm": get_instance_realm(),
},
team=self.team,
)
@ -1073,6 +1078,10 @@ def team_api_test_factory():
"product_key": "product_analytics",
"$current_url": "https://posthogtest.com/my-url",
"$session_id": "test_session_id",
"intent_context": None,
"intent_created_at": datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC),
"intent_updated_at": datetime(2024, 1, 5, 0, 0, 0, tzinfo=UTC),
"realm": get_instance_realm(),
},
team=self.team,
)