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:
parent
30cfe5322e
commit
e6877d3436
@ -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) => {
|
||||
|
@ -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 {
|
||||
|
@ -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`, {
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
)
|
||||
|
@ -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,
|
||||
)
|
||||
|
@ -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.',
|
||||
|
@ -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
|
||||
'''
|
||||
|
@ -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,
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user