diff --git a/js/pages/configuration/configuration.js b/js/pages/configuration/configuration.js
index 76ac15225..1a95dd460 100644
--- a/js/pages/configuration/configuration.js
+++ b/js/pages/configuration/configuration.js
@@ -13,6 +13,7 @@ define([
'services/Poll',
'services/job/jobDetail',
'services/CacheAPI',
+ 'services/ConceptSet',
'less!./configuration.less',
'components/heading'
], function (
@@ -30,6 +31,7 @@ define([
{PollService},
jobDetail,
cacheApi,
+ conceptSetService
) {
class Configuration extends AutoBind(Page) {
constructor(params) {
@@ -42,6 +44,7 @@ define([
this.jobListing = sharedState.jobListing;
this.sourceJobs = new Map();
this.sources = sharedState.sources;
+ this.reindexJob = ko.observable();
this.priorityOptions = [
{id: 'session', name: ko.i18n('configuration.priorityOptions.session', 'Current Session')},
@@ -80,9 +83,45 @@ define([
});
this.intervalId = PollService.add({
- callback: () => this.checkJobs(),
+ callback: () => {
+ this.checkJobs();
+ this.checkReindexJob();
+ },
interval: config.pollInterval
});
+
+ this.searchAvailable = ko.observable(false);
+
+ this.checkSearchAvailable();
+ }
+
+ async checkSearchAvailable () {
+ this.loading(true);
+ try {
+ const data = await conceptSetService.checkSearchAvailable();
+ this.searchAvailable(data);
+ } catch(e) {
+ throw new Error(e);
+ } finally {
+ this.loading(false);
+ }
+ }
+
+ async reindexConceptSets() {
+ const confirmAction = confirm(ko.unwrap(ko.i18n('configuration.confirms.reindexSource', 'Reindexing may take a long time. It depends on amount and complexity of concept sets')));
+ if (!confirmAction) {
+ return;
+ }
+ try {
+ const data =await conceptSetService.reindexConceptSets();
+ if (data.status === 'RUNNING') {
+ alert(ko.unwrap(ko.i18n('configuration.alerts.reindexRunning', 'Reindexing of concept sets is currently in progress')));
+ } else {
+ this.reindexJob(data);
+ }
+ } catch(e) {
+ throw new Error(e);
+ }
}
dispose() {
@@ -111,6 +150,21 @@ define([
});
}
+ async checkReindexJob() {
+ try {
+ let data;
+ if (this.reindexJob()) {
+ data = await conceptSetService.statusReindexConceptSets(this.reindexJob().executionId);
+ } else {
+ data = await conceptSetService.statusReindexConceptSets();
+ }
+ this.reindexJob(data);
+ } catch(e) {
+ this.reindexJob(null);
+ throw new Error(e);
+ }
+ }
+
async onPageCreated() {
this.loading(true);
await sourceApi.initSourcesConfig();
@@ -186,6 +240,9 @@ define([
var selectedSource = sharedState.sources().find((item) => { return item.vocabularyUrl === newVocabUrl; });
sharedState.priorityScope() === 'application' && sharedState.defaultVocabularyUrl(newVocabUrl);
this.updateSourceDaimonPriority(selectedSource.sourceKey, 'Vocabulary');
+ if (this.searchAvailable()) {
+ alert(ko.unwrap(ko.i18n('configuration.alerts.changeSource', 'You are changing current source, we recommend you to do reindexing concept sets')));
+ }
return true;
};
@@ -238,6 +295,36 @@ define([
return this.getButtonStyles(source.connectionCheck());
}
+ getReindexButtonStyles() {
+ let iconClass = 'fa-caret-right';
+ let buttonClass = 'btn-primary';
+ let state = sourceApi.buttonCheckState.unknown;
+ if (this.reindexJob()) {
+ switch(this.reindexJob().status) {
+ case 'COMPLETED':
+ state = sourceApi.buttonCheckState.success;
+ break;
+ case 'FAILED':
+ state = sourceApi.buttonCheckState.failed;
+ break;
+ case 'CREATED':
+ case 'RUNNING':
+ state = sourceApi.buttonCheckState.checking;
+ break;
+ }
+ }
+ return this.getButtonStyles(state);
+ }
+
+ getReindexButtonTitle() {
+ if (this.reindexJob() && this.reindexJob().status !== 'UNAVAILABLE') {
+ return ko.unwrap(ko.i18nformat('configuration.buttons.reindexCSStatus', 'Concept Sets Reindex (<%=doneCount%> of <%=maxCount%>)',
+ {doneCount: this.reindexJob().doneCount, maxCount: this.reindexJob().maxCount})());
+ } else {
+ return ko.unwrap(ko.i18n('configuration.buttons.reindexCS', 'Concept Sets Reindex'));
+ }
+ }
+
getButtonStyles(sourceState) {
let iconClass = 'fa-caret-right';
let buttonClass = 'btn-primary';
diff --git a/js/pages/configuration/configuration.less b/js/pages/configuration/configuration.less
index 0567e502b..9a9ef7dae 100644
--- a/js/pages/configuration/configuration.less
+++ b/js/pages/configuration/configuration.less
@@ -16,4 +16,17 @@
&__manage-btn {
width: 175px;
}
+}
+
+.status-container {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+}
+.status {
+ display: flex;
+ margin-bottom: 8px;
+ :last-child {
+ margin-bottom: 0;
+ }
}
\ No newline at end of file
diff --git a/js/services/ConceptSet.js b/js/services/ConceptSet.js
index 435d050db..43e187fc1 100644
--- a/js/services/ConceptSet.js
+++ b/js/services/ConceptSet.js
@@ -29,6 +29,25 @@ define(function (require) {
alert(message);
self.isLoading(false);
});
+ };
+
+ function searchConceptSets(searchParams) {
+ const sourceKey = sharedState.sourceKeyOfVocabUrl();
+ return httpService.doPost(`${config.api.url}conceptset/${sourceKey}/search`, searchParams).then(({ data }) => data);
+ }
+
+ function checkSearchAvailable() {
+ return httpService.doGet(config.api.url + 'conceptset/searchAvailable').then(({ data }) => data);
+ }
+
+ function reindexConceptSets() {
+ const sourceKey = sharedState.sourceKeyOfVocabUrl();
+ return httpService.doGet(`${config.api.url}conceptset/${sourceKey}/index`).then(({ data }) => data);
+ }
+
+ function statusReindexConceptSets(jobExecutionId) {
+ const sourceKey = sharedState.sourceKeyOfVocabUrl();
+ return httpService.doGet(`${config.api.url}conceptset/${sourceKey}/index/${jobExecutionId || -1}/status`).then(({ data }) => data);
}
function lookupIdentifiers(identifiers) {
@@ -140,7 +159,11 @@ define(function (require) {
getVersion,
getVersionExpression,
updateVersion,
- copyVersion
+ copyVersion,
+ searchConceptSets,
+ checkSearchAvailable,
+ reindexConceptSets,
+ statusReindexConceptSets
};
return api;
diff --git a/js/styles/atlas.css b/js/styles/atlas.css
index 6e314e9ac..55fd96047 100644
--- a/js/styles/atlas.css
+++ b/js/styles/atlas.css
@@ -601,7 +601,6 @@ th {
.configureHeader .left{
display: flex;
- align-items: center;
box-sizing: border-box;
}
diff --git a/js/utils/DatatableUtils.js b/js/utils/DatatableUtils.js
index bb39cdc57..525fd1b17 100644
--- a/js/utils/DatatableUtils.js
+++ b/js/utils/DatatableUtils.js
@@ -83,7 +83,11 @@ define(['knockout', 'services/MomentAPI', 'xss', 'appConfig', 'services/AuthAPI'
const addTagGroupsToFacets = (list, facets) => {
extractTagGroups(list).sort().reverse().forEach(tg => {
+ if (facets.filter(f => f.isTagGroup && f.caption === tg.name).length > 0) { // do not add facet if it is already there
+ return;
+ }
facets.unshift({
+ isTagGroup: true,
caption: tg.name,
binding: (o) => {
let tags = o.tags && o.tags.length > 0
@@ -100,7 +104,11 @@ define(['knockout', 'services/MomentAPI', 'xss', 'appConfig', 'services/AuthAPI'
const addTagGroupsToColumns = (list, columns) => {
extractTagGroups(list).forEach(tg => {
+ if (ko.unwrap(columns).filter(c => c.isTagGroup && c.title === tg.name).length > 0) { // do not add column if it is already there
+ return;
+ }
columns.push({
+ isTagGroup: true,
title: tg.name,
width: '100px', // default width
visible: !!tg.showGroup,