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
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
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
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
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
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)
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
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())
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
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
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
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
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