Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/hardware by trees checkout #567

Merged
merged 2 commits into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion backend/kernelCI_app/views/hardwareDetailsView.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ def get_trees(self, hardware_id, origin, start_datetime, end_datetime):
*tree_id_fields,
).order_by(
*tree_id_fields,
"-start_time"
"-build__checkout__start_time"
)

trees = []
Expand Down
77 changes: 42 additions & 35 deletions backend/kernelCI_app/views/hardwareView.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,46 @@
from django.db.models import Subquery
from django.http import JsonResponse
from django.views import View
from collections import defaultdict
from kernelCI_app.helpers.date import parse_start_and_end_timestamps_in_seconds_to_datetime
from kernelCI_app.helpers.date import (
parse_start_and_end_timestamps_in_seconds_to_datetime,
)
from kernelCI_app.helpers.errorHandling import ExceptionWithJsonResponse
from kernelCI_app.models import Tests
from kernelCI_app.models import Tests, Checkouts
from kernelCI_app.helpers.build import build_status_map
from kernelCI_app.constants.general import DEFAULT_ORIGIN


class HardwareView(View):
def __getSlowResult(self, start_date, end_date, origin):
def _getResults(self, start_date, end_date, origin):
tree_id_fields = [
"tree_name",
"git_repository_branch",
"git_repository_url",
]

processedBuilds = set()

checkouts_subquery = (
Checkouts.objects.filter(
origin=origin,
start_time__gte=start_date,
start_time__lte=end_date,
)
.order_by(
*tree_id_fields,
"-start_time",
)
.distinct(*tree_id_fields)
.values_list("git_commit_hash", flat=True)
)

tests = Tests.objects.filter(
start_time__gte=start_date,
start_time__lte=end_date,
environment_compatible__isnull=False,
origin=origin
).values("environment_compatible", "status", "build__valid", "path")
build__checkout__git_commit_hash__in=Subquery(checkouts_subquery),
).values(
"environment_compatible", "status", "build__valid", "path", "build__id"
)

hardware = {}
for test in tests:
Expand All @@ -35,6 +60,11 @@ def __getSlowResult(self, start_date, end_date, origin):
else:
hardware[compatible][statusCount][test["status"]] += 1

buildKey = test["build__id"] + compatible
if buildKey in processedBuilds:
continue
processedBuilds.add(buildKey)

build_status = build_status_map.get(test["build__valid"])
if build_status is not None:
hardware[compatible]["buildCount"][build_status] += 1
Expand All @@ -43,43 +73,20 @@ def __getSlowResult(self, start_date, end_date, origin):

return result

def __getFastResult(self, start_date, end_date, origin):
hardwares = set()
tests = Tests.objects.filter(
start_time__gte=start_date,
start_time__lte=end_date,
environment_compatible__isnull=False,
origin=origin
).values("environment_compatible")

for test in tests:
for compatible in test["environment_compatible"]:
hardwares.add(compatible)

hardwareResult = []
for hardware in hardwares:
hardwareResult.append({"hardwareName": hardware})
result = {"hardware": hardwareResult}
return result

def get(self, request):
mode = request.GET.get("mode", "fast")
origin = request.GET.get("origin", DEFAULT_ORIGIN)
start_timestamp_str = request.GET.get("startTimestampInSeconds")
end_timestamp_str = request.GET.get("endTimeStampInSeconds")

try:
(start_date, end_date) = parse_start_and_end_timestamps_in_seconds_to_datetime(
start_timestamp_str, end_timestamp_str
(start_date, end_date) = (
parse_start_and_end_timestamps_in_seconds_to_datetime(
start_timestamp_str, end_timestamp_str
)
)
except ExceptionWithJsonResponse as e:
return e.getJsonResponse()

if mode == "fast":
result = self.__getFastResult(start_date, end_date, origin)
elif mode == "slow":
result = self.__getSlowResult(start_date, end_date, origin)
else:
return JsonResponse({"error": "Invalid mode"}, status=400)
result = self._getResults(start_date, end_date, origin)

return JsonResponse(result, safe=False)
56 changes: 4 additions & 52 deletions dashboard/src/api/Hardware.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,12 @@ import { useQuery } from '@tanstack/react-query';

import { useSearch } from '@tanstack/react-router';

import type {
HardwareFastResponse,
HardwareListingResponse,
} from '@/types/hardware';
import type { HardwareListingResponse } from '@/types/hardware';

import type { TOrigins } from '@/types/general';

import http from './api';
const fetchTreeCheckoutData = async (
const fetchHardwareListing = async (
origin: TOrigins,
startTimestampInSeconds: number,
endTimeStampInSeconds: number,
Expand All @@ -27,14 +24,9 @@ const fetchTreeCheckoutData = async (
return res.data;
};

export const useHardwareListingSlow = (
export const useHardwareListing = (
startTimestampInSeconds: number,
endTimestampInSeconds: number,
{
enabled,
}: {
enabled: boolean;
},
): UseQueryResult<HardwareListingResponse> => {
const { origin } = useSearch({ from: '/hardware' });

Expand All @@ -48,47 +40,7 @@ export const useHardwareListingSlow = (
return useQuery({
queryKey,
queryFn: () =>
fetchTreeCheckoutData(
origin,
startTimestampInSeconds,
endTimestampInSeconds,
),
enabled,
});
};

const fetchHardwareListingFastData = async (
origin: TOrigins,
startTimestampInSeconds: number,
endTimeStampInSeconds: number,
): Promise<HardwareFastResponse> => {
const res = await http.get('/api/hardware/', {
params: {
startTimestampInSeconds,
endTimeStampInSeconds,
mode: 'fast',
origin,
},
});
return res.data;
};

export const useHardwareListingFast = (
startTimestampInSeconds: number,
endTimestampInSeconds: number,
): UseQueryResult<HardwareFastResponse> => {
const { origin } = useSearch({ from: '/hardware' });
const queryKey = [
'hardwareListingFast',
startTimestampInSeconds,
endTimestampInSeconds,
origin,
];

return useQuery({
queryKey,
queryFn: () =>
fetchHardwareListingFastData(
fetchHardwareListing(
origin,
startTimestampInSeconds,
endTimestampInSeconds,
Expand Down
93 changes: 32 additions & 61 deletions dashboard/src/pages/Hardware/HardwareListingPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,9 @@ import QuerySwitcher from '@/components/QuerySwitcher/QuerySwitcher';

import { Toaster } from '@/components/ui/toaster';

import type {
HardwareFastItem,
HardwareListingItem,
HardwareTableItem,
} from '@/types/hardware';
import type { HardwareTableItem } from '@/types/hardware';

import { useHardwareListingFast, useHardwareListingSlow } from '@/api/Hardware';
import { useHardwareListing } from '@/api/Hardware';

import { dateObjectToTimestampInSeconds, daysToSeconds } from '@/utils/date';

Expand All @@ -22,16 +18,6 @@ interface HardwareListingPageProps {
inputFilter: string;
}

function isCompleteHardware(
data: HardwareListingItem | HardwareFastItem,
): data is HardwareListingItem {
return (
'buildCount' in data &&
'testStatusCount' in data &&
'bootStatusCount' in data
);
}

const calculateTimeStamp = (
intervalInDays: number,
): {
Expand Down Expand Up @@ -59,61 +45,46 @@ const HardwareListingPage = ({
setTimeStamps(calculateTimeStamp(intervalInDays));
}, [intervalInDays]);

const { data: fastData, status: fastStatus } = useHardwareListingFast(
const { data, error, status } = useHardwareListing(
startTimestampInSeconds,
endTimestampInSeconds,
);

const { data, error, isLoading } = useHardwareListingSlow(
startTimestampInSeconds,
endTimestampInSeconds,
{
enabled: fastStatus === 'success' && !!fastData,
},
);

const listItems: HardwareTableItem[] = useMemo(() => {
if (!fastData || fastStatus === 'error') return [];
if (!data || error) return [];

const hasCompleteData = !isLoading && !!data;
const currentData = hasCompleteData ? data.hardware : fastData.hardware;
const currentData = data.hardware;

return currentData
.filter(hardware => {
return hardware.hardwareName?.includes(inputFilter);
})
.map((hardware): HardwareTableItem => {
const buildCount = isCompleteHardware(hardware)
? {
valid: hardware.buildCount?.valid,
invalid: hardware.buildCount?.invalid,
null: hardware.buildCount?.null,
}
: undefined;

const testStatusCount = isCompleteHardware(hardware)
? {
DONE: hardware.testStatusCount.DONE,
ERROR: hardware.testStatusCount.ERROR,
FAIL: hardware.testStatusCount.FAIL,
MISS: hardware.testStatusCount.MISS,
PASS: hardware.testStatusCount.PASS,
SKIP: hardware.testStatusCount.SKIP,
NULL: hardware.testStatusCount.NULL,
}
: undefined;

const bootStatusCount = isCompleteHardware(hardware)
? {
DONE: hardware.bootStatusCount.DONE,
ERROR: hardware.bootStatusCount.ERROR,
FAIL: hardware.bootStatusCount.FAIL,
MISS: hardware.bootStatusCount.MISS,
PASS: hardware.bootStatusCount.PASS,
SKIP: hardware.bootStatusCount.SKIP,
NULL: hardware.bootStatusCount.NULL,
}
: undefined;
const buildCount = {
valid: hardware.buildCount?.valid,
invalid: hardware.buildCount?.invalid,
null: hardware.buildCount?.null,
};

const testStatusCount = {
DONE: hardware.testStatusCount.DONE,
ERROR: hardware.testStatusCount.ERROR,
FAIL: hardware.testStatusCount.FAIL,
MISS: hardware.testStatusCount.MISS,
PASS: hardware.testStatusCount.PASS,
SKIP: hardware.testStatusCount.SKIP,
NULL: hardware.testStatusCount.NULL,
};

const bootStatusCount = {
DONE: hardware.bootStatusCount.DONE,
ERROR: hardware.bootStatusCount.ERROR,
FAIL: hardware.bootStatusCount.FAIL,
MISS: hardware.bootStatusCount.MISS,
PASS: hardware.bootStatusCount.PASS,
SKIP: hardware.bootStatusCount.SKIP,
NULL: hardware.bootStatusCount.NULL,
};

return {
hardwareName: hardware.hardwareName ?? '',
Expand All @@ -123,14 +94,14 @@ const HardwareListingPage = ({
};
})
.sort((a, b) => a.hardwareName.localeCompare(b.hardwareName));
}, [data, fastData, inputFilter, isLoading, fastStatus]);
}, [data, error, inputFilter]);

if (error) {
return <div>Error: {error.message}</div>;
}

return (
<QuerySwitcher status={fastStatus} data={fastData}>
<QuerySwitcher status={status} data={data}>
<Toaster />
<div className="flex flex-col gap-6">
<HardwareTable
Expand Down
14 changes: 4 additions & 10 deletions dashboard/src/types/hardware.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,22 @@
import type { StatusCount } from './general';

export interface HardwareFastItem {
hardwareName: string;
}
export type HardwareFastResponse = {
hardware: HardwareFastItem[];
};

interface BuildCount {
valid: number;
invalid: number;
null: number;
}

export interface HardwareRestItem {
export interface HardwareItem {
hardwareName: string;
buildCount: BuildCount;
testStatusCount: StatusCount;
bootStatusCount: StatusCount;
}

export type HardwareListingItem = HardwareRestItem & HardwareFastItem;
export type HardwareListingItem = HardwareItem;

export interface HardwareListingResponse {
hardware: HardwareListingItem[];
}

export type HardwareTableItem = HardwareFastItem & Partial<HardwareRestItem>;
export type HardwareTableItem = HardwareItem;
Loading