Skip to content

Commit

Permalink
Merge pull request #485 from jjaquish/jjaquish/importance-by-componen…
Browse files Browse the repository at this point in the history
…t-widget

Initial draft commit for importance widget component
  • Loading branch information
LightOfHeaven1994 authored Apr 2, 2024
2 parents 80f23de + 8d17fea commit bae88c4
Show file tree
Hide file tree
Showing 7 changed files with 301 additions and 1 deletion.
43 changes: 43 additions & 0 deletions backend/ibutsu_server/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,49 @@
],
"type": "widget",
},
"importance-component": {
"id": "importance-component",
"title": "Importance by component",
"description": "Test results filtered by component and broken down by importance",
"params": [
{
"name": "job_name",
"description": "The name of the jenkins job to pull from",
"type": "string",
"required": True,
"default": "",
},
{
"name": "group_field",
"description": "the field in a result to group by, typically 'component'",
"type": "string",
"required": True,
"default": "component",
},
{
"name": "env",
"description": "The environment to filter by",
"type": "string",
"required": False,
"default": "",
},
{
"name": "components",
"description": "The component(s) to filter by",
"type": "string",
"required": True,
"default": "",
},
{
"name": "builds",
"description": "The number of Jenkins builds to analyze.",
"type": "integer",
"default": 5,
"required": False,
},
],
"type": "widget",
},
"accessibility-dashboard-view": {
"id": "accessibility-dashboard-view",
"title": "Accessibility Dashboard View",
Expand Down
2 changes: 2 additions & 0 deletions backend/ibutsu_server/controllers/widget_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from ibutsu_server.widgets.accessibility_dashboard_view import get_accessibility_dashboard_view
from ibutsu_server.widgets.compare_runs_view import get_comparison_data
from ibutsu_server.widgets.filter_heatmap import get_filter_heatmap
from ibutsu_server.widgets.importance_component import get_importance_component
from ibutsu_server.widgets.jenkins_heatmap import get_jenkins_heatmap
from ibutsu_server.widgets.jenkins_job_analysis import get_jenkins_analysis_data
from ibutsu_server.widgets.jenkins_job_analysis import get_jenkins_bar_chart
Expand All @@ -24,6 +25,7 @@
"jenkins-bar-chart": get_jenkins_bar_chart,
"jenkins-heatmap": get_jenkins_heatmap,
"filter-heatmap": get_filter_heatmap,
"importance-component": get_importance_component,
"jenkins-job-view": get_jenkins_job_view,
"jenkins-line-chart": get_jenkins_line_chart,
"run-aggregator": get_recent_run_data,
Expand Down
127 changes: 127 additions & 0 deletions backend/ibutsu_server/widgets/importance_component.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
from ibutsu_server.constants import BARCHART_MAX_BUILDS
from ibutsu_server.constants import JJV_RUN_LIMIT
from ibutsu_server.db.models import Result
from ibutsu_server.db.models import Run
from ibutsu_server.filters import string_to_column
from ibutsu_server.widgets.jenkins_job_view import get_jenkins_job_view


def get_importance_component(
env="prod", group_field="component", job_name="", builds=5, components="", project=None
):
# taken from get_jenkins_line_chart in jenkins_job_analysis.py
run_limit = int((JJV_RUN_LIMIT / BARCHART_MAX_BUILDS) * builds)
jobs = get_jenkins_job_view(
filter_=f"job_name={job_name}", page_size=builds, project=project, run_limit=run_limit
).get("jobs")

# A list of job build numbers to filter our runs by
job_ids = []
for job in jobs:
job_ids.append(job["build_number"])

# query for RUN ids
# metadata has to have a string to column to work
# because it is a sqlalchemy property otherwise (AFAIK)
bnumdat = string_to_column("metadata.jenkins.build_number", Run)
run_data = (
Run.query.filter(bnumdat.in_(job_ids), Run.component.in_(components.split(",")))
.add_columns(Run.id, bnumdat.label("build_number"))
.all()
)

# get a list of the job IDs
run_info = {}
for run in run_data:
run_info[run.id] = run.build_number

mdat = string_to_column("metadata.importance", Result)
result_data = (
Result.query.filter(
Result.run_id.in_(run_info.keys()), Result.component.in_(components.split(","))
)
.add_columns(
Result.run_id, Result.component, Result.id, Result.result, mdat.label("importance")
)
.all()
)

"""
This starts a (probably) over complicated bit of data maniplation
to get sdatdict in a proper state to be broken down into
sdatret, which is the format we need for the widget.
"""
sdatdict = {}
bnums = set()
importances = ["critical", "high", "medium", "low"]
for datum in result_data:
# getting the components from the results
if datum.component not in sdatdict.keys():
sdatdict[datum.component] = {}

# getting the build numbers from the results
if run_info[datum.run_id] not in sdatdict[datum.component].keys():
bnums.add(run_info[datum.run_id])
sdatdict[datum.component][run_info[datum.run_id]] = {}

# Adding all importances from our constant
if datum.importance not in sdatdict[datum.component][run_info[datum.run_id]].keys():
sdatdict[datum.component][run_info[datum.run_id]][datum.importance] = []
# adding the result value
sdatdict[datum.component][run_info[datum.run_id]][datum.importance].append(
{"result": datum.result, "result_id": datum.id}
)

# This adds the extra importance values that didn't appear in the results
for component in sdatdict.keys():
for bnum in sdatdict[component].keys():
for importance in importances:
if importance not in sdatdict[component][bnum].keys():
sdatdict[component][bnum][importance] = []

# this is to change result values into numbers
# TODO: This doesn't handle xpassed, xfailed, skipped, etc. so figure that out
for component in sdatdict.keys():
for bnum in sdatdict[component].keys():
for importance in sdatdict[component][bnum].keys():
total = 0
passed = 0
res_list = []
for item in sdatdict[component][bnum][importance]:
total += 1
res_list.append(item["result_id"])
if item["result"] == "passed":
passed += 1

if total != 0:
sdatdict[component][bnum][importance] = {
"percentage": round(passed / total, 2),
"result_list": res_list,
}
else:
sdatdict[component][bnum][importance] = {
"percentage": 0,
"result_list": res_list,
}

for bnum in bnums:
if bnum not in sdatdict[component].keys():
sdatdict[component][bnum] = {}
for importance in importances:
sdatdict[component][bnum][importance] = {"percentage": "NA", "result_list": []}

# Need this broken down more for the table
table_data = []
for key in sdatdict.keys():
table_data.append(
{
"component": key,
"bnums": sorted(list(bnums)),
"importances": importances,
"data": sdatdict[key],
}
)

# return data, for sanity
data = {"table_data": table_data}
return data
3 changes: 2 additions & 1 deletion frontend/src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ export const KNOWN_WIDGETS = [
'result-summary',
'result-aggregator',
'jenkins-bar-chart',
'jenkins-line-chart'
'jenkins-line-chart',
'importance-component'
];
export const STRING_OPERATIONS = {
'eq': '=',
Expand Down
13 changes: 13 additions & 0 deletions frontend/src/dashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
GenericAreaWidget,
GenericBarWidget,
FilterHeatmapWidget,
ImportanceComponentWidget,
ResultAggregatorWidget,
ResultSummaryWidget
} from './widgets';
Expand Down Expand Up @@ -411,6 +412,18 @@ export class Dashboard extends React.Component {
onEditClick={() => this.onEditWidgetClick(widget.id)}
/>
}
{(widget.type === "widget" && widget.widget === "importance-component") &&
<ImportanceComponentWidget
title={widget.title}
params={widget.params}
barWidth={20}
horizontal={true}
hideDropdown={true}
widgetEndpoint="importance-component"
onDeleteClick={() => this.onDeleteWidgetClick(widget.id)}
onEditClick={() => this.onEditWidgetClick(widget.id)}
/>
}
</GridItem>
);
}
Expand Down
113 changes: 113 additions & 0 deletions frontend/src/widgets/importancecomponent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import React from 'react';
import PropTypes from 'prop-types';

import {
Card,
CardBody,
Text
} from '@patternfly/react-core';

import {
Table,
Thead,
Th,
Tbody,
Tr,
Td
} from '@patternfly/react-table';

import { Link } from 'react-router-dom';

import { HttpClient } from '../services/http';
import { Settings } from '../settings';
import { WidgetHeader } from '../components/widget-components';

export class ImportanceComponentWidget extends React.Component {
static propTypes = {
title: PropTypes.string,
params: PropTypes.object,
onDeleteClick: PropTypes.func,
onEditClick: PropTypes.func
}

constructor(props) {
super(props);
this.title = props.title || 'Importance Component Widget';
this.params = props.params || {};
this.state = {
data: {
table_data: []
},
isLoading: true,
};
}

getData = () => {
this.setState({isLoading: true})
HttpClient.get([Settings.serverUrl, 'widget', 'importance-component'], this.params)
.then(response => {
response = HttpClient.handleResponse(response, 'response');
if (!response.ok) {
throw Error(response.statusText);
}
return response.json();
})
.then(data => this.setState({data: data, isLoading: false}))
.catch(error => {
this.setState({dataError: true});
console.log(error);
});
}

componentDidMount() {
this.getData();
}

componentDidUpdate(prevProps) {
if (prevProps.params !== this.props.params) {
this.params = this.props.params;
this.getData();
}
}

render() {
return (
<Card>
<WidgetHeader title={this.title} getDataFunc={this.getData} onEditClick={this.props.onEditClick} onDeleteClick={this.props.onDeleteClick}/>
{(!this.state.dataError && this.state.isLoading) &&
<CardBody>
<Text component="h2">Loading ...</Text>
</CardBody>
}
{(!this.state.dataError && !this.state.isLoading) &&
<CardBody>
{this.state.data.table_data.map((tdat) => (
<>
<Text key={tdat.component} component="h2">{tdat.component}</Text>
<Table aria-label="importance-component-table" variant="compact">
<Thead>
<Tr>
{["-", ...tdat.bnums].map((buildnum) => (
<Th key={buildnum}>{buildnum}</Th>
))}
</Tr>
</Thead>
<Tbody>
{tdat.importances.map((importance) => (
<Tr key={importance}>
<Text component="h2">{importance}</Text>
{tdat.bnums.map((buildnum) => (
<Td key={buildnum}><Link to={`/results?id[in]=${tdat.data[buildnum][importance]["result_list"].join(";")}`}>{tdat.data[buildnum][importance]["percentage"]}</Link></Td>
))}
</Tr>
))}
</Tbody>
</Table>
</>
))}
</CardBody>
}
</Card>
);
}
}
1 change: 1 addition & 0 deletions frontend/src/widgets/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export { GenericAreaWidget } from './genericarea';
export { GenericBarWidget } from './genericbar';
export { FilterHeatmapWidget } from './filterheatmap';
export { ResultAggregatorWidget } from './resultaggregator';
export { ImportanceComponentWidget } from './importancecomponent';

0 comments on commit bae88c4

Please sign in to comment.