|
1 | 1 | import logging
|
2 |
| -from dataclasses import dataclass |
3 | 2 | from datetime import datetime, timedelta
|
4 |
| -from typing import Any |
5 | 3 |
|
6 | 4 | from django.conf import settings
|
7 | 5 | from django.contrib.postgres.constraints import ExclusionConstraint
|
|
14 | 12 | from sentry.backup.scopes import RelocationScope
|
15 | 13 | from sentry.db.models import DefaultFieldsModel, FlexibleForeignKey, region_silo_model, sane_repr
|
16 | 14 | from sentry.db.models.fields.hybrid_cloud_foreign_key import HybridCloudForeignKey
|
| 15 | +from sentry.db.models.manager.base_query_set import BaseQuerySet |
17 | 16 | from sentry.models.activity import Activity
|
18 | 17 | from sentry.models.group import Group, GroupStatus
|
19 |
| -from sentry.types.activity import ActivityType |
20 | 18 |
|
21 | 19 | logger = logging.getLogger(__name__)
|
22 | 20 |
|
23 | 21 |
|
24 |
| -@dataclass |
25 |
| -class OpenPeriod: |
26 |
| - start: datetime |
27 |
| - end: datetime | None |
28 |
| - duration: timedelta | None |
29 |
| - is_open: bool |
30 |
| - last_checked: datetime |
31 |
| - |
32 |
| - def to_dict(self) -> dict[str, Any]: |
33 |
| - return { |
34 |
| - "start": self.start, |
35 |
| - "end": self.end, |
36 |
| - "duration": self.duration, |
37 |
| - "isOpen": self.is_open, |
38 |
| - "lastChecked": self.last_checked, |
39 |
| - } |
40 |
| - |
41 |
| - |
42 | 22 | class TsTzRange(models.Func):
|
43 | 23 | function = "TSTZRANGE"
|
44 | 24 | output_field = DateTimeRangeField()
|
@@ -144,104 +124,23 @@ def get_open_periods_for_group(
|
144 | 124 | group: Group,
|
145 | 125 | query_start: datetime | None = None,
|
146 | 126 | query_end: datetime | None = None,
|
147 |
| - offset: int | None = None, |
148 | 127 | limit: int | None = None,
|
149 |
| -) -> list[Any]: |
150 |
| - |
| 128 | +) -> BaseQuerySet[GroupOpenPeriod] | list[None]: |
151 | 129 | if not features.has("organizations:issue-open-periods", group.organization):
|
152 | 130 | return []
|
153 | 131 |
|
154 |
| - # Try to get open periods from the GroupOpenPeriod table first |
155 |
| - group_open_periods = GroupOpenPeriod.objects.filter(group=group) |
156 |
| - if group_open_periods.exists() and query_start: |
157 |
| - group_open_periods = group_open_periods.filter( |
158 |
| - date_started__gte=query_start, date_ended__lte=query_end, id__gte=offset or 0 |
159 |
| - ).order_by("-date_started")[:limit] |
160 |
| - |
161 |
| - return [ |
162 |
| - OpenPeriod( |
163 |
| - start=period.date_started, |
164 |
| - end=period.date_ended, |
165 |
| - duration=period.date_ended - period.date_started if period.date_ended else None, |
166 |
| - is_open=period.date_ended is None, |
167 |
| - last_checked=get_last_checked_for_open_period(group), |
168 |
| - ) |
169 |
| - for period in group_open_periods |
170 |
| - ] |
171 |
| - |
172 |
| - # If there are no open periods in the table, we need to calculate them |
173 |
| - # from the activity log. |
174 |
| - # TODO(snigdha): This is temporary until we have backfilled the GroupOpenPeriod table |
175 |
| - logger.warning("Open periods not fully backfilled", extra={"group_id": group.id}) |
176 |
| - |
177 |
| - if query_start is None or query_end is None: |
178 |
| - query_start = timezone.now() - timedelta(days=90) |
179 |
| - query_end = timezone.now() |
180 |
| - |
181 |
| - query_limit = limit * 2 if limit else None |
182 |
| - # Filter to REGRESSION and RESOLVED activties to find the bounds of each open period. |
183 |
| - # The only UNRESOLVED activity we would care about is the first UNRESOLVED activity for the group creation, |
184 |
| - # but we don't create an entry for that . |
185 |
| - activities = Activity.objects.filter( |
186 |
| - group=group, |
187 |
| - type__in=[ActivityType.SET_REGRESSION.value, ActivityType.SET_RESOLVED.value], |
188 |
| - datetime__gte=query_start, |
189 |
| - datetime__lte=query_end, |
190 |
| - ).order_by("-datetime")[:query_limit] |
191 |
| - |
192 |
| - open_periods = [] |
193 |
| - start: datetime | None = None |
194 |
| - end: datetime | None = None |
195 |
| - last_checked = get_last_checked_for_open_period(group) |
196 |
| - |
197 |
| - # Handle currently open period |
198 |
| - if group.status == GroupStatus.UNRESOLVED and len(activities) > 0: |
199 |
| - open_periods.append( |
200 |
| - OpenPeriod( |
201 |
| - start=activities[0].datetime, |
202 |
| - end=None, |
203 |
| - duration=None, |
204 |
| - is_open=True, |
205 |
| - last_checked=last_checked, |
206 |
| - ) |
207 |
| - ) |
208 |
| - activities = activities[1:] |
209 |
| - |
210 |
| - for activity in activities: |
211 |
| - if activity.type == ActivityType.SET_RESOLVED.value: |
212 |
| - end = activity.datetime |
213 |
| - elif activity.type == ActivityType.SET_REGRESSION.value: |
214 |
| - start = activity.datetime |
215 |
| - if end is not None: |
216 |
| - open_periods.append( |
217 |
| - OpenPeriod( |
218 |
| - start=start, |
219 |
| - end=end, |
220 |
| - duration=end - start, |
221 |
| - is_open=False, |
222 |
| - last_checked=end, |
223 |
| - ) |
224 |
| - ) |
225 |
| - end = None |
226 |
| - |
227 |
| - # Add the very first open period, which has no UNRESOLVED activity for the group creation |
228 |
| - open_periods.append( |
229 |
| - OpenPeriod( |
230 |
| - start=group.first_seen, |
231 |
| - end=end if end else None, |
232 |
| - duration=end - group.first_seen if end else None, |
233 |
| - is_open=False if end else True, |
234 |
| - last_checked=end if end else last_checked, |
235 |
| - ) |
236 |
| - ) |
237 |
| - |
238 |
| - if offset and limit: |
239 |
| - return open_periods[offset : offset + limit] |
| 132 | + if not query_start: |
| 133 | + # use whichever date is more recent to reduce the query range. first_seen could be > 90 days ago |
| 134 | + query_start = max(group.first_seen, timezone.now() - timedelta(days=90)) |
240 | 135 |
|
241 |
| - if limit: |
242 |
| - return open_periods[:limit] |
| 136 | + group_open_periods = GroupOpenPeriod.objects.filter( |
| 137 | + group=group, |
| 138 | + date_started__gte=query_start, |
| 139 | + ).order_by("-date_started") |
| 140 | + if query_end: |
| 141 | + group_open_periods = group_open_periods.filter(date_ended__lte=query_end) |
243 | 142 |
|
244 |
| - return open_periods |
| 143 | + return group_open_periods[:limit] |
245 | 144 |
|
246 | 145 |
|
247 | 146 | def create_open_period(group: Group, start_time: datetime) -> None:
|
|
0 commit comments