Example #1
0
    def update(
        self,
        instance: Dashboard,
        validated_data: Dict,
        *args: Any,
        **kwargs: Any,
    ) -> Dashboard:
        user = cast(User, self.context["request"].user)
        can_user_restrict = instance.can_user_restrict(user.id)
        if "restriction_level" in validated_data and not can_user_restrict:
            raise exceptions.PermissionDenied(
                "Only the dashboard owner and project admins have the restriction rights required to change the dashboard's restriction level."
            )

        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:
            report_user_action(user, "dashboard updated",
                               instance.get_analytics_metadata())

        return instance
Example #2
0
    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)

        variants = (validated_data.get("filters", {}).get("multivariate", {})
                    or {}).get("variants", [])
        variant_rollout_sum = 0
        for variant in variants:
            variant_rollout_sum += variant.get("rollout_percentage")

        if len(variants) > 0 and variant_rollout_sum != 100:
            raise exceptions.ValidationError(
                "Invalid variant definitions: Variant rollout percentages must sum to 100."
            )

        FeatureFlag.objects.filter(key=validated_data["key"],
                                   team=self.context["team_id"],
                                   deleted=True).delete()
        instance: FeatureFlag = super().create(validated_data)
        instance.update_cohorts()

        report_user_action(
            request.user,
            "feature flag created",
            instance.get_analytics_metadata(),
        )

        return instance
Example #3
0
    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)
        instance.refresh_from_db()
        report_user_action(
            self.context["request"].user,
            "action updated",
            {
                **instance.get_analytics_metadata(),
                "updated_by_creator": self.context["request"].user == instance.created_by,
            },
        )
        return instance
Example #4
0
 def update(self, instance: FeatureFlagOverride,
            validated_data: Dict) -> FeatureFlagOverride:
     self._ensure_team_and_feature_flag_match(validated_data)
     request = self.context["request"]
     instance = super().update(instance, validated_data)
     report_user_action(request.user, self._analytics_updated_event_name,
                        instance.get_analytics_metadata())
     return instance
Example #5
0
    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")},
            )

        report_user_action(validated_data["created_by"], "action created", instance.get_analytics_metadata())

        return instance
Example #6
0
    def destroy(self, request, *args, **kwgars):

        instance = self.get_object()  # type: ignore

        metadata = instance.get_analytics_metadata() if hasattr(
            instance,
            "get_analytics_metadata",
        ) else {}

        self.perform_destroy(instance)

        report_user_action(request.user,
                           f"{instance._meta.verbose_name} deleted", metadata)
        return response.Response(status=status.HTTP_204_NO_CONTENT)
Example #7
0
    def wrapper(*args, **kwargs):
        instance = args[0].get_object()
        user = args[1].user
        metadata = instance.get_analytics_metadata() if hasattr(
            instance,
            "get_analytics_metadata",
        ) else {}

        func_result = func(*args, **kwargs)

        report_user_action(user, f"{instance._meta.verbose_name} deleted",
                           metadata)

        return func_result
Example #8
0
def annotation_created(sender, instance, created, raw, using, **kwargs):
    """Trigger action_defined hooks on Annotation creation."""

    if created:
        raw_hook_event.send(
            sender=None,
            event_name="annotation_created",
            instance=instance,
            payload=AnnotationSerializer(instance).data,
            user=instance.team,
        )

    if instance.created_by:
        event_name: str = "annotation created" if created else "annotation updated"
        report_user_action(instance.created_by, event_name,
                           instance.get_analytics_metadata())
Example #9
0
    def update(self, instance: FeatureFlag, validated_data: Dict, *args: Any,
               **kwargs: Any) -> FeatureFlag:
        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)
        instance.update_cohorts()

        report_user_action(
            request.user,
            "feature flag updated",
            instance.get_analytics_metadata(),
        )
        return instance
Example #10
0
    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:
            pending_version = get_and_update_pending_version(cohort)

            calculate_cohort_ch.delay(cohort.id, pending_version)

        report_user_action(request.user, "cohort created",
                           cohort.get_analytics_metadata())
        return cohort
Example #11
0
 def create(self, validated_data: Dict) -> FeatureFlagOverride:
     self._ensure_team_and_feature_flag_match(validated_data)
     feature_flag_override, created = FeatureFlagOverride.objects.update_or_create(
         feature_flag=validated_data["feature_flag"],
         user=validated_data["user"],
         team_id=self.context["team_id"],
         defaults={"override_value": validated_data["override_value"]},
     )
     request = self.context["request"]
     if created:
         report_user_action(
             request.user,
             self._analytics_created_event_name,
             feature_flag_override.get_analytics_metadata(),
         )
     else:
         report_user_action(
             request.user,
             self._analytics_updated_event_name,
             feature_flag_override.get_analytics_metadata(),
         )
     return feature_flag_override
Example #12
0
    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.description = validated_data.get("description",
                                                cohort.description)
        cohort.groups = validated_data.get("groups", cohort.groups)
        cohort.is_static = validated_data.get("is_static", cohort.is_static)
        deleted_state = validated_data.get("deleted", None)

        is_deletion_change = deleted_state is not None and cohort.deleted != deleted_state
        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:
                # You can't update a static cohort using the trend/stickiness thing
                if request.FILES.get("csv"):
                    self._calculate_static_by_csv(request.FILES["csv"], cohort)
            else:
                # Increment based on pending versions
                pending_version = get_and_update_pending_version(cohort)

                calculate_cohort_ch.delay(cohort.id, pending_version)

        report_user_action(
            request.user,
            "cohort updated",
            {
                **cohort.get_analytics_metadata(), "updated_by_creator":
                request.user == cohort.created_by
            },
        )

        return cohort
Example #13
0
    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)
        use_dashboard: int = validated_data.pop("use_dashboard", None)
        validated_data = self._update_creation_mode(validated_data,
                                                    use_template,
                                                    use_dashboard)
        tags = validated_data.pop(
            "tags", None
        )  # tags are created separately below as global tag relationships
        dashboard = Dashboard.objects.create(team=team, **validated_data)

        if use_template:
            try:
                create_dashboard_from_template(use_template, dashboard)
            except AttributeError:
                raise serializers.ValidationError(
                    {"use_template": "Invalid value provided."})

        elif use_dashboard:
            try:
                from posthog.api.insight import InsightSerializer

                existing_dashboard = Dashboard.objects.get(id=use_dashboard,
                                                           team=team)
                existing_dashboard_items = existing_dashboard.items.all()
                for dashboard_item in existing_dashboard_items:
                    override_dashboard_item_data = {
                        "id": None,  # to create a new Insight
                        "dashboard": dashboard.pk,
                        "last_refresh": now(),
                    }
                    new_data = {
                        **InsightSerializer(
                            dashboard_item,
                            context=self.context,
                        ).data,
                        **override_dashboard_item_data,
                    }
                    new_tags = new_data.pop("tags", None)
                    insight_serializer = InsightSerializer(
                        data=new_data,
                        context=self.context,
                    )
                    insight_serializer.is_valid()
                    insight_serializer.save()

                    # Create new insight's tags separately. Force create tags on dashboard duplication.
                    self._attempt_set_tags(new_tags,
                                           insight_serializer.instance,
                                           force_create=True)

            except Dashboard.DoesNotExist:
                raise serializers.ValidationError(
                    {"use_dashboard": "Invalid value provided"})

        elif request.data.get("items"):
            for item in request.data["items"]:
                Insight.objects.create(
                    **{
                        key: value
                        for key, value in item.items()
                        if key not in ("id", "deleted", "dashboard", "team")
                    },
                    dashboard=dashboard,
                    team=team,
                )

        # Manual tag creation since this create method doesn't call super()
        self._attempt_set_tags(tags, dashboard)

        report_user_action(
            request.user,
            "dashboard created",
            {
                **dashboard.get_analytics_metadata(),
                "from_template":
                bool(use_template),
                "template_key":
                use_template,
                "duplicated":
                bool(use_dashboard),
                "dashboard_id":
                use_dashboard,
            },
        )

        return dashboard