Commit c0c56b82 authored by Piotr Gawron's avatar Piotr Gawron
Browse files

chemical panel support autocomplete functionality

parent 7eb81245
......@@ -363,6 +363,12 @@ ServerConnector.getSuggestedQueryListUrl = function (queryParams, filterParams)
params: filterParams
});
};
ServerConnector.getChemicalSuggestedQueryListUrl = function (queryParams, filterParams) {
return this.getApiUrl({
url: this.getProjectUrl(queryParams) + "chemicals/suggestedQueryList",
params: filterParams
});
};
ServerConnector.addCommentUrl = function (queryParams) {
return this.getApiUrl({
......@@ -1645,6 +1651,18 @@ ServerConnector.getSuggestedQueryList = function (projectId) {
});
};
ServerConnector.getChemicalSuggestedQueryList = function (projectId) {
var self = this;
return self.getProjectId(projectId).then(function (result) {
projectId = result;
return self.sendGetRequest(self.getChemicalSuggestedQueryListUrl({
projectId: projectId
}));
}).then(function (content) {
return JSON.parse(content);
});
};
ServerConnector.getOverlayTypes = function () {
var self = this;
return self.getConfiguration().then(function (configuration) {
......
......@@ -84,17 +84,17 @@ AbstractPanel.prototype._initializeGui = function (placeholder) {
this.setControlElement(PanelControlElementType.SEARCH_LABEL, searchLabel);
var searchInputDiv = Functions.createElement({
type: "table",
type: "table"
});
searchQueryDiv.appendChild(searchInputDiv);
var searchInputRow = Functions.createElement({
type: "tr",
type: "tr"
});
searchInputDiv.appendChild(searchInputRow);
var searchInputCell = Functions.createElement({
type: "td",
type: "td"
});
searchInputRow.appendChild(searchInputCell);
......@@ -110,7 +110,7 @@ AbstractPanel.prototype._initializeGui = function (placeholder) {
this.setControlElement(PanelControlElementType.SEARCH_INPUT, searchInput);
var searchButtonCell = Functions.createElement({
type: "td",
type: "td"
});
searchInputRow.appendChild(searchButtonCell);
......@@ -300,7 +300,7 @@ AbstractPanel.prototype.createTargetRow = function (target, icon) {
inline: true
}));
descColumn.appendChild(guiUtils.createAnnotations("References: ", target.getReferences(), {
showType: false,
showType: false
}));
if (submaps.length > 0) {
descColumn.appendChild(guiUtils.createLabel("Available in submaps: "));
......@@ -316,7 +316,7 @@ AbstractPanel.prototype.createTargetRow = function (target, icon) {
type: "button",
className: "minerva-open-submap-button",
content: model.getName(),
onclick: onclick,
onclick: onclick
});
descColumn.appendChild(button);
}
......@@ -324,4 +324,58 @@ AbstractPanel.prototype.createTargetRow = function (target, icon) {
return result;
};
AbstractPanel.prototype.computeAutocompleteDictionary = function (queries) {
var result = {};
var i, j, k, mainString, substring, list;
//compute dict for prefixes
for (i = 0; i < queries.length; i++) {
mainString = queries[i].toLowerCase();
for (j = 0; j < mainString.length; j++) {
substring = mainString.substring(0, j + 1);
if (result[substring] !== undefined) {
continue;
}
list = [];
for (k = 0; k < 5; k++) {
if (k + i >= queries.length) {
break;
} else if (queries[k + i].toLowerCase().startsWith(substring.toLowerCase())) {
list.push(queries[k + i]);
}
}
result[substring] = list;
}
}
//and now we add substring from inside
for (i = 0; i < queries.length; i++) {
mainString = queries[i].toLowerCase();
for (var startSubstring = 1; startSubstring < mainString.length; startSubstring++) {
for (var endSubstring = startSubstring + 1; endSubstring <= mainString.length; endSubstring++) {
substring = mainString.substring(startSubstring, endSubstring);
if (!mainString.startsWith(substring)) {
if (result[substring] === undefined) {
result[substring] = [];
}
if (result[substring].length < 5) {
var found = false;
for (k = 0; k < result[substring].length; k++) {
found |= result[substring][k] === mainString;
}
if (!found) {
result[substring].push(mainString);
}
}
}
}
}
}
return result;
};
module.exports = AbstractPanel;
......@@ -72,4 +72,23 @@ ChemicalPanel.prototype.init = function () {
ChemicalPanel.prototype.destroy = function () {
return Promise.resolve();
};
ChemicalPanel.prototype.getAutocomplete = function (query) {
if (this._searchAutocomplete === undefined) {
this.refreshSearchAutocomplete();
return [];
}
return this._searchAutocomplete[query];
};
ChemicalPanel.prototype.refreshSearchAutocomplete = function () {
var self = this;
self._searchAutocomplete = [];
return ServerConnector.getChemicalSuggestedQueryList().then(function (queries) {
self._searchAutocomplete = self.computeAutocompleteDictionary(queries);
return self._searchAutocomplete;
});
};
module.exports = ChemicalPanel;
......@@ -9,14 +9,15 @@ var Alias = require('../../map/data/Alias');
var PanelControlElementType = require('../PanelControlElementType');
var Reaction = require('../../map/data/Reaction');
// noinspection JSUnusedLocalSymbols
var logger = require('../../logger');
var Functions = require('../../Functions');
function GenericSearchPanel(params) {
params.panelName = "search";
params.helpTip = "<p>search tab allows to search for particular elements or interactions in the map</p>"
+ "<p>perfect match tick box active: only terms with an exact match to the query will be returned</p>"
+ "<p>separate multiple search by semicolon</p>";
+ "<p>perfect match tick box active: only terms with an exact match to the query will be returned</p>"
+ "<p>separate multiple search by semicolon</p>";
params.placeholder = "keyword";
AbstractDbPanel.call(this, params);
......@@ -27,25 +28,25 @@ function GenericSearchPanel(params) {
GenericSearchPanel.prototype = Object.create(AbstractDbPanel.prototype);
GenericSearchPanel.prototype.constructor = GenericSearchPanel;
GenericSearchPanel.prototype.createSearchGui = function() {
GenericSearchPanel.prototype.createSearchGui = function () {
var searchDiv = this.getControlElement(PanelControlElementType.SEARCH_DIV);
var perfectMatchCheckbox = Functions.createElement({
type : "input",
name : "searchPerfectMatch",
inputType : "checkbox",
type: "input",
name: "searchPerfectMatch",
inputType: "checkbox"
});
searchDiv.appendChild(perfectMatchCheckbox);
this.setControlElement(PanelControlElementType.SEARCH_PERFECT_MATCH_CHECKBOX, perfectMatchCheckbox);
var perfectMatchLabel = Functions.createElement({
type : "span",
content : "PERFECT MATCH"
type: "span",
content: "PERFECT MATCH"
});
searchDiv.appendChild(perfectMatchLabel);
};
GenericSearchPanel.prototype.createTableElement = function(element, icon) {
GenericSearchPanel.prototype.createTableElement = function (element, icon) {
if (element instanceof Alias) {
return this.createAliasElement(element, icon);
} else if (element instanceof Reaction) {
......@@ -55,11 +56,11 @@ GenericSearchPanel.prototype.createTableElement = function(element, icon) {
}
};
GenericSearchPanel.prototype.createPreamble = function() {
GenericSearchPanel.prototype.createPreamble = function () {
return document.createElement("div");
};
GenericSearchPanel.prototype.createReactionElement = function(reaction) {
GenericSearchPanel.prototype.createReactionElement = function (reaction) {
var self = this;
var guiUtils = self.getGuiUtils();
var result = document.createElement("tr");
......@@ -67,7 +68,7 @@ GenericSearchPanel.prototype.createReactionElement = function(reaction) {
result.appendChild(td);
var div = guiUtils.createReactionElement({
reaction : reaction
reaction: reaction
});
div.appendChild(guiUtils.createSeparator());
......@@ -75,7 +76,7 @@ GenericSearchPanel.prototype.createReactionElement = function(reaction) {
return result;
};
GenericSearchPanel.prototype.createAliasElement = function(alias, icon) {
GenericSearchPanel.prototype.createAliasElement = function (alias, icon) {
var self = this;
var guiUtils = self.getGuiUtils();
......@@ -83,8 +84,8 @@ GenericSearchPanel.prototype.createAliasElement = function(alias, icon) {
var td = document.createElement("td");
result.appendChild(td);
var div = guiUtils.createAliasElement({
alias : alias,
icon : icon
alias: alias,
icon: icon
});
div.appendChild(guiUtils.createSeparator());
......@@ -93,18 +94,32 @@ GenericSearchPanel.prototype.createAliasElement = function(alias, icon) {
return result;
};
GenericSearchPanel.prototype.searchByQuery = function() {
GenericSearchPanel.prototype.searchByQuery = function () {
var self = this;
var query = this.getControlElement(PanelControlElementType.SEARCH_INPUT).value;
var perfect = this.getControlElement(PanelControlElementType.SEARCH_PERFECT_MATCH_CHECKBOX).checked;
return self.getOverlayDb().searchByQuery(query, perfect, true);
};
GenericSearchPanel.prototype.getAutocomplete = function(query) {
return this.getMap().getSearchAutocomplete(query);
GenericSearchPanel.prototype.getAutocomplete = function (query) {
if (this._searchAutocomplete === undefined) {
this.refreshSearchAutocomplete();
return [];
}
return this._searchAutocomplete[query];
};
GenericSearchPanel.prototype.init = function() {
GenericSearchPanel.prototype.refreshSearchAutocomplete = function () {
var self = this;
self._searchAutocomplete = [];
return ServerConnector.getSuggestedQueryList().then(function (queries) {
self._searchAutocomplete = self.computeAutocompleteDictionary(queries);
return self._searchAutocomplete;
});
};
GenericSearchPanel.prototype.init = function () {
var query = ServerConnector.getSessionData().getSearchQuery();
if (query !== undefined) {
return this.getOverlayDb().searchByEncodedQuery(query, false);
......@@ -113,4 +128,5 @@ GenericSearchPanel.prototype.init = function() {
}
};
module.exports = GenericSearchPanel;
......@@ -60,19 +60,19 @@ SearchPanel.prototype.getPanelsDefinition = function() {
return [ {
name : "GENERIC",
className : "fa-search",
panelClass : GenericSearchPanel,
panelClass : GenericSearchPanel
}, {
name : "DRUG",
className : "fa-map-marker",
panelClass : DrugPanel,
panelClass : DrugPanel
}, {
name : "CHEMICAL",
className : "fa-map-marker",
panelClass : ChemicalPanel,
panelClass : ChemicalPanel
}, {
name : "MiRNA",
className : "fa-map-marker",
panelClass : MiRnaPanel,
panelClass : MiRnaPanel
} ];
};
......@@ -148,7 +148,7 @@ SearchPanel.prototype.addTab = function(params, navElement, contentElement) {
this._panels.push(new params.panelClass({
element : contentDiv,
customMap : self.getMap(),
parent : self,
parent : self
}));
};
......
......@@ -1113,76 +1113,6 @@ CustomMap.prototype.fetchIdentifiedElements = function (elements, complete) {
};
CustomMap.prototype.computeAutocompleteDictionary = function (queries) {
var result = {};
var i, j, k, mainString, substring, list;
//compute dict for prefixes
for (i = 0; i < queries.length; i++) {
mainString = queries[i];
for (j = 0; j < mainString.length; j++) {
substring = mainString.substring(0, j + 1);
if (result[substring] !== undefined) {
continue;
}
list = [];
for (k = 0; k < 5; k++) {
if (k + i >= queries.length) {
break;
} else if (queries[k + i].toLowerCase().startsWith(substring.toLowerCase())) {
list.push(queries[k + i]);
}
}
result[substring] = list;
}
}
//and now we add substring from inside
for (i = 0; i < queries.length; i++) {
mainString = queries[i];
for (var startSubstring = 1; startSubstring < mainString.length; startSubstring++) {
for (var endSubstring = startSubstring + 1; endSubstring <= mainString.length; endSubstring++) {
substring = mainString.substring(startSubstring, endSubstring);
if (!mainString.startsWith(substring)) {
if (result[substring] === undefined) {
result[substring] = [];
}
if (result[substring].length < 5) {
var found = false;
for (k = 0; k < result[substring].length; k++) {
found |= result[substring][k] === mainString;
}
if (!found) {
result[substring].push(mainString);
}
}
}
}
}
}
return result;
};
CustomMap.prototype.refreshSearchAutocomplete = function () {
var self = this;
self._searchAutocomplete = [];
return ServerConnector.getSuggestedQueryList().then(function (queries) {
self._searchAutocomplete = self.computeAutocompleteDictionary(queries);
return self._searchAutocomplete;
});
};
CustomMap.prototype.getSearchAutocomplete = function (query) {
if (this._searchAutocomplete === undefined) {
this.refreshSearchAutocomplete();
return [];
}
return this._searchAutocomplete[query];
};
CustomMap.prototype.setSelectedPolygon = function (polygonData) {
this._selectedPolygon = polygonData;
......
"use strict";
require('../../mocha-config.js');
require('../../mocha-config');
var Annotation = require('../../../../main/js/map/data/Annotation');
var Chemical = require('../../../../main/js/map/data/Chemical');
......@@ -11,34 +11,44 @@ var chai = require('chai');
var assert = chai.assert;
var logger = require('../../logger');
describe('ChemicalPanel', function() {
describe('ChemicalPanel', function () {
function createPanel(){
var map = helper.createCustomMap();
map.getModel().setId(15781);
helper.createChemicalDbOverlay(map);
return new ChemicalPanel({
element: testDiv,
customMap: map
});
}
describe('constructor', function() {
it('default', function() {
describe('constructor', function () {
it('default', function () {
var map = helper.createCustomMap();
helper.createChemicalDbOverlay(map);
var panel = new ChemicalPanel({
element : testDiv,
customMap : map
element: testDiv,
customMap: map
});
assert.equal(logger.getWarnings().length, 0);
assert.ok(panel.isDisabled());
});
it('map with disease', function() {
return ServerConnector.getProject().then(function(project) {
it('map with disease', function () {
return ServerConnector.getProject().then(function (project) {
project.setDisease(new Annotation({
link : "http://bioportal.bioontology.org/ontologies/1351?p=terms&conceptid=D010300",
link: "http://bioportal.bioontology.org/ontologies/1351?p=terms&conceptid=D010300",
type: "MESH_2012",
resource : "D010300",
resource: "D010300",
}));
var map = helper.createCustomMap(project);
helper.createChemicalDbOverlay(map);
var panel = new ChemicalPanel({
element : testDiv,
customMap : map
element: testDiv,
customMap: map
});
assert.equal(logger.getWarnings().length, 0);
assert.notOk(panel.isDisabled());
......@@ -46,64 +56,72 @@ describe('ChemicalPanel', function() {
});
});
describe('createPreamble', function() {
it('default', function() {
describe('createPreamble', function () {
it('default', function () {
var map = helper.createCustomMap();
helper.createChemicalDbOverlay(map);
var panel = new ChemicalPanel({
element : testDiv,
customMap : map
element: testDiv,
customMap: map
});
assert.ok(panel.createPreamble().innerHTML.indexOf("NOT FOUND") > 0);
});
it('for empty', function() {
it('for empty', function () {
var map = helper.createCustomMap();
helper.createChemicalDbOverlay(map);
var panel = new ChemicalPanel({
element : testDiv,
customMap : map
element: testDiv,
customMap: map
});
assert.ok(panel.createPreamble(new Chemical()).innerHTML.indexOf("NOT FOUND") > 0);
});
});
it('on searchResults changed', function() {
it('on searchResults changed', function () {
var map = helper.createCustomMap();
map.getModel().setId(15781);
var chemicalDbOverlay = helper.createChemicalDbOverlay(map);
new ChemicalPanel({
element : testDiv,
customMap : map
element: testDiv,
customMap: map
});
return chemicalDbOverlay.searchByQuery("rotenone").then(function() {
return chemicalDbOverlay.searchByQuery("rotenone").then(function () {
assert.equal(logger.getWarnings().length, 1);
assert.ok(testDiv.innerHTML.indexOf("marker/mechanism") >= 0);
});
});
it('searchByQuery', function() {
it('searchByQuery', function () {
var map = helper.createCustomMap();
map.getModel().setId(15781);
helper.createChemicalDbOverlay(map);
var panel = new ChemicalPanel({
element : testDiv,
customMap : map
element: testDiv,
customMap: map
});
panel.getControlElement(PanelControlElementType.SEARCH_INPUT).value = "rotenone";
return panel.searchByQuery().then(function() {
return panel.searchByQuery().then(function () {
assert.equal(logger.getWarnings().length, 1);
assert.ok(testDiv.innerHTML.indexOf("Rotenone ") >= 0);
});
});
it("refreshSearchAutocomplete", function () {
var panel = createPanel();
return panel.refreshSearchAutocomplete("s").then(function (data) {
assert.ok(data);
});
});
});
......@@ -10,84 +10,94 @@ var chai = require('chai');
var assert = chai.assert;
var logger = require('../../logger');
describe('GenericSearchPanel', function() {
describe('GenericSearchPanel', function () {
var createPanel = function() {
var map = helper.createCustomMap();
map.getModel().setId(15781);
var searchDbOverlay = helper.createSearchDbOverlay(map);
it('constructor', function() {
return new GenericSearchPanel({
element: testDiv,
customMap: map
});
};
it('constructor', function () {
var map = helper.createCustomMap();
helper.createSearchDbOverlay(map);
new GenericSearchPanel({
element : testDiv,
customMap : map