Skip to content
Merged
16 changes: 16 additions & 0 deletions QualityControl/public/common/enums/root.enum.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* @license
* Copyright 2019-2020 CERN and copyright holders of ALICE O2.
* See http://alice-o2.web.cern.ch/copyright for details of the copyright holders.
* All rights not expressly granted are reserved.
*
* This software is distributed under the terms of the GNU General Public
* License v3 (GPL Version 3), copied verbatim in the file "COPYING".
*
* In applying this license CERN does not waive the privileges and immunities
* granted to it by virtue of its status as an Intergovernmental Organization
* or submit itself to any jurisdiction.
*/

export const JS_ROOT_ERROR_LABEL = 'ROOT_ERROR';
export const JS_ROOT_FAILED_TO_PLOT_MESSAGE = 'JSROOT failed to plot the object';
20 changes: 7 additions & 13 deletions QualityControl/public/common/object/draw.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@

/* global JSROOT */

import { h, iconWarning } from '/js/src/index.js';
import { h } from '/js/src/index.js';
import { generateDrawingOptionString, isObjectOfTypeChecker } from './../../../library/qcObject/utils.js';
import checkersPanel from './checkersPanel.js';
import { keyedTimerDebouncer, pointerId } from '../utils.js';
import { failureToDrawPanel } from './failureToDrawPanel.js';

/**
* Renders a QCObject as a virtual DOM node using JSROOT.
Expand All @@ -32,14 +33,11 @@ import { keyedTimerDebouncer, pointerId } from '../utils.js';
* @param {(Error) => void} failFn - optional function to execute upon drawing failure
* @returns {vnode} output virtual-dom, a single div with JSROOT attached to it
*/
export const draw = (remoteData, options = {}, drawingOptions = [], failFn = () => {}) =>
export const draw = (remoteData = {}, options = {}, drawingOptions = [], failFn = () => {}) =>
remoteData?.match({
NotAsked: () => null,
Loading: () => h('.flex-column.items-center.justify-center', [h('.animate-slow-appearance', 'Loading')]),
Failure: (error) => h('.error-box.danger.flex-column.justify-center.f6.text-center', {}, [
h('span.error-icon', { title: 'Error' }, iconWarning()),
h('span', error),
]),
Failure: (error) => failureToDrawPanel(error),
Success: (data) => drawObject(data, options, drawingOptions, failFn),
});

Expand All @@ -55,9 +53,11 @@ export const draw = (remoteData, options = {}, drawingOptions = [], failFn = ()
*/
export const drawObject = (object, options = {}, drawingOptions = [], failFn = () => {}) => {
const { qcObject, etag } = object;
const { root } = qcObject;
const { root, rootError } = qcObject;
if (isObjectOfTypeChecker(root)) {
return checkersPanel(root);
} else if (rootError) {
return failureToDrawPanel(rootError);
}

drawingOptions = Array.from(new Set(drawingOptions));
Expand Down Expand Up @@ -123,15 +123,11 @@ const drawOnCreate = async (dom, root, drawingOptions, failFn) => {
const finalDrawingOptions = generateDrawingOptionString(root, drawingOptions);
JSROOT.draw(dom, root, finalDrawingOptions).then((painter) => {
if (painter === null) {
// eslint-disable-next-line no-console
console.error('null painter in JSROOT');
if (typeof failFn === 'function') {
failFn(new Error('null painter in JSROOT'));
}
}
}).catch((error) => {
// eslint-disable-next-line no-console
console.error(error);
if (typeof failFn === 'function') {
failFn(error);
}
Expand Down Expand Up @@ -235,8 +231,6 @@ const redraw = (dom, root, drawingOptions, failFn) => {
try {
JSROOT.redraw(dom, root, finalDrawingOptions);
} catch (error) {
// eslint-disable-next-line no-console
console.error(error);
if (typeof failFn === 'function') {
failFn(error);
}
Expand Down
25 changes: 25 additions & 0 deletions QualityControl/public/common/object/failureToDrawPanel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* @license
* Copyright 2019-2020 CERN and copyright holders of ALICE O2.
* See http://alice-o2.web.cern.ch/copyright for details of the copyright holders.
* All rights not expressly granted are reserved.
*
* This software is distributed under the terms of the GNU General Public
* License v3 (GPL Version 3), copied verbatim in the file "COPYING".
*
* In applying this license CERN does not waive the privileges and immunities
* granted to it by virtue of its status as an Intergovernmental Organization
* or submit itself to any jurisdiction.
*/

import { h, iconWarning } from '/js/src/index.js';

/**
* Panel to show when an object failed to be drawn
* @param {string} error - error message to show
* @returns {vnode} - virtual node element
*/
export const failureToDrawPanel = (error) =>h('.error-box.danger.flex-column.justify-center.f6.text-center', {}, [
h('span.error-icon', { title: 'Error' }, iconWarning()),
h('span', error),
]);
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* @license
* Copyright 2019-2020 CERN and copyright holders of ALICE O2.
* See http://alice-o2.web.cern.ch/copyright for details of the copyright holders.
* All rights not expressly granted are reserved.
*
* This software is distributed under the terms of the GNU General Public
* License v3 (GPL Version 3), copied verbatim in the file "COPYING".
*
* In applying this license CERN does not waive the privileges and immunities
* granted to it by virtue of its status as an Intergovernmental Organization
* or submit itself to any jurisdiction.
*/

/**
* @type {QcObjectRemoteData}
* should contain:
* {
* ...objectProperties as per ObjectDTO: '' // built specifically for the page
* root: JSON version of the root object to plot
* rootError: '' // error message if root object could not be retrieved
* timestampList: '',
* }
*/

import { RemoteData } from '/js/src/index.js';
import { JS_ROOT_ERROR_LABEL, JS_ROOT_FAILED_TO_PLOT_MESSAGE } from '../enums/root.enum.js';

/**
* Update the RemoteData object to include an error message on the qcObject
* @param {QcObjectRemoteData} qcObjectRemoteData - the RemoteData object containing the qcObject
* @param {string} error - the failure message to display
* @returns {QcObjectRemoteData} - updated RemoteData object with error message
*/
export const updateWithPlotErrorOnQcRemoteData = (qcObjectRemoteData, error) => {
if (qcObjectRemoteData.isSuccess()) {
const updatedQcObject = {
...qcObjectRemoteData.payload.qcObject,
rootError: `${JS_ROOT_ERROR_LABEL}: ${error || JS_ROOT_FAILED_TO_PLOT_MESSAGE}`,
};
qcObjectRemoteData = RemoteData.success({ ...qcObjectRemoteData.payload, qcObject: updatedQcObject });
} else {
qcObjectRemoteData = RemoteData.failure('Cannot update error message on a non-successful RemoteData object');
}
return qcObjectRemoteData;
};
2 changes: 1 addition & 1 deletion QualityControl/public/layout/view/page.js
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ const drawComponent = (model, tabObject) => {
display: 'flex',
'flex-direction': 'column',
},
}, draw(
}, objectFromQcdbAsRemoteData && draw(
objectFromQcdbAsRemoteData,
{},
toUseDrawingOptions,
Expand Down
25 changes: 12 additions & 13 deletions QualityControl/public/layout/view/panels/objectTreeSidebar.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,20 @@ export default (model) =>
Loading: () => h('.flex-column.items-center', [spinner(2), h('.f6', 'Loading Objects')]),
Success: (objects) => {
let objectsToDisplay = [];
const { searchInput = '' } = model.object;
const { searchInput = '', selectedObject, objects: objectsRemoteDataMap = {} } = model.object;
if (searchInput.trim() !== '') {
objectsToDisplay = objects.filter((qcObject) =>
qcObject.name.toLowerCase().includes(searchInput.toLowerCase()));
}
const objectRemoteData = objectsRemoteDataMap[selectedObject?.name];
return [
searchForm(model),
h('.flex-column.flex-grow', {}, [
searchInput.trim() !== ''
? virtualTable(model, 'side', objectsToDisplay)
: h('.scroll-y', treeTable(model)),
]),
objectPreview(model),
objectRemoteData && objectPreview(selectedObject?.name, objectRemoteData),
];
},
Failure: (error) => h('.f6.danger.flex-column.text-center', [
Expand Down Expand Up @@ -186,19 +187,17 @@ const leafRow = (model, sideTree, level) => {
/**
* Shows a JSROOT plot of selected object inside the tree of sidebar allowing the user to preview object and decide
* if it should be added to layout
* @param {Model} model - root model of the application
* @param {string} name - name of the selected object
* @param {QcObjectRemoteData} objectRemoteData - RemoteData of the selected object
* @returns {vnode} - virtual node element
*/
const objectPreview = (model) => {
const isSelected = model.object.selected;
if (isSelected) {
return isSelected && h(
const objectPreview = (name, objectRemoteData = null) =>
objectRemoteData
? h(
'.bg-white',
{ style: 'height: 20em' },
draw(model.object.objects[model.object.selected.name], {}, [], (error) => {
model.object.invalidObject(model.object.selected.name, error.message);
draw(objectRemoteData, {}, [], (error) => {
model.object.invalidObject(name, error.message);
}),
);
}
return null;
};
)
: null;
7 changes: 4 additions & 3 deletions QualityControl/public/object/QCObject.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { simpleDebouncer, prettyFormatDate, setBrowserTabTitle } from './../comm
import { isObjectOfTypeChecker } from './../library/qcObject/utils.js';
import { BaseViewModel } from '../common/abstracts/BaseViewModel.js';
import { StorageKeysEnum } from '../common/enums/storageKeys.enum.js';
import { updateWithPlotErrorOnQcRemoteData } from '../common/object/updateWithPlotErrorOnQcRemoteData.js';

/**
* Model namespace for all about QC's objects (not javascript objects)
Expand Down Expand Up @@ -347,11 +348,11 @@ export default class QCObject extends BaseViewModel {
/**
* Indicate that the object loaded is wrong. Used after trying to print it with jsroot
* @param {string} name - name of the object
* @param {string} reason - the reason for invalidating the object
* @param {object} details - object containing detail information for invalidation
* @returns {undefined}
*/
invalidObject(name, reason) {
this.objects[name] = RemoteData.failure(reason || 'JSROOT was unable to draw this object');
invalidObject(name, details) {
this.objects[name] = updateWithPlotErrorOnQcRemoteData(this.objects[name], details);
this.notify();
}

Expand Down
2 changes: 1 addition & 1 deletion QualityControl/public/object/objectTreePage.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export default (model) => {
*/
function objectPanel(model) {
const selectedObjectName = model.object.selected.name;
if (model.object.objects && model.object.objects[selectedObjectName]) {
if (model.object.objects && model.object.objects?.[selectedObjectName]) {
return model.object.objects[selectedObjectName].match({
NotAsked: () => null,
Loading: () =>
Expand Down
4 changes: 3 additions & 1 deletion QualityControl/public/pages/objectView/ObjectViewModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { BaseViewModel } from '../../common/abstracts/BaseViewModel.js';
import { setBrowserTabTitle } from '../../common/utils.js';
import { RemoteData, BrowserStorage } from '/js/src/index.js';
import { StorageKeysEnum } from '../../common/enums/storageKeys.enum.js';
import { updateWithPlotErrorOnQcRemoteData } from '../../common/object/updateWithPlotErrorOnQcRemoteData.js';

/**
* Model namespace for ObjectViewPage
Expand All @@ -36,6 +37,7 @@ export default class ObjectViewModel extends BaseViewModel {
* {
* ...objectProperties as per ObjectDTO: '' // built specifically for the page
* root: JSON version of the root object to plot
* rootError: '' // error message if root object could not be retrieved
* timestampList: '',
* }
*/
Expand Down Expand Up @@ -217,7 +219,7 @@ export default class ObjectViewModel extends BaseViewModel {
* @param {string} message - the failure message to display
*/
drawingFailureOccurred(message) {
this.selected = RemoteData.failure(message || 'Failed to draw JSROOT plot');
this.selected = updateWithPlotErrorOnQcRemoteData(this.selected, message);
this.notify();
}
}
7 changes: 5 additions & 2 deletions QualityControl/public/pages/objectView/ObjectViewPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ const objectPlotAndInfo = (objectViewModel) =>
Success: (qcObject) => {
const {
id,
name,
qcObject: { root = {} } = {},
validFrom,
ignoreDefaults = false,
drawOptions = [],
Expand All @@ -55,6 +57,7 @@ const objectPlotAndInfo = (objectViewModel) =>
layoutDisplayOptions
: [...drawOptions, ...displayHints, ...layoutDisplayOptions];
const isObjectInfoVisible = objectViewModel.objectInfoVisible;

return h('.w-100.h-100.flex-column.scroll-off#ObjectPlot', [
h('.flex-row.justify-center.items-center.h-10', [
h(
Expand All @@ -66,9 +69,9 @@ const objectPlotAndInfo = (objectViewModel) =>
),
),
h('.item-action-row.flex-row.g1.p2', [
downloadRootImageDropdown(qcObject.name, qcObject.qcObject.root, drawingOptions),
downloadRootImageDropdown(name, root, drawingOptions),
downloadButton({
href: objectViewModel.getDownloadQcdbObjectUrl(qcObject.id),
href: objectViewModel.getDownloadQcdbObjectUrl(id),
title: 'Download root object',
}),
visibilityToggleButton(
Expand Down
Loading