diff --git a/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_report.scss b/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_report.scss index 34fd70c89c704649e53031de34068ab382a7742d..ac9ed42c071bbe40358cff2eb0ae364966d6d024 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_report.scss +++ b/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_report.scss @@ -51,4 +51,23 @@ float: right; margin-left: 5px; font-size: 20px; +} + +.float-btn-right-bottom { + position: fixed; + right: 20px; + bottom: 10px; +} + +.checkbox-label { + cursor: pointer; +} + +.checkbox-label-disabled { + cursor: not-allowed; +} + +.csv-options-dlg-content { + padding: 5px 10px 5px 10px; + border: 1px solid lightgrey; } \ No newline at end of file diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/cycle/report.category.data.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/cycle/report.category.data.js index 066c8b18141e41e63f1280dbc871f7dd504f68c1..bd4b8277a40a0ae915f7a973999420e05a8918e5 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/cycle/report.category.data.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/cycle/report.category.data.js @@ -20,7 +20,7 @@ class CycleCategoryWiseData extends Component{ getBarData() { let reportData = this.getReportData(false); if (this.props.setExportData && reportData.length>0) { - this.props.setExportData('DataIngestedPerCateegory', reportData); + this.props.setExportData('DataIngestedPerCategory', reportData); } return { labels: _.map(reportData, 'cycle'), @@ -100,11 +100,11 @@ class CycleCategoryWiseData extends Component{ const cycleData = repData.data_ingested_per_site_and_category; categoryData['cycle'] = repData.cycle; // TODO: Update field names and values once API is complete - categoryData['rawIF'] = cycleData["Interferometric Observation"].dataproducts.length; - categoryData['rawBF'] = cycleData["Beamformed Observation"].dataproducts.length; + categoryData['rawIF'] = cycleData["Interferometric Observation"].size__sum; + categoryData['rawBF'] = cycleData["Beamformed Observation"].size__sum; categoryData['rawTBB'] = cycleData.rawTBB; - categoryData['preprocIF'] = cycleData["Preprocessing Pipeline"].dataproducts.length; - categoryData['pulpBF'] = cycleData["Pulsar Pipeline"].dataproducts.length; + categoryData['preprocIF'] = cycleData["Preprocessing Pipeline"].size__sum; + categoryData['pulpBF'] = cycleData["Pulsar Pipeline"].size__sum; categoryData['dynspecBF'] = cycleData.dynspecBF; reportData.push(categoryData); }); diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/cycle/report.cycle.completion.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/cycle/report.cycle.completion.js index bdd10c32521f630bd8b76238ce1880d0ef7dfaf7..bd23220f57f438044fa2f48a1159c82332e367a2 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/cycle/report.cycle.completion.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/cycle/report.cycle.completion.js @@ -127,7 +127,8 @@ class CycleCompletionLevel extends Component { const completionLevel = repData.completion_level; reportData.push( {cycle: repData.cycle, performed: completionLevel.total?parseInt((completionLevel.succeeded/completionLevel.total*100).toFixed(0)):null, - prognosis: completionLevel.total?parseInt((completionLevel.prognosis/completionLevel.total*100).toFixed(0)):null, + prognosis: completionLevel.total?parseInt((completionLevel.prognosis/completionLevel.total*100).toFixed(0)):null, + // prognosis: completionLevel.prognosis, target: repData.completion_level.target?repData.completion_level.target*100:null} ); }); if (forTable && reportData.length > 0) { diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/cycle/report.failure.rate.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/cycle/report.failure.rate.js index 4fbf5c78569aef4c8c7ca17c9a7427d743bbd062..3dced22f2f7a7717140da9747300b67ac87acf72 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/cycle/report.failure.rate.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/cycle/report.failure.rate.js @@ -101,13 +101,14 @@ class CycleFailureRate extends Component { // [{cycle: 'Cycle 15', failures: [{month: "2020-06-01", duration: 1200}, {month: "2020-07-01", duration: 1300},....]}] let reportData = {chartData: [], monthTableData: [], cycleTableData: []}; _.map(data, repData=> { - const monthlyFailures = repData.failures; + const monthlyFailures = repData.failures.months; let cycleMonthlyFailures = [] for (const failureData of monthlyFailures) { cycleMonthlyFailures.push({ cycle: repData.cycle, date: failureData.month, month: moment(failureData.month).format("YYYY-MM"), - duration: failureData.duration?(failureData.duration/timeConversionFactor).toFixed(2):null + // duration: failureData.duration?(failureData.duration/timeConversionFactor).toFixed(2):null + duration: failureData.total_failed?(failureData.total_failed/timeConversionFactor).toFixed(2):null }); } reportData.chartData = reportData.chartData.concat(cycleMonthlyFailures); @@ -115,7 +116,7 @@ class CycleFailureRate extends Component { cycle: repData.cycle, period: _.minBy(cycleMonthlyFailures, 'date')? `${(_.minBy(cycleMonthlyFailures, 'date')).month} - ${(_.maxBy(cycleMonthlyFailures, 'date')).month}`:'', - duration: ((_.sumBy(monthlyFailures, 'duration'))/timeConversionFactor).toFixed(2) + duration: ((_.sumBy(monthlyFailures, 'total_failed'))/timeConversionFactor).toFixed(2) }); }); reportData.monthTableData = reportData.chartData; diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/cycle/report.main.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/cycle/report.main.js index 7e932aceb97f77e7447a5a08279a2c617207bbba..1838c322cfd2de742ebdea4475095d28ede75d6c 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/cycle/report.main.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/cycle/report.main.js @@ -9,10 +9,12 @@ import ReactToPrint from "react-to-print"; import { AutoComplete } from 'primereact/autocomplete'; import { Calendar } from 'primereact/calendar'; +import { Checkbox } from 'primereact/checkbox'; import { Button } from 'primereact/button'; import { ProgressBar } from 'primereact/progressbar'; import { AppLoader } from '../../../layout/components/AppLoader'; import { appGrowl } from '../../../layout/components/AppGrowl'; +import { CustomDialog } from '../../../layout/components/CustomDialog'; import CycleService from '../../../services/cycle.service'; import ReportService from '../../../services/report.service'; import CycleReportIntro from './report.intro'; @@ -51,7 +53,8 @@ class CycleReportMain extends Component { pageLoading: true, selectedCycles: [], // Used by the Autocomplete field reportCycles: [], // Used to generate the report for selected cycles - reportPeriod: [] // Period of reporting + reportPeriod: [], // Period of reporting + dialog: null }; this.searchCycles = this.searchCycles.bind(this); this.selectCycles = this.selectCycles.bind(this); @@ -61,6 +64,12 @@ class CycleReportMain extends Component { this.downloadCSV = this.downloadCSV.bind(this); this.downloadPDF = this.downloadPDF.bind(this); this.clearAll = this.clearAll.bind(this); + this.toggleScroll = this.toggleScroll.bind(this); + this.csvDialogContent = this.csvDialogContent.bind(this); + this.showCSVReportDialog = this.showCSVReportDialog.bind(this); + this.changeCSVOption = this.changeCSVOption.bind(this); + this.selectAllCSVOptions = this.selectAllCSVOptions.bind(this); + this.closeDialog = this.closeDialog.bind(this); } componentDidMount() { @@ -68,6 +77,7 @@ class CycleReportMain extends Component { .then(resourceList => {this.resourceList = resourceList}); CycleService.getAllCyclesWithQuota() .then(cycles => {this.cycles = cycles; this.setState({cycles: cycles, pageLoading: false})}); + window.addEventListener('scroll', this.toggleScroll); } /** @@ -155,13 +165,103 @@ class CycleReportMain extends Component { this.setState({isDownloading: false}); } + /** + * Report list shown in the dialog as checkbox to include or exclude while exporting CSV files. + * @returns Component + */ + csvDialogContent() { + let allReports = _.keys(REPORT_VARIABLE_MAP); + allReports = allReports.splice(1); + const repWithData = _.keys(this.cyclesReportData); //this.state.reportNames; + let csvOptions = []; + for (const report of allReports) { + let csvOption = {name: report}; + csvOption.disabled = repWithData.indexOf(report) < 0; + csvOption.checked = this.state.reportNames.indexOf(report) >= 0; + csvOptions.push(csvOption); + } + return ( + <div className="fluid" className="csv-options-dlg-content"> + <div className="col-lg-12" style={{padding: '5px'}}> + <Checkbox inputId={`cb-all`} value="all" + checked={this.state.reportNames.length === _.keys(REPORT_VARIABLE_MAP).length-1} + onChange={e => {this.selectAllCSVOptions()}}></Checkbox> + <label htmlFor={`cb-all`} className="p-checkbox-label checkbox-label">All</label> + </div> + {csvOptions.map((item, index) => ( + <div className="col-lg-12" style={{padding: '5px'}}> + <Checkbox inputId={`cb-${index}`} value={item.name} + checked={item.checked} disabled={item.disabled} + onChange={e => {this.changeCSVOption(item.name, !item.checked)}}></Checkbox> + <label htmlFor={`cb-${index}`} className={`p-checkbox-label checkbox-label${item.disabled?'-disabled':''}`}>{item.name}</label> + </div> + ))} + </div>); + } + + /** + * Function called when report is selected or unselected from the list + * @param {string} reportName - name of the report passed from the checkbox component of the list dialog + * @param {boolean} selected - to include or exclude + */ + changeCSVOption(reportName, selected) { + let reportNames = this.state.reportNames; + if (selected) { + reportNames.push(reportName); + } else { + reportNames = _.filter(reportNames, rep => rep !== reportName); + } + this.setState({reportNames: reportNames}); + } + + /** + * Select or Unselect all reports in the dialog + */ + selectAllCSVOptions() { + let allReports = _.keys(REPORT_VARIABLE_MAP); + allReports = allReports.splice(1); + let repWithData = _.keys(this.cyclesReportData); + if (repWithData.length > this.state.reportNames.length) { + this.setState({reportNames: repWithData}); + } else { + this.setState({reportNames: []}); + } + } + + /** + * To show the dialog with reports to be selected or unselected for downloading as CSV files. + * @returns + */ + showCSVReportDialog() { + const reportNames = (this.state.reportNames && this.state.reportNames.length > 0)? + this.state.reportNames: _.keys(this.cyclesReportData); + if (reportNames.length === 0) { + appGrowl.show({severity: 'info', summary: 'No Data', detail: 'None of the report has data to download'}); + return; + } + let dialog = { header: "CSV Report(s) to download", width: "40vw", actions: null, showIcon: false}; + dialog.detail = "Following selected reports will be downloaded. Report(s) with no data are disabled. Select/Unselect the reports of your interest and click 'Ok'"; + dialog.content = this.csvDialogContent; + dialog.onSubmit = this.downloadCSV; + dialog.actions = [{ id: 'yes', title: 'Ok', callback: this.downloadCSV}, + { id: 'no', title: 'Cancel', callback: this.closeDialog}] + this.setState({csvDlgVisible: true, dialog: dialog, reportNames: reportNames}); + } + + /** + * Closes the dialog. + */ + closeDialog() { + this.setState({csvDlgVisible: false}); + } + /** * Function to download the report data in CSV format */ downloadCSV() { - const reportNames = _.keys(this.cyclesReportData); + const reportNames = this.state.reportNames; if (reportNames.length === 0) { - appGrowl.show({severity: 'info', summary: 'No Data', detail: 'None of the report has data to download'}); + appGrowl.show({severity: 'info', summary: 'No Report Selected', detail: 'No report selected to download'}); } // For every sub-report of the main report get report data for(const reportName of reportNames) { @@ -205,13 +305,15 @@ class CycleReportMain extends Component { a.click(); window.URL.revokeObjectURL(url); } + this.setState({csvDlgVisible: false}); } /** * Function called to set report cycles from the selected cycles. */ async setReportCycles() { - this.setState({isLoading: true}); + this.cyclesReportData = null; + this.setState({isLoading: true, cyclesReportData: [], reportNames: null}); const cycleNames = _.map(_.orderBy(this.state.selectedCycles, 'start'), 'name'); let cyclesReportData = []; @@ -259,6 +361,27 @@ class CycleReportMain extends Component { this.setState({selectedCycles:[], reportPeriod:[], reportCycles:[]}); } + /** + * Callback function to window scroll event listener to set or unset flag to show or hide ScrollToTop button + */ + toggleScroll() { + const scrolled = document.documentElement.scrollTop; + if (scrolled > 300){ + this.setState({scrollTopVisible: true}); + } else if (scrolled <= 300){ + this.setState({scrollTopVisible: false}); + } + }; + + /** + * Function to scroll to top of the window on clicking ScrollToTop button. + */ + scrollToTop() { + window.scrollTo({ + top: 75, + behavior: 'smooth'}); + } + render() { return( <React.Fragment> @@ -294,7 +417,7 @@ class CycleReportMain extends Component { <div ref={(el) => (this.reportDownloadBarRef = el)} > <Link to={{}} className="report-download-bar" style={{color: "#148048"}} title="Download Report Data in CSV format. Each report data will be downloaded as separate .csv file. Enable auto downloading multiple files in browser settings." - onClick={this.downloadCSV}> + onClick={this.showCSVReportDialog}> <i className="fas fa-file-csv"></i> </Link> <Link to={{}} className="report-download-bar" style={{color: "#f20f00"}} title="Download Report as PDF" @@ -377,8 +500,20 @@ class CycleReportMain extends Component { {/* Dummy reference div to scroll down before exporting to PDF so that all components will be rendered properly before exporting */} <div ref={(el) => {this.reportEndRef = el}}></div> + {/* Scroll To Top button */} + {this.state.scrollTopVisible && + <Button className="p-button-info p-button-raised float-btn-right-bottom" + onClick={this.scrollToTop} + icon="pi pi-arrow-up" tooltip="Move to top"></Button> + } </> } + {this.state.csvDlgVisible && + <CustomDialog type="confirmation" visible={this.state.csvDlgVisible} + header={this.state.dialog.header} message={this.state.dialog.detail} actions={this.state.dialog.actions} + content={this.state.dialog.content} width={this.state.dialog.width} showIcon={this.state.dialog.showIcon} + onClose={this.closeDialog} onCancel={this.closeDialog} onSubmit={this.state.dialog.onSubmit}/> + } </React.Fragment> ); } diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/cycle/report.project.summary.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/cycle/report.project.summary.js index 9762566d3abe795cf768b45ac0bf033c0a46316c..d62705003a45ea5aadbef609192c9fe3b98910f6 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/cycle/report.project.summary.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/cycle/report.project.summary.js @@ -30,7 +30,7 @@ class CycleProjectSummary extends Component{ projectData['cycle'] = repData.cycle; projectData['project'] = projData.project; // TODO: Update field names and values once API is complete - projectData['durationObserved'] = (projData.durations.total/timeConversionFactor).toFixed(2); + projectData['durationObserved'] = (projData.durations.total_observed/timeConversionFactor).toFixed(2); projectData['durationProcessed'] = projData.durations.processed; projectData['dataToSara'] = projData["Sara"]; projectData['dataToJuelich'] = projData["Juelich"]; diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/project.report.main.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/project.report.main.js index b84b2b3fb3bfb329b730a69835f0d9f96db80017..6412c28ce9a0bd76e71ac59e001c67c950adf742 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/project.report.main.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/project.report.main.js @@ -58,6 +58,7 @@ class ProjectReportMain extends Component { this.downloadCSV = this.downloadCSV.bind(this); this.downloadPDF = this.downloadPDF.bind(this); this.clearAll = this.clearAll.bind(this); + this.toggleScroll = this.toggleScroll.bind(this); } componentDidMount() { @@ -67,6 +68,7 @@ class ProjectReportMain extends Component { .then(cycles => {this.cycles = cycles}); ProjectService.getProjects() .then(projects => { this.setState({projects: projects, pageLoading: false}) }); + window.addEventListener('scroll', this.toggleScroll); } /** @@ -276,6 +278,27 @@ class ProjectReportMain extends Component { ); } + /** + * Callback function to window scroll event listener to set or unset flag to show or hide ScrollToTop button + */ + toggleScroll() { + const scrolled = document.documentElement.scrollTop; + if (scrolled > 300){ + this.setState({scrollTopVisible: true}); + } else if (scrolled <= 300){ + this.setState({scrollTopVisible: false}); + } + }; + + /** + * Function to scroll to top of the window on clicking ScrollToTop button. + */ + scrollToTop() { + window.scrollTo({ + top: 75, + behavior: 'smooth'}); + } + render() { return( <React.Fragment> @@ -327,6 +350,12 @@ class ProjectReportMain extends Component { {/* Dummy reference div to scroll down before exporting to PDF so that all components will be rendered properly before exporting */} <div ref={(el) => {this.reportEndRef = el}}></div> + {/* Scroll To Top button */} + {this.state.scrollTopVisible && + <Button className="p-button-info p-button-raised float-btn-right-bottom" + onClick={this.scrollToTop} + icon="pi pi-arrow-up" tooltip="Move to top"></Button> + } </> } </React.Fragment> diff --git a/SAS/TMSS/frontend/tmss_webapp/src/services/report.service.js b/SAS/TMSS/frontend/tmss_webapp/src/services/report.service.js index c254a0a24232b3450ad7f710f31c2f964ff3a405..ed6a9e0e719ab143c72d9b1510ce8bc35bca929e 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/services/report.service.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/services/report.service.js @@ -8,6 +8,72 @@ const ReportService = { try { const response = await axios.get(`/api/project/${project}/report/`); reportData = response.data; + // TODO: TEST DATA to be removed + reportData = { + "project": "test_for_report", + "quota": [ + { + "id": 4, + "resource_type_id": "CEP Processing Time", + "value": 7200 + }, + { + "id": 5, + "resource_type_id": "LOFAR Observing Time", + "value": 7200 + }, + { + "id": 6, + "resource_type_id": "LTA Storage", + "value": 64*1024*1024*1024 + } + ], + "SUBs": { + "successful": [ + { + "id": 1, + "name": "my_scheduling_unit_blueprint_30a70066-c09b-48f1-8061-c7e20017af9d", + "status": "finished", + "start": "2021-07-29T14:15:44.330405", + "stop": "2021-07-29T14:25:44.330407", + "duration": 600.000002, + "observed_duration": 600.000002, + "target": null, + "SAS ID": { + "observation control": [2000014, 200010], + "preprocessing pipeline": [], + "pulsar pipeline": [200023] + }, + "ingested_date": "2021-07-29T14:25:44.330407", + "ingested_data_size": 246 + } + ], + "failed": [] + }, + "durations": { + "total": 2400.000011, + "total_not_cancelled": 1800.000008, + "total_succeeded": 600.000002, + "total_failed": 600.000003, + "total_observed": 1200.0000049999999, + "total_observed_succeeded": 600.000002, + "total_observed_failed": 600.000003, + "total_succeeded_A": 600.000002, + "total_observed_succeeded_A": 600.000002, + "total_succeeded_B": 0, + "total_observed_succeeded_B": 0, + "not_cancelled_perc": 0.75, + "succeeded_perc": 0.25, + "failed_perc": 0.25, + "observed_perc": 0.50, + "observed_succeeded_perc": 0.25, + "observed_failed_perc": 0.25}, + "LTA dataproducts": {"size__sum": 246}, + "SAPs exposure": { + "target1": 7200.0, + "target2": 3200.0 + } + } } catch(error) { console.error(error); reportData.error = error; @@ -214,16 +280,16 @@ const ReportService = { }, "data_ingested_per_site_and_category": { "Interferometric Observation": { - "dataproducts": [1,2,3,4,6,7,8,8] + "size__sum": 140 }, "Beamformed Observation": { - "dataproducts": [2,3,4] + "size__sum": 150 }, "Preprocessing Pipeline": { - "dataproducts": [1,2,3,4,5,6,7,10,12,13,45] + "size__sum": 160 }, "Pulsar Pipeline": { - "dataproducts": [1,1,1,1,1,2] + "size__sum": 170 } }, "projects_summary": [ @@ -242,8 +308,9 @@ const ReportService = { }, "durations": { "total": 12720, + "total_observevd": 200, "total_succeeded": 0, - "total_not_cancelled": 12720, + "total_not_cancelled": 12520, "total_failed": 0 }, "LTA dataproducts": { @@ -333,7 +400,7 @@ const ReportService = { "idle/test": 150 } }, - "failures": [{month: "2020-03-01", duration: 1200}, {month: "2020-04-01", duration: 1300}] + "failures": {months: [{month: "2020-03-01", total_failed: 1200}, {month: "2020-04-01", total_failed: 1300}]} }, "Cycle 13": { "cycle": "Cycle 13", @@ -521,16 +588,16 @@ const ReportService = { }, "data_ingested_per_site_and_category": { "Interferometric Observation": { - "dataproducts": [1,2,3,4] + "size__sum": 140 }, "Beamformed Observation": { - "dataproducts": [2,3,4] + "size__sum": 150 }, "Preprocessing Pipeline": { - "dataproducts": [1,2,3,4,5,6,7] + "size__sum": 160 }, "Pulsar Pipeline": { - "dataproducts": [1,1,1,1] + "size__sum": 170 } }, "projects_summary": [ @@ -549,8 +616,9 @@ const ReportService = { }, "durations": { "total": 12720, + "total_observed": 700, "total_succeeded": 0, - "total_not_cancelled": 12720, + "total_not_cancelled": 12020, "total_failed": 0 }, "LTA dataproducts": { @@ -640,7 +708,7 @@ const ReportService = { "idle/test": 143 } }, - "failures": [] + "failures": {months: []} }, "Cycle 14": { "cycle": "Cycle 14", @@ -828,16 +896,16 @@ const ReportService = { }, "data_ingested_per_site_and_category": { "Interferometric Observation": { - "dataproducts": [1,3,4] + "size__sum": 140 }, "Beamformed Observation": { - "dataproducts": [2,2,3,4] + "size__sum": 150 }, "Preprocessing Pipeline": { - "dataproducts": [1,4,5,6,7] + "size__sum": 160 }, "Pulsar Pipeline": { - "dataproducts": [1,1] + "size__sum": 170 } }, "projects_summary": [ @@ -856,8 +924,9 @@ const ReportService = { }, "durations": { "total": 12720, + "total_observed": 10000, "total_succeeded": 0, - "total_not_cancelled": 12720, + "total_not_cancelled": 2720, "total_failed": 0 }, "LTA dataproducts": { @@ -947,7 +1016,7 @@ const ReportService = { "idle/test": 955 } }, - "failures": [{month: "2020-03-01", duration: 1200}, {month: "2020-04-01", duration: 1300}] + "failures": {months:[{month: "2020-03-01", total_failed: 1200}, {month: "2020-04-01", total_failed: 1300}]} }, "Cycle 15": { "cycle": "Cycle 15", @@ -1135,16 +1204,16 @@ const ReportService = { }, "data_ingested_per_site_and_category": { "Interferometric Observation": { - "dataproducts": [1,2,3,4,5] + "size__sum": 140 }, "Beamformed Observation": { - "dataproducts": [2,3,4,6,7] + "size__sum": 150 }, "Preprocessing Pipeline": { - "dataproducts": [1,2,3] + "size__sum": 160 }, "Pulsar Pipeline": { - "dataproducts": [1,1,1,1,2,3,1] + "size__sum": 170 } }, "projects_summary": [ @@ -1163,6 +1232,7 @@ const ReportService = { }, "durations": { "total": 12720, + "total_observed": 0, "total_succeeded": 0, "total_not_cancelled": 12720, "total_failed": 0 @@ -1254,7 +1324,7 @@ const ReportService = { "idle/test": 980 } }, - "failures": [{month: "2020-06-01", duration: 1200}, {month: "2020-07-01", duration: 1300}] + "failures": {months: [{month: "2020-06-01", total_failed: 1200}, {month: "2020-07-01", total_failed: 1300}]} } }