Skip to content

Commit

Permalink
improvement(graphs): Mark dependency changes
Browse files Browse the repository at this point in the history
In performance tests it's important to note changes of dependencies,
like kernel, drivers, stress tools.

This commit changes the way point is displayed (gets white background)
and adds changed packages details to the tooltip.

closes: scylladb#490
  • Loading branch information
soyacz committed Nov 13, 2024
1 parent eb3097c commit 3f468b3
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 36 deletions.
39 changes: 28 additions & 11 deletions argus/backend/service/results_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,13 +127,30 @@ class RunsDetails:
shapes = ["circle", "triangle", "rect", "star", "dash", "crossRot", "line"]


def get_sorted_data_for_column_and_row(data: List[ArgusGenericResultData], column: str, row: str) -> List[Dict[str, Any]]:
return sorted([{"x": entry.sut_timestamp.strftime('%Y-%m-%dT%H:%M:%SZ'),
def get_sorted_data_for_column_and_row(data: List[ArgusGenericResultData], column: str, row: str,
runs_details: RunsDetails) -> List[Dict[str, Any]]:
points = sorted([{"x": entry.sut_timestamp.strftime('%Y-%m-%dT%H:%M:%SZ'),
"y": entry.value,
"id": entry.run_id}
"id": entry.run_id,
}
for entry in data if entry.column == column and entry.row == row],
key=lambda point: point["x"])

if not points:
return points
packages = runs_details.packages
points[0]['changes'] = []
prev_versions = {pkg.name: pkg.version for pkg in packages.get(points[0]["id"], [])}
for point in points[1:]:
changes = []
current_versions = {pkg.name: pkg.version for pkg in packages.get(point["id"], [])}
for pkg_name in current_versions.keys() | prev_versions.keys():
curr_ver = current_versions.get(pkg_name)
prev_ver = prev_versions.get(pkg_name)
if curr_ver != prev_ver:
changes.append({'name': pkg_name, 'prev_version': prev_ver, 'curr_version': curr_ver})
point['changes'] = [f"{change['name']}: {change['prev_version']} -> {change['curr_version']}" for change in changes]
prev_versions = current_versions
return points

def get_min_max_y(datasets: List[Dict[str, Any]]) -> (float, float):
"""0.5 - 1.5 of min/max of 50% results"""
Expand Down Expand Up @@ -191,7 +208,8 @@ def calculate_limits(points: List[dict], best_results: List, validation_rules_li


def create_datasets_for_column(table: ArgusGenericResultMetadata, data: list[ArgusGenericResultData],
best_results: dict[str, List[BestResult]], releases_map: ReleasesMap, column: ColumnMetadata) -> List[Dict]:
best_results: dict[str, List[BestResult]], releases_map: ReleasesMap, column: ColumnMetadata,
runs_details: RunsDetails) -> List[Dict]:
"""
Create datasets (series) for a specific column, splitting by version and showing limit lines.
"""
Expand All @@ -200,7 +218,7 @@ def create_datasets_for_column(table: ArgusGenericResultMetadata, data: list[Arg

for idx, row in enumerate(table.rows_meta):
line_color = colors[idx % len(colors)]
points = get_sorted_data_for_column_and_row(data, column.name, row)
points = get_sorted_data_for_column_and_row(data, column.name, row, runs_details)

datasets.extend(create_release_datasets(points, row, releases_map, line_color))

Expand All @@ -226,7 +244,7 @@ def create_release_datasets(points: list[Dict], row: str, releases_map: Releases
"label": f"{release} - {row}",
"borderColor": line_color,
"borderWidth": 2,
"pointRadius": 2,
"pointRadius": 3,
"showLine": True,
"data": release_points,
"pointStyle": shapes[v_idx % len(shapes)]
Expand Down Expand Up @@ -324,15 +342,15 @@ def _split_results_by_release(packages: dict[str, list[PackageVersion]], main_pa


def create_chartjs(table: ArgusGenericResultMetadata, data: list[ArgusGenericResultData], best_results: dict[str, List[BestResult]],
releases_map: ReleasesMap) -> List[Dict]:
releases_map: ReleasesMap, runs_details: RunsDetails) -> List[Dict]:
"""
Create Chart.js-compatible graph for each column in the table.
"""
graphs = []
columns = [column for column in table.columns_meta if column.type != "TEXT"]

for column in columns:
datasets = create_datasets_for_column(table, data, best_results, releases_map, column)
datasets = create_datasets_for_column(table, data, best_results, releases_map, column, runs_details)

if datasets:
min_y, max_y = get_min_max_y(datasets)
Expand Down Expand Up @@ -430,8 +448,7 @@ def get_test_graphs(self, test_id: UUID, start_date: datetime | None = None, end
best_results = self.get_best_results(test_id=test_id, name=table.name)
main_package = _identify_most_changed_package([pkg for sublist in runs_details.packages.values() for pkg in sublist])
releases_map = _split_results_by_release(runs_details.packages, main_package=main_package)
graphs.extend(create_chartjs(table, data, best_results,
releases_map=releases_map))
graphs.extend(create_chartjs(table, data, best_results, releases_map=releases_map, runs_details=runs_details))
releases_filters.update(releases_map.keys())
ticks = calculate_graph_ticks(graphs)
return graphs, ticks, list(releases_filters)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
create_limit_dataset,
calculate_limits,
calculate_graph_ticks, _identify_most_changed_package, _split_results_by_release,
BestResult
BestResult, RunsDetails
)
from argus.backend.models.result import ArgusGenericResultMetadata, ArgusGenericResultData, ColumnMetadata, ValidationRules

Expand Down Expand Up @@ -49,24 +49,32 @@ def test_split_results_by_versions_should_group_correctly(package_data):


def test_get_sorted_data_for_column_and_row():
run_id1 = uuid4()
run_id2 = uuid4()
run_id3 = uuid4()
data = [
ArgusGenericResultData(run_id=uuid4(), column="col1", row="row1", value=1.5, status="PASS", sut_timestamp=datetime(2023, 10, 23)),
ArgusGenericResultData(run_id=uuid4(), column="col1", row="row1", value=2.5, status="PASS", sut_timestamp=datetime(2023, 10, 24)),
ArgusGenericResultData(run_id=uuid4(), column="col1", row="row1", value=0.5, status="PASS", sut_timestamp=datetime(2023, 10, 22)),
ArgusGenericResultData(run_id=uuid4(), column="col1", row="row2", value=3.5, status="PASS", sut_timestamp=datetime(2023, 10, 25)),
ArgusGenericResultData(run_id=uuid4(), column="col2", row="row1", value=4.5, status="PASS", sut_timestamp=datetime(2023, 10, 26)),
ArgusGenericResultData(run_id=run_id1, column="col1", row="row1", value=1.5, status="PASS",
sut_timestamp=datetime(2023, 10, 23)),
ArgusGenericResultData(run_id=run_id2, column="col1", row="row1", value=2.5, status="PASS",
sut_timestamp=datetime(2023, 10, 24)),
ArgusGenericResultData(run_id=run_id3, column="col1", row="row1", value=0.5, status="PASS",
sut_timestamp=datetime(2023, 10, 22)),
]
result = get_sorted_data_for_column_and_row(data, "col1", "row1")
packages = {
run_id3: [PackageVersion(name='pkg1', version='1.0', date='', revision_id='', build_id='')],
run_id1: [PackageVersion(name='pkg1', version='1.0', date='', revision_id='', build_id=''),
PackageVersion(name='pkg2', version='1.0', date='', revision_id='', build_id='')],
run_id2: [PackageVersion(name='pkg1', version='1.1', date='', revision_id='', build_id='')],
}
runs_details = RunsDetails(ignored=[], packages=packages)
result = get_sorted_data_for_column_and_row(data, "col1", "row1", runs_details)
expected = [
{"x": "2023-10-22T00:00:00Z", "y": 0.5},
{"x": "2023-10-23T00:00:00Z", "y": 1.5},
{"x": "2023-10-24T00:00:00Z", "y": 2.5},
{"x": "2023-10-22T00:00:00Z", "y": 0.5, "changes": []},
{"x": "2023-10-23T00:00:00Z", "y": 1.5, "changes": ["pkg2: None -> 1.0"]},
{"x": "2023-10-24T00:00:00Z", "y": 2.5, "changes": ["pkg2: 1.0 -> None", "pkg1: 1.0 -> 1.1"]},
]

result_without_id = [{"x": item["x"], "y": item["y"]} for item in result]

assert result_without_id == expected

result_data = [{"x": item["x"], "y": item["y"], "changes": item["changes"]} for item in result]
assert result_data == expected

def test_get_min_max_y():
datasets = [
Expand Down Expand Up @@ -138,7 +146,8 @@ def test_create_datasets_for_column():
best_results = {}
releases_map = {"2024.2": [point.run_id for point in data][:1], "2024.3": [point.run_id for point in data][2:]}
column = table.columns_meta[0]
datasets = create_datasets_for_column(table, data, best_results, releases_map, column)
runs_details = RunsDetails(ignored=[], packages={})
datasets = create_datasets_for_column(table, data, best_results, releases_map, column, runs_details)
assert len(datasets) == 2
labels = [dataset["label"] for dataset in datasets]
assert "2024.2 - row1" in labels
Expand Down
20 changes: 13 additions & 7 deletions argus/backend/tests/results_service/test_create_chartjs.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from uuid import uuid4

from argus.backend.models.result import ArgusGenericResultMetadata, ColumnMetadata, ArgusGenericResultData, ValidationRules
from argus.backend.service.results_service import create_chartjs, BestResult
from argus.backend.service.results_service import create_chartjs, BestResult, RunsDetails


def test_create_chartjs_without_validation_rules_should_create_chart_without_limits_series():
Expand Down Expand Up @@ -31,7 +31,8 @@ def test_create_chartjs_without_validation_rules_should_create_chart_without_lim
'col1:row1': [BestResult(key='col1:row1', value=100.0, result_date=datetime(2021, 1, 1), run_id=str(uuid4()))]
}
releases_map = {"1.0": [point.run_id for point in data]}
graphs = create_chartjs(table, data, best_results, releases_map)
runs_details = RunsDetails(ignored=[], packages={})
graphs = create_chartjs(table, data, best_results, releases_map, runs_details)
assert len(graphs) == 1
assert len(graphs[0]['data']['datasets']) == 1 # no limits series

Expand Down Expand Up @@ -59,7 +60,8 @@ def test_create_chartjs_without_best_results_should_not_fail():
]
best_results = {}
releases_map = {"1.0": [point.run_id for point in data]}
graphs = create_chartjs(table, data, best_results, releases_map)
runs_details = RunsDetails(ignored=[], packages={})
graphs = create_chartjs(table, data, best_results, releases_map, runs_details)
assert len(graphs) == 1
assert len(graphs[0]['data']['datasets']) == 1 # no limits series

Expand Down Expand Up @@ -91,7 +93,8 @@ def test_create_chartjs_with_validation_rules_should_add_limit_series():
'col1:row1': [BestResult(key='col1:row1', value=100.0, result_date=datetime(2021, 1, 1), run_id=str(uuid4()))]
}
releases_map = {"1.0": [point.run_id for point in data]}
graphs = create_chartjs(table, data, best_results, releases_map)
runs_details = RunsDetails(ignored=[], packages={})
graphs = create_chartjs(table, data, best_results, releases_map, runs_details)
assert 'limit' in graphs[0]['data']['datasets'][0]['data'][0]

def test_chartjs_with_multiple_best_results_and_validation_rules_should_adjust_limits_for_each_point():
Expand Down Expand Up @@ -138,7 +141,8 @@ def test_chartjs_with_multiple_best_results_and_validation_rules_should_adjust_l
]
}
releases_map = {"1.0": [point.run_id for point in data]}
graphs = create_chartjs(table, data, best_results, releases_map)
runs_details = RunsDetails(ignored=[], packages={})
graphs = create_chartjs(table, data, best_results, releases_map, runs_details)
datasets = graphs[0]['data']['datasets']
limits = [point.get('limit') for dataset in datasets for point in dataset['data'] if 'limit' in point]
assert len(limits) == 2
Expand All @@ -156,7 +160,8 @@ def test_create_chartjs_no_data_should_not_fail():
data = []
best_results = {}
releases_map = {"1.0": []}
graphs = create_chartjs(table, data, best_results, releases_map)
runs_details = RunsDetails(ignored=[], packages={})
graphs = create_chartjs(table, data, best_results, releases_map, runs_details)
assert len(graphs) == 0

def test_create_chartjs_multiple_columns_and_rows():
Expand Down Expand Up @@ -203,7 +208,8 @@ def test_create_chartjs_multiple_columns_and_rows():
]
}
releases_map = {"1.0": [point.run_id for point in data]}
graphs = create_chartjs(table, data, best_results, releases_map)
runs_details = RunsDetails(ignored=[], packages={})
graphs = create_chartjs(table, data, best_results, releases_map, runs_details)
assert len(graphs) == 2
assert len(graphs[0]['data']['datasets']) == 2 # should have also limits dataset
assert len(graphs[1]['data']['datasets']) == 1 # no limits series
14 changes: 12 additions & 2 deletions frontend/TestRun/ResultsGraph.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,16 @@
graph.options.responsive = false;
graph.options.lazy = true;
graph.options.plugins.tooltip = {
usePointStyle: true,
callbacks: {
label: function (tooltipItem) {
const y = tooltipItem.parsed.y.toFixed(2);
const x = new Date(tooltipItem.parsed.x).toLocaleDateString("sv-SE");
const ori = tooltipItem.raw.ori;
const limit = tooltipItem.raw.limit;
return `${x}: ${ori ? ori : y} (limit: ${limit?.toFixed(2) || "N/A"})`;
}
const changes = tooltipItem.raw.changes;
return [`${x}: ${ori ? ori : y} (limit: ${limit?.toFixed(2) || "N/A"})`, ...changes];
},
}
};
graph.options.scales.x.min = ticks["min"];
Expand All @@ -94,6 +96,14 @@
}
}
};
graph.data.datasets.forEach((dataset) => {
const pointBackgroundColors = dataset.data.map((point) =>
point.changes?.length > 0 ? 'white' : dataset.backgroundColor || dataset.borderColor
);
dataset.pointBackgroundColor = pointBackgroundColors;
});
chart = new Chart(
document.getElementById(`graph-${test_id}-${index}`),
{type: "scatter", data: graph.data, options: {...graph.options, ...actions}}
Expand Down

0 comments on commit 3f468b3

Please sign in to comment.