From 03eb0a8bf1e48b38ed17656d6056605be12ab749 Mon Sep 17 00:00:00 2001 From: rbokhorst <rbokhorst@astron.nl> Date: Thu, 18 Oct 2018 08:06:00 +0000 Subject: [PATCH] OSB-29: summary panel stuff --- .../src/components/StationTestSummary.css | 40 +---- .../src/components/StationTestSummary.js | 147 +++++++++++------- .../src/pages/LandingPage.js | 13 +- .../src/testdata/station_test_summary.js | 17 +- .../src/utils/autoLoader.js | 8 +- .../maintenancedb_view/src/utils/utils.js | 9 +- 6 files changed, 116 insertions(+), 118 deletions(-) diff --git a/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/components/StationTestSummary.css b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/components/StationTestSummary.css index 938763dba19..36a9d440f28 100644 --- a/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/components/StationTestSummary.css +++ b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/components/StationTestSummary.css @@ -1,36 +1,6 @@ -.card { - margin-top: 1em; - box-shadow: 0 .1em .2em .2em grey; -} -.content { - text-align: center; - vertical-align: bottom; -} -.content-title { - background-color: lightgrey; -} - -.tableheader { - font-weight: bold; -} - -.detailsbutton { - background-color: lightgrey !important; - color: black !important; - font-weight: bold; -} - -.danger, -.no_error { - color: rgb(218, 242, 244); -} - -.danger { - background-color: rgb(210, 7, 7) !important; - border-color: rgb(210, 7, 7) !important; -} - -.no_error { - background-color: rgb(90, 180, 0) !important; - border-color: rgb(90, 180, 0) !important; +.stsToolbar { + position: absolute; + right: 0.5rem; + top: 0.1rem; + display: inline-block; } diff --git a/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/components/StationTestSummary.js b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/components/StationTestSummary.js index f32174cafca..81ef837e9f9 100644 --- a/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/components/StationTestSummary.js +++ b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/components/StationTestSummary.js @@ -1,8 +1,9 @@ import React, { Component } from 'react'; import { Table, Popover, PopoverHeader, PopoverBody, Button, ButtonGroup } from 'reactstrap'; -import { unique_id } from '../utils/utils.js' +import { unique_id, capitalize } from '../utils/utils.js' import AutoLoadWrapper from '../utils/autoLoader.js' import axios from 'axios'; +//import stdata from '../testdata/station_test_summary.js' import './StationTestSummary.css' @@ -55,58 +56,37 @@ class StationTestBadge extends Component { } -/** - * RTSMBadge; class to render one RTSM badge in the SORow. - */ -class RTSMBadge extends Component { - - getClass() { - let total = this.props.data.total_component_errors; - let color = total>10 ? "so-serious" : - total>5 ? "so-alarming" : - total>0 ? "so-warning" : "so-good"; - return "so-pill " + color; - } - - render () { - let data = this.props.data; - return <div className="so-rtsmbadge">{ data.observation_id }<span className={ this.getClass() }>{ data.total_component_errors }</span></div>; - } -} - - /** * SORow; Class to render the row for a station in the StationOverview. */ class STSRow extends Component { - renderStationName() { - return this.props.data.station_name; - } - renderStartDate() { return this.props.date; } - renderStationTests() { - let data = this.props.data; - return data.station_tests.map( (testData) => <StationTestBadge station={data.station_name} data={ testData } /> ); - } - - renderRTSM() { - if (! this.props.data.rtsm || this.props.data.rtsm.length === 0) { - return "No RTSM data found" - } - return this.props.data.rtsm.map( (testData) => <RTSMBadge data={ testData } /> ); + renderStartTime() { + let arr = this.props.data.start_datetime.match(/T(.*):..Z/); + return arr[1]; } render() { + let props = this.props, + component = props.component, + errors = props.data.component_error_summary[component] || {}, + cols = []; + + props.errorTypes.forEach((type) => { + cols.push(<td key={unique_id()}>{ errors[type] }</td>); + }); + return ( <tr> <th scope="row">{ this.renderStartDate() }</th> - <td>{ this.renderStationName() }</td> - <td></td> - <td></td> + <td>{ props.time }</td> + <td>{ props.station }</td> + <td>{ component }</td> + { cols } </tr> ); } @@ -121,7 +101,7 @@ class ToolBar extends Component { render() { return ( - <div style={{position: "absolute", right: 0, display: "inline-block"}}> + <div className="stsToolbar"> { this.props.allErrorTypes ? <Button color="info" size="xs" onClick={() => this.onErrorsOnlyClick()} active>All types</Button> : <Button color="info" size="xs" onClick={() => this.onErrorsOnlyClick()} outline>All types</Button> @@ -139,16 +119,55 @@ class StationTestSummaryC extends Component { static errorTypesURL = '/api/view/ctrl_list_component_error_types'; + activeErrorTypes = []; // Result of filtering state.errorTypes + state = { - allErrorTypes: false, // true: error types shown, even when there are no errors for that type - errorTypes: [] + allErrorTypes: false, // true: error types shown, even when there are no errors for that type + errorTypes: [] // Result of API call }; /* Handle changes of selected filters in the ToolBar */ onToolbarChange(key, value) { - let obj = {}; - obj[key] = value; - this.setState(obj); + let obj = {}; + obj[key] = value; + this.setState(obj); + } + + + filterErrorTypes() { + let typesFound = {}, + retTypes = []; + + if (this.state.errorTypes.length === 0 || this.props.data.length === 0){ + return []; + } + + // Create index object for all error types in the data + this.props.data.forEach( (stationData) => { + let esummary = stationData.component_error_summary; + let key; + for(key in esummary){ + let errors = Object.keys(esummary[key]); + errors.forEach((e) => typesFound[e] = 1); + } + }); + + // Loop over all error types and check which ones are present in the data + this.state.errorTypes.forEach((t) => { + if (typesFound[t]) { + retTypes.push(t); + } + }) + + return retTypes; + } + + setActiveErrorTypes() { + if (this.state.allErrorTypes) { + this.activeErrorTypes = this.state.errorTypes; + } else { + this.activeErrorTypes = this.filterErrorTypes(); + } } fetchErrorTypes() { @@ -160,6 +179,8 @@ class StationTestSummaryC extends Component { }) .catch(error => { console.log(error); + // Try again in 30s + setTimeout(() => this.fetchErrorTypes(), 30000); }); } @@ -171,20 +192,34 @@ class StationTestSummaryC extends Component { let rows = [], prevDate = null; - this.props.data.map( (stationData) => { - let key = unique_id(); + this.props.data.forEach( (stationData) => { let date = (stationData.date != prevDate ? stationData.date : ""); - rows.push(<STSRow key={key} date={date} data={stationData} />) + let components = Object.keys(stationData.component_error_summary).sort(); + let station = stationData.station_name; + let time = (stationData.start_datetime.match(/T(.*):..Z/))[1]; + + components.forEach( (component) => { + rows.push(<STSRow key={ unique_id() } date={date} time={time} component={component} station={station} data={stationData} errorTypes={this.activeErrorTypes}/>) + date = station = time = ""; + }); + if (components.length === 0) { + rows.push(<STSRow key={ unique_id() } date={date} time={time} component={"-"} station={station} data={stationData} errorTypes={this.activeErrorTypes}/>) + } prevDate = stationData.date; }); + return rows; } renderTableHeaders() { - return this.state.errorTypes.map((err) => <th>{err}</th>); + return this.activeErrorTypes.map((err) => <th key={unique_id()}>{capitalize(err)}</th>); } render() { + console.log("render stationTestSummary"); + + this.setActiveErrorTypes(); + return ( <React.Fragment> <h5 className="react-grid-item-header"> @@ -192,14 +227,15 @@ class StationTestSummaryC extends Component { <ToolBar onChange={(key,value) => this.onToolbarChange(key,value) } allErrorTypes={this.state.allErrorTypes} /> </h5> <div className="react-grid-item-body"> - <Table size="sm" className="so-table"> + <Table bordered hover size="sm" className="so-table"> <thead> - <tr> - <th>Date</th> - <th>Station</th> - <th>Component</th> - {this.renderTableHeaders()} - </tr> + <tr> + <th>Date</th> + <th>Time</th> + <th>Station</th> + <th>Comp.</th> + {this.renderTableHeaders()} + </tr> </thead> <tbody> { this.getTableRows() } @@ -212,6 +248,7 @@ class StationTestSummaryC extends Component { } + /* Add some magic; use the AutoLoadWrapper to create a HOC that handles the auto-loading of the data for StationOverviewC. */ diff --git a/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/LandingPage.js b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/LandingPage.js index 66595b03e01..919d5f09721 100644 --- a/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/LandingPage.js +++ b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/LandingPage.js @@ -11,9 +11,7 @@ import 'react-resizable/css/styles.css'; import '../themes/lofar-styles.css'; // Testing -import station_overview_data from '../testdata/station_overview.js' import latest_obs_data from '../testdata/latest_observations.js' -import station_details_data from '../testdata/station_details.js' const ResponsiveGridLayout = WidthProvider(Responsive); @@ -72,13 +70,14 @@ class ToolBar extends Component { <option value="C">Core stations</option> <option value="R">Remote stations</option> <option value="I">ILT stations</option> - </select> + </select> + { this.props.errorsOnly - ? <Button color="info" size="md" onClick={()=>this.onErrorsOnlyClick()} active>Errors only</Button> - : <Button color="info" size="md" onClick={()=>this.onErrorsOnlyClick()} outline>Errors only</Button> + ? <Button color="info" size="sm" onClick={()=>this.onErrorsOnlyClick()} active>Errors only</Button> + : <Button color="info" size="sm" onClick={()=>this.onErrorsOnlyClick()} outline>Errors only</Button> } - <ButtonGroup size="xs"> + <ButtonGroup size="sm"> period: <Button color="info" onClick={() => this.onPeriodClick(7)} active={this.props.period === 7}>1 wk</Button> <Button color="info" onClick={() => this.onPeriodClick(14)} active={this.props.period === 14}>2 wk</Button> @@ -153,7 +152,7 @@ class LandingPage extends Component { })} {createGridPanel({ key: "bl", renderHeader: false, - body: <StationTestSummary url={this.getStationTestSummaryURL()} reloadInterval="6000"/> + body: <StationTestSummary url={this.getStationTestSummaryURL()}/> })} {createGridPanel({ key: "br", renderHeader: true, title: "Statistics" })} </ResponsiveGridLayout> diff --git a/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/testdata/station_test_summary.js b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/testdata/station_test_summary.js index 61164101f78..aedbde378a3 100644 --- a/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/testdata/station_test_summary.js +++ b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/testdata/station_test_summary.js @@ -1,4 +1,4 @@ -const data = [ +const stdata = [ { "station_name": "CS002C", "total_component_errors": 18, @@ -31,19 +31,6 @@ const data = [ "end_datetime": "2018-10-11T00:52:24Z", "checks": "RV SPU RBC SH1 F1 D1 O1 SP1 NS1=60 S1 SH3 F3 D3 O3 SP3 NS3=60 S3 M5 O5 SN5 SP5 NS5=60 E7 TV TBC TM", "component_error_summary": { - "HBA": { - "HIGH_NOISE": 4, - "MODEM": 2, - "OSCILLATION": 1, - "C_SUMMATOR": 1 - }, - "LBL": { - "LOW_NOISE": 1, - "RF_FAIL": 1 - }, - "TBB": { - "TEMPERATURE": 1 - } } }, { "station_name": "CS005C", @@ -812,4 +799,4 @@ const data = [ } ]; -export default data; +export default stdata; diff --git a/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/utils/autoLoader.js b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/utils/autoLoader.js index 3071cc310d6..7bf4512d95a 100644 --- a/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/utils/autoLoader.js +++ b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/utils/autoLoader.js @@ -1,4 +1,4 @@ -import React, { Component } from 'react'; +import React from 'react'; import axios from 'axios'; @@ -25,7 +25,7 @@ function AutoLoadWrapper(WrappedComponent) { /* Called when props changed, before the render phase */ static getDerivedStateFromProps(props, state) { // Store prevUrl in state so we can compare when props change. - if (props.url != state.prevUrl) { + if (props.url !== state.prevUrl) { console.log('getDerived; '); return { isLoading: true, @@ -47,7 +47,7 @@ function AutoLoadWrapper(WrappedComponent) { } // Create cancellation token - if (typeof this._source != typeof undefined) { + if (typeof this._source !== typeof undefined) { this._source.cancel('Operation canceled due to new request.') } this._source = axios.CancelToken.source(); @@ -84,7 +84,7 @@ function AutoLoadWrapper(WrappedComponent) { } componentDidUpdate(prevProps, prevState) { - if (prevProps.url != this.props.url) { + if (prevProps.url !== this.props.url) { console.log('props changed: ' + prevProps.url+ ' '+this.props.url); this.fetchData(); } diff --git a/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/utils/utils.js b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/utils/utils.js index d8412b90dd9..f6080b07b67 100644 --- a/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/utils/utils.js +++ b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/utils/utils.js @@ -1,9 +1,14 @@ let lastId = 0; -function unique_id(prefix='id_') { +function unique_id(prefix='id_') { lastId++; return `${prefix}${lastId}`; } -export { unique_id }; +function capitalize(s) { + if (typeof s !== 'string') return '' + return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase() +} + +export { unique_id, capitalize }; -- GitLab