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

Initial draft commit for importance widget component #485

Merged
Show file tree
Hide file tree
Changes from 6 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
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": False,
LightOfHeaven1994 marked this conversation as resolved.
Show resolved Hide resolved
"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=None, 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(",")))
LightOfHeaven1994 marked this conversation as resolved.
Show resolved Hide resolved
.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
LightOfHeaven1994 marked this conversation as resolved.
Show resolved Hide resolved
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';
Loading