Skip to content

Commit

Permalink
compute blast radius
Browse files Browse the repository at this point in the history
  • Loading branch information
neilkakkar committed Jan 26, 2024
1 parent b77359e commit 97390e7
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 6 deletions.
1 change: 1 addition & 0 deletions posthog/api/feature_flag.py
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,7 @@ def user_blast_radius(self, request: request.Request, **kwargs):
condition = request.data.get("condition") or {}
group_type_index = request.data.get("group_type_index", None)

# TODO: Handle distinct_id and $group_key properties, which are not currently supported
users_affected, total_users = get_user_blast_radius(self.team, condition, group_type_index)

return Response(
Expand Down
8 changes: 4 additions & 4 deletions posthog/api/test/__snapshots__/test_feature_flag.ambr
Original file line number Diff line number Diff line change
Expand Up @@ -276,12 +276,12 @@
(SELECT id
FROM person
WHERE team_id = 2
AND ((has(['none'], replaceRegexpAll(JSONExtractRaw(properties, 'group'), '^"|"$', ''))
OR has(['1', '2', '3'], replaceRegexpAll(JSONExtractRaw(properties, 'group'), '^"|"$', '')))) )
AND (((has(['none'], replaceRegexpAll(JSONExtractRaw(properties, 'group'), '^"|"$', '')))
OR (has(['1', '2', '3'], replaceRegexpAll(JSONExtractRaw(properties, 'group'), '^"|"$', ''))))) )
GROUP BY id
HAVING max(is_deleted) = 0
AND ((has(['none'], replaceRegexpAll(JSONExtractRaw(argMax(person.properties, version), 'group'), '^"|"$', ''))
OR has(['1', '2', '3'], replaceRegexpAll(JSONExtractRaw(argMax(person.properties, version), 'group'), '^"|"$', '')))) SETTINGS optimize_aggregation_in_order = 1)
AND (((has(['none'], replaceRegexpAll(JSONExtractRaw(argMax(person.properties, version), 'group'), '^"|"$', '')))
OR (has(['1', '2', '3'], replaceRegexpAll(JSONExtractRaw(argMax(person.properties, version), 'group'), '^"|"$', ''))))) SETTINGS optimize_aggregation_in_order = 1)
'''
# ---
# name: TestBlastRadius.test_user_blast_radius_with_single_cohort.1
Expand Down
31 changes: 31 additions & 0 deletions posthog/api/test/test_feature_flag.py
Original file line number Diff line number Diff line change
Expand Up @@ -4197,6 +4197,37 @@ def test_user_blast_radius(self):
response_json = response.json()
self.assertDictContainsSubset({"users_affected": 4, "total_users": 10}, response_json)

@freeze_time("2024-01-11")
def test_user_blast_radius_with_relative_date_filters(self):
for i in range(8):
_create_person(
team_id=self.team.pk,
distinct_ids=[f"person{i}"],
properties={"group": f"{i}", "created_at": f"2023-0{i+1}-04"},
)

response = self.client.post(
f"/api/projects/{self.team.id}/feature_flags/user_blast_radius",
{
"condition": {
"properties": [
{
"key": "created_at",
"type": "person",
"value": "-10m",
"operator": "is_date_before",
}
],
"rollout_percentage": 100,
}
},
)

self.assertEqual(response.status_code, status.HTTP_200_OK)

response_json = response.json()
self.assertDictContainsSubset({"users_affected": 3, "total_users": 8}, response_json)

def test_user_blast_radius_with_zero_users(self):
response = self.client.post(
f"/api/projects/{self.team.id}/feature_flags/user_blast_radius",
Expand Down
19 changes: 17 additions & 2 deletions posthog/models/feature_flag/user_blast_radius.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,19 @@
from posthog.models.filters import Filter
from posthog.models.property import GroupTypeIndex
from posthog.models.team.team import Team
from posthog.queries.base import relative_date_parse_for_feature_flag_matching


def replace_proxy_properties(team: Team, feature_flag_condition: dict):
prop_groups = Filter(data=feature_flag_condition, team=team).property_groups

for prop in prop_groups.flat:
if prop.operator in ("is_date_before", "is_date_after"):
relative_date = relative_date_parse_for_feature_flag_matching(str(prop.value))
if relative_date:
prop.value = relative_date.strftime("%Y-%m-%d %H:%M:%S")

return Filter(data={"properties": prop_groups.to_dict()}, team=team)


def get_user_blast_radius(
Expand All @@ -19,14 +32,16 @@ def get_user_blast_radius(
# No rollout % calculations here, since it makes more sense to compute that on the frontend
properties = feature_flag_condition.get("properties") or []

cleaned_filter = replace_proxy_properties(team, feature_flag_condition)

if group_type_index is not None:
try:
from ee.clickhouse.queries.groups_join_query import GroupsJoinQuery
except Exception:
return 0, 0

if len(properties) > 0:
filter = Filter(data=feature_flag_condition, team=team)
filter = cleaned_filter

for property in filter.property_groups.flat:
if property.group_type_index is None or (property.group_type_index != group_type_index):
Expand All @@ -50,7 +65,7 @@ def get_user_blast_radius(
return total_affected_count, team.groups_seen_so_far(group_type_index)

if len(properties) > 0:
filter = Filter(data=feature_flag_condition, team=team)
filter = cleaned_filter
cohort_filters = []
for property in filter.property_groups.flat:
if property.type in ["cohort", "precalculated-cohort", "static-cohort"]:
Expand Down

0 comments on commit 97390e7

Please sign in to comment.