Exemple #1
0
    def get_attrs(self, item_list, user):
        attrs = super(StreamGroupSerializerSnuba,
                      self).get_attrs(item_list, user)
        if self.stats_period and not self._collapse("stats"):
            partial_get_stats = functools.partial(
                self.get_stats,
                item_list=item_list,
                user=user,
                environment_ids=self.environment_ids)
            stats = partial_get_stats()
            filtered_stats = (partial_get_stats(
                conditions=self.conditions) if self.conditions
                              and not self._collapse("filtered") else None)
            if self._expand("inbox"):
                inbox_stats = get_inbox_details(item_list)
            for item in item_list:
                if filtered_stats:
                    attrs[item].update(
                        {"filtered_stats": filtered_stats[item.id]})
                if self._expand("inbox"):
                    attrs[item].update({"inbox": inbox_stats.get(item.id)})

                attrs[item].update({"stats": stats[item.id]})

        return attrs
Exemple #2
0
    def get_attrs(self, item_list, user):
        if not self._collapse("base"):
            attrs = super().get_attrs(item_list, user)
        else:
            seen_stats = self._get_seen_stats(item_list, user)
            if seen_stats:
                attrs = {item: seen_stats.get(item, {}) for item in item_list}
            else:
                attrs = {item: {} for item in item_list}

        if self.stats_period and not self._collapse("stats"):
            partial_get_stats = functools.partial(
                self.get_stats,
                item_list=item_list,
                user=user,
                environment_ids=self.environment_ids)
            stats = partial_get_stats()
            filtered_stats = (partial_get_stats(
                conditions=self.conditions) if self.conditions
                              and not self._collapse("filtered") else None)
            for item in item_list:
                if filtered_stats:
                    attrs[item].update(
                        {"filtered_stats": filtered_stats[item.id]})
                attrs[item].update({"stats": stats[item.id]})

        if self._expand("inbox"):
            inbox_stats = get_inbox_details(item_list)
            for item in item_list:
                attrs[item].update({"inbox": inbox_stats.get(item.id)})

        if self._expand("owners"):
            owner_details = get_owner_details(item_list)
            for item in item_list:
                attrs[item].update({"owners": owner_details.get(item.id)})

        return attrs
Exemple #3
0
    def get(self, request: Request, group) -> Response:
        """
        Retrieve an Issue
        `````````````````

        Return details on an individual issue. This returns the basic stats for
        the issue (title, last seen, first seen), some overall numbers (number
        of comments, user reports) as well as the summarized event data.

        :pparam string issue_id: the ID of the issue to retrieve.
        :auth: required
        """
        from sentry.utils import snuba

        try:
            # TODO(dcramer): handle unauthenticated/public response

            organization = group.project.organization
            environments = get_environments(request, organization)
            environment_ids = [e.id for e in environments]
            expand = request.GET.getlist("expand", [])
            collapse = request.GET.getlist("collapse", [])

            # WARNING: the rest of this endpoint relies on this serializer
            # populating the cache SO don't move this :)
            data = serialize(
                group, request.user,
                GroupSerializerSnuba(environment_ids=environment_ids))

            # TODO: these probably should be another endpoint
            activity = self._get_activity(request, group, num=100)
            seen_by = self._get_seen_by(request, group)

            if "release" not in collapse:
                first_release, last_release = get_first_last_release(
                    request, group)
                data.update({
                    "firstRelease": first_release,
                    "lastRelease": last_release,
                })

            get_range = functools.partial(tsdb.get_range,
                                          environment_ids=environment_ids)

            tags = tagstore.get_group_tag_keys(group.project_id,
                                               group.id,
                                               environment_ids,
                                               limit=100)
            if not environment_ids:
                user_reports = UserReport.objects.filter(group_id=group.id)
            else:
                user_reports = UserReport.objects.filter(
                    group_id=group.id, environment_id__in=environment_ids)

            now = timezone.now()
            hourly_stats = tsdb.rollup(
                get_range(model=tsdb.models.group,
                          keys=[group.id],
                          end=now,
                          start=now - timedelta(days=1)),
                3600,
            )[group.id]
            daily_stats = tsdb.rollup(
                get_range(
                    model=tsdb.models.group,
                    keys=[group.id],
                    end=now,
                    start=now - timedelta(days=30),
                ),
                3600 * 24,
            )[group.id]

            participants = GroupSubscriptionManager.get_participating_users(
                group)

            if "inbox" in expand:
                inbox_map = get_inbox_details([group])
                inbox_reason = inbox_map.get(group.id)
                data.update({"inbox": inbox_reason})

            action_list = self._get_actions(request, group)
            data.update({
                "activity":
                serialize(activity, request.user),
                "seenBy":
                seen_by,
                "participants":
                serialize(participants, request.user),
                "pluginActions":
                action_list,
                "pluginIssues":
                self._get_available_issue_plugins(request, group),
                "pluginContexts":
                self._get_context_plugins(request, group),
                "userReportCount":
                user_reports.count(),
                "tags":
                sorted(serialize(tags, request.user), key=lambda x: x["name"]),
                "stats": {
                    "24h": hourly_stats,
                    "30d": daily_stats
                },
            })

            metrics.incr(
                "group.update.http_response",
                sample_rate=1.0,
                tags={
                    "status": 200,
                    "detail": "group_details:get:response"
                },
            )
            return Response(data)
        except snuba.RateLimitExceeded:
            metrics.incr(
                "group.update.http_response",
                sample_rate=1.0,
                tags={
                    "status": 429,
                    "detail": "group_details:get:snuba.RateLimitExceeded"
                },
            )
            raise
        except Exception:
            metrics.incr(
                "group.update.http_response",
                sample_rate=1.0,
                tags={
                    "status": 500,
                    "detail": "group_details:get:Exception"
                },
            )
            raise
Exemple #4
0
def update_groups(request,
                  projects,
                  organization_id,
                  search_fn,
                  has_inbox=False):
    group_ids = request.GET.getlist("id")
    if group_ids:
        group_list = Group.objects.filter(
            project__organization_id=organization_id,
            project__in=projects,
            id__in=group_ids)
        # filter down group ids to only valid matches
        group_ids = [g.id for g in group_list]
        if not group_ids:
            return Response(status=204)
    else:
        group_list = None

    # TODO(jess): We may want to look into refactoring GroupValidator
    # to support multiple projects, but this is pretty complicated
    # because of the assignee validation. Punting on this for now.
    for project in projects:
        serializer = GroupValidator(
            data=request.data,
            partial=True,
            context={
                "project": project,
                "access": getattr(request, "access", None)
            },
        )
        if not serializer.is_valid():
            return Response(serializer.errors, status=400)

    result = dict(serializer.validated_data)

    # so we won't have to requery for each group
    project_lookup = {p.id: p for p in projects}

    acting_user = request.user if request.user.is_authenticated() else None

    if not group_ids:
        try:
            # bulk mutations are limited to 1000 items
            # TODO(dcramer): it'd be nice to support more than this, but its
            # a bit too complicated right now
            cursor_result, _ = search_fn({
                "limit": 1000,
                "paginator_options": {
                    "max_limit": 1000
                }
            })
        except ValidationError as exc:
            return Response({"detail": six.text_type(exc)}, status=400)

        group_list = list(cursor_result)
        group_ids = [g.id for g in group_list]

    is_bulk = len(group_ids) > 1

    group_project_ids = {g.project_id for g in group_list}
    # filter projects down to only those that have groups in the search results
    projects = [p for p in projects if p.id in group_project_ids]

    queryset = Group.objects.filter(id__in=group_ids)

    discard = result.get("discard")
    if discard:

        return handle_discard(request, list(queryset), projects, acting_user)

    statusDetails = result.pop("statusDetails", result)
    status = result.get("status")
    release = None
    commit = None

    if status in ("resolved", "resolvedInNextRelease"):
        if status == "resolvedInNextRelease" or statusDetails.get(
                "inNextRelease"):
            # TODO(jess): We may want to support this for multi project, but punting on it for now
            if len(projects) > 1:
                return Response(
                    {
                        "detail":
                        "Cannot set resolved in next release for multiple projects."
                    },
                    status=400,
                )
            release = (
                statusDetails.get("inNextRelease") or Release.objects.filter(
                    projects=projects[0],
                    organization_id=projects[0].organization_id).extra(
                        select={
                            "sort": "COALESCE(date_released, date_added)"
                        }).order_by("-sort")[0])
            activity_type = Activity.SET_RESOLVED_IN_RELEASE
            activity_data = {
                # no version yet
                "version": ""
            }
            status_details = {
                "inNextRelease":
                True,
                "actor":
                serialize(extract_lazy_object(request.user), request.user),
            }
            res_type = GroupResolution.Type.in_next_release
            res_type_str = "in_next_release"
            res_status = GroupResolution.Status.pending
        elif statusDetails.get("inRelease"):
            # TODO(jess): We could update validation to check if release
            # applies to multiple projects, but I think we agreed to punt
            # on this for now
            if len(projects) > 1:
                return Response(
                    {
                        "detail":
                        "Cannot set resolved in release for multiple projects."
                    },
                    status=400)
            release = statusDetails["inRelease"]
            activity_type = Activity.SET_RESOLVED_IN_RELEASE
            activity_data = {
                # no version yet
                "version": release.version
            }
            status_details = {
                "inRelease":
                release.version,
                "actor":
                serialize(extract_lazy_object(request.user), request.user),
            }
            res_type = GroupResolution.Type.in_release
            res_type_str = "in_release"
            res_status = GroupResolution.Status.resolved
        elif statusDetails.get("inCommit"):
            # TODO(jess): Same here, this is probably something we could do, but
            # punting for now.
            if len(projects) > 1:
                return Response(
                    {
                        "detail":
                        "Cannot set resolved in commit for multiple projects."
                    },
                    status=400)
            commit = statusDetails["inCommit"]
            activity_type = Activity.SET_RESOLVED_IN_COMMIT
            activity_data = {"commit": commit.id}
            status_details = {
                "inCommit":
                serialize(commit, request.user),
                "actor":
                serialize(extract_lazy_object(request.user), request.user),
            }
            res_type_str = "in_commit"
        else:
            res_type_str = "now"
            activity_type = Activity.SET_RESOLVED
            activity_data = {}
            status_details = {}

        now = timezone.now()
        metrics.incr("group.resolved",
                     instance=res_type_str,
                     skip_internal=True)

        # if we've specified a commit, let's see if its already been released
        # this will allow us to associate the resolution to a release as if we
        # were simply using 'inRelease' above
        # Note: this is different than the way commit resolution works on deploy
        # creation, as a given deploy is connected to an explicit release, and
        # in this case we're simply choosing the most recent release which contains
        # the commit.
        if commit and not release:
            # TODO(jess): If we support multiple projects for release / commit resolution,
            # we need to update this to find the release for each project (we shouldn't assume
            # it's the same)
            try:
                release = (Release.objects.filter(
                    projects__in=projects, releasecommit__commit=commit).extra(
                        select={
                            "sort": "COALESCE(date_released, date_added)"
                        }).order_by("-sort")[0])
                res_type = GroupResolution.Type.in_release
                res_status = GroupResolution.Status.resolved
            except IndexError:
                release = None

        for group in group_list:
            with transaction.atomic():
                resolution = None
                if release:
                    resolution_params = {
                        "release":
                        release,
                        "type":
                        res_type,
                        "status":
                        res_status,
                        "actor_id":
                        request.user.id
                        if request.user.is_authenticated() else None,
                    }
                    resolution, created = GroupResolution.objects.get_or_create(
                        group=group, defaults=resolution_params)
                    if not created:
                        resolution.update(datetime=timezone.now(),
                                          **resolution_params)

                if commit:
                    GroupLink.objects.create(
                        group_id=group.id,
                        project_id=group.project_id,
                        linked_type=GroupLink.LinkedType.commit,
                        relationship=GroupLink.Relationship.resolves,
                        linked_id=commit.id,
                    )

                affected = Group.objects.filter(id=group.id).update(
                    status=GroupStatus.RESOLVED, resolved_at=now)
                if not resolution:
                    created = affected

                group.status = GroupStatus.RESOLVED
                group.resolved_at = now
                remove_group_from_inbox(group)
                if has_inbox:
                    result["inbox"] = None

                assigned_to = self_subscribe_and_assign_issue(
                    acting_user, group)
                if assigned_to is not None:
                    result["assignedTo"] = assigned_to

                if created:
                    activity = Activity.objects.create(
                        project=project_lookup[group.project_id],
                        group=group,
                        type=activity_type,
                        user=acting_user,
                        ident=resolution.id if resolution else None,
                        data=activity_data,
                    )
                    # TODO(dcramer): we need a solution for activity rollups
                    # before sending notifications on bulk changes
                    if not is_bulk:
                        activity.send_notification()

            issue_resolved.send_robust(
                organization_id=organization_id,
                user=acting_user or request.user,
                group=group,
                project=project_lookup[group.project_id],
                resolution_type=res_type_str,
                sender=update_groups,
            )

            kick_off_status_syncs.apply_async(kwargs={
                "project_id": group.project_id,
                "group_id": group.id
            })

        result.update({"status": "resolved", "statusDetails": status_details})

    elif status:
        new_status = STATUS_UPDATE_CHOICES[result["status"]]

        with transaction.atomic():
            happened = queryset.exclude(status=new_status).update(
                status=new_status)

            GroupResolution.objects.filter(group__in=group_ids).delete()
            if new_status == GroupStatus.UNRESOLVED:
                for group in group_list:
                    add_group_to_inbox(group, GroupInboxReason.MANUAL)
                if has_inbox:
                    result["inbox"] = get_inbox_details([group_list[0]
                                                         ])[group_list[0].id]
                result["statusDetails"] = {}
            elif new_status == GroupStatus.IGNORED:
                metrics.incr("group.ignored", skip_internal=True)
                for group in group_ids:
                    remove_group_from_inbox(group)
                if has_inbox:
                    result["inbox"] = None

                ignore_duration = (statusDetails.pop("ignoreDuration", None)
                                   or statusDetails.pop(
                                       "snoozeDuration", None)) or None
                ignore_count = statusDetails.pop("ignoreCount", None) or None
                ignore_window = statusDetails.pop("ignoreWindow", None) or None
                ignore_user_count = statusDetails.pop("ignoreUserCount",
                                                      None) or None
                ignore_user_window = statusDetails.pop("ignoreUserWindow",
                                                       None) or None
                if ignore_duration or ignore_count or ignore_user_count:
                    if ignore_duration:
                        ignore_until = timezone.now() + timedelta(
                            minutes=ignore_duration)
                    else:
                        ignore_until = None
                    for group in group_list:
                        state = {}
                        if ignore_count and not ignore_window:
                            state["times_seen"] = group.times_seen
                        if ignore_user_count and not ignore_user_window:
                            state["users_seen"] = group.count_users_seen()
                        GroupSnooze.objects.create_or_update(
                            group=group,
                            values={
                                "until":
                                ignore_until,
                                "count":
                                ignore_count,
                                "window":
                                ignore_window,
                                "user_count":
                                ignore_user_count,
                                "user_window":
                                ignore_user_window,
                                "state":
                                state,
                                "actor_id":
                                request.user.id
                                if request.user.is_authenticated() else None,
                            },
                        )
                        result["statusDetails"] = {
                            "ignoreCount":
                            ignore_count,
                            "ignoreUntil":
                            ignore_until,
                            "ignoreUserCount":
                            ignore_user_count,
                            "ignoreUserWindow":
                            ignore_user_window,
                            "ignoreWindow":
                            ignore_window,
                            "actor":
                            serialize(extract_lazy_object(request.user),
                                      request.user),
                        }
                else:
                    GroupSnooze.objects.filter(group__in=group_ids).delete()
                    ignore_until = None
                    result["statusDetails"] = {}
            else:
                result["statusDetails"] = {}

        if group_list and happened:
            if new_status == GroupStatus.UNRESOLVED:
                activity_type = Activity.SET_UNRESOLVED
                activity_data = {}

                for group in group_list:
                    if group.status == GroupStatus.IGNORED:
                        issue_unignored.send_robust(
                            project=project,
                            user=acting_user,
                            group=group,
                            transition_type="manual",
                            sender=update_groups,
                        )
                    else:
                        issue_unresolved.send_robust(
                            project=project,
                            user=acting_user,
                            group=group,
                            transition_type="manual",
                            sender=update_groups,
                        )
            elif new_status == GroupStatus.IGNORED:
                activity_type = Activity.SET_IGNORED
                activity_data = {
                    "ignoreCount": ignore_count,
                    "ignoreDuration": ignore_duration,
                    "ignoreUntil": ignore_until,
                    "ignoreUserCount": ignore_user_count,
                    "ignoreUserWindow": ignore_user_window,
                    "ignoreWindow": ignore_window,
                }

                groups_by_project_id = defaultdict(list)
                for group in group_list:
                    groups_by_project_id[group.project_id].append(group)

                for project in projects:
                    project_groups = groups_by_project_id.get(project.id)
                    if project_groups:
                        issue_ignored.send_robust(
                            project=project,
                            user=acting_user,
                            group_list=project_groups,
                            activity_data=activity_data,
                            sender=update_groups,
                        )

            for group in group_list:
                group.status = new_status

                activity = Activity.objects.create(
                    project=project_lookup[group.project_id],
                    group=group,
                    type=activity_type,
                    user=acting_user,
                    data=activity_data,
                )
                # TODO(dcramer): we need a solution for activity rollups
                # before sending notifications on bulk changes
                if not is_bulk:
                    if acting_user:
                        GroupSubscription.objects.subscribe(
                            user=acting_user,
                            group=group,
                            reason=GroupSubscriptionReason.status_change,
                        )
                    activity.send_notification()

                if new_status == GroupStatus.UNRESOLVED:
                    kick_off_status_syncs.apply_async(kwargs={
                        "project_id": group.project_id,
                        "group_id": group.id
                    })

    if "assignedTo" in result:
        assigned_actor = result["assignedTo"]
        if assigned_actor:
            for group in group_list:
                resolved_actor = assigned_actor.resolve()

                GroupAssignee.objects.assign(group, resolved_actor,
                                             acting_user)
            result["assignedTo"] = serialize(assigned_actor.resolve(),
                                             acting_user, ActorSerializer())
        else:
            for group in group_list:
                GroupAssignee.objects.deassign(group, acting_user)

    is_member_map = {
        project.id: project.member_set.filter(user=acting_user).exists()
        for project in projects
    }
    if result.get("hasSeen"):
        for group in group_list:
            if is_member_map.get(group.project_id):
                instance, created = create_or_update(
                    GroupSeen,
                    group=group,
                    user=acting_user,
                    project=project_lookup[group.project_id],
                    values={"last_seen": timezone.now()},
                )
    elif result.get("hasSeen") is False:
        GroupSeen.objects.filter(group__in=group_ids,
                                 user=acting_user).delete()

    if result.get("isBookmarked"):
        for group in group_list:
            GroupBookmark.objects.get_or_create(
                project=project_lookup[group.project_id],
                group=group,
                user=acting_user)
            GroupSubscription.objects.subscribe(
                user=acting_user,
                group=group,
                reason=GroupSubscriptionReason.bookmark)
    elif result.get("isBookmarked") is False:
        GroupBookmark.objects.filter(group__in=group_ids,
                                     user=acting_user).delete()

    # TODO(dcramer): we could make these more efficient by first
    # querying for rich rows are present (if N > 2), flipping the flag
    # on those rows, and then creating the missing rows
    if result.get("isSubscribed") in (True, False):
        is_subscribed = result["isSubscribed"]
        for group in group_list:
            # NOTE: Subscribing without an initiating event (assignment,
            # commenting, etc.) clears out the previous subscription reason
            # to avoid showing confusing messaging as a result of this
            # action. It'd be jarring to go directly from "you are not
            # subscribed" to "you were subscribed due since you were
            # assigned" just by clicking the "subscribe" button (and you
            # may no longer be assigned to the issue anyway.)
            GroupSubscription.objects.create_or_update(
                user=acting_user,
                group=group,
                project=project_lookup[group.project_id],
                values={
                    "is_active": is_subscribed,
                    "reason": GroupSubscriptionReason.unknown
                },
            )

        result["subscriptionDetails"] = {
            "reason":
            SUBSCRIPTION_REASON_MAP.get(GroupSubscriptionReason.unknown,
                                        "unknown")
        }

    if "isPublic" in result:
        # We always want to delete an existing share, because triggering
        # an isPublic=True even when it's already public, should trigger
        # regenerating.
        for group in group_list:
            if GroupShare.objects.filter(group=group).delete():
                result["shareId"] = None
                Activity.objects.create(
                    project=project_lookup[group.project_id],
                    group=group,
                    type=Activity.SET_PRIVATE,
                    user=acting_user,
                )

    if result.get("isPublic"):
        for group in group_list:
            share, created = GroupShare.objects.get_or_create(
                project=project_lookup[group.project_id],
                group=group,
                user=acting_user)
            if created:
                result["shareId"] = share.uuid
                Activity.objects.create(
                    project=project_lookup[group.project_id],
                    group=group,
                    type=Activity.SET_PUBLIC,
                    user=acting_user,
                )

    # XXX(dcramer): this feels a bit shady like it should be its own
    # endpoint
    if result.get("merge") and len(group_list) > 1:
        # don't allow merging cross project
        if len(projects) > 1:
            return Response({
                "detail":
                "Merging across multiple projects is not supported"
            })
        group_list_by_times_seen = sorted(group_list,
                                          key=lambda g: (g.times_seen, g.id),
                                          reverse=True)
        primary_group, groups_to_merge = group_list_by_times_seen[
            0], group_list_by_times_seen[1:]

        group_ids_to_merge = [g.id for g in groups_to_merge]
        eventstream_state = eventstream.start_merge(primary_group.project_id,
                                                    group_ids_to_merge,
                                                    primary_group.id)

        Group.objects.filter(id__in=group_ids_to_merge).update(
            status=GroupStatus.PENDING_MERGE)

        transaction_id = uuid4().hex
        merge_groups.delay(
            from_object_ids=group_ids_to_merge,
            to_object_id=primary_group.id,
            transaction_id=transaction_id,
            eventstream_state=eventstream_state,
        )

        Activity.objects.create(
            project=project_lookup[primary_group.project_id],
            group=primary_group,
            type=Activity.MERGE,
            user=acting_user,
            data={"issues": [{
                "id": c.id
            } for c in groups_to_merge]},
        )

        result["merge"] = {
            "parent": six.text_type(primary_group.id),
            "children": [six.text_type(g.id) for g in groups_to_merge],
        }

    # Support moving groups in or out of the inbox
    inbox = result.get("inbox", None)
    if inbox is not None:
        if inbox:
            for group in group_list:
                add_group_to_inbox(group, GroupInboxReason.MANUAL)
        elif not inbox:
            GroupInbox.objects.filter(group__in=group_ids).delete()
            for group in group_list:
                issue_mark_reviewed.send_robust(
                    project=project,
                    user=acting_user,
                    group=group,
                    sender=update_groups,
                )
        result["inbox"] = inbox

    return Response(result)
Exemple #5
0
    def get_attrs(self, item_list, user):
        if not self._collapse("base"):
            attrs = super().get_attrs(item_list, user)
        else:
            seen_stats = self._get_seen_stats(item_list, user)
            if seen_stats:
                attrs = {item: seen_stats.get(item, {}) for item in item_list}
            else:
                attrs = {item: {} for item in item_list}

        if self.stats_period and not self._collapse("stats"):
            partial_get_stats = functools.partial(
                self.get_stats, item_list=item_list, user=user, environment_ids=self.environment_ids
            )
            stats = partial_get_stats()
            filtered_stats = (
                partial_get_stats(conditions=self.conditions)
                if self.conditions and not self._collapse("filtered")
                else None
            )
            for item in item_list:
                if filtered_stats:
                    attrs[item].update({"filtered_stats": filtered_stats[item.id]})
                attrs[item].update({"stats": stats[item.id]})

            if self._expand("sessions"):
                uniq_project_ids = list({item.project_id for item in item_list})
                cache_keys = {pid: self._build_session_cache_key(pid) for pid in uniq_project_ids}
                cache_data = cache.get_many(cache_keys.values())
                missed_items = []
                for item in item_list:
                    num_sessions = cache_data.get(cache_keys[item.project_id])
                    if num_sessions is None:
                        found = "miss"
                        missed_items.append(item)
                    else:
                        found = "hit"
                        attrs[item].update(
                            {
                                "sessionCount": num_sessions,
                            }
                        )
                    metrics.incr(f"group.get_session_counts.{found}")

                if missed_items:
                    filters = {"project_id": list({item.project_id for item in missed_items})}
                    if self.environment_ids:
                        filters["environment"] = self.environment_ids

                    result_totals = raw_query(
                        selected_columns=["sessions"],
                        dataset=Dataset.Sessions,
                        start=self.start,
                        end=self.end,
                        filter_keys=filters,
                        groupby=["project_id"],
                        referrer="serializers.GroupSerializerSnuba.session_totals",
                    )

                    results = {}
                    for data in result_totals["data"]:
                        cache_key = self._build_session_cache_key(data["project_id"])
                        results[data["project_id"]] = data["sessions"]
                        cache.set(cache_key, data["sessions"], 3600)

                    for item in missed_items:
                        if item.project_id in results.keys():
                            attrs[item].update(
                                {
                                    "sessionCount": results[item.project_id],
                                }
                            )
                        else:
                            attrs[item].update({"sessionCount": None})

        if self._expand("inbox"):
            inbox_stats = get_inbox_details(item_list)
            for item in item_list:
                attrs[item].update({"inbox": inbox_stats.get(item.id)})

        if self._expand("owners"):
            owner_details = get_owner_details(item_list)
            for item in item_list:
                attrs[item].update({"owners": owner_details.get(item.id)})

        return attrs
Exemple #6
0
    def get_attrs(self, item_list, user):
        if not self._collapse("base"):
            attrs = super().get_attrs(item_list, user)
        else:
            seen_stats = self._get_seen_stats(item_list, user)
            if seen_stats:
                attrs = {item: seen_stats.get(item, {}) for item in item_list}
            else:
                attrs = {item: {} for item in item_list}

        if self.stats_period and not self._collapse("stats"):
            partial_get_stats = functools.partial(
                self.get_stats,
                item_list=item_list,
                user=user,
                environment_ids=self.environment_ids)
            stats = partial_get_stats()
            filtered_stats = (partial_get_stats(
                conditions=self.conditions) if self.conditions
                              and not self._collapse("filtered") else None)
            for item in item_list:
                if filtered_stats:
                    attrs[item].update(
                        {"filtered_stats": filtered_stats[item.id]})
                attrs[item].update({"stats": stats[item.id]})

            if self._expand("sessions"):
                uniq_project_ids = list(
                    {item.project_id
                     for item in item_list})
                cache_keys = {
                    pid: self._build_session_cache_key(pid)
                    for pid in uniq_project_ids
                }
                cache_data = cache.get_many(cache_keys.values())
                missed_items = []
                for item in item_list:
                    num_sessions = cache_data.get(cache_keys[item.project_id])
                    if num_sessions is None:
                        found = "miss"
                        missed_items.append(item)
                    else:
                        found = "hit"
                        attrs[item].update({
                            "sessionCount": num_sessions,
                        })
                    metrics.incr(f"group.get_session_counts.{found}")

                if missed_items:
                    project_ids = list(
                        {item.project_id
                         for item in missed_items})
                    project_sessions = release_health.get_num_sessions_per_project(
                        project_ids,
                        self.start,
                        self.end,
                        self.environment_ids,
                    )

                    results = {}
                    for project_id, count in project_sessions:
                        cache_key = self._build_session_cache_key(project_id)
                        results[project_id] = count
                        cache.set(cache_key, count, 3600)

                    for item in missed_items:
                        if item.project_id in results.keys():
                            attrs[item].update({
                                "sessionCount":
                                results[item.project_id],
                            })
                        else:
                            attrs[item].update({"sessionCount": None})

        if self._expand("inbox"):
            inbox_stats = get_inbox_details(item_list)
            for item in item_list:
                attrs[item].update({"inbox": inbox_stats.get(item.id)})

        if self._expand("owners"):
            owner_details = get_owner_details(item_list)
            for item in item_list:
                attrs[item].update({"owners": owner_details.get(item.id)})

        return attrs