Skip to content
This repository was archived by the owner on Jan 14, 2025. It is now read-only.

Commit 3eeac8e

Browse files
authored
Views and components refactor (#122)
1 parent 2713c89 commit 3eeac8e

11 files changed

Lines changed: 228 additions & 110 deletions

File tree

app/public/Model.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import ServiceUnavailableModel from './model/ServiceUnavailableModel.js';
1818
import { RCT } from './config.js';
1919
import Flags from './views/flags/Flags.js';
2020
import Detectors from './views/detectors/Detectors.js';
21+
import Runs from './views/runs/Runs.js';
2122
const { roles } = RCT;
2223

2324
export default class Model extends Observable {
@@ -37,6 +38,9 @@ export default class Model extends Observable {
3738
this.mode = null;
3839
this.submodels = {};
3940

41+
this.runs = new Runs(this);
42+
this.runs.bubbleTo(this);
43+
4044
this.flags = new Flags(this);
4145
this.flags.bubbleTo(this);
4246

app/public/views/flags/visualization/flagsVisualization.js renamed to app/public/components/flags/flagsVisualization.js

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@
1313
*/
1414

1515
import { h } from '/js/src/index.js';
16-
import flagVisualization from '../../../components/flags/flagVisualization.js';
16+
import flagVisualization from './flagVisualization.js';
1717
import { RCT } from '../../../../config.js';
18-
const { pageNames: PN } = RCT;
18+
const { flagReasonColors } = RCT;
1919

2020
function filterDistinct(a) {
2121
return a.filter((value, index, array) => array.indexOf(value) === index);
@@ -30,8 +30,7 @@ const dateFormatter = (sec) => {
3030
return h('', h('.skinny', dateString), timeString);
3131
};
3232

33-
export default function flagsVisualization(model, dataPass, run, flagsData) {
34-
const runData = model.fetchedData[PN.runsPerDataPass][dataPass].payload.rows.find((e) => e.run_number.toString() === run.toString());
33+
export default function flagsVisualization(runData, flagsData) {
3534
const { time_start, time_end } = runData;
3635

3736
const distinctFlagReasons = filterDistinct(flagsData.map((flag) => flag.flag_reason.replace(/\s+/g, '')));
@@ -42,18 +41,17 @@ export default function flagsVisualization(model, dataPass, run, flagsData) {
4241
}, {});
4342

4443
const flagColor = (flagReason) => {
45-
const colors = RCT.flagReasonColors;
4644
switch (flagReason) {
4745
case 'LimitedAcceptance':
48-
return colors.limitedAcceptance;
46+
return flagReasonColors.limitedAcceptance;
4947
case 'Notbad':
50-
return colors.neutral;
48+
return flagReasonColors.neutral;
5149
case 'CertifiedbyExpert':
52-
return colors.neutral;
50+
return flagReasonColors.neutral;
5351
case 'UnknownQuality':
54-
return colors.neutral;
52+
return flagReasonColors.neutral;
5553
default:
56-
return colors.bad;
54+
return flagReasonColors.bad;
5755
}
5856
};
5957

app/public/model/PrimaryModel.js

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,25 +48,58 @@ export default class PrimaryModel extends Observable {
4848
this.notify();
4949
}
5050

51-
handleLocationChange() {
51+
async handleLocationChange() {
5252
const url = this.router.getUrl();
53+
const { page } = this.router.params;
5354
switch (url.pathname) {
54-
case '/':
55-
if (! this.router.params['page']) {
55+
case '/': {
56+
if (! page) {
5657
this.router.go(`/?page=${pageNames.periods}&${dataReqParams.rowsOnSite}=50&${dataReqParams.site}=1&sorting=-name`);
5758
} else {
59+
await this.pageNavigation(url, page);
5860
this.fetchedData.reqForData()
5961
.then(() => {})
6062
.catch(() => {});
6163
}
6264
break;
65+
}
6366
case '/admin/':
6467
throw 'TODO';
6568
default:
6669
break;
6770
}
6871
}
6972

73+
async pageNavigation(url, page) {
74+
switch (page) {
75+
case pageNames.flags: {
76+
const dataPassName = this.router.params['data_pass_name'];
77+
if (dataPassName) {
78+
await this.parent.runs.fetchRunsPerDataPass(dataPassName).then(() => {}).catch(() => {});
79+
80+
const dpSearchParams = `?page=${pageNames.runsPerDataPass}&index=${dataPassName}`;
81+
const siteReqParams = `&${dataReqParams.rowsOnSite}=50&${dataReqParams.site}=1`;
82+
const dpUrl = new URL(url.origin + url.pathname + dpSearchParams + siteReqParams);
83+
this.fetchedData.reqForData(true, dpUrl).then(() => {
84+
const runNumbers = this.fetchedData[pageNames.runsPerDataPass][dataPassName].payload.rows.map((row) => row.run_number);
85+
this.parent.runs.fetchFlagsSummary(dataPassName, runNumbers).then(() => {
86+
this.fetchedData.reqForData();
87+
}).catch(() => {});
88+
});
89+
} else {
90+
this.goToDefaultPageUrl(pageNames.flags);
91+
}
92+
break;
93+
}
94+
default: {
95+
this.fetchedData.reqForData()
96+
.then(() => {})
97+
.catch(() => {});
98+
break;
99+
}
100+
}
101+
}
102+
70103
async logout() {
71104
const logoutEndpoint = '/api/logout/';
72105
const { result, status, ok } = this.loader.post(logoutEndpoint);
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/**
2+
* @license
3+
* Copyright 2019-2020 CERN and copyright holders of ALICE O2.
4+
* See http://alice-o2.web.cern.ch/copyright for details of the copyright holders.
5+
* All rights not expressly granted are reserved.
6+
*
7+
* This software is distributed under the terms of the GNU General Public
8+
* License v3 (GPL Version 3), copied verbatim in the file "COPYING".
9+
*
10+
* In applying this license CERN does not waive the privileges and immunities
11+
* granted to it by virtue of its status as an Intergovernmental Organization
12+
* or submit itself to any jurisdiction.
13+
*/
14+
15+
import { h, iconDataTransferDownload, iconReload, iconShareBoxed } from '/js/src/index.js';
16+
import filter from '../../userView/data/table/filtering/filter.js';
17+
import downloadCSV from '../../../../utils/csvExport.js';
18+
import pageSettings from '../../userView/data/pageSettings/pageSettings.js';
19+
import flagsVisualization from '../../../components/flags/flagsVisualization.js';
20+
import flagsTable from './flagsTable.js';
21+
import flagBreadCrumbs from '../../../../components/flags/flagBreadcrumbs.js';
22+
import { defaultRunNumbers } from '../../../../utils/defaults.js';
23+
import noSubPageSelected from '../../userView/data/table/noSubPageSelected.js';
24+
25+
export default function flagsContent(model, runs, detectors, flags) {
26+
const urlParams = model.router.getUrl().searchParams;
27+
28+
const dataPassName = urlParams.get('data_pass_name');
29+
const run = urlParams.get('run_numbers');
30+
const detector = urlParams.get('detector');
31+
32+
const detectorName = detectors.getDetectorName(detector);
33+
const flagsData = flags.getFlags(run, detectorName);
34+
const runData = runs.getRun(dataPassName, run);
35+
36+
const functionalities = (model) => h('.btn-group',
37+
h('button.btn.btn-secondary.icon-only-button', {
38+
onclick: () => {
39+
model.fetchedData.reqForData(true);
40+
model.notify();
41+
},
42+
}, iconReload()),
43+
44+
h('button.btn.btn-secondary.icon-only-button', {
45+
onclick: () => {
46+
downloadCSV(model);
47+
},
48+
}, iconDataTransferDownload()),
49+
50+
h('button.btn.btn-secondary.icon-only-button', {
51+
onclick: () => {
52+
navigator.clipboard.writeText(model.router.getUrl().toString())
53+
.then(() => {
54+
})
55+
.catch(() => {
56+
});
57+
},
58+
}, iconShareBoxed()),
59+
60+
h('button.btn.icon-only-button', {
61+
className: model.searchFieldsVisible ? 'btn-primary' : 'btn-secondary',
62+
onclick: () => model.changeSearchFieldsVisibility(),
63+
}, model.searchFieldsVisible ? h('.slider-20-off-white.abs-center') : h('.slider-20-primary.abs-center')));
64+
65+
return run > defaultRunNumbers && runData
66+
? h('div.main-content', [
67+
h('div.flex-wrap.justify-between.items-center',
68+
h('div.flex-wrap.justify-between.items-center',
69+
flagBreadCrumbs(model, dataPassName, run, detectorName),
70+
h('button.btn.btn-secondary', {
71+
onclick: () => {
72+
document.getElementById('pageSettingsModal').style.display = 'block';
73+
document.addEventListener('click', (event) => {
74+
const modalContent = document.getElementsByClassName('modal-content');
75+
const modal = document.getElementsByClassName('modal');
76+
if (Array.from(modalContent).find((e) => e != event.target)
77+
&& Array.from(modal).find((e) => e == event.target)
78+
&& document.getElementById('pageSettingsModal')) {
79+
document.getElementById('pageSettingsModal').style.display = 'none';
80+
}
81+
});
82+
},
83+
}, h('.settings-20'))),
84+
85+
h('div', functionalities(model))),
86+
model.searchFieldsVisible ? filter(model) : '',
87+
88+
flagsVisualization(runData, flagsData),
89+
flagsTable(model, flagsData),
90+
h('.modal', { id: 'pageSettingsModal' },
91+
h('.modal-content.abs-center.p3', {
92+
id: 'pageSettingsModalContent',
93+
}, pageSettings(model, () => {
94+
document.getElementById('pageSettingsModal').style.display = 'none';
95+
}))),
96+
])
97+
: noSubPageSelected(model);
98+
}

app/public/views/flags/overview/flagsPanel.js

Lines changed: 3 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -12,86 +12,16 @@
1212
* or submit itself to any jurisdiction.
1313
*/
1414

15-
import { h, iconDataTransferDownload, iconReload, iconShareBoxed } from '/js/src/index.js';
16-
import filter from '../../userView/data/table/filtering/filter.js';
17-
import downloadCSV from '../../../../utils/csvExport.js';
18-
import pageSettings from '../../userView/data/pageSettings/pageSettings.js';
19-
import flagsVisualization from '../visualization/flagsVisualization.js';
20-
import flagsTable from './flagsTable.js';
21-
import flagBreadCrumbs from '../../../../components/flags/flagBreadcrumbs.js';
2215
import { defaultRunNumbers } from '../../../../utils/defaults.js';
2316
import noSubPageSelected from '../../userView/data/table/noSubPageSelected.js';
17+
import flagsContent from './flagsContent.js';
2418

25-
export default function flagsPanel(model, detectors, flags) {
19+
export default function flagsPanel(model, runs, detectors, flags) {
2620
const urlParams = model.router.getUrl().searchParams;
2721

28-
const dataPassName = urlParams.get('data_pass_name');
2922
const run = urlParams.get('run_numbers');
30-
const detector = urlParams.get('detector');
31-
32-
const detectorName = detectors.getDetectorName(detector);
33-
const flagsData = flags.getFlags(run, detectorName);
34-
35-
const functionalities = (model) => h('.btn-group',
36-
h('button.btn.btn-secondary.icon-only-button', {
37-
onclick: () => {
38-
model.fetchedData.reqForData(true);
39-
model.notify();
40-
},
41-
}, iconReload()),
42-
43-
h('button.btn.btn-secondary.icon-only-button', {
44-
onclick: () => {
45-
downloadCSV(model);
46-
},
47-
}, iconDataTransferDownload()),
48-
49-
h('button.btn.btn-secondary.icon-only-button', {
50-
onclick: () => {
51-
navigator.clipboard.writeText(model.router.getUrl().toString())
52-
.then(() => {
53-
})
54-
.catch(() => {
55-
});
56-
},
57-
}, iconShareBoxed()),
58-
59-
h('button.btn.icon-only-button', {
60-
className: model.searchFieldsVisible ? 'btn-primary' : 'btn-secondary',
61-
onclick: () => model.changeSearchFieldsVisibility(),
62-
}, model.searchFieldsVisible ? h('.slider-20-off-white.abs-center') : h('.slider-20-primary.abs-center')));
6323

6424
return run > defaultRunNumbers
65-
? h('div.main-content', [
66-
h('div.flex-wrap.justify-between.items-center',
67-
h('div.flex-wrap.justify-between.items-center',
68-
flagBreadCrumbs(model, dataPassName, run, detectorName),
69-
h('button.btn.btn-secondary', {
70-
onclick: () => {
71-
document.getElementById('pageSettingsModal').style.display = 'block';
72-
document.addEventListener('click', (event) => {
73-
const modalContent = document.getElementsByClassName('modal-content');
74-
const modal = document.getElementsByClassName('modal');
75-
if (Array.from(modalContent).find((e) => e != event.target)
76-
&& Array.from(modal).find((e) => e == event.target)
77-
&& document.getElementById('pageSettingsModal')) {
78-
document.getElementById('pageSettingsModal').style.display = 'none';
79-
}
80-
});
81-
},
82-
}, h('.settings-20'))),
83-
84-
h('div', functionalities(model))),
85-
model.searchFieldsVisible ? filter(model) : '',
86-
87-
flagsVisualization(model, dataPassName, run, flagsData),
88-
flagsTable(model, flagsData),
89-
h('.modal', { id: 'pageSettingsModal' },
90-
h('.modal-content.abs-center.p3', {
91-
id: 'pageSettingsModalContent',
92-
}, pageSettings(model, () => {
93-
document.getElementById('pageSettingsModal').style.display = 'none';
94-
}))),
95-
])
25+
? flagsContent(model, runs, detectors, flags)
9626
: noSubPageSelected(model);
9727
}

app/public/views/runs/Runs.js

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/**
2+
* @license
3+
* Copyright CERN and copyright holders of ALICE O2. This software is
4+
* distributed under the terms of the GNU General Public License v3 (GPL
5+
* Version 3), copied verbatim in the file "COPYING".
6+
*
7+
* See http://alice-o2.web.cern.ch/license for full licensing information.
8+
*
9+
* In applying this license CERN does not waive the privileges and immunities
10+
* granted to it by virtue of its status as an Intergovernmental Organization
11+
* or submit itself to any jurisdiction.
12+
*/
13+
14+
import { Observable } from '/js/src/index.js';
15+
import { RCT } from '../../config.js';
16+
const { pageNames: PN } = RCT;
17+
const { dataReqParams: DRP } = RCT;
18+
19+
/**
20+
* Model representing handlers for the runs page
21+
*
22+
* @implements {OverviewModel}
23+
*/
24+
export default class Runs extends Observable {
25+
/**
26+
* The constructor of the Overview model object
27+
*
28+
* @param {Model} model Pass the model to access the defined functions
29+
*/
30+
constructor(model) {
31+
super();
32+
this.model = model;
33+
}
34+
35+
async fetchFlagsSummary(dataPass, runNumbers) {
36+
const submodel = this.model.submodels[this.model.mode];
37+
const url = submodel.router.getUrl();
38+
const search = `?page=${PN.flags}&data_pass_name=${dataPass}&run_numbers=${runNumbers}&${DRP.rowsOnSite}=50&${DRP.site}=1`;
39+
const flagsUrl = new URL(url.origin + url.pathname + search);
40+
await submodel.fetchedData.reqForData(true, flagsUrl);
41+
}
42+
43+
async fetchRunsPerDataPass(dataPass) {
44+
const submodel = this.model.submodels[this.model.mode];
45+
const page = submodel.fetchedData[PN.dataPasses];
46+
const [pIndex] = Object.keys(page);
47+
const { url } = page[pIndex].payload;
48+
const dpSearchParams = `?page=${PN.runsPerDataPass}&index=${dataPass}&${DRP.rowsOnSite}=50&${DRP.site}=1`;
49+
await submodel.fetchedData.reqForData(true, new URL(url.origin + url.pathname + dpSearchParams));
50+
51+
const runNumbers = submodel.fetchedData[PN.runsPerDataPass][dataPass].payload.rows.map((row) => row.run_number);
52+
await this.fetchFlagsSummary(dataPass, runNumbers);
53+
}
54+
55+
getRunsPerDataPass(dataPass) {
56+
return this.model.submodels[this.model.mode].fetchedData[PN.runsPerDataPass][dataPass].payload.rows;
57+
}
58+
59+
getRun(dataPass, runNumber) {
60+
const runsPerDataPass = this.getRunsPerDataPass(dataPass);
61+
return runsPerDataPass.find((run) => run.run_number.toString() === runNumber.toString());
62+
}
63+
}

app/public/views/userView.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export default function userPanel(model) {
2424
h('section.outline-gray.flex-grow.relative.user-panel-main-content', [
2525
h('.scroll-y.absolute-fill',
2626
{ id: 'user-panel-main-content' },
27-
dataPanel(submodel, model.detectors, model.flags)),
27+
dataPanel(submodel, model.runs, model.detectors, model.flags)),
2828
]),
2929
]),
3030
]);

0 commit comments

Comments
 (0)