Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions src/sentry/api/endpoints/team_time_to_resolution.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from datetime import timedelta

from django.db.models import Avg, F
from django.db.models.functions import TruncDay
from django.db.models.functions import Coalesce, TruncDay
from rest_framework.response import Response

from sentry.api.base import EnvironmentMixin
Expand All @@ -29,16 +29,17 @@ def get(self, request, team):
)
.annotate(bucket=TruncDay("date_added"))
.values("bucket", "prev_history_date")
.annotate(ttr=F("date_added") - F("prev_history_date"))
# We do the coalesce here to handle historical data. At some point every `RESOLVED` row
# will have a non-null `prev_history_date`, and at that point we could remove this.
.annotate(
ttr=F("date_added") - Coalesce(F("prev_history_date"), F("group__first_seen"))
)
.annotate(avg_ttr=Avg("ttr"))
)
sums = defaultdict(lambda: {"sum": timedelta(), "count": 0})
for gh in history_list:
key = str(gh["bucket"].date())
if gh["ttr"] is not None:
# If a `GroupHistory` row has no `prev_history_date` then this will end up being
# None. Isn't a problem long term, but for older data we will be missing this value.
sums[key]["sum"] += gh["ttr"]
sums[key]["sum"] += gh["ttr"]
sums[key]["count"] += 1

avgs = {}
Expand Down
6 changes: 6 additions & 0 deletions src/sentry/testutils/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -1092,10 +1092,15 @@ def create_group_history(
release: Optional[Release] = None,
actor: Actor = None,
prev_history: GroupHistory = None,
date_added: datetime = None,
) -> GroupHistory:
prev_history_date = None
if prev_history:
prev_history_date = prev_history.date_added

kwargs = {}
if date_added:
kwargs["date_added"] = date_added
return GroupHistory.objects.create(
organization=group.organization,
group=group,
Expand All @@ -1105,4 +1110,5 @@ def create_group_history(
status=status,
prev_history=prev_history,
prev_history_date=prev_history_date,
**kwargs,
)
84 changes: 29 additions & 55 deletions tests/sentry/api/endpoints/test_team_time_to_resolution.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from django.utils.timezone import now
from freezegun import freeze_time

from sentry.models import GroupHistory, GroupHistoryStatus
from sentry.models import GroupHistoryStatus
from sentry.testutils import APITestCase
from sentry.testutils.helpers.datetime import before_now

Expand All @@ -16,49 +16,37 @@ def test_simple(self):
project1 = self.create_project(teams=[self.team], slug="foo")
project2 = self.create_project(teams=[self.team], slug="bar")
group1 = self.create_group(checksum="a" * 32, project=project1, times_seen=10)
group2 = self.create_group(checksum="b" * 32, project=project2, times_seen=5)
group2 = self.create_group(
checksum="b" * 32, project=project2, times_seen=5, first_seen=before_now(days=20)
)

gh1 = GroupHistory.objects.create(
organization=self.organization,
group=group1,
project=project1,
gh1 = self.create_group_history(
group1,
GroupHistoryStatus.UNRESOLVED,
actor=self.user.actor,
date_added=before_now(days=5),
status=GroupHistoryStatus.UNRESOLVED,
prev_history=None,
prev_history_date=None,
)

GroupHistory.objects.create(
organization=self.organization,
group=group1,
project=project1,
self.create_group_history(
group1,
GroupHistoryStatus.RESOLVED,
actor=self.user.actor,
status=GroupHistoryStatus.RESOLVED,
prev_history=gh1,
prev_history_date=gh1.date_added,
date_added=before_now(days=2),
)

gh2 = GroupHistory.objects.create(
organization=self.organization,
group=group2,
project=project2,
gh2 = self.create_group_history(
group2,
GroupHistoryStatus.UNRESOLVED,
actor=self.user.actor,
date_added=before_now(days=10),
status=GroupHistoryStatus.UNRESOLVED,
prev_history=None,
prev_history_date=None,
)

GroupHistory.objects.create(
organization=self.organization,
group=group2,
project=project2,
self.create_group_history(
group2,
GroupHistoryStatus.RESOLVED,
actor=self.user.actor,
status=GroupHistoryStatus.RESOLVED,
prev_history=gh2,
prev_history_date=gh2.date_added,
)
today = str(now().date())
yesterday = str((now() - timedelta(days=1)).date())
Expand All @@ -73,49 +61,35 @@ def test_simple(self):
assert response.data[yesterday]["avg"] == 0

# Lower "todays" average by adding another resolution, but this time 5 days instead of 10 (avg is 7.5 now)
gh2 = GroupHistory.objects.create(
organization=self.organization,
group=group2,
project=project2,
gh2 = self.create_group_history(
group2,
GroupHistoryStatus.UNRESOLVED,
actor=self.user.actor,
date_added=before_now(days=5),
status=GroupHistoryStatus.UNRESOLVED,
prev_history=None,
prev_history_date=None,
)
GroupHistory.objects.create(
organization=self.organization,
group=group2,
project=project2,
self.create_group_history(
group2,
GroupHistoryStatus.RESOLVED,
actor=self.user.actor,
status=GroupHistoryStatus.RESOLVED,
prev_history=gh2,
prev_history_date=gh2.date_added,
)

# making sure it doesnt bork anything
GroupHistory.objects.create(
organization=self.organization,
group=group2,
project=project2,
self.create_group_history(
group2,
GroupHistoryStatus.DELETED,
actor=self.user.actor,
status=GroupHistoryStatus.DELETED,
prev_history=gh2,
prev_history_date=gh2.date_added,
)
# Make sure that if we have a `GroupHistory` row with no prev history then we don't crash.
GroupHistory.objects.create(
organization=self.organization,
group=group2,
project=project2,
self.create_group_history(
group2,
GroupHistoryStatus.RESOLVED,
actor=self.user.actor,
status=GroupHistoryStatus.DELETED,
prev_history=None,
prev_history_date=None,
)

response = self.get_success_response(self.team.organization.slug, self.team.slug)
assert len(response.data) == 90
assert response.data[today]["avg"] == timedelta(days=7, hours=12).total_seconds()
assert response.data[today]["avg"] == timedelta(days=11, hours=16).total_seconds()
assert response.data[two_days_ago]["avg"] == timedelta(days=3).total_seconds()
assert response.data[yesterday]["avg"] == 0