diff --git a/SAS/TMSS/frontend/tmss_webapp/package.json b/SAS/TMSS/frontend/tmss_webapp/package.json index 83746e1b42d0680dd0c6290d09b532ab34054cc8..510bc7ac3fec8c0ea00f5ebe5a671dfc84f531b8 100644 --- a/SAS/TMSS/frontend/tmss_webapp/package.json +++ b/SAS/TMSS/frontend/tmss_webapp/package.json @@ -50,6 +50,7 @@ "react-bootstrap-datetimepicker": "0.0.22", "react-calendar-timeline": "^0.28.0", "react-chartjs-2": "^3.0.3", + "chartjs-plugin-annotation": "^1.4.0", "react-dom": "^18.2.0", "react-flatpickr": "^3.10.12", "react-frame-component": "^4.1.2", diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/project.report.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/project.report.js index bf24c454a933848d78ce8d48a594d0c99a71934e..3f3bf0db31b0bb7d92e8f86439e3edbad40784c5 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/project.report.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/project.report.js @@ -2,13 +2,17 @@ import React, { Component } from 'react'; import { Link } from 'react-router-dom'; import moment from 'moment'; import _ from 'lodash'; +import { Chart } from 'chart.js'; import { Bar } from 'react-chartjs-2'; +import annotationPlugin from 'chartjs-plugin-annotation'; import ProjectService from '../../services/project.service'; import ReportService from '../../services/report.service'; import UnitConverter from '../../utils/unit.converter'; import UIConstants from '../../utils/ui.constants'; +Chart.register(annotationPlugin); + /** * Component to get report data and display them for a Project. */ @@ -57,7 +61,7 @@ class ProjectReport extends Component { projectResources[quota.resource_type_id] = quota; } } - let suStatsList = [], resourceUtilization = [], totalSucceededFailed = 0; + let suStatsList = [], resourceUtilization = []; // Calculate SUB resource values from project resource valies. // If it is received from report API, this needs to be modified. if (projectReport["SUBs"]) { @@ -67,7 +71,6 @@ class ProjectReport extends Component { const timeFactor = UnitConverter.resourceUnitMap["time"].conversionFactor; const dataSizeFactor = UnitConverter.resourceUnitMap["bytes"].conversionFactor; let totalSUBObsTime = 0, totalProcessTime = 0, totalLTAStorage = 0; - totalSucceededFailed = Math.ceil((projectReport.durations.total_observed_succeeded + projectReport.durations.total_observed_failed)/projectObservingTime*100); for (const subStatus of _.keys(projectReport["SUBs"])) { let subs = projectReport["SUBs"][subStatus]; for (const sub of subs) { @@ -106,7 +109,7 @@ class ProjectReport extends Component { reportSub.observationSASId = reportSub["SAS ID"]["observation control"].join(); reportSub.pipelinseSASId = (reportSub["SAS ID"]["preprocessing pipeline"].concat(reportSub["SAS ID"]["pulsar pipeline"])).join(", "); } - let observTimeUtilization = {type: 'Observing', value: (projectReport.durations.total_observed_failed/timeFactor).toFixed(2), + let observTimeUtilization = {type: 'Observing', value: (projectReport.durations.total_observed/timeFactor).toFixed(2), percent: (totalSUBObsTime/projectObservingTime*100).toFixed(2), succeeded: (projectReport.durations.total_observed_succeeded/projectObservingTime*100).toFixed(2), succeededValue:((projectReport.durations.total_observed_succeeded || 0)/timeFactor).toFixed(2), @@ -141,9 +144,17 @@ class ProjectReport extends Component { this.props.passReportData(project.name, {reportData: projectReport, projectResources: projectResources, suStatsList: suStatsList, resourceUtilization: resourceUtilization}); } + const resourceUtilizationRates = resourceUtilization.map(resource => { + const utilizationRate = parseFloat((resource.succeeded||0) + (resource.acceptancePending||0) + (resource.failed||0)).toFixed(2); + if (isNaN(utilizationRate) || !isFinite(utilizationRate)){ + return 0; + } + return utilizationRate; + }); + resourceUtilizationRates.push(100); this.setState({project: project, reportData: projectReport, projectResources: projectResources, suStatsList: suStatsList, resourceUtilization: resourceUtilization, - maxUtilization : _.max([100, totalSucceededFailed])}); + maxUtilization : Math.max.apply(null, resourceUtilizationRates)}); } @@ -325,6 +336,22 @@ class ProjectReport extends Component { } } } + }, + annotation: { + annotations: [{ + type: 'box', + drawTime: "beforeDatasetsDraw", + xScaleID: 'x', + yScaleID: 'y', + xMin: 100, + xMax: this.state.maxUtilization, + borderWidth: 0, + backgroundColor: 'rgb(250, 25, 25)', + label: { + enabled: false, + content: '' + } + }] } } }; @@ -383,7 +410,7 @@ class ProjectReport extends Component { {resourceUtilization.length > 0 && <div className="resource-utilization" id={`${this.props.project.name}-resource-utilization`} style={{paddingTop: "10px", paddingBottom: "10px"}}> - <Bar data={barData} options={barOptions} width="50%" height="10"/> + <Bar data={barData} options={barOptions} plugins={annotationPlugin} width="50%" height="10"/> </div> } </div>