Skip to content
Snippets Groups Projects
Commit 393e5999 authored by Mattia Mancini's avatar Mattia Mancini
Browse files

OSB-29: add test type selection and averaging window in station statistics

parent 754a4dc3
No related branches found
No related tags found
2 merge requests!89Monitoring maintenance Epic branch merge,!1Resolve OSB-13 "Monitoringmaintenance "
Showing
with 125 additions and 35 deletions
<!DOCTYPE html> :<!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
......
...@@ -5,3 +5,9 @@ ...@@ -5,3 +5,9 @@
/* Data colors */ /* Data colors */
.hoverable:hover { .hoverable:hover {
background-color: #bdbdbd; } background-color: #bdbdbd; }
.table-wrapper {
width: 10em;
max-height: 10em;
overflow: auto;
display: block; }
...@@ -28,11 +28,16 @@ class RTSMBadge extends Component { ...@@ -28,11 +28,16 @@ class RTSMBadge extends Component {
class SORow extends Component { class SORow extends Component {
constructor(props){ constructor(props){
super(props); super(props);
this.state = {popoverOpen:false}; this.state = {popoverOpen:false, mouseOverPopup:false};
this.id = unique_id(); this.id = unique_id();
this.togglePopover = this.togglePopover.bind(this); this.togglePopover = this.togglePopover.bind(this);
this.mouseDown = this.mouseDown.bind(this);
} }
mouseDown(e){
if(e.button === 1)
this.setState({mouseOverPopup: !this.state.mouseOverPopup});
}
renderObservationID() { renderObservationID() {
return this.props.data.observation_id; return this.props.data.observation_id;
} }
...@@ -46,7 +51,9 @@ class SORow extends Component { ...@@ -46,7 +51,9 @@ class SORow extends Component {
} }
togglePopover(){ togglePopover(){
this.setState({popoverOpen: !this.state.popoverOpen}); if(this.state.mouseOverPopup === false){
this.setState({popoverOpen: !this.state.popoverOpen});
}
} }
render() { render() {
...@@ -55,20 +62,22 @@ class SORow extends Component { ...@@ -55,20 +62,22 @@ class SORow extends Component {
const stations_and_errors = data.station_involved.map((station) => const stations_and_errors = data.station_involved.map((station) =>
<tr><th scope="row">{station.station_name}</th> <td>{station.n_errors}</td></tr>) <tr><th scope="row">{station.station_name}</th> <td>{station.n_errors}</td></tr>)
return ( return (
<tr id={this.id} onMouseOver={this.togglePopover} onMouseOut={this.togglePopover} className="hoverable"> <tr id={this.id} onMouseOver={this.togglePopover} onMouseOut={this.togglePopover}
onMouseDown={this.mouseDown}
className="hoverable">
<th scope="row">{ this.renderObservationID() }</th> <th scope="row">{ this.renderObservationID() }</th>
<td>{ moment(start_datetime).format('lll') }</td> <td>{ moment(start_datetime).format('lll') }</td>
<td>{ this.renderStationsInvolved() }</td> <td>{ this.renderStationsInvolved() }</td>
<td>{ this.renderStationsWithProblems() }</td> <td>{ this.renderStationsWithProblems() }</td>
<td>{ total_component_errors }</td> <td>{ total_component_errors }</td>
<Popover placement="auto" isOpen={this.state.popoverOpen} target={ this.id } toggle={this.togglePopover}> <Popover placement="auto-start" isOpen={this.state.popoverOpen} target={ this.id }>
<PopoverHeader>{data.observation_id}</PopoverHeader> <PopoverHeader>{data.observation_id}</PopoverHeader>
<PopoverBody> <PopoverBody>
<strong>Start:</strong> { start_datetime}<br/> <strong>Start:</strong> { start_datetime}<br/>
<strong>End:</strong> { end_datetime }<br/> <strong>End:</strong> { end_datetime }<br/>
<strong>Mode:</strong> { mode.join(',') }<br/> <strong>Mode:</strong> { mode.join(',') }<br/>
<Table size="sm" className="so-table"> <Table size="sm" className="so-table table-wrapper">
<thead><th>Station name</th><th>errors</th></thead> <thead><th>Station name</th><th>errors</th></thead>
<tbody>{stations_and_errors}</tbody> <tbody>{stations_and_errors}</tbody>
</Table> </Table>
......
...@@ -7,3 +7,10 @@ ...@@ -7,3 +7,10 @@
.hoverable:hover { .hoverable:hover {
background-color: $secondary; background-color: $secondary;
} }
.table-wrapper {
width: 10em;
max-height: 10em;
overflow: auto;
display: block;
}
...@@ -2,28 +2,65 @@ import React, { Component } from 'react'; ...@@ -2,28 +2,65 @@ import React, { Component } from 'react';
import AutoLoadWrapper from '../utils/autoLoader.js' import AutoLoadWrapper from '../utils/autoLoader.js'
import { unique_id } from '../utils/utils.js' import { unique_id } from '../utils/utils.js'
import ReactVegaLite from 'react-vega-lite' import ReactVegaLite from 'react-vega-lite'
import { Table, Badge, Popover, PopoverHeader, PopoverBody } from 'reactstrap'; import { Table, Badge, Popover, PopoverHeader, PopoverBody, Form, Input } from 'reactstrap';
/** import { connect } from 'react-redux';
* SORow; Class to render the row for a station in the StationOverview. import {setStationStatisticsTestType, setStationStatisticsAveragingWindow} from '../redux/actions/actions'
*/
class SORow extends Component {
render() {
return ( class ToolBarC extends Component {
<tr className="hoverable">
<th scope="row">{ "ciao" }</th> setAveragingWindow(e){
</tr> this.props.setStationStatisticsAveragingWindow(e.target.value);
); }
setTestType(e){
this.props.setStationStatisticsTestType(e.target.value);
}
render(){
return (<div className="sts-toolbar">
<Form inline>
<select className="form-control custom-select custom-select-sm"
id="selected-group" value={this.props.test_type}
onChange={(e)=>this.setTestType(e)}
style={{width: 'auto'}}>
<option value="B">Both test types</option>
<option value="R">RTSM</option>
<option value="S">StationTest</option>
</select>
<Input type="select" className="form-control custom-select custom-select-sm"
onChange={(e)=>this.setAveragingWindow(e)}>
<option value={1}>day</option>
<option value={7}>week</option>
<option value={30}>month</option>
</Input>
</Form>
</div>);
} }
} }
const mapDispatchToPropsToolBar = {
setStationStatisticsAveragingWindow,
setStationStatisticsTestType
};
const ToolBar = connect(null, mapDispatchToPropsToolBar)(ToolBarC);
class StationStatisticsC extends Component { class StationStatisticsC extends Component {
constructor(props){
super(props);
this.state = {};
this.ref = React.createRef();
this.getErrorsPerTypeSpec.bind(this);
}
getErrorsPerStation() { getErrorsPerStation() {
if(this.props.data.errors_per_station !== undefined){ if(this.props.data.errors_per_station !== undefined){
return {values:this.props.data.errors_per_station}; return {values:this.props.data.errors_per_station};
} }
} }
getErrorsPerType() { getErrorsPerType() {
if(this.props.data.errors_per_type !== undefined){ if(this.props.data.errors_per_type !== undefined){
return {values:this.props.data.errors_per_type}; return {values:this.props.data.errors_per_type};
...@@ -31,7 +68,7 @@ class StationStatisticsC extends Component { ...@@ -31,7 +68,7 @@ class StationStatisticsC extends Component {
} }
getErrorsPerTypeSpec(){ getErrorsPerTypeSpec(){
return {"$schema": "https://vega.github.io/schema/vega-lite/v2.json", let base_schema = {"$schema": "https://vega.github.io/schema/vega-lite/v2.json",
"mark": "bar", "mark": "bar",
"encoding": { "encoding": {
"x": { "x": {
...@@ -50,13 +87,21 @@ class StationStatisticsC extends Component { ...@@ -50,13 +87,21 @@ class StationStatisticsC extends Component {
} }
} }
} }
return base_schema;
} }
render() { render() {
return ( return (
<div className="station-statistics-ctrl"> <React.Fragment>
<h5 className="react-grid-item-header">
Station statistics
<ToolBar />
</h5>
<div className="react-grid-item-body" id="plot" ref={this.ref} >
<ReactVegaLite spec={this.getErrorsPerTypeSpec()} <ReactVegaLite spec={this.getErrorsPerTypeSpec()}
data={this.getErrorsPerType()} /> data={this.getErrorsPerType()} />
</div> </div>
</React.Fragment>
); );
} }
} }
......
...@@ -37,7 +37,7 @@ class STSRow extends Component { ...@@ -37,7 +37,7 @@ class STSRow extends Component {
return ( return (
<tr> <tr>
<th scope="row" style={{"white-space": "nowrap"}}>{ this.renderStartDate() }</th> <th scope="row" style={{whiteSpace: "nowrap"}}>{ this.renderStartDate() }</th>
<th>{ props.time }</th> <th>{ props.time }</th>
<th>{ props.station }</th> <th>{ props.station }</th>
<td>{ component }</td> <td>{ component }</td>
......
...@@ -94,17 +94,17 @@ class ToolBarC extends Component { ...@@ -94,17 +94,17 @@ class ToolBarC extends Component {
} }
const mapStateToProps = state => { const mapStateToPropsToolBar = state => {
return { ...state.mainFilters }; return { ...state.mainFilters};
}; };
const mapDispatchToProps = { const mapDispatchToPropsToolBar = {
setStationGroup, setStationGroup,
setErrorsOnly, setErrorsOnly,
setPeriod setPeriod
}; };
const ToolBar = connect(mapStateToProps, mapDispatchToProps)(ToolBarC); const ToolBar = connect(mapStateToPropsToolBar, mapDispatchToPropsToolBar)(ToolBarC);
...@@ -142,10 +142,11 @@ class LandingPageC extends Component { ...@@ -142,10 +142,11 @@ class LandingPageC extends Component {
return `/api/view/ctrl_latest_observation?format=json&station_group=${this.props.selectedStationGroup}&from_date=${nDaysAgo_String}&errors_only=${this.props.errorsOnly}`; return `/api/view/ctrl_latest_observation?format=json&station_group=${this.props.selectedStationGroup}&from_date=${nDaysAgo_String}&errors_only=${this.props.errorsOnly}`;
} }
getStationStatisticsURL() { getStationStatisticsURL() {
var nDaysAgo = moment().add(-this.props.period*100, 'days').format('YYYY-MM-DD'); var nDaysAgo = moment().add(-this.props.period, 'days').format('YYYY-MM-DD');
var now = moment().format('YYYY-MM-DD'); var now = moment().format('YYYY-MM-DD');
var averaging_interval = 12 var averaging_interval = this.props.station_statistics.averaging_window
return `/api/view/ctrl_stationtest_statistics?format=json&from_date=${nDaysAgo}&to_date=${now}&averaging_interval=${averaging_interval}`; var test_type = this.props.station_statistics.test_type
return `/api/view/ctrl_stationtest_statistics?format=json&from_date=${nDaysAgo}&to_date=${now}&averaging_interval=${averaging_interval}&test_type=${test_type}`;
} }
render() { render() {
return (<div> return (<div>
...@@ -165,7 +166,7 @@ class LandingPageC extends Component { ...@@ -165,7 +166,7 @@ class LandingPageC extends Component {
body: <StationTestSummary url={this.getStationTestSummaryURL()}/> body: <StationTestSummary url={this.getStationTestSummaryURL()}/>
})} })}
{createGridPanel({ {createGridPanel({
key: "br", renderHeader: true, title: "Station Statistics", key: "br", renderHeader: false,
body: <StationStatistics url={this.getStationStatisticsURL()}/> })} body: <StationStatistics url={this.getStationStatisticsURL()}/> })}
</ResponsiveGridLayout> </ResponsiveGridLayout>
</div>); </div>);
...@@ -174,7 +175,7 @@ class LandingPageC extends Component { ...@@ -174,7 +175,7 @@ class LandingPageC extends Component {
const LandingPage = connect(state => { const LandingPage = connect(state => {
return { ...state.mainFilters }; return { ...state.mainFilters, ...state.landing_page };
}, null)(LandingPageC); }, null)(LandingPageC);
......
...@@ -2,8 +2,9 @@ ...@@ -2,8 +2,9 @@
export const SET_STATION_GROUP = "SET_STATION_GROUP"; export const SET_STATION_GROUP = "SET_STATION_GROUP";
export const SET_ERRORS_ONLY = "SET_ERRORS_ONLY"; export const SET_ERRORS_ONLY = "SET_ERRORS_ONLY";
export const SET_PERIOD = "SET_PERIOD"; export const SET_PERIOD = "SET_PERIOD";
export const FETCH_ERRORTYPES_BEGIN = 'FETCH_ERRORTYPES_BEGIN'; export const FETCH_ERRORTYPES_BEGIN = 'FETCH_ERRORTYPES_BEGIN';
export const FETCH_ERRORTYPES_SUCCESS = 'FETCH_ERRORTYPES_SUCCESS'; export const FETCH_ERRORTYPES_SUCCESS = 'FETCH_ERRORTYPES_SUCCESS';
export const FETCH_ERRORTYPES_FAILURE = 'FETCH_ERRORTYPES_FAILURE'; export const FETCH_ERRORTYPES_FAILURE = 'FETCH_ERRORTYPES_FAILURE';
export const SET_COMPONENT_SIZES = "SET_COMPONENT_SIZES";
export const SET_AVERAGING_WINDOW = "SET_AVERAGING_WINDOW";
export const SET_TEST_TYPE = "SET_TEST_TYPE";
import { SET_STATION_GROUP, SET_ERRORS_ONLY, SET_PERIOD } from "./actionTypes"; import { SET_STATION_GROUP,
SET_ERRORS_ONLY,
SET_PERIOD,
SET_COMPONENT_SIZES, SET_AVERAGING_WINDOW, SET_TEST_TYPE } from "./actionTypes";
...@@ -17,3 +20,19 @@ export const setPeriod = period => ({ ...@@ -17,3 +20,19 @@ export const setPeriod = period => ({
type: SET_PERIOD, type: SET_PERIOD,
payload: { period } payload: { period }
}); });
export const setComponentsSizes = (componentName, componentSizes) => ({
type: SET_COMPONENT_SIZES,
payload: { componentName: componentSizes }
});
export const setStationStatisticsAveragingWindow = averagingWindow => ({
type: SET_AVERAGING_WINDOW,
payload: { averagingWindow }
});
export const setStationStatisticsTestType = test_type => ({
type: SET_TEST_TYPE,
payload: { test_type }
});
import { combineReducers } from "redux"; import { combineReducers } from "redux";
import appInitData from "./appInitData"; import appInitData from "./appInitData";
import mainFilters from "./mainFilters"; import mainFilters from "./mainFilters";
import landingPageReducers from "./landingPageReducers";
export default combineReducers({ export default combineReducers({
appInitData, appInitData,
mainFilters mainFilters,
landing_page:landingPageReducers
}); });
...@@ -6,7 +6,7 @@ import { ...@@ -6,7 +6,7 @@ import {
const initialState = { const initialState = {
selectedStationGroup: 'R', selectedStationGroup: 'A',
errorsOnly: false, errorsOnly: false,
period: 14 // days period: 14 // days
}; };
......
...@@ -95,7 +95,7 @@ function AutoLoadWrapper(WrappedComponent) { ...@@ -95,7 +95,7 @@ function AutoLoadWrapper(WrappedComponent) {
} }
render() { render() {
console.log('render'); console.log('render', this);
let loadingHtml = ""; let loadingHtml = "";
if (this.state.isLoading) { if (this.state.isLoading) {
loadingHtml = <div className="alLoading"></div>; loadingHtml = <div className="alLoading"></div>;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment