Skip to content

Commit bcf23a2

Browse files
AlexJansongraduta
andauthored
[OGUI-1852] Open Bookkeeping from RunNumber ObjectInfoPanel (#3272)
* add backend changes to update BKP status to include URLs needed on front-end * load these new fields in window object * display link to BKP next to runNumber if BKP configuration is present --------- Co-authored-by: George Raduta <[email protected]>
1 parent d20b20d commit bcf23a2

File tree

15 files changed

+244
-92
lines changed

15 files changed

+244
-92
lines changed

QualityControl/common/library/enums/Status/integratedServices.enum.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,5 @@ export const IntegratedServices = Object.freeze({
2222
QC: 'qc',
2323
CCDB: 'ccdb',
2424
KAFKA: 'kafka',
25+
BOOKKEEPING: 'bookkeeping',
2526
});

QualityControl/lib/QCModel.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,13 @@ export const setupQcModel = async (ws, eventEmitter) => {
8686
const userController = new UserController(userRepository);
8787
const layoutController = new LayoutController(layoutRepository);
8888

89-
const statusService = new StatusService({ version: packageJSON?.version ?? '-' }, { qc: config.qc ?? {} });
89+
const statusService = new StatusService(
90+
{ version: packageJSON?.version ?? '-' },
91+
{
92+
qc: config.qc ?? {},
93+
bookkeeping: config.bookkeeping ?? {},
94+
},
95+
);
9096
const statusController = new StatusController(statusService);
9197

9298
if (config?.kafka?.enabled) {
@@ -118,6 +124,7 @@ export const setupQcModel = async (ws, eventEmitter) => {
118124
const intervalsService = new IntervalsService();
119125

120126
const bookkeepingService = new BookkeepingService(config.bookkeeping);
127+
statusService.bookkeepingService = bookkeepingService;
121128
try {
122129
await bookkeepingService.connect();
123130
} catch (error) {

QualityControl/lib/services/BookkeepingService.js

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { httpGetJson } from '../utils/httpRequests.js';
1717
import { LogManager } from '@aliceo2/web-ui';
1818
import { wrapRunStatus } from '../dtos/BookkeepingDto.js';
1919

20-
const GET_BKP_DATABASE_STATUS_PATH = '/api/status/database';
20+
export const GET_BKP_GUI_STATUS_PATH = '/api/status/gui';
2121
const GET_RUN_TYPES_PATH = '/api/runTypes';
2222
const GET_RUN_PATH = '/api/runs';
2323
export const GET_DETECTORS_PATH = '/api/detectors';
@@ -36,6 +36,7 @@ export class BookkeepingService {
3636
this.active = false;
3737
this.error = null;
3838

39+
this._url = '';
3940
this._hostname = '';
4041
this._port = null;
4142
this._token = '';
@@ -56,6 +57,7 @@ export class BookkeepingService {
5657
const { url, token } = this.config || {};
5758
try {
5859
const normalizedURL = new URL(url);
60+
this._url = normalizedURL.href;
5961
this._hostname = normalizedURL.hostname;
6062
this._protocol = normalizedURL.protocol;
6163
this._port = normalizedURL.port || (normalizedURL.protocol === 'https:' ? 443 : 80);
@@ -95,13 +97,14 @@ export class BookkeepingService {
9597
const { data } = await httpGetJson(
9698
this._hostname,
9799
this._port,
98-
`${GET_BKP_DATABASE_STATUS_PATH}?token=${this._token}`,
100+
`${GET_BKP_GUI_STATUS_PATH}?token=${this._token}`,
99101
{
100102
protocol: this._protocol,
101103
rejectUnauthorized: false,
102104
},
103105
);
104106
if (data && data?.status?.ok && data?.status?.configured) {
107+
this._version = data.version || 'unknown';
105108
this._logger.infoMessage('Successfully connected to Bookkeeping');
106109
return true;
107110
} else {
@@ -278,4 +281,22 @@ export class BookkeepingService {
278281
_createRunPath(runNumber) {
279282
return this._createPath(`${GET_RUN_PATH}/${runNumber}`);
280283
}
284+
285+
/**
286+
* Get the URL of the bookkeeping service
287+
* @readonly
288+
* @returns {string} the URL of the bookkeeping service
289+
*/
290+
get url() {
291+
return this._url;
292+
}
293+
294+
/**
295+
* Get the version of the bookkeeping service
296+
* @readonly
297+
* @returns {string} the version of the bookkeeping service
298+
*/
299+
get version() {
300+
return this._version;
301+
}
281302
}

QualityControl/lib/services/Status.service.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ export class StatusService {
3939
*/
4040
this._dataService = undefined;
4141

42+
/**
43+
* @type {BookkeepingService}
44+
*/
45+
this._bookkeepingService = undefined;
46+
4247
/**
4348
* @type {WebSocket}
4449
*/
@@ -73,6 +78,9 @@ export class StatusService {
7378
case IntegratedServices.KAFKA:
7479
result = this.retrieveKafkaServiceStatus();
7580
break;
81+
case IntegratedServices.BOOKKEEPING:
82+
result = this.retrieveBookkeepingServiceStatus();
83+
break;
7684
}
7785
return result;
7886
}
@@ -153,6 +161,37 @@ export class StatusService {
153161
};
154162
}
155163

164+
/**
165+
* Retrieve the bookkeeping service status response and its public configuration
166+
* @returns {object} - status of the bookkeeping service
167+
*/
168+
retrieveBookkeepingServiceStatus() {
169+
if (this._bookkeepingService?.active) {
170+
return {
171+
name: IntegratedServices.BOOKKEEPING,
172+
version: this._bookkeepingService.version,
173+
status: { ok: true, category: ServiceStatus.SUCCESS },
174+
extras: {
175+
BASE_URL: this._bookkeepingService.url,
176+
PARTIAL_RUN_DETAILS: '?page=run-detail&runNumber=',
177+
},
178+
};
179+
} else if (this._bookkeepingService.config) {
180+
return {
181+
name: IntegratedServices.BOOKKEEPING,
182+
status: {
183+
ok: false,
184+
category: ServiceStatus.ERROR,
185+
message: this._bookkeepingService.error || 'Unable to connect to Bookkeeping service',
186+
},
187+
};
188+
}
189+
return {
190+
name: IntegratedServices.BOOKKEEPING,
191+
status: { ok: false, category: ServiceStatus.NOT_CONFIGURED },
192+
};
193+
}
194+
156195
/*
157196
* Getters & Setters
158197
*/
@@ -166,6 +205,15 @@ export class StatusService {
166205
this._dataService = dataService;
167206
}
168207

208+
/**
209+
* Set service to be used for querying status of the Bookkeeping service.
210+
* @param {BookkeepingService} bookkeepingService - service used for retrieving Bookkeeping status
211+
* @returns {void}
212+
*/
213+
set bookkeepingService(bookkeepingService) {
214+
this._bookkeepingService = bookkeepingService;
215+
}
216+
169217
/**
170218
* Set instance of websocket server
171219
* @param {WebSocket} ws - instance of the WS server

QualityControl/public/Model.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ export default class Model extends Observable {
182182
await this.filterModel.filterService.initFilterService();
183183
await this.filterModel.setFilterFromURL();
184184
this.filterModel.setFilterToURL();
185+
await this.aboutViewModel.retrieveIndividualServiceStatus(IntegratedServices.BOOKKEEPING);
185186

186187
this.services.layout.getLayoutsByUserId(this.session.personid, RequestFields.LAYOUT_CARD);
187188

QualityControl/public/app.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@
147147
font-weight: 500;
148148
}
149149

150-
&>div:hover {
150+
& > div > div:hover {
151151
font-weight: 700;
152152
}
153153
}

QualityControl/public/common/filters/filterViews.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ export function filtersPanel(filterModel, viewModel) {
118118
isRunModeActivated && runStatusPanel(runStatus),
119119
]),
120120
lastUpdatePanel(runStatus, lastRefresh, refreshRate),
121-
cleanRunInformationPanel(cleanRunInformation),
121+
cleanRunInformationPanel(cleanRunInformation, filterModel.filterMap['RunNumber']),
122122
detectorsQualitiesPanel(detectorsQualities),
123123
],
124124
);

QualityControl/public/common/filters/runMode/runStatusPanel.js

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212
*/
1313

1414
import { RunStatus } from '../../../../../library/runStatus.enum.js';
15-
import { h } from '/js/src/index.js';
16-
import { camelToTitleCase } from '../../utils.js';
15+
import { h, iconExternalLink } from '/js/src/index.js';
16+
import { camelToTitleCase, getBkpRunDetailsUrl } from '../../utils.js';
1717
import { statusBadge } from '../../badge.js';
1818

1919
/**
@@ -59,23 +59,36 @@ export const lastUpdatePanel = (runStatus, lastRefresh, refreshRate = 15000) =>
5959
/**
6060
* Renders the run information panel
6161
* @param {object} cleanRunInformation - The `RunInformation` without `detectorsQualities`
62+
* @param {string} runNumber - The current selected filter run number
6263
* @returns {vnode} - virtual node element
6364
*/
64-
export const cleanRunInformationPanel = (cleanRunInformation) =>
65+
export const cleanRunInformationPanel = (cleanRunInformation, runNumber) =>
6566
cleanRunInformation && Object.keys(cleanRunInformation).length > 0 && h(
6667
'.flex-row.g4.items-center.f7.gray-darker.text-center.ph4',
6768
{
6869
id: 'header-run-information',
6970
style: 'overflow-x: auto; margin: 0 auto;',
7071
},
71-
Object.entries(cleanRunInformation).map(([key, value]) =>
72+
[
7273
h('.flex-row.g1', {
74+
style: 'flex: 0 0 auto;',
75+
}, [
76+
h('span', 'Open run in Bookkeeping'),
77+
h('a', {
78+
id: 'openRunInBookkeeping',
79+
title: 'Open run in Bookkeeping',
80+
href: getBkpRunDetailsUrl(runNumber),
81+
target: '_blank',
82+
}, iconExternalLink()),
83+
]),
84+
Object.entries(cleanRunInformation).map(([key, value]) => h('.flex-row.g1', {
7385
key: `${key}-${value}`,
7486
style: 'flex: 0 0 auto;',
7587
}, [
7688
h('strong', `${camelToTitleCase(key)}:`),
7789
h('span', `${value}`),
7890
])),
91+
],
7992
);
8093

8194
/**

QualityControl/public/common/object/objectInfoCard.js

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@
1313
*/
1414

1515
import { h, isContextSecure } from '/js/src/index.js';
16+
import { iconExternalLink } from '/js/src/icons.js';
1617
import { camelToTitleCase, copyToClipboard, prettyFormatDate } from './../utils.js';
18+
import { getBkpRunDetailsUrl } from '../../common/utils.js';
1719

1820
const SPECIFIC_KEY_LABELS = {
1921
id: 'ID (etag)',
@@ -62,10 +64,25 @@ const infoRow = (key, value, infoRowAttributes) => {
6264
const formattedKey = getUILabel(key);
6365

6466
const hasValue = value != null && value !== '' && (!Array.isArray(value) || value.length !== 0);
67+
const bkpRunDetailsUrl = key === 'runNumber' ? getBkpRunDetailsUrl(value) : null;
6568

6669
return h(`.flex-row.g2.info-row${highlightedClasses}`, [
6770
h('b.w-25.w-wrapped', formattedKey),
68-
h('.w-75.cursor-pointer', hasValue && infoRowAttributes(formattedKey, formattedValue), formattedValue),
71+
h('.flex-row.w-75', [
72+
h(
73+
'.cursor-pointer.flex-row',
74+
hasValue && infoRowAttributes(formattedKey, formattedValue),
75+
formattedValue,
76+
),
77+
bkpRunDetailsUrl && hasValue
78+
? h('a.ph2.text-right.actionable-icon', {
79+
id: 'openRunInBookkeeping',
80+
title: 'Open run in Bookkeeping',
81+
href: bkpRunDetailsUrl,
82+
target: '_blank',
83+
}, iconExternalLink())
84+
: '',
85+
]),
6986
]);
7087
};
7188

QualityControl/public/common/utils.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import { isUserRoleSufficient } from '../../../../library/userRole.enum.js';
1616
import { generateDrawingOptionString } from '../../library/qcObject/utils.js';
1717

18-
/* global JSROOT */
18+
/* global JSROOT BOOKKEEPING */
1919

2020
/**
2121
* Map of allowed `ROOT.makeImage` file extensions to MIME types
@@ -254,3 +254,15 @@ export const isOnLeftSideOfViewport = (element) => {
254254
const isLeft = rect.left - rect.width < window.innerWidth / 2;
255255
return isLeft;
256256
};
257+
258+
/**
259+
* Retrieves the URL to the run details page in Bookkeeping for the given run number
260+
* @param {number|string} runNumber - The run number to generate the URL for
261+
* @returns {string|null} The URL to the run details page, or null if Bookkeeping is not configured
262+
*/
263+
export const getBkpRunDetailsUrl = (runNumber) => {
264+
if (typeof BOOKKEEPING !== 'undefined' && BOOKKEEPING && BOOKKEEPING.RUN_DETAILS) {
265+
return BOOKKEEPING.RUN_DETAILS + runNumber;
266+
}
267+
return null;
268+
};

0 commit comments

Comments
 (0)