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: issue details navigation #734

Merged
merged 5 commits into from
Jan 9, 2025
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
1 change: 1 addition & 0 deletions backend/kernelCI_app/helpers/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ def is_issue_from_test(incident_test_id: Optional[str], is_known_issue: bool) ->
return incident_test_id is not None or not is_known_issue


# TODO: consider issue_version in the filter
Francisco2002 marked this conversation as resolved.
Show resolved Hide resolved
def is_issue_filtered_out(issue_id: Optional[str], issue_filters: set) -> bool:
return issue_id not in issue_filters

Expand Down
8 changes: 7 additions & 1 deletion backend/kernelCI_app/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,22 @@ class IncidentInfo(TypedDict):

class Issue(TypedDict):
id: str
version: str
comment: Optional[str]
report_url: Optional[str]
incidents_info: IncidentInfo


def create_issue(
*, issue_id: str, issue_comment: Optional[str], issue_report_url: Optional[str]
*,
issue_id: str,
issue_version: str,
issue_comment: Optional[str],
issue_report_url: Optional[str]
) -> Issue:
return {
"id": issue_id,
"version": issue_version,
"comment": issue_comment,
"report_url": issue_report_url,
"incidents_info": {"incidentsCount": 1},
Expand Down
9 changes: 0 additions & 9 deletions backend/kernelCI_app/viewCommon.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from typing import TypedDict
from kernelCI_app.utils import create_issue
from kernelCI_app.helpers.build import build_status_map


Expand Down Expand Up @@ -42,11 +41,3 @@ def create_details_build_summary(builds: list[BuildDict]):
"configs": config_summ,
"architectures": arch_summ,
}


def get_details_issue(record):
return create_issue(
issue_id=record["issue_id"],
issue_comment=record["issue_comment"],
issue_report_url=record["issue_report_url"],
)
9 changes: 8 additions & 1 deletion backend/kernelCI_app/views/hardwareDetailsView.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ def get_build(record: Dict, tree_idx: int):
"git_repository_branch": record["build__checkout__git_repository_branch"],
"tree_name": record["build__checkout__tree_name"],
"issue_id": record["build__incidents__issue__id"],
"issue_version": record["build__incidents__issue__version"],
"tree_index": tree_idx,
}

Expand Down Expand Up @@ -142,6 +143,7 @@ def is_record_tree_selected(record, tree, is_all_selected: bool):
# TODO unify with treeDetails
def update_issues(
issue_id: Optional[str],
issue_version: Optional[str],
incident_test_id: Optional[str],
build_valid: Optional[bool],
issue_comment: Optional[str],
Expand All @@ -156,13 +158,14 @@ def update_issues(
elif issue_from == "test":
can_insert_issue = should_increment_test_issue(issue_id, incident_test_id)

if issue_id and can_insert_issue:
if issue_id and issue_version is not None and can_insert_issue:
existing_issue = task["issues"].get(issue_id)
if existing_issue:
existing_issue["incidents_info"]["incidentsCount"] += 1
else:
new_issue = create_issue(
issue_id=issue_id,
issue_version=issue_version,
issue_comment=issue_comment,
issue_report_url=issue_report_url,
)
Expand Down Expand Up @@ -306,6 +309,7 @@ def handle_test(self, record, tests):

update_issues(
issue_id=record["incidents__issue__id"],
issue_version=record["incidents__issue__version"],
incident_test_id=record["incidents__test_id"],
build_valid=record["build__valid"],
issue_comment=record["incidents__issue__comment"],
Expand Down Expand Up @@ -420,6 +424,7 @@ def sanitize_records(self, records, trees: List, is_all_selected: bool):
builds["items"].append(build)
update_issues(
issue_id=record["incidents__issue__id"],
issue_version=record["incidents__issue__version"],
incident_test_id=record["incidents__test_id"],
build_valid=record["build__valid"],
issue_comment=record["incidents__issue__comment"],
Expand Down Expand Up @@ -582,10 +587,12 @@ def get_full_tests(
"build__checkout__tree_name",
"incidents__id",
"incidents__issue__id",
"incidents__issue__version",
"incidents__issue__comment",
"incidents__issue__report_url",
"incidents__test_id",
"build__incidents__issue__id",
"build__incidents__issue__version",
).filter(
start_time__gte=start_date,
start_time__lte=end_date,
Expand Down
43 changes: 24 additions & 19 deletions backend/kernelCI_app/views/issuesView.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
from django.http import HttpResponseBadRequest, JsonResponse
from typing import Dict, List, Optional
from django.http import JsonResponse
from django.db import connection
from django.views import View

from kernelCI_app.utils import convert_issues_dict_to_list, create_issue, getErrorResponseBody
from kernelCI_app.helpers.errorHandling import create_error_response
from kernelCI_app.utils import (
Issue,
convert_issues_dict_to_list,
create_issue,
)


class IssueView(View):
fields = [
"incident_id",
"id",
"comment",
"report_url"
]
fields = ["incident_id", "id", "version", "comment", "report_url"]

def get_dict_record(self, row):
def get_dict_record(self, row) -> Dict[str, str]:
record = {}
for idx, field in enumerate(self.fields):
record[field] = row[idx]
return record

def sanitize_rows(self, rows):
def sanitize_rows(self, rows) -> List[Issue]:
result = {}
for row in rows:
record = self.get_dict_record(row)
Expand All @@ -28,17 +29,19 @@ def sanitize_rows(self, rows):
currentIssue["incidents_info"]["incidentsCount"] += 1
else:
result[record["id"]] = create_issue(
issue_id=record['id'],
issue_comment=record['comment'],
issue_report_url=record['report_url'],
issue_id=record["id"],
issue_version=record["version"],
issue_comment=record["comment"],
issue_report_url=record["report_url"],
)
return convert_issues_dict_to_list(result)

def get_test_issues(self, test_id):
def get_test_issues(self, test_id: str) -> List[Issue]:
query = """
SELECT
incidents.id,
issues.id,
issues.version,
issues.comment,
issues.report_url
FROM incidents
Expand All @@ -51,12 +54,12 @@ def get_test_issues(self, test_id):
rows = cursor.fetchall()
return self.sanitize_rows(rows)

def get_build_issues(self, build_id):
def get_build_issues(self, build_id: str) -> List[Issue]:
query = """
SELECT
incidents.id,
incidents.present,
issues.id,
issues.version,
issues.comment,
issues.report_url
FROM incidents
Expand All @@ -69,11 +72,13 @@ def get_build_issues(self, build_id):
rows = cursor.fetchall()
return self.sanitize_rows(rows)

def get(self, _request, test_id=None, build_id=None):
def get(
self, _request, test_id: Optional[str] = None, build_id: Optional[str] = None
) -> JsonResponse:
if test_id:
return JsonResponse(self.get_test_issues(test_id), safe=False)
if build_id:
return JsonResponse(self.get_build_issues(build_id), safe=False)
return HttpResponseBadRequest(
getErrorResponseBody("A test or build ID must be provided")
return create_error_response(
error_message="A test or build ID must be provided",
)
50 changes: 31 additions & 19 deletions backend/kernelCI_app/views/treeDetailsView.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from typing import Dict, List, Tuple
from django.http import JsonResponse
from django.views import View
from kernelCI_app.helpers.filters import (
Expand All @@ -6,6 +7,7 @@
FilterParams,
)
from kernelCI_app.utils import (
Issue,
convert_issues_dict_to_list,
extract_error_message,
create_issue,
Expand All @@ -23,6 +25,8 @@

from kernelCI_app.viewCommon import create_details_build_summary

type IssueDict = Dict[Tuple[str, str], Issue]


class TreeDetails(View):
def __init__(self):
Expand All @@ -35,8 +39,8 @@ def __init__(self):
self.testPlatformsWithErrors = set()
self.testFailReasons = {}
self.testArchSummary = {}
self.testIssues = []
self.testIssuesTable = {}
self.testIssues: List[Issue] = []
self.testIssuesTable: IssueDict = {}
self.testEnvironmentCompatible = defaultdict(lambda: defaultdict(int))
self.testEnvironmentMisc = defaultdict(lambda: defaultdict(int))
self.bootHistory = []
Expand All @@ -45,8 +49,8 @@ def __init__(self):
self.bootPlatformsFailing = set()
self.bootFailReasons = {}
self.bootArchSummary = {}
self.bootIssues = []
self.bootsIssuesTable = {}
self.bootIssues: List[Issue] = []
self.bootsIssuesTable: IssueDict = {}
self.bootEnvironmentCompatible = defaultdict(lambda: defaultdict(int))
self.bootEnvironmentMisc = defaultdict(lambda: defaultdict(int))
self.incidentsIssueRelationship = defaultdict(
Expand All @@ -58,9 +62,9 @@ def __init__(self):
self.builds = []
self.processed_builds = set()
self.build_summary = {}
self.build_issues = []
self.build_issues: List[Issue] = []
self.failed_builds_with_unknown_issues = 0
self.processed_build_issues = {}
self.processed_build_issues: IssueDict = {}
self.tree_url = ""

def setup_filters(self):
Expand All @@ -79,7 +83,7 @@ def setup_filters(self):
self.filterBuildValid = self.filterParams.filterBuildValid
self.filterIssues = self.filterParams.filterIssues

def __getCurrentRowData(self, currentRow):
def _getCurrentRowData(self, currentRow):
tmp_test_env_comp_key = 14
currentRowData = {
"test_id": currentRow[1],
Expand Down Expand Up @@ -115,8 +119,9 @@ def __getCurrentRowData(self, currentRow):
"incident_test_id": currentRow[32],
"incident_present": currentRow[33],
"issue_id": currentRow[34],
"issue_comment": currentRow[35],
"issue_report_url": currentRow[36],
"issue_version": currentRow[35],
"issue_comment": currentRow[36],
"issue_report_url": currentRow[37],
}

environment_misc = handle_environment_misc(
Expand Down Expand Up @@ -161,7 +166,7 @@ def __getCurrentRowData(self, currentRow):

return currentRowData

def __processBootsTest(self, currentRowData):
def _processBootsTest(self, currentRowData):
testId = currentRowData["test_id"]
testStatus = currentRowData["test_status"]
testDuration = currentRowData["test_duration"]
Expand All @@ -173,6 +178,7 @@ def __processBootsTest(self, currentRowData):
historyItem = currentRowData["history_item"]
incident_id = currentRowData["incident_id"]
issue_id = currentRowData["issue_id"]
issue_version = currentRowData["issue_version"]
issue_comment = currentRowData["issue_comment"]
issue_report_url = currentRowData["issue_report_url"]
testEnvironmentCompatible = currentRowData["test_environment_compatible"]
Expand All @@ -195,12 +201,13 @@ def __processBootsTest(self, currentRowData):
)

if issue_id and can_insert_issue:
currentIssue = self.bootsIssuesTable.get(issue_id)
currentIssue = self.bootsIssuesTable.get((issue_id, issue_version))
if currentIssue:
currentIssue["incidents_info"]["incidentsCount"] += 1
else:
self.bootsIssuesTable[issue_id] = create_issue(
self.bootsIssuesTable[(issue_id, issue_version)] = create_issue(
issue_id=issue_id,
issue_version=issue_version,
issue_comment=issue_comment,
issue_report_url=issue_report_url,
)
Expand Down Expand Up @@ -250,6 +257,7 @@ def __processNonBootsTest(self, currentRowData):
testError = currentRowData["test_error"]
historyItem = currentRowData["history_item"]
issue_id = currentRowData["issue_id"]
issue_version = currentRowData["issue_version"]
issue_comment = currentRowData["issue_comment"]
issue_report_url = currentRowData["issue_report_url"]
testEnvironmentCompatible = currentRowData["test_environment_compatible"]
Expand All @@ -270,12 +278,13 @@ def __processNonBootsTest(self, currentRowData):
can_insert_issue = should_increment_test_issue(issue_id, incident_test_id)

if issue_id and can_insert_issue:
currentIssue = self.testIssuesTable.get(issue_id)
currentIssue = self.testIssuesTable.get((issue_id, issue_version))
if currentIssue:
currentIssue["incidents_info"]["incidentsCount"] += 1
else:
self.testIssuesTable[issue_id] = create_issue(
self.testIssuesTable[(issue_id, issue_version)] = create_issue(
issue_id=issue_id,
issue_version=issue_version,
issue_comment=issue_comment,
issue_report_url=issue_report_url,
)
Expand Down Expand Up @@ -315,6 +324,7 @@ def __processNonBootsTest(self, currentRowData):
def _process_builds(self, row_data):
build_id = row_data["build_id"]
issue_id = row_data["issue_id"]
issue_version = row_data["issue_version"]
build_valid = row_data["build_valid"]
build_duration = row_data["build_duration"]

Expand All @@ -326,12 +336,13 @@ def _process_builds(self, row_data):
return

if issue_id and (build_valid is False or build_valid is None):
current_issue = self.processed_build_issues.get(issue_id)
current_issue = self.processed_build_issues.get((issue_id, issue_version))
if current_issue:
current_issue["incidents_info"]["incidentsCount"] += 1
else:
self.processed_build_issues[issue_id] = create_issue(
issue_id=row_data["issue_id"],
self.processed_build_issues[(issue_id, issue_version)] = create_issue(
issue_id=issue_id,
issue_version=issue_version,
issue_comment=row_data["issue_comment"],
issue_report_url=row_data["issue_report_url"],
)
Expand All @@ -345,7 +356,7 @@ def _process_builds(self, row_data):

def _sanitize_rows(self, rows):
for row in rows:
row_data = self.__getCurrentRowData(row)
row_data = self._getCurrentRowData(row)

test_path = row_data["test_path"]
test_environment_compatible = row_data["test_environment_compatible"]
Expand Down Expand Up @@ -383,7 +394,7 @@ def _sanitize_rows(self, rows):

if test_path:
if test_path.startswith("boot"):
self.__processBootsTest(row_data)
self._processBootsTest(row_data)
else:
self.__processNonBootsTest(row_data)

Expand Down Expand Up @@ -432,6 +443,7 @@ def get(self, request, commit_hash: str | None):
incidents.test_id AS incidents_test_id,
incidents.present AS incidents_present,
issues.id AS issues_id,
issues.version AS issues_version,
issues.comment AS issues_comment,
issues.report_url AS issues_report_url
FROM
Expand Down
2 changes: 1 addition & 1 deletion dashboard/src/components/Breadcrumb/Breadcrumb.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const BreadcrumbList = ({ ...props }: BreadcrumbListProps): JSX.Element => {

type BreadcrumbLinkProps = Pick<
LinkProps,
'to' | 'params' | 'from' | 'search' | 'children'
'to' | 'params' | 'from' | 'search' | 'children' | 'state'
>;

const BreadcrumbLink = ({
Expand Down
Loading
Loading