Skip to content

Commit fc1a1eb

Browse files
author
Gustavo Flores
committed
feat: add hardware selectors and revision API
1 parent 76bf104 commit fc1a1eb

8 files changed

Lines changed: 547 additions & 1 deletion

File tree

backend/kernelCI_app/queries/hardware.py

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,104 @@ def get_hardware_listing_data(
201201
return cursor.fetchall()
202202

203203

204+
def get_hardware_selectors(origin: str) -> list[dict]:
205+
params = {"origin": origin}
206+
207+
query = """
208+
WITH hwbuilds AS (
209+
SELECT DISTINCT
210+
builds.id,
211+
builds.checkout_id,
212+
tests.environment_misc ->> 'platform' AS platform
213+
FROM tests
214+
INNER JOIN builds ON builds.id = tests.build_id
215+
WHERE tests.origin = %(origin)s
216+
AND tests.start_time > (NOW() - INTERVAL '5 days')
217+
AND tests.environment_misc ->> 'platform' IS NOT NULL
218+
),
219+
hwtrees AS (
220+
SELECT
221+
hwbuilds.platform,
222+
checkouts.git_repository_url,
223+
checkouts.tree_name
224+
FROM checkouts
225+
INNER JOIN hwbuilds ON hwbuilds.checkout_id = checkouts.id
226+
)
227+
SELECT DISTINCT
228+
checkouts.git_repository_url,
229+
checkouts.tree_name,
230+
checkouts.git_repository_branch,
231+
checkouts.git_commit_hash,
232+
checkouts.git_commit_name,
233+
checkouts.start_time
234+
FROM checkouts
235+
INNER JOIN hwtrees
236+
ON hwtrees.tree_name = checkouts.tree_name
237+
AND hwtrees.git_repository_url = checkouts.git_repository_url
238+
"""
239+
240+
with connection.cursor() as cursor:
241+
cursor.execute(query, params)
242+
return dict_fetchall(cursor)
243+
244+
245+
def get_hardware_listing_data_by_revision(
246+
*,
247+
origin: str,
248+
tree_name: str,
249+
git_repository_url: str,
250+
git_repository_branch: str,
251+
git_commit_hash: str,
252+
) -> list[dict]:
253+
count_clauses = _get_hardware_listing_count_clauses()
254+
params = {
255+
"origin": origin,
256+
"tree_name": tree_name,
257+
"git_repository_url": git_repository_url,
258+
"git_repository_branch": git_repository_branch,
259+
"git_commit_hash": git_commit_hash,
260+
}
261+
262+
query = f"""
263+
WITH relevant_tests AS (
264+
SELECT
265+
tests.environment_compatible AS hardware,
266+
tests.environment_misc ->> 'platform' AS platform,
267+
tests.status,
268+
tests.path,
269+
tests.id,
270+
b.id AS build_id,
271+
b.status AS build_status
272+
FROM
273+
checkouts c
274+
INNER JOIN builds b ON b.checkout_id = c.id
275+
INNER JOIN tests ON tests.build_id = b.id
276+
WHERE
277+
c.tree_name = %(tree_name)s
278+
AND c.git_repository_url = %(git_repository_url)s
279+
AND c.git_repository_branch = %(git_repository_branch)s
280+
AND c.git_commit_hash = %(git_commit_hash)s
281+
AND tests.origin = %(origin)s
282+
AND tests.environment_misc ->> 'platform' IS NOT NULL
283+
)
284+
SELECT
285+
relevant_tests.platform,
286+
relevant_tests.hardware,
287+
{count_clauses}
288+
FROM
289+
relevant_tests
290+
GROUP BY
291+
relevant_tests.platform,
292+
relevant_tests.hardware
293+
ORDER BY
294+
relevant_tests.platform ASC
295+
"""
296+
297+
with connection.cursor() as cursor:
298+
cursor.execute(query, params)
299+
return dict_fetchall(cursor)
300+
301+
204302
def get_hardware_listing_data_bulk(
205303
keys: list[tuple[str, str]],
206304
start_date: datetime,

backend/kernelCI_app/tests/unitTests/url_patterns_test.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,3 +133,11 @@ def test_tree_commit_hash_patterns(self):
133133
url = reverse("treeDetailsSummaryView", kwargs={"commit_hash": commit_hash})
134134
resolved = resolve(url)
135135
assert resolved.kwargs["commit_hash"] == commit_hash
136+
137+
def test_hardware_selectors_route(self):
138+
url = reverse("hardwareSelectors")
139+
resolved = resolve(url)
140+
141+
assert url == "/api/hardware/selectors/"
142+
assert resolved.url_name == "hardwareSelectors"
143+
assert "hardware_id" not in resolved.kwargs
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
from typing import Annotated
2+
3+
from pydantic import BaseModel, BeforeValidator, Field
4+
5+
from kernelCI_app.constants.general import DEFAULT_ORIGIN
6+
from kernelCI_app.constants.localization import DocStrings
7+
8+
9+
class HardwareListingByRevisionQueryParamsDocumentationOnly(BaseModel):
10+
origin: Annotated[
11+
str,
12+
Field(
13+
default=DEFAULT_ORIGIN,
14+
description=DocStrings.HARDWARE_LISTING_ORIGIN_DESCRIPTION,
15+
),
16+
]
17+
tree_name: str = Field(description=DocStrings.TREE_NAME_PATH_DESCRIPTION)
18+
git_repository_url: str = Field(
19+
description=DocStrings.TREE_QUERY_GIT_URL_DESCRIPTION
20+
)
21+
git_repository_branch: str = Field(
22+
description=DocStrings.DEFAULT_GIT_BRANCH_DESCRIPTION
23+
)
24+
git_commit_hash: str = Field(description=DocStrings.COMMIT_HASH_PATH_DESCRIPTION)
25+
26+
27+
class HardwareListingByRevisionQueryParams(BaseModel):
28+
origin: Annotated[
29+
str,
30+
Field(default=DEFAULT_ORIGIN),
31+
BeforeValidator(lambda o: DEFAULT_ORIGIN if o is None else o),
32+
]
33+
tree_name: str
34+
git_repository_url: str
35+
git_repository_branch: str
36+
git_commit_hash: str
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
from datetime import datetime
2+
from typing import Annotated
3+
4+
from pydantic import BaseModel, BeforeValidator, Field
5+
6+
from kernelCI_app.constants.general import DEFAULT_ORIGIN
7+
from kernelCI_app.constants.localization import DocStrings
8+
9+
10+
class HardwareSelectorRevision(BaseModel):
11+
git_commit_hash: str
12+
git_commit_name: str | None = None
13+
start_time: datetime
14+
15+
16+
class HardwareSelectorBranch(BaseModel):
17+
git_repository_url: str
18+
git_repository_branch: str
19+
revisions: list[HardwareSelectorRevision]
20+
21+
22+
class HardwareSelectorTree(BaseModel):
23+
tree_name: str
24+
branches: list[HardwareSelectorBranch]
25+
26+
27+
class HardwareSelectorsResponse(BaseModel):
28+
trees: list[HardwareSelectorTree]
29+
30+
31+
class HardwareSelectorsQueryParamsDocumentationOnly(BaseModel):
32+
origin: Annotated[
33+
str,
34+
Field(
35+
default=DEFAULT_ORIGIN,
36+
description=DocStrings.HARDWARE_LISTING_ORIGIN_DESCRIPTION,
37+
),
38+
]
39+
40+
41+
class HardwareSelectorsQueryParams(BaseModel):
42+
origin: Annotated[
43+
str,
44+
Field(default=DEFAULT_ORIGIN),
45+
BeforeValidator(lambda o: DEFAULT_ORIGIN if o is None else o),
46+
]

backend/kernelCI_app/urls.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,11 @@ def view_cache(view):
115115
name="testIssues",
116116
),
117117
path("log-downloader/", view_cache(views.LogDownloaderView), name="logDownloader"),
118+
path(
119+
"hardware/selectors/",
120+
view_cache(views.HardwareSelectorsView),
121+
name="hardwareSelectors",
122+
),
118123
path(
119124
"hardware/<str:hardware_id>",
120125
view_cache(views.HardwareDetails),
@@ -146,6 +151,11 @@ def view_cache(view):
146151
name="hardwareDetailsTests",
147152
),
148153
path("hardware/", view_cache(views.HardwareView), name="hardware"),
154+
path(
155+
"hardware-by-revision/",
156+
view_cache(views.HardwareByRevisionView),
157+
name="hardwareByRevision",
158+
),
149159
path("hardware-v2/", view_cache(views.HardwareViewV2), name="hardware-v2"),
150160
path("issue/", view_cache(views.IssueView), name="issue"),
151161
path(
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
from http import HTTPStatus
2+
3+
from drf_spectacular.utils import extend_schema
4+
from pydantic import ValidationError
5+
from rest_framework.request import Request
6+
from rest_framework.response import Response
7+
from rest_framework.views import APIView
8+
9+
from kernelCI_app.queries.hardware import get_hardware_listing_data_by_revision
10+
from kernelCI_app.typeModels.hardwareListing import (
11+
HardwareItem,
12+
HardwareListingResponse,
13+
)
14+
from kernelCI_app.typeModels.hardwareListingByRevision import (
15+
HardwareListingByRevisionQueryParams,
16+
HardwareListingByRevisionQueryParamsDocumentationOnly,
17+
)
18+
19+
20+
class HardwareByRevisionView(APIView):
21+
def _sanitize_records(self, hardwares_raw: list[dict]) -> list[HardwareItem]:
22+
hardwares = []
23+
for hardware in hardwares_raw:
24+
hardwares.append(
25+
HardwareItem(
26+
platform=hardware["platform"],
27+
hardware=hardware["hardware"],
28+
build_status_summary={
29+
"PASS": hardware["pass_builds"],
30+
"FAIL": hardware["fail_builds"],
31+
"NULL": hardware["null_builds"],
32+
"ERROR": hardware["error_builds"],
33+
"MISS": hardware["miss_builds"],
34+
"DONE": hardware["done_builds"],
35+
"SKIP": hardware["skip_builds"],
36+
},
37+
boot_status_summary={
38+
"PASS": hardware["pass_boots"],
39+
"FAIL": hardware["fail_boots"],
40+
"NULL": hardware["null_boots"],
41+
"ERROR": hardware["error_boots"],
42+
"MISS": hardware["miss_boots"],
43+
"DONE": hardware["done_boots"],
44+
"SKIP": hardware["skip_boots"],
45+
},
46+
test_status_summary={
47+
"PASS": hardware["pass_tests"],
48+
"FAIL": hardware["fail_tests"],
49+
"NULL": hardware["null_tests"],
50+
"ERROR": hardware["error_tests"],
51+
"MISS": hardware["miss_tests"],
52+
"DONE": hardware["done_tests"],
53+
"SKIP": hardware["skip_tests"],
54+
},
55+
)
56+
)
57+
58+
return hardwares
59+
60+
@extend_schema(
61+
parameters=[HardwareListingByRevisionQueryParamsDocumentationOnly],
62+
responses=HardwareListingResponse,
63+
)
64+
def get(self, request: Request):
65+
try:
66+
query_params = HardwareListingByRevisionQueryParams(
67+
origin=request.GET.get("origin"),
68+
tree_name=request.GET.get("tree_name"),
69+
git_repository_url=request.GET.get("git_repository_url"),
70+
git_repository_branch=request.GET.get("git_repository_branch"),
71+
git_commit_hash=request.GET.get("git_commit_hash"),
72+
)
73+
except ValidationError as e:
74+
return Response(data=e.json(), status=HTTPStatus.BAD_REQUEST)
75+
76+
hardwares_raw = get_hardware_listing_data_by_revision(
77+
origin=query_params.origin,
78+
tree_name=query_params.tree_name,
79+
git_repository_url=query_params.git_repository_url,
80+
git_repository_branch=query_params.git_repository_branch,
81+
git_commit_hash=query_params.git_commit_hash,
82+
)
83+
84+
try:
85+
sanitized_records = self._sanitize_records(hardwares_raw=hardwares_raw)
86+
result = HardwareListingResponse(hardware=sanitized_records)
87+
except ValidationError as e:
88+
return Response(data=e.json(), status=HTTPStatus.INTERNAL_SERVER_ERROR)
89+
90+
return Response(data=result.model_dump(), status=HTTPStatus.OK)

0 commit comments

Comments
 (0)