def list()

in treeherder/webapp/api/push.py [0:0]


    def list(self, request, project):
        """
        GET method for list of ``push`` records with revisions
        """
        # What is the upper limit on the number of pushes returned by the api
        max_push_count = 1000

        # make a mutable copy of these params
        filter_params = request.query_params.copy()

        # This will contain some meta data about the request and results
        meta = {}
        # support ranges for date as well as revisions(changes) like old tbpl
        for param in [
            "fromchange",
            "tochange",
            "startdate",
            "enddate",
            "revision",
            "commit_revision",
        ]:
            v = filter_params.get(param, None)
            if v:
                del filter_params[param]
                meta[param] = v

        all_repos = request.query_params.get("all_repos")

        pushes = Push.objects.order_by("-time")
        if not all_repos:
            try:
                repository = Repository.objects.get(name=project)
            except Repository.DoesNotExist:
                return Response(
                    {"detail": f"No project with name {project}"}, status=HTTP_404_NOT_FOUND
                )

            pushes = pushes.filter(repository=repository)

        search_param = filter_params.get("search")
        if search_param:
            repository = Repository.objects.get(name=project)
            # Subquery to check if a commit exists with the search term
            commit_exists_subquery = Commit.objects.filter(
                push_id=OuterRef("id"), search_vector=SearchQuery(search_param)
            ).values("id")
            pushes = (
                Push.objects.annotate(has_matching_commit=Exists(commit_exists_subquery))
                .filter(
                    Q(repository=repository)
                    & (
                        Q(has_matching_commit=True)
                        | Q(author__icontains=search_param)
                        | Q(revision__icontains=search_param)
                    )
                )
                .distinct()
                .order_by("-time")[:200]
            )  # Get most recent results and limit result to 200
        for param, value in meta.items():
            if param == "fromchange":
                revision_field = "revision__startswith" if len(value) < 40 else "revision"
                filter_kwargs = {revision_field: value, "repository": repository}
                frompush_time = Push.objects.values_list("time", flat=True).get(**filter_kwargs)
                pushes = pushes.filter(time__gte=frompush_time)
                filter_params.update({"push_timestamp__gte": to_timestamp(frompush_time)})
                self.report_if_short_revision(param, value)

            elif param == "tochange":
                revision_field = "revision__startswith" if len(value) < 40 else "revision"
                filter_kwargs = {revision_field: value, "repository": repository}
                topush_time = Push.objects.values_list("time", flat=True).get(**filter_kwargs)
                pushes = pushes.filter(time__lte=topush_time)
                filter_params.update({"push_timestamp__lte": to_timestamp(topush_time)})
                self.report_if_short_revision(param, value)

            elif param == "startdate":
                pushes = pushes.filter(time__gte=to_datetime(value))
                filter_params.update({"push_timestamp__gte": to_timestamp(to_datetime(value))})
            elif param == "enddate":
                real_end_date = to_datetime(value) + datetime.timedelta(days=1)
                pushes = pushes.filter(time__lte=real_end_date)
                filter_params.update({"push_timestamp__lt": to_timestamp(real_end_date)})
            elif param == "revision":
                # revision must be the tip revision of the push itself
                revision_field = "revision__startswith" if len(value) < 40 else "revision"
                filter_kwargs = {revision_field: value}
                pushes = pushes.filter(**filter_kwargs)
                rev_key = (
                    "revisions_long_revision"
                    if len(meta["revision"]) == 40
                    else "revisions_short_revision"
                )
                filter_params.update({rev_key: meta["revision"]})
                self.report_if_short_revision(param, value)
            elif param == "commit_revision":
                # revision can be either the revision of the push itself, or
                # any of the commits it refers to
                pushes = pushes.filter(commits__revision=value)
                self.report_if_short_revision(param, value)

        for param in [
            "push_timestamp__lt",
            "push_timestamp__lte",
            "push_timestamp__gt",
            "push_timestamp__gte",
        ]:
            if filter_params.get(param):
                # translate push timestamp directly into a filter
                try:
                    value = datetime.datetime.fromtimestamp(float(filter_params.get(param)))
                except ValueError:
                    return Response(
                        {"detail": f"Invalid timestamp specified for {param}"},
                        status=HTTP_400_BAD_REQUEST,
                    )
                pushes = pushes.filter(**{param.replace("push_timestamp", "time"): value})

        for param in ["id__lt", "id__lte", "id__gt", "id__gte", "id"]:
            try:
                value = int(filter_params.get(param, 0))
            except ValueError:
                return Response(
                    {"detail": f"Invalid timestamp specified for {param}"},
                    status=HTTP_400_BAD_REQUEST,
                )
            if value:
                pushes = pushes.filter(**{param: value})

        id_in = filter_params.get("id__in")
        if id_in:
            try:
                id_in_list = [int(id) for id in id_in.split(",")]
            except ValueError:
                return Response(
                    {"detail": "Invalid id__in specification"}, status=HTTP_400_BAD_REQUEST
                )
            pushes = pushes.filter(id__in=id_in_list)

        author = filter_params.get("author")
        if author:
            if author.startswith("-"):
                author = author[1::]
                pushes = pushes.exclude(author__iexact=author)
            else:
                pushes = pushes.filter(author__iexact=author)

        author_contains = filter_params.get("author_contains")
        if author_contains:
            pushes = pushes.filter(author__icontains=author_contains)

        if filter_params.get("hide_reviewbot_pushes") == "true":
            pushes = pushes.exclude(author="reviewbot")

        try:
            count = int(filter_params.get("count", 10))
        except ValueError:
            return Response({"detail": "Valid count value required"}, status=HTTP_400_BAD_REQUEST)

        if count > max_push_count:
            msg = f"Specified count exceeds api limit: {max_push_count}"
            return Response({"detail": msg}, status=HTTP_400_BAD_REQUEST)

        if count < 1:
            msg = f"count requires a positive integer, not: {count}"
            return Response({"detail": msg}, status=HTTP_400_BAD_REQUEST)

        # we used to have a "full" parameter for this endpoint so you could
        # specify to not fetch the revision information if it was set to
        # false. however AFAIK no one ever used it (default was to fetch
        # everything), so let's just leave it out. it doesn't break
        # anything to send extra data when not required.
        pushes = pushes.select_related("repository").prefetch_related("commits")[:count]
        serializer = PushSerializer(pushes, many=True)

        meta["count"] = len(pushes)
        meta["repository"] = "all" if all_repos else project
        meta["filter_params"] = filter_params

        resp = {"meta": meta, "results": serializer.data}

        return Response(resp)