def to_representation(self, instance) -> Dict: data = UserSerializer(instance=instance).data data[ "redirect_url"] = "/personalization" if self.enable_new_onboarding( ) else "/ingestion" return data
class DashboardSerializer(serializers.ModelSerializer): items = serializers.SerializerMethodField() # type: ignore created_by = UserSerializer(read_only=True) use_template = serializers.CharField(write_only=True, allow_blank=True, required=False) class Meta: model = Dashboard fields = [ "id", "name", "description", "pinned", "items", "created_at", "created_by", "is_shared", "share_token", "deleted", "creation_mode", "use_template", "filters", "tags", ] read_only_fields = ("creation_mode",) def create(self, validated_data: Dict, *args: Any, **kwargs: Any) -> Dashboard: request = self.context["request"] validated_data["created_by"] = request.user team = Team.objects.get(id=self.context["team_id"]) use_template: str = validated_data.pop("use_template", None) creation_mode = "template" if use_template else "default" dashboard = Dashboard.objects.create(team=team, creation_mode=creation_mode, **validated_data) if use_template: try: create_dashboard_from_template(use_template, dashboard) except AttributeError: raise serializers.ValidationError({"use_template": "Invalid value provided."}) elif request.data.get("items"): for item in request.data["items"]: DashboardItem.objects.create( **{key: value for key, value in item.items() if key not in ("id", "deleted", "dashboard", "team")}, dashboard=dashboard, team=team, ) posthoganalytics.capture( request.user.distinct_id, "dashboard created", {**dashboard.get_analytics_metadata(), "from_template": bool(use_template), "template_key": use_template}, ) return dashboard def update( # type: ignore self, instance: Dashboard, validated_data: Dict, *args: Any, **kwargs: Any, ) -> Dashboard: validated_data.pop("use_template", None) # Remove attribute if present if validated_data.get("is_shared") and not instance.share_token: instance.share_token = secrets.token_urlsafe(22) instance = super().update(instance, validated_data) if "request" in self.context: posthoganalytics.capture( self.context["request"].user.distinct_id, "dashboard updated", instance.get_analytics_metadata() ) return instance def get_items(self, dashboard: Dashboard): if self.context["view"].action == "list": return None items = dashboard.items.filter(deleted=False).order_by("order").all() self.context.update({"dashboard": dashboard}) return DashboardItemSerializer(items, many=True, context=self.context).data
def to_representation(self, instance): serializer = UserSerializer(instance=instance) return serializer.data
def render_template(template_name: str, request: HttpRequest, context: Dict = {}) -> HttpResponse: from loginas.utils import is_impersonated_session template = get_template(template_name) context["opt_out_capture"] = os.getenv( "OPT_OUT_CAPTURE", False) or is_impersonated_session(request) context["self_capture"] = settings.SELF_CAPTURE if os.environ.get("SENTRY_DSN"): context["sentry_dsn"] = os.environ["SENTRY_DSN"] if settings.DEBUG and not settings.TEST: context["debug"] = True context["git_rev"] = get_git_commit() context["git_branch"] = get_git_branch() if settings.E2E_TESTING: context["e2e_testing"] = True if settings.SELF_CAPTURE: api_token = get_self_capture_api_token(request) if api_token: context["js_posthog_api_key"] = f"'{api_token}'" context["js_posthog_host"] = "window.location.origin" else: context["js_posthog_api_key"] = "'sTMFPsFhdP1Ssg'" context["js_posthog_host"] = "'https://app.posthog.com'" context["js_capture_internal_metrics"] = settings.CAPTURE_INTERNAL_METRICS context["js_url"] = settings.JS_URL posthog_app_context: Dict[str, Any] = { "persisted_feature_flags": settings.PERSISTED_FEATURE_FLAGS, "anonymous": not request.user or not request.user.is_authenticated, } # Set the frontend app context if not request.GET.get("no-preloaded-app-context"): from posthog.api.team import TeamSerializer from posthog.api.user import User, UserSerializer from posthog.views import preflight_check posthog_app_context = { "current_user": None, "current_team": None, "preflight": json.loads(preflight_check(request).getvalue()), "default_event_name": get_default_event_name(), "switched_team": getattr(request, "switched_team", False), **posthog_app_context, } if request.user.pk: user_serialized = UserSerializer(request.user, context={"request": request}, many=False) posthog_app_context["current_user"] = user_serialized.data team = cast(User, request.user).team if team: team_serialized = TeamSerializer(team, context={"request": request}, many=False) posthog_app_context["current_team"] = team_serialized.data context["posthog_app_context"] = json.dumps(posthog_app_context, default=json_uuid_convert) html = template.render(context, request=request) return HttpResponse(html)
def get_created_by(self, dashboard_item: DashboardItem): if dashboard_item.created_by: return UserSerializer(dashboard_item.created_by).data
class FeatureFlagSerializer(serializers.HyperlinkedModelSerializer): created_by = UserSerializer(required=False, read_only=True) # :TRICKY: Needed for backwards compatibility filters = serializers.DictField(source="get_filters", required=False) is_simple_flag = serializers.SerializerMethodField() rollout_percentage = serializers.SerializerMethodField() class Meta: model = FeatureFlag fields = [ "id", "name", "key", "filters", "deleted", "active", "created_by", "created_at", "is_simple_flag", "rollout_percentage", ] # Simple flags are ones that only have rollout_percentage # That means server side libraries are able to gate these flags without calling to the server def get_is_simple_flag(self, feature_flag: FeatureFlag) -> bool: return len(feature_flag.groups) == 1 and all( len(group.get("properties", [])) == 0 for group in feature_flag.groups) def get_rollout_percentage(self, feature_flag: FeatureFlag) -> Optional[int]: if self.get_is_simple_flag(feature_flag): return feature_flag.groups[0].get("rollout_percentage") else: return None def validate_key(self, value): exclude_kwargs = {} if self.instance: exclude_kwargs = {"pk": self.instance.pk} if (FeatureFlag.objects.filter( key=value, team=self.context["request"].user.team, deleted=False).exclude(**exclude_kwargs).exists()): raise serializers.ValidationError( "There is already a feature flag with this key.", code="unique") return value def create(self, validated_data: Dict, *args: Any, **kwargs: Any) -> FeatureFlag: request = self.context["request"] validated_data["created_by"] = request.user validated_data["team_id"] = self.context["team_id"] self._update_filters(validated_data) FeatureFlag.objects.filter(key=validated_data["key"], team=request.user.team, deleted=True).delete() instance = super().create(validated_data) posthoganalytics.capture( request.user.distinct_id, "feature flag created", instance.get_analytics_metadata(), ) return instance def update(self, instance: FeatureFlag, validated_data: Dict, *args: Any, **kwargs: Any) -> FeatureFlag: # type: ignore request = self.context["request"] validated_key = validated_data.get("key", None) if validated_key: FeatureFlag.objects.filter(key=validated_key, team=instance.team, deleted=True).delete() self._update_filters(validated_data) instance = super().update(instance, validated_data) posthoganalytics.capture( request.user.distinct_id, "feature flag updated", instance.get_analytics_metadata(), ) return instance def _update_filters(self, validated_data): if "get_filters" in validated_data: validated_data["filters"] = validated_data.pop("get_filters")
def render_template(template_name: str, request: HttpRequest, context: Dict = {}) -> HttpResponse: from posthog.models import Team template = get_template(template_name) # Get the current user's team (or first team in the instance) to set opt out capture & self capture configs team: Optional[Team] = None try: team = request.user.team # type: ignore except (Team.DoesNotExist, AttributeError): team = Team.objects.first() context["self_capture"] = False context["opt_out_capture"] = os.getenv("OPT_OUT_CAPTURE", False) if os.environ.get("SENTRY_DSN"): context["sentry_dsn"] = os.environ["SENTRY_DSN"] if settings.DEBUG and not settings.TEST: context["debug"] = True context["git_rev"] = get_git_commit() context["git_branch"] = get_git_branch() if settings.SELF_CAPTURE: context["self_capture"] = True if team: context["js_posthog_api_key"] = f"'{team.api_token}'" context["js_posthog_host"] = "window.location.origin" else: context["js_posthog_api_key"] = "'sTMFPsFhdP1Ssg'" context["js_posthog_host"] = "'https://app.posthog.com'" context["js_capture_internal_metrics"] = settings.CAPTURE_INTERNAL_METRICS # Set the frontend app context if not request.GET.get("no-preloaded-app-context"): from posthog.api.user import UserSerializer from posthog.models import EventDefinition from posthog.views import preflight_check posthog_app_context: Dict = { "current_user": None, "preflight": json.loads(preflight_check(request).getvalue()), "default_event_name": get_default_event_name(), "persisted_feature_flags": settings.PERSISTED_FEATURE_FLAGS, } if request.user.pk: user = UserSerializer(request.user, context={"request": request}, many=False) posthog_app_context["current_user"] = user.data context["posthog_app_context"] = json.dumps(posthog_app_context, default=json_uuid_convert) else: context["posthog_app_context"] = "null" html = template.render(context, request=request) return HttpResponse(html)
class ActionSerializer(serializers.HyperlinkedModelSerializer): steps = ActionStepSerializer(many=True, required=False) count = serializers.SerializerMethodField() created_by = UserSerializer(read_only=True) class Meta: model = Action fields = [ "id", "name", "post_to_slack", "slack_message_format", "steps", "created_at", "deleted", "count", "is_calculating", "last_calculated_at", "created_by", "team_id", ] extra_kwargs = {"team_id": {"read_only": True}} def get_count(self, action: Action) -> Optional[int]: if hasattr(action, "count"): return action.count # type: ignore return None def _calculate_action(self, action: Action) -> None: calculate_action.delay(action_id=action.pk) def validate(self, attrs): exclude_args = {} if self.instance: include_args = {"team": self.instance.team} exclude_args = {"id": self.instance.pk} else: attrs["team_id"] = self.context["view"].team_id include_args = {"team_id": attrs["team_id"]} if Action.objects.filter( name=attrs["name"], deleted=False, **include_args).exclude(**exclude_args).exists(): raise serializers.ValidationError( {"name": "This project already has an action with that name."}, code="unique") return attrs def create(self, validated_data: Any) -> Any: steps = validated_data.pop("steps", []) validated_data["created_by"] = self.context["request"].user instance = super().create(validated_data) for step in steps: ActionStep.objects.create( action=instance, **{ key: value for key, value in step.items() if key not in ("isNew", "selection") }, ) self._calculate_action(instance) posthoganalytics.capture(validated_data["created_by"].distinct_id, "action created", instance.get_analytics_metadata()) return instance def update(self, instance: Any, validated_data: Dict[str, Any]) -> Any: steps = validated_data.pop("steps", None) # If there's no steps property at all we just ignore it # If there is a step property but it's an empty array [], we'll delete all the steps if steps is not None: # remove steps not in the request step_ids = [step["id"] for step in steps if step.get("id")] instance.steps.exclude(pk__in=step_ids).delete() for step in steps: if step.get("id"): step_instance = ActionStep.objects.get(pk=step["id"]) step_serializer = ActionStepSerializer( instance=step_instance) step_serializer.update(step_instance, step) else: ActionStep.objects.create( action=instance, **{ key: value for key, value in step.items() if key not in ("isNew", "selection") }, ) instance = super().update(instance, validated_data) self._calculate_action(instance) instance.refresh_from_db() posthoganalytics.capture( self.context["request"].user.distinct_id, "action updated", { **instance.get_analytics_metadata(), "updated_by_creator": self.context["request"].user == instance.created_by, }, ) return instance
class CohortSerializer(serializers.ModelSerializer): created_by = UserSerializer(required=False, read_only=True) count = serializers.SerializerMethodField() class Meta: model = Cohort fields = [ "id", "name", "groups", "deleted", "is_calculating", "created_by", "created_at", "last_calculation", "errors_calculating", "count", "is_static", ] read_only_fields = [ "id", "is_calculating", "created_by", "created_at", "last_calculation", "errors_calculating", "count", ] def _handle_csv(self, file, cohort: Cohort) -> None: decoded_file = file.read().decode("utf-8").splitlines() reader = csv.reader(decoded_file) distinct_ids_and_emails = [ row[0] for row in reader if len(row) > 0 and row ] calculate_cohort_from_csv.delay(cohort.pk, distinct_ids_and_emails) def create(self, validated_data: Dict, *args: Any, **kwargs: Any) -> Cohort: request = self.context["request"] validated_data["created_by"] = request.user if not validated_data.get("is_static"): validated_data["is_calculating"] = True cohort = Cohort.objects.create(team_id=self.context["team_id"], **validated_data) if request.FILES.get("csv"): self._handle_csv(request.FILES["csv"], cohort) posthoganalytics.capture(request.user.distinct_id, "cohort created", cohort.get_analytics_metadata()) if not cohort.is_static: calculate_cohort.delay(cohort_id=cohort.pk) return cohort def update(self, cohort: Cohort, validated_data: Dict, *args: Any, **kwargs: Any) -> Cohort: # type: ignore request = self.context["request"] cohort.name = validated_data.get("name", cohort.name) cohort.groups = validated_data.get("groups", cohort.groups) cohort.deleted = validated_data.get("deleted", cohort.deleted) if not cohort.is_static: cohort.is_calculating = True cohort.save() if request.FILES.get("csv") and cohort.is_static: self._handle_csv(request.FILES["csv"], cohort) posthoganalytics.capture( request.user.distinct_id, "cohort updated", { **cohort.get_analytics_metadata(), "updated_by_creator": request.user == cohort.created_by }, ) if not cohort.is_static: calculate_cohort.delay(cohort_id=cohort.pk) return cohort def get_count(self, action: Cohort) -> Optional[int]: if hasattr(action, "count"): return action.count # type: ignore return None
class CohortSerializer(serializers.ModelSerializer): created_by = UserSerializer(required=False, read_only=True) count = serializers.SerializerMethodField() earliest_timestamp_func = lambda team_id: Event.objects.earliest_timestamp( team_id) class Meta: model = Cohort fields = [ "id", "name", "groups", "deleted", "is_calculating", "created_by", "created_at", "last_calculation", "errors_calculating", "count", "is_static", ] read_only_fields = [ "id", "is_calculating", "created_by", "created_at", "last_calculation", "errors_calculating", "count", ] def _handle_csv(self, file, cohort: Cohort) -> None: decoded_file = file.read().decode("utf-8").splitlines() reader = csv.reader(decoded_file) distinct_ids_and_emails = [ row[0] for row in reader if len(row) > 0 and row ] calculate_cohort_from_list.delay(cohort.pk, distinct_ids_and_emails) def create(self, validated_data: Dict, *args: Any, **kwargs: Any) -> Cohort: request = self.context["request"] validated_data["created_by"] = request.user if not validated_data.get("is_static"): validated_data["is_calculating"] = True cohort = Cohort.objects.create(team_id=self.context["team_id"], **validated_data) if cohort.is_static: self._handle_static(cohort, request) else: calculate_cohort.delay(cohort_id=cohort.pk) posthoganalytics.capture(request.user.distinct_id, "cohort created", cohort.get_analytics_metadata()) return cohort def _handle_static(self, cohort: Cohort, request: Request): if request.FILES.get("csv"): self._calculate_static_by_csv(request.FILES["csv"], cohort) else: try: filter = Filter(request=request) team = request.user.team target_entity = get_target_entity(request) if filter.shown_as == TRENDS_STICKINESS: stickiness_filter = StickinessFilter( request=request, team=team, get_earliest_timestamp=self.earliest_timestamp_func) self._handle_stickiness_people(target_entity, cohort, stickiness_filter) else: self._handle_trend_people(target_entity, cohort, filter) except Exception as e: capture_exception(e) raise ValueError("This cohort has no conditions") def _calculate_static_by_csv(self, file, cohort: Cohort) -> None: decoded_file = file.read().decode("utf-8").splitlines() reader = csv.reader(decoded_file) distinct_ids_and_emails = [ row[0] for row in reader if len(row) > 0 and row ] calculate_cohort_from_list.delay(cohort.pk, distinct_ids_and_emails) def _calculate_static_by_people(self, people: List[str], cohort: Cohort) -> None: calculate_cohort_from_list.delay(cohort.pk, people) def _handle_stickiness_people(self, target_entity: Entity, cohort: Cohort, filter: StickinessFilter) -> None: events = stickiness_process_entity_type(target_entity, cohort.team, filter) events = stickiness_format_intervals(events, filter) people = stickiness_fetch_people(events, cohort.team, filter) ids = [ person.distinct_ids[0] for person in people if len(person.distinct_ids) ] self._calculate_static_by_people(ids, cohort) def _handle_trend_people(self, target_entity: Entity, cohort: Cohort, filter: Filter) -> None: events = filter_by_type(entity=target_entity, team=cohort.team, filter=filter) people = calculate_people(team=cohort.team, events=events, filter=filter) ids = [ person.distinct_ids[0] for person in people if len(person.distinct_ids) ] self._calculate_static_by_people(ids, cohort) def update(self, cohort: Cohort, validated_data: Dict, *args: Any, **kwargs: Any) -> Cohort: # type: ignore request = self.context["request"] cohort.name = validated_data.get("name", cohort.name) cohort.groups = validated_data.get("groups", cohort.groups) deleted_state = validated_data.get("deleted", None) is_deletion_change = deleted_state is not None if is_deletion_change: cohort.deleted = deleted_state if not cohort.is_static and not is_deletion_change: cohort.is_calculating = True cohort.save() if not deleted_state: if cohort.is_static: self._handle_static(cohort, request) else: calculate_cohort.delay(cohort_id=cohort.pk) posthoganalytics.capture( request.user.distinct_id, "cohort updated", { **cohort.get_analytics_metadata(), "updated_by_creator": request.user == cohort.created_by }, ) return cohort def get_count(self, action: Cohort) -> Optional[int]: if hasattr(action, "count"): return action.count # type: ignore return None