From 1fc401bfcc424419f0644f941b43d6d13a21de76 Mon Sep 17 00:00:00 2001 From: Zwiterrion Date: Wed, 6 Sep 2023 16:25:56 +0200 Subject: [PATCH] visualize global score and net score --- .../javascript/src/components/inputs/Table.js | 13 +- .../src/extensions/greenscore/GlobalScore.js | 52 +++++ .../{MixBarChart.js => RulesRadarchart.js} | 61 ++++-- .../src/extensions/greenscore/page.js | 195 +++++++++++++----- .../src/extensions/greenscore/util.js | 13 +- 5 files changed, 267 insertions(+), 67 deletions(-) create mode 100644 otoroshi/javascript/src/extensions/greenscore/GlobalScore.js rename otoroshi/javascript/src/extensions/greenscore/{MixBarChart.js => RulesRadarchart.js} (64%) diff --git a/otoroshi/javascript/src/components/inputs/Table.js b/otoroshi/javascript/src/components/inputs/Table.js index 2f3fa476ec..c0cf60fd3e 100644 --- a/otoroshi/javascript/src/components/inputs/Table.js +++ b/otoroshi/javascript/src/components/inputs/Table.js @@ -148,10 +148,10 @@ export class Table extends Component { return (this.state.showAddForm || this.state.showEditForm ? this.props.fetchItems() : this.props.fetchItems({ - ...paginationState, - pageSize: this.state.rowsPerPage, - page: page + 1, - }) + ...paginationState, + pageSize: this.state.rowsPerPage, + page: page + 1, + }) ).then((rawItems) => { if (Array.isArray(rawItems)) { this.setState({ @@ -566,8 +566,11 @@ export class Table extends Component {
{ + return acc + group.routes.reduce((acc, route) => calculateGreenScore(route.rulesConfig).score + acc, 0) / group.routes.length; + }, 0) / props.groups.length; + + const { letter, rank, ...rest } = getRankAndLetterFromScore(score); + + return
+
+ {!props.raw && <>{letter !== "@" ? letter : '-'} + } + {props.raw &&
+ {rest.score} + {`/${props.groups.length * 6000}`} +
} +
+

{props.raw ? 'Net' : 'Global'} score

+ +
+ +
+
+}; diff --git a/otoroshi/javascript/src/extensions/greenscore/MixBarChart.js b/otoroshi/javascript/src/extensions/greenscore/RulesRadarchart.js similarity index 64% rename from otoroshi/javascript/src/extensions/greenscore/MixBarChart.js rename to otoroshi/javascript/src/extensions/greenscore/RulesRadarchart.js index 96fa3672fb..a03373b5a6 100644 --- a/otoroshi/javascript/src/extensions/greenscore/MixBarChart.js +++ b/otoroshi/javascript/src/extensions/greenscore/RulesRadarchart.js @@ -1,8 +1,8 @@ import React, { PureComponent } from 'react'; -import { Radar, RadarChart, PolarGrid, PolarAngleAxis, PolarRadiusAxis, ResponsiveContainer } from 'recharts'; +import { Radar, RadarChart, PolarGrid, PolarAngleAxis, PolarRadiusAxis, ResponsiveContainer, Text } from 'recharts'; import { caclculateRuleGroup } from './util'; -export default class MixBarChart extends PureComponent { +export default class RulesRadarchart extends PureComponent { calculate = routes => { return routes.reduce((acc, route) => { @@ -20,6 +20,29 @@ export default class MixBarChart extends PureComponent { ]) } + renderPolarAngleAxis = props => { + const newPoint = this.movePointAtAngle([props.x, props.y], props.payload.index * 90, 12); + const texts = props.payload.value.split(" "); + + return texts + .map((text, i) => + {text} + + ); + } + + movePointAtAngle = (point, angle, distance) => [ + point[0] + (Math.sin(angle) * distance), + point[1] - (Math.cos(angle) * distance) + ]; + render() { const values = this.props.groups.reduce((acc, item) => { const values = this.calculate(item.routes); @@ -41,16 +64,15 @@ export default class MixBarChart extends PureComponent { { subject: 'Design', value: values[1].value / this.props.groups.length }, { subject: 'Usage', value: values[2].value / this.props.groups.length }, { subject: 'Log retention', value: values[3].value / this.props.groups.length } - ] + ]; - console.log(data, [ - { subject: 'Architecture', value: data[0].value / 1500 }, - { subject: 'Design', value: data[1].value / 2400 }, - { subject: 'Usage', value: data[2].value / 1500 }, - { subject: 'Log retention', value: data[3].value / 600 } - ]) - - return
+ return
- + this.renderPolarAngleAxis(props)} /> + +
+ +
} } diff --git a/otoroshi/javascript/src/extensions/greenscore/page.js b/otoroshi/javascript/src/extensions/greenscore/page.js index c839b0e932..11b345eaac 100644 --- a/otoroshi/javascript/src/extensions/greenscore/page.js +++ b/otoroshi/javascript/src/extensions/greenscore/page.js @@ -5,11 +5,14 @@ import { Table } from '../../components/inputs/Table'; import { v4 as uuid } from 'uuid'; import { calculateGreenScore, getRankAndLetterFromScore } from './util'; import GreenScoreRoutesForm from './routesForm'; +import RulesRadarchart from './RulesRadarchart'; +import { GlobalScore } from './GlobalScore'; export default class GreenScoreConfigsPage extends React.Component { state = { routes: [], rulesTemplate: undefined, + groups: [] }; formSchema = { @@ -61,11 +64,13 @@ export default class GreenScoreConfigsPage extends React.Component { { title: 'Name', filterId: 'name', + notFilterable: true, content: (item) => item.name, }, { title: 'Description', filterId: 'description', + notFilterable: true, content: (item) => item.description, }, { @@ -103,7 +108,7 @@ export default class GreenScoreConfigsPage extends React.Component { ]; componentDidMount() { - this.props.setTitle(`All Green Score configs.`); + this.props.setTitle(`Green Score groups`); nextClient .forEntity(nextClient.ENTITIES.ROUTES) @@ -122,64 +127,156 @@ export default class GreenScoreConfigsPage extends React.Component { rulesTemplate, }) ); - } - render() { const client = BackOfficeServices.apisClient( 'green-score.extensions.otoroshi.io', 'v1', 'green-scores' ); - return ( - ({ - id: 'green-score-config_' + uuid(), - name: 'My Green Score', - description: 'An awesome Green Score', - tags: [], - metadata: {}, - routes: [], - config: {}, - })} - itemName="Green Score config" - formSchema={this.formSchema} - formFlow={this.formFlow} - columns={this.columns} - stayAfterSave={true} - fetchItems={client.findAll} - updateItem={client.update} - deleteItem={client.delete} - createItem={client.create} - navigateTo={(item) => { - window.location = `/bo/dashboard/extensions/green-score/green-score-configs/edit/${item.id}`; - }} - itemUrl={(item) => - `/bo/dashboard/extensions/green-score/green-score-configs/edit/${item.id}` + client.findAll() + .then(groups => { + this.setState({ + groups: groups.map(group => ({ + ...group, + opened: false + })) + }) + }) + } + + openScore = group => { + this.setState({ + groups: this.state.groups.map(g => { + if (g.id === group.id) { + return { + ...g, + opened: !g.opened + } } - showActions={true} - showLink={true} - rowNavigation={true} - extractKey={(item) => item.id} - export={true} - kubernetesKind={'GreenScore'} - /> + return g; + }) + }) + } + + render() { + console.log(this.state) + const client = BackOfficeServices.apisClient( + 'green-score.extensions.otoroshi.io', + 'v1', + 'green-scores' ); + + const { groups, routes } = this.state; + + return
+
+ + + +
+ +
+
({ + id: 'green-score-config_' + uuid(), + name: 'My Green Score', + description: 'An awesome Green Score', + tags: [], + metadata: {}, + routes: [], + config: {}, + })} + itemName="Green Score config" + formSchema={this.formSchema} + formFlow={this.formFlow} + columns={this.columns} + stayAfterSave={true} + fetchItems={client.findAll} + updateItem={client.update} + deleteItem={client.delete} + createItem={client.create} + navigateTo={(item) => { + window.location = `/bo/dashboard/extensions/green-score/green-score-configs/edit/${item.id}`; + }} + itemUrl={(item) => + `/bo/dashboard/extensions/green-score/green-score-configs/edit/${item.id}` + } + showActions={true} + showLink={false} + rowNavigation={true} + extractKey={(item) => item.id} + export={true} + kubernetesKind={'GreenScore'} + /> + {/* {groups.map((group, i) => { + return
this.openScore(group)}> + +
+ {group.name} + + + + +
this.openScore(group)}> + +
+
+ {group.opened &&
+ {group.routes.map(route => { + return
+ {routes.find(r => r.id === route.routeId).name} + + +
+
+ })} +
} +
+ })} */} + + } } const GreenScoreColumm = (props) => { const score = - props.routes.reduce((acc, route) => calculateGreenScore(route.rulesConfig).score + acc, 0) / - props.routes.length; + (props.routes || [props.route]).reduce((acc, route) => calculateGreenScore(route.rulesConfig).score + acc, 0) / + (props.routes || [props.route]).length; const { letter, rank } = getRankAndLetterFromScore(score); return ( -
+
{letter}
); @@ -200,14 +297,9 @@ const ThresholdsScoreColumn = (props) => { .then(setScore); }, []); - const route = props.value.routes[props.index]; + const route = props.value.routes ? props.value.routes[props.index] : props.route; const { thresholds } = route.rulesConfig; - console.log(score, thresholds); - - function getRank(obj, idx) { - return Object.keys(obj)[idx] || Object.keys(obj)[Object.keys(obj).length - 1]; - } if (score && score.scores.length > 0) { const dataOutIdx = Object.values(thresholds.dataOut).findIndex( (value) => score.scores[0].dataOutReservoir <= value @@ -223,13 +315,18 @@ const ThresholdsScoreColumn = (props) => { ((dataOutIdx !== -1 ? dataOutIdx : Object.keys(thresholds.dataOut).length) + (headersOutIdx !== -1 ? headersOutIdx : Object.keys(thresholds.headersOut).length) + (pluginsIdx !== -1 ? pluginsIdx : Object.keys(thresholds.plugins).length)) / - 3 + 3 ); const { letter, rank } = getRankAndLetterFromScore((3 - thresholdsScore) * 2000); return ( -
+
{letter}
); diff --git a/otoroshi/javascript/src/extensions/greenscore/util.js b/otoroshi/javascript/src/extensions/greenscore/util.js index de6501c504..ee2dbc564a 100644 --- a/otoroshi/javascript/src/extensions/greenscore/util.js +++ b/otoroshi/javascript/src/extensions/greenscore/util.js @@ -8,6 +8,17 @@ const GREEN_SCORE_GRADES = { '#c0392b': (rank) => rank < 1000, }; +export function caclculateRuleGroup(routeRules, ruleId) { + return routeRules.sections + .find(s => s.id === ruleId) + .rules + .reduce((acc, rule) => { + return (acc += rule.enabled + ? MAX_GREEN_SCORE_NOTE * (rule.section_weight / 100) * (rule.weight / 100) + : 0); + }, 0) +} + export function calculateGreenScore(routeRules) { const { sections } = routeRules; @@ -35,7 +46,7 @@ export function getRankAndLetterFromScore(score) { const rankIdx = Object.entries(GREEN_SCORE_GRADES).findIndex((grade) => grade[1](score)); return { - score, + score: isNaN(score) ? 0 : score, rank: rankIdx === -1 ? 'Not evaluated' : Object.keys(GREEN_SCORE_GRADES)[rankIdx], letter: String.fromCharCode(65 + rankIdx), };