Skip to content

Commit a1359f2

Browse files
authored
feat(notifications): adjust hardware summary template with 3-level layout (#1726)
- Adjust the hardware summary template with a 3-level layout. - Add configuration to filter test results by labs. Signed-off-by: Yushan Li <yushan.li@oss.qualcomm.com>
1 parent 0963da1 commit a1359f2

10 files changed

Lines changed: 134 additions & 53 deletions

File tree

backend/data/notifications/example-hardware-subscription.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
qcs9100-ride: # The hardware platform name
66
origin: maestro
7+
labs:
8+
lava-kci-qualcomm: https://lava-oss.qualcomm.com
79
default_recipients:
810
- Recipient One <Recipient1@example.com>
911
- Recipient Two <Recipient2@example.com>

backend/data/notifications/subscriptions/qcs615_hardware.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
qcs615-ride:
22
origin: maestro
3+
labs:
4+
lava-kci-qualcomm: https://lava-oss.qualcomm.com
35
default_recipients:
46
- Trilok Soni <tsoni@quicinc.com>
57
- Shiraz Hashim <shashim@qti.qualcomm.com>

backend/data/notifications/subscriptions/qcs6490_hardware.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
qcs6490-rb3gen2:
22
origin: maestro
3+
labs:
4+
lava-kci-qualcomm: https://lava-oss.qualcomm.com
35
default_recipients:
46
- Trilok Soni <tsoni@quicinc.com>
57
- Shiraz Hashim <shashim@qti.qualcomm.com>

backend/data/notifications/subscriptions/qcs8300_hardware.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
qcs8300-ride:
22
origin: maestro
3+
labs:
4+
lava-kci-qualcomm: https://lava-oss.qualcomm.com
35
default_recipients:
46
- Trilok Soni <tsoni@quicinc.com>
57
- Shiraz Hashim <shashim@qti.qualcomm.com>

backend/data/notifications/subscriptions/qcs9100_hardware.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
qcs9100-ride:
22
origin: maestro
3+
labs:
4+
lava-kci-qualcomm: https://lava-oss.qualcomm.com
35
default_recipients:
46
- Trilok Soni <tsoni@quicinc.com>
57
- Shiraz Hashim <shashim@qti.qualcomm.com>

backend/data/notifications/subscriptions/x1e80100_hardware.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
x1e80100:
22
origin: maestro
3+
labs:
4+
lava-kci-qualcomm: https://lava-oss.qualcomm.com
35
default_recipients:
46
- Trilok Soni <tsoni@quicinc.com>
57
- Shiraz Hashim <shashim@qti.qualcomm.com>

backend/kernelCI_app/management/commands/helpers/summary.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ def process_hardware_submissions_files(
176176
) -> tuple[set[HardwareKey], dict[HardwareKey, dict[str, Any]]]:
177177
"""
178178
Processes all hardware submission files and returns the set of HardwareKey
179-
and the dict linking each hardware to its default recipients
179+
and the dict linking each hardware to its default recipients and labs
180180
"""
181181
(base_dir, signup_folder) = _assign_default_folders(
182182
base_dir=base_dir, signup_folder=signup_folder
@@ -189,7 +189,10 @@ def process_hardware_submissions_files(
189189
"default_recipients": [
190190
"Recipient One <Recipient1@example.com>",
191191
"Recipient Two <Recipient2@example.com>"
192-
]
192+
],
193+
"labs": {
194+
"lava-collabora": "https://lava.collabora.dev",
195+
},
193196
}
194197
"""
195198

@@ -200,6 +203,8 @@ def process_hardware_submissions_files(
200203
201204
imx6q-sabrelite:
202205
origin: maestro
206+
labs:
207+
lava-collabora: https://lava.collabora.dev
203208
default_recipients:
204209
- Recipient One <Recipient1@example.com>
205210
- Recipient Two <Recipient2@example.com>
@@ -212,11 +217,13 @@ def process_hardware_submissions_files(
212217
if hardware_origins is not None and origin not in hardware_origins:
213218
continue
214219
default_recipients = hardware_values.get("default_recipients", [])
220+
labs = hardware_values.get("labs")
215221

216222
hardware_key = (hardware_name, origin)
217223
hardware_key_set.add(hardware_key)
218224
hardware_prop_map[hardware_key] = {
219-
"default_recipients": default_recipients
225+
"default_recipients": default_recipients,
226+
"labs": labs,
220227
}
221228
else:
222229
log_message(

backend/kernelCI_app/management/commands/notifications.py

Lines changed: 44 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -637,20 +637,31 @@ def generate_hardware_summary_report(
637637
start_date = now - timedelta(days=7)
638638
end_date = now
639639

640-
# process the hardware submission files
640+
# Process the hardware submission files
641641
hardware_key_set, hardware_prop_map = process_hardware_submissions_files(
642642
signup_folder=signup_folder,
643643
hardware_origins=hardware_origins,
644644
)
645-
646-
# get detailed data for all hardware
645+
# Map used to filter tests by lab
646+
labs_by_key = {}
647+
for key, props in hardware_prop_map.items():
648+
labs = props.get("labs")
649+
lab_names = set(labs.keys())
650+
labs_by_key[key] = lab_names
651+
652+
# Get detailed data for all hardware
647653
hardwares_data_raw = get_hardware_summary_data(
648654
keys=list(hardware_key_set),
649655
start_date=start_date,
650656
end_date=end_date,
657+
labs_by_key=labs_by_key,
651658
)
652659

653-
hardwares_data_dict = defaultdict(list)
660+
hardwares_data_dict = defaultdict(
661+
lambda: defaultdict(
662+
lambda: defaultdict(lambda: defaultdict(lambda: defaultdict(list)))
663+
)
664+
)
654665
for raw in hardwares_data_raw:
655666
try:
656667
environment_misc = json.loads(raw.get("environment_misc", "{}"))
@@ -663,50 +674,65 @@ def generate_hardware_summary_report(
663674
raw["runtime"] = misc.get("runtime")
664675
origin = raw.get("test_origin")
665676
key = (hardware_id, origin)
666-
hardwares_data_dict[key].append(raw)
677+
tree = raw.get("tree_name")
678+
branch = raw.get("git_repository_branch")
679+
commit = raw.get("git_commit_hash")
680+
path = raw.get("path")
681+
dot = path.find(".")
682+
suite = path[:dot] if dot != -1 else path
667683

668-
# get the total build/boot/test counts for each hardware
684+
hardwares_data_dict[key][suite][tree][branch][commit].append(raw)
685+
686+
# Get the total build/boot/test counts for each hardware
669687
hardwares_list_raw = get_hardware_listing_data_bulk(
670688
keys=list(hardware_key_set),
671689
start_date=start_date,
672690
end_date=end_date,
691+
labs_by_key=labs_by_key,
673692
)
674693

675694
# Iterate through each hardware record to render report, extract recipient, send email
676695
for hardware_id, origin in hardware_key_set:
677-
hardware_data = hardwares_data_dict.get((hardware_id, origin), [])
696+
hardware_data = hardwares_data_dict.get((hardware_id, origin), {})
678697
hardware_raw = next(
679-
(row for row in hardwares_list_raw if row.get("platform") == hardware_id),
698+
(
699+
row
700+
for row in hardwares_list_raw
701+
if row.get("platform") == hardware_id
702+
and row.get("test_origin") == origin
703+
),
680704
None,
681705
)
682706
if hardware_raw is None:
683-
print(f"Hardware {hardware_id} not found in listing data")
707+
print(f"Hardware {hardware_id} from {origin} not found in listing data")
684708
continue
685709

686710
hardware_item = sanitize_hardware(hardware_raw)
687-
build_status_group = group_status(hardware_item.build_status_summary)
688-
boot_status_group = group_status(hardware_item.boot_status_summary)
689-
test_status_group = group_status(hardware_item.test_status_summary)
711+
build_status_group_all = group_status(hardware_item.build_status_summary)
712+
boot_status_group_all = group_status(hardware_item.boot_status_summary)
713+
test_status_group_all = group_status(hardware_item.test_status_summary)
690714

691-
# render the template
715+
# Render the template
692716
template = setup_jinja_template("hardware_report.txt.j2")
693717
report = {}
718+
labs_for_key = hardware_prop_map[(hardware_id, origin)]["labs"]
694719
report["content"] = template.render(
695720
hardware_id=hardware_id,
696721
hardware_data=hardware_data,
697-
build_status_group=build_status_group,
698-
boot_status_group=boot_status_group,
699-
test_status_group=test_status_group,
722+
labs_for_key=labs_for_key,
723+
build_status_group_all=build_status_group_all,
724+
boot_status_group_all=boot_status_group_all,
725+
test_status_group_all=test_status_group_all,
700726
)
701727
report["title"] = (
702728
f"hardware {hardware_id} summary - {now.strftime("%Y-%m-%d %H:%M %Z")}"
703729
)
704730

705-
# extract recipient
731+
# Extract recipient
706732
hardware_report = hardware_prop_map.get((hardware_id, origin), {})
707733
recipients = hardware_report.get("default_recipients", [])
708734

709-
# send email
735+
# Send email
710736
send_email_report(
711737
service=service,
712738
report=report,
Lines changed: 33 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,42 @@
11
{% extends "base.txt" %}
2+
{% set IND = ' ' %}
23
{% block header %}{% endblock %}
34
{% block content %}
45
Hello,
56

67
Status summary for {{ hardware_id }}
8+
Builds:{{ "\t" }}{{ "{:>5}".format(build_status_group_all["success"]) }} ✅
9+
{{- "{:>5}".format(build_status_group_all["failed"]) }} ❌
10+
{{- "{:>5}".format(build_status_group_all["inconclusive"]) }} ⚠️
11+
Boots: {{ "\t" }}{{ "{:>5}".format(boot_status_group_all["success"]) }} ✅
12+
{{- "{:>5}".format(boot_status_group_all["failed"]) }} ❌
13+
{{- "{:>5}".format(boot_status_group_all["inconclusive"]) }} ⚠️
14+
Tests: {{ "\t" }}{{ "{:>5}".format(test_status_group_all["success"]) }} ✅
15+
{{- "{:>5}".format(test_status_group_all["failed"]) }} ❌
16+
{{- "{:>5}".format(test_status_group_all["inconclusive"]) }} ⚠️
17+
================================================================
18+
{% for suite_name, trees in hardware_data.items() %}
19+
>{{ IND }}TEST SUITE: {{ suite_name }}
20+
------------------------------------------------------------------------------------------------------------
21+
{%- for tree_name, branches in trees.items() %}
22+
{%- for branch_name, commits in branches.items() %}
23+
{{ IND * 4 }}>>{{ IND }}TREE: {{ tree_name }}{{ IND }}|{{ IND }}BRANCH: {{ branch_name }}
724

8-
Builds:{{ "\t" }}{{ "{:>5}".format(build_status_group["success"]) }} ✅
9-
{{- "{:>5}".format(build_status_group["failed"]) }} ❌
10-
{{- "{:>5}".format(build_status_group["inconclusive"]) }} ⚠️
11-
Boots: {{ "\t" }}{{ "{:>5}".format(boot_status_group["success"]) }} ✅
12-
{{- "{:>5}".format(boot_status_group["failed"]) }} ❌
13-
{{- "{:>5}".format(boot_status_group["inconclusive"]) }} ⚠️
14-
Tests: {{ "\t" }}{{ "{:>5}".format(test_status_group["success"]) }} ✅
15-
{{- "{:>5}".format(test_status_group["failed"]) }} ❌
16-
{{- "{:>5}".format(test_status_group["inconclusive"]) }} ⚠️
17-
-------------
18-
{% for data in hardware_data %}
19-
#kernelci test {{ data["id"] }}
20-
- Path: {{ data["path"] }}
21-
- Status: {{ data["status"] }}
22-
- Comment: {{ data["comment"] }}
23-
- Starttime: {{ data["start_time"] }}
24-
- Tree: {{ data["tree_name"] }}/{{ data["git_repository_branch"] }}
25-
- Origin: {{ data["test_origin"] }}
26-
- Test_lab: {{ data["runtime"] }}
27-
- Lava_job_id: {{ data["job_id"] }}
28-
- Commit: {{ data["git_commit_hash"] }}
29-
- Dashboard: https://dashboard.kernelci.org/test/{{ data["id"] }}
30-
-------------
25+
{%- for commit_hash, commit_data in commits.items() %}
26+
{{ IND * 8 }}>>>{{ IND }}COMMIT: {{ commit_hash }}
27+
28+
{%- for data in commit_data | sort(attribute='path') %}
29+
{{ IND * 12 }}- Path: {{ data["path"] }}
30+
{{ IND * 12 }}- Status: {{ data["status"] }}
31+
{{ IND * 12 }}- Origin: {{ data["test_origin"] }}
32+
{{ IND * 12 }}- Starttime: {{ data["start_time"] }}
33+
{{ IND * 12 }}- Test_lab: {{ data["runtime"] }}
34+
{{ IND * 12 }}- Lava_job: {{ labs_for_key[data["runtime"]]}}/scheduler/job/{{ data["job_id"] }}
35+
{{ IND * 12 }}- Dashboard: https://dashboard.kernelci.org/test/{{ data["id"] }}
36+
{{ IND * 12 }}------------------------------------------------------------
37+
{% endfor %}
38+
{%- endfor -%}
39+
{%- endfor -%}
40+
{%- endfor -%}
3141
{% endfor %}
3242
{%- endblock -%}

backend/kernelCI_app/queries/hardware.py

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -175,14 +175,27 @@ def get_hardware_listing_data(
175175

176176

177177
def get_hardware_listing_data_bulk(
178-
keys: list[tuple[str, str]], start_date: datetime, end_date: datetime
178+
keys: list[tuple[str, str]],
179+
start_date: datetime,
180+
end_date: datetime,
181+
labs_by_key: dict[tuple[str, str], set[str]],
179182
) -> list[dict]:
180183
if not keys:
181184
return []
182185

183186
count_clauses = _get_hardware_listing_count_clauses()
187+
188+
triples: list[tuple[str, str, str]] = []
189+
for hardware_id, origin in keys:
190+
lab_names = labs_by_key[(hardware_id, origin)]
191+
for lab in lab_names:
192+
triples.append((hardware_id, origin, lab))
193+
184194
values_clause = ", ".join(
185-
[f"(%(hardware_id_{i})s, %(origin_{i})s)" for i in range(len(keys))]
195+
[
196+
f"(%(hardware_id_{i})s, %(origin_{i})s, %(lab_name_{i})s)"
197+
for i in range(len(triples))
198+
]
186199
)
187200

188201
query = f"""
@@ -206,12 +219,13 @@ def get_hardware_listing_data_bulk(
206219
AND "tests"."start_time" <= %(end_date)s
207220
AND EXISTS (
208221
SELECT 1
209-
FROM (VALUES {values_clause}) AS key_list(hardware_id, origin)
222+
FROM (VALUES {values_clause}) AS key_list(hardware_id, origin, lab_name)
210223
WHERE(
211224
tests.environment_compatible @> ARRAY[key_list.hardware_id]::TEXT[]
212225
OR tests.environment_misc ->> 'platform' = key_list.hardware_id
213226
)
214227
AND tests.origin = key_list.origin
228+
AND tests.misc ->> 'runtime' = key_list.lab_name
215229
)
216230
)
217231
SELECT
@@ -231,9 +245,10 @@ def get_hardware_listing_data_bulk(
231245
"start_date": start_date,
232246
"end_date": end_date,
233247
}
234-
for i, (hardware_id, origin) in enumerate(keys):
248+
for i, (hardware_id, origin, lab_name) in enumerate(triples):
235249
params[f"hardware_id_{i}"] = hardware_id
236250
params[f"origin_{i}"] = origin
251+
params[f"lab_name_{i}"] = lab_name
237252

238253
with connection.cursor() as cursor:
239254
cursor.execute(query, params)
@@ -412,7 +427,11 @@ def query_records(
412427

413428

414429
def get_hardware_summary_data(
415-
*, keys: list[tuple[str, str]], start_date: datetime, end_date: datetime
430+
*,
431+
keys: list[tuple[str, str]],
432+
start_date: datetime,
433+
end_date: datetime,
434+
labs_by_key: dict[tuple[str, str], set[str]],
416435
) -> list[dict] | None:
417436
"""
418437
Get hardware summary data for given keys within a date range.
@@ -421,8 +440,14 @@ def get_hardware_summary_data(
421440
if not keys:
422441
return []
423442

443+
triples: list[tuple[str, str, str]] = []
444+
for hardware_id, origin in keys:
445+
lab_names = labs_by_key[(hardware_id, origin)]
446+
for lab in lab_names:
447+
triples.append((hardware_id, origin, lab))
448+
424449
with connection.cursor() as cursor:
425-
values_clause = ", ".join(["(%s, %s)"] * len(keys))
450+
values_clause = ", ".join(["(%s, %s, %s)"] * len(triples))
426451

427452
query = f"""
428453
SELECT
@@ -466,20 +491,21 @@ def get_hardware_summary_data(
466491
AND tests.status = 'FAIL'
467492
AND EXISTS (
468493
SELECT 1
469-
FROM (VALUES {values_clause}) AS key_list(hardware_id, origin)
494+
FROM (VALUES {values_clause}) AS key_list(hardware_id, origin, lab_name)
470495
WHERE(
471496
tests.environment_compatible @> ARRAY[key_list.hardware_id]::TEXT[]
472497
OR tests.environment_misc ->> 'platform' = key_list.hardware_id
473498
)
474499
AND tests.origin = key_list.origin
500+
AND tests.misc ->> 'runtime' = key_list.lab_name
475501
)
476502
ORDER BY
477503
tests.start_time DESC
478504
"""
479505

480506
params = [start_date, end_date]
481-
for hardware_id, origin in keys:
482-
params.extend([hardware_id, origin])
507+
for hardware_id, origin, lab_name in triples:
508+
params.extend([hardware_id, origin, lab_name])
483509

484510
cursor.execute(query, params)
485511

0 commit comments

Comments
 (0)