|
| 1 | +from django.http import HttpRequest |
| 2 | +from drf_spectacular.utils import extend_schema |
| 3 | +from kernelCI_app.constants.localization import ClientStrings |
| 4 | +from kernelCI_app.helpers.database import dict_fetchall |
| 5 | +from kernelCI_app.helpers.errorHandling import create_api_error_response |
| 6 | +from rest_framework.views import APIView |
| 7 | +from rest_framework.response import Response |
| 8 | +from kernelCI_app.helpers.trees import sanitize_tree |
| 9 | +from kernelCI_app.typeModels.commonListing import ListingQueryParameters |
| 10 | +from http import HTTPStatus |
| 11 | +from kernelCI_app.typeModels.treeListing import TreeListingResponse |
| 12 | +from pydantic import ValidationError |
| 13 | + |
| 14 | +from django.db import connections |
| 15 | + |
| 16 | + |
| 17 | +# TODO: move to queries folder |
| 18 | +# TODO: add unit tests |
| 19 | +# TODO: stop using relative intervals! |
| 20 | +def get_new_tree_listing_data(origin: str, interval_in_days: int) -> list[dict]: |
| 21 | + """ |
| 22 | + Fetches data from the tree_listing table for direct use in the frontend. |
| 23 | + The status counts are NOT grouped in the return. |
| 24 | + """ |
| 25 | + |
| 26 | + query = """ |
| 27 | + SELECT |
| 28 | + id, |
| 29 | + _timestamp, |
| 30 | + checkout_id, |
| 31 | + origin, |
| 32 | + tree_name, |
| 33 | + git_repository_url, |
| 34 | + git_repository_branch, |
| 35 | + git_commit_hash, |
| 36 | + git_commit_name, |
| 37 | + git_commit_tags, |
| 38 | + start_time, |
| 39 | + origin_builds_finish_time, |
| 40 | + origin_tests_finish_time, |
| 41 | +
|
| 42 | + pass_builds, |
| 43 | + fail_builds, |
| 44 | + done_builds, |
| 45 | + miss_builds, |
| 46 | + skip_builds, |
| 47 | + error_builds, |
| 48 | + null_builds, |
| 49 | +
|
| 50 | + pass_boots, |
| 51 | + fail_boots, |
| 52 | + done_boots, |
| 53 | + miss_boots, |
| 54 | + skip_boots, |
| 55 | + error_boots, |
| 56 | + null_boots, |
| 57 | +
|
| 58 | + pass_tests, |
| 59 | + fail_tests, |
| 60 | + done_tests, |
| 61 | + miss_tests, |
| 62 | + skip_tests, |
| 63 | + error_tests, |
| 64 | + null_tests |
| 65 | + FROM |
| 66 | + tree_listing |
| 67 | + WHERE |
| 68 | + origin = %s |
| 69 | + AND start_time >= NOW() - INTERVAL '%s days' |
| 70 | + """ |
| 71 | + |
| 72 | + params = [origin, interval_in_days] |
| 73 | + |
| 74 | + with connections["default"].cursor() as cursor: |
| 75 | + cursor.execute(query, params) |
| 76 | + result = dict_fetchall(cursor) |
| 77 | + |
| 78 | + return result |
| 79 | + |
| 80 | + |
| 81 | +class TreeListingView(APIView): |
| 82 | + @extend_schema( |
| 83 | + responses=TreeListingResponse, |
| 84 | + parameters=[ListingQueryParameters], |
| 85 | + methods=["GET"], |
| 86 | + ) |
| 87 | + def get(self, request: HttpRequest) -> Response: |
| 88 | + """ |
| 89 | + Returns the checkout data for trees in a specific origin, in the last X days. |
| 90 | + The data includes the number of builds, boots and tests, grouped by status, for each checkout. |
| 91 | +
|
| 92 | + Query params (`ListingQueryParameters`): |
| 93 | + - origin: str |
| 94 | + - interval_in_days: int |
| 95 | +
|
| 96 | + Status returns: |
| 97 | + - 200: A list of checkouts with their respective data. |
| 98 | + - 400: Bad request, invalid parameters. |
| 99 | + - 500: Internal server error, something went wrong on the server. Usually validation. |
| 100 | + """ |
| 101 | + |
| 102 | + try: |
| 103 | + request_params = ListingQueryParameters( |
| 104 | + origin=request.GET.get("origin"), |
| 105 | + interval_in_days=request.GET.get("interval_in_days"), |
| 106 | + ) |
| 107 | + except ValidationError as e: |
| 108 | + return Response(data=e.json(), status=HTTPStatus.BAD_REQUEST) |
| 109 | + |
| 110 | + rows = get_new_tree_listing_data( |
| 111 | + origin=request_params.origin, |
| 112 | + interval_in_days=request_params.interval_in_days, |
| 113 | + ) |
| 114 | + |
| 115 | + if not rows: |
| 116 | + return create_api_error_response( |
| 117 | + error_message=ClientStrings.NO_TREES_FOUND, status_code=HTTPStatus.OK |
| 118 | + ) |
| 119 | + |
| 120 | + try: |
| 121 | + valid_response = TreeListingResponse( |
| 122 | + root=[sanitize_tree(row) for row in rows] |
| 123 | + ) |
| 124 | + except ValidationError as e: |
| 125 | + return Response(data=e.json(), status=HTTPStatus.INTERNAL_SERVER_ERROR) |
| 126 | + |
| 127 | + return Response(valid_response.model_dump(by_alias=True)) |
0 commit comments