Commit 8e5b88e1 authored by Piotr Gawron's avatar Piotr Gawron
Browse files

12.1.3 into master

parent 9684a25b
......@@ -278,7 +278,7 @@ test_install_debian_package:
- echo "PUT $debian_file /docker/incoming" | sftp -o StrictHostKeyChecking=no user@debian-repo
- ssh root@debian-repo /usr/local/sbin/reprepro-import
- apt-add-repository "deb http://ppa.launchpad.net/webupd8team/java/ubuntu trusty main"
- apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys EEA14886
- apt-key adv --no-tty --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys EEA14886
- apt-add-repository "deb http://debian-repo/ unstable main"
- apt-get update
# auto accept oracle license
......
minerva (12.2.0~alpha.0) stable; urgency=medium
* Bug fix: export to CellDesigner align inhibition reaction properly
* Bug fix: export/import to/from SBML handles Heterodimer Association
* Bug fix: export/import to/from SBML handles Heterodimer Association
reaction properly
* Small improvement: export to SBML includes unit factors
minerva (12.1.3) stable; urgency=medium
* Bug fix: add project button is disabled when user has no privileges
* Bug fix: projectId was missing in the edit window
* Bug fix: user could provide invalid mesh ID that caused errors when
browsing map
* Bug fix: user created from LDAP connection doesn't have a password
stored in the database
-- Piotr Gawron <piotr.gawron@uni.lu> Fri, 14 Dec 2018 14:00:00 +0200
minerva (12.1.2) stable; urgency=medium
* Bug fix: vmh annotations should be properly matching id entered in the input
* Bug fix: opacity was not working when highliting objects in plugins
* Bug fix: integer configurtion options (like SMTP_PORT) are properly
validated before saving
* Bug fix: inside of reaction box wasn't properly aligned
* Bug fix: too long data overlay name disturbed size of the map div and
interaction with the map
* Bug fix: search for some drugs resulted in internal server error
* Bug fix: LDAP login is case insensitive
* Bug fix: adding user could end up with error in the frontend
-- Piotr Gawron <piotr.gawron@uni.lu> Wed, 12 Dec 2018 16:00:00 +0200
minerva (12.1.1) stable; urgency=medium
* Bug fix: lineWidth in data overlays was ignored
......
......@@ -156,6 +156,9 @@ public class ChEMBLParser extends DrugAnnotation implements IExternalService {
throw new InvalidArgumentException(
"Target must be of a type: " + MiriamType.CHEMBL_TARGET + ". But found: " + md.getDataType());
}
if (md.getResource().isEmpty()) {
throw new InvalidArgumentException("Invalid target id" + md);
}
try {
Target target = new Target();
target.setSource(md);
......@@ -264,13 +267,13 @@ public class ChEMBLParser extends DrugAnnotation implements IExternalService {
}
/**
* REturns list of targes for given drug.
* Returns list of targets for given drug.
*
* @param drugId
* identifier of a drug for which targets are looked
* @throws DrugSearchException
* thrown when there are problems with connection to ChEMBL database
* @return list of targes for given drug
* @return list of targets for given drug
*/
List<Target> getTargetsByDrugId(MiriamData drugId) throws DrugSearchException {
if (!MiriamType.CHEMBL_COMPOUND.equals(drugId.getDataType())) {
......@@ -278,8 +281,8 @@ public class ChEMBLParser extends DrugAnnotation implements IExternalService {
}
List<Target> targets = new ArrayList<>();
try {
String page = getWebPageContent(TARGET_MECHANISM_BY_DRUG_ID_API_URL + drugId.getResource());
String url = TARGET_MECHANISM_BY_DRUG_ID_API_URL + drugId.getResource();
String page = getWebPageContent(url);
Document document = getXmlDocumentFromString(page);
Node response = getNode("response", document);
......@@ -290,14 +293,17 @@ public class ChEMBLParser extends DrugAnnotation implements IExternalService {
if (node.getNodeType() == Node.ELEMENT_NODE) {
if (node.getNodeName().equalsIgnoreCase("mechanism")) {
Node chemblTargetIdNode = getNode("target_chembl_id", node);
MiriamData targetMiriam = new MiriamData(MiriamType.CHEMBL_TARGET, chemblTargetIdNode.getTextContent());
String chemblTargetId = chemblTargetIdNode.getTextContent();
if (chemblTargetId != null && !chemblTargetId.isEmpty()) {
MiriamData targetMiriam = new MiriamData(MiriamType.CHEMBL_TARGET, chemblTargetId);
Target target = getTargetFromId(targetMiriam);
Node referenceNode = getNode("mechanism_refs", node);
Target target = getTargetFromId(targetMiriam);
Node referenceNode = getNode("mechanism_refs", node);
Set<MiriamData> references = parseReferences(referenceNode);
target.addReferences(references);
targets.add(target);
Set<MiriamData> references = parseReferences(referenceNode);
target.addReferences(references);
targets.add(target);
}
}
}
}
......@@ -327,7 +333,7 @@ public class ChEMBLParser extends DrugAnnotation implements IExternalService {
Node typeNode = super.getNode("ref_type", node);
if ("PubMed".equalsIgnoreCase(typeNode.getTextContent())) {
Node idNode = super.getNode("ref_id", node);
String id = idNode.getTextContent();
String id = idNode.getTextContent().trim();
result.add(new MiriamData(MiriamType.PUBMED, id));
}
} else {
......
......@@ -305,13 +305,8 @@ public class DrugbankHTMLParser extends DrugAnnotation implements IExternalServi
* @return list of drug {@link Target} parsed from input string
*/
List<Target> getTargetsForDrug(String page) throws DrugSearchException {
List<Target> result = new ArrayList<>();
try {
int pageStart;
Target target = new Target();
target.setType(TargetType.SINGLE_PROTEIN);
pageStart = page.indexOf("bond-list-container targets");
int pageStart = page.indexOf("bond-list-container targets");
int end = page.indexOf("bond-list-container enzymes");
if (end < 0) {
......@@ -328,28 +323,35 @@ public class DrugbankHTMLParser extends DrugAnnotation implements IExternalServi
}
}
while (pageStart > 0 && pageStart < end) {
int targetStart = page.indexOf("Details</a>", pageStart);
if (targetStart < 0) {
break;
}
int nextTargetStart = page.indexOf("Details</a>", targetStart + 1);
if (nextTargetStart < 0) {
nextTargetStart = end;
}
target = parseTarget(page.substring(targetStart, nextTargetStart));
if (target != null) {
result.add(target);
}
pageStart = nextTargetStart;
}
List<Target> result = extractTargetsFromPageContent(page, pageStart, end);
return result;
} catch (TaxonomySearchException e) {
throw new DrugSearchException("Problem with finidng information about organism", e);
} catch (UniprotSearchException e) {
throw new DrugSearchException("Problem with finidng information about protein", e);
}
}
List<Target> extractTargetsFromPageContent(String page, int pageStart, int end)
throws UniprotSearchException, TaxonomySearchException {
List<Target> result = new ArrayList<>();
while (pageStart > 0 && pageStart < end) {
int targetStart = page.indexOf("Details</a>", pageStart);
if (targetStart < 0 || targetStart > end) {
break;
}
int nextTargetStart = page.indexOf("Details</a>", targetStart + 1);
if (nextTargetStart < 0) {
nextTargetStart = end;
}
Target target = parseTarget(page.substring(targetStart, nextTargetStart));
if (target != null) {
result.add(target);
}
pageStart = nextTargetStart;
}
return result;
}
......@@ -743,7 +745,7 @@ public class DrugbankHTMLParser extends DrugAnnotation implements IExternalServi
* Sometimes when we search for targets drugbank returns something similar to
* the target and we need to filter it out (here is an example for TF targets:
* https://www.drugbank.ca/unearth/q?utf8=%E2%9C%93&searcher=targets&query=TF)
*
*
* @param page - target webpage content
* @param hgncTarget - HGNC identifier that should match the target
* @return
......
......@@ -110,6 +110,9 @@ public class MeSHParser extends CachableInterface implements IExternalService {
if (meshID == null || meshID.getResource() == null) {
throw new InvalidArgumentException("mesh cannot be null ");
}
if (meshID.getResource().indexOf(" ") >= 0) {
return null;
}
MeSH mesh = null;
// look for Mesh in the cache
......
......@@ -1102,6 +1102,19 @@ public class ChEMBLParserTest extends AnnotationTestFunctions {
}
}
@Test
public void testParseReferencesWithSpecInId() throws Exception {
try {
Node node = super.getXmlDocumentFromFile("testFiles/chembl/references_with_space.xml");
Set<MiriamData> references = chemblParser.parseReferences(node.getFirstChild());
assertEquals(1, references.size());
assertFalse(references.iterator().next().getResource().contains(" "));
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
@Test
public void testGetDrugByInvalidDrugId() throws Exception {
try {
......
......@@ -780,5 +780,22 @@ public class DrugbankHTMLParserTest extends AnnotationTestFunctions {
}
}
@Test
public void testExtractTargetsFromPageContent() throws Exception {
try {
String pageContent = " Details</a>blablablablabla Details</a>";
List<Target> result = drugBankHTMLParser.extractTargetsFromPageContent(pageContent,2,3);
assertEquals(0, result.size());
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
}
......@@ -110,6 +110,18 @@ public class MeSHParserTest extends AnnotationTestFunctions {
}
}
@Test
public void testIsValidWithSpace() throws Exception {
try {
MiriamData meshID = new MiriamData(MiriamType.MESH_2012, "some disease");
assertFalse(meshParser.isValidMeshId(meshID));
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
@Test
public void testGetInvalidMesh() throws Exception {
try {
......
......@@ -190,6 +190,21 @@ public class ReconAnnotatorTest extends AnnotationTestFunctions {
}
}
@Test
public void testAnnotatingForOxygen() throws Exception {
try {
SimpleMolecule molecule = new SimpleMolecule("id");
molecule.setName("o2");
reconAnnotator.annotateElement(molecule);
assertTrue(molecule.getMiriamData().size() > 0);
assertEquals(0, getWarnings().size());
assertEquals("O2", molecule.getFormula());
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
@Test
public void testAnnotatingWithWhitespace() throws Exception {
try {
......
<mechanism_refs>
<mechanism>
<ref_id>18540918 </ref_id>
<ref_type>PubMed</ref_type>
<ref_url>http://europepmc.org/abstract/MED/18540918</ref_url>
</mechanism>
</mechanism_refs>
\ No newline at end of file
......@@ -161,6 +161,9 @@ public class ReactionConverter extends BioEntityConverter<Reaction> {
// draw rect border
graphics.draw(rect);
// unrotate the graphics
graphics.rotate(-angle, pointX, pointY);
// draw text inside rect
Font tmpFont = graphics.getFont();
graphics.setFont(descFont);
......@@ -182,8 +185,6 @@ public class ReactionConverter extends BioEntityConverter<Reaction> {
graphics.draw(path);
}
// unrotate the graphics
graphics.rotate(-angle, pointX, pointY);
}
}
......
......@@ -2,6 +2,9 @@ package lcsb.mapviewer.converter.graphics.bioEntity.reaction;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyDouble;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
......@@ -20,9 +23,11 @@ import org.apache.commons.io.output.ByteArrayOutputStream;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InOrder;
import org.mockito.Mockito;
import lcsb.mapviewer.commands.ColorExtractor;
import lcsb.mapviewer.common.geometry.PointTransformation;
import lcsb.mapviewer.converter.graphics.ConverterParams;
import lcsb.mapviewer.converter.graphics.GraphicsTestFunctions;
import lcsb.mapviewer.model.graphics.PolylineData;
......@@ -36,6 +41,8 @@ import lcsb.mapviewer.model.map.reaction.Product;
import lcsb.mapviewer.model.map.reaction.Reactant;
import lcsb.mapviewer.model.map.reaction.Reaction;
import lcsb.mapviewer.model.map.reaction.SplitOperator;
import lcsb.mapviewer.model.map.reaction.type.KnownTransitionOmittedReaction;
import lcsb.mapviewer.model.map.reaction.type.UnknownTransitionReaction;
import lcsb.mapviewer.model.map.species.GenericProtein;
public class ReactionConverterTest extends GraphicsTestFunctions {
......@@ -391,4 +398,53 @@ public class ReactionConverterTest extends GraphicsTestFunctions {
}
}
private Reaction createUnknownTransitionReaction(double angle) {
PointTransformation pt = new PointTransformation();
Reaction result = new UnknownTransitionReaction();
Point2D center = new Point2D.Double(100, 90);
Point2D reactantCenter = new Point2D.Double(10, 90);
Point2D productCenter = new Point2D.Double(200, 90);
Point2D reactantEnd = new Point2D.Double(90, 90);
Point2D productEnd = new Point2D.Double(110, 90);
Reactant reactant = new Reactant(new GenericProtein("s2"));
reactant.setLine(
new PolylineData(pt.rotatePoint(reactantEnd, angle, center), pt.rotatePoint(reactantCenter, angle, center)));
reactant.getLine().setWidth(1.0);
Product product = new Product(new GenericProtein("s3"));
product.setLine(
new PolylineData(pt.rotatePoint(productCenter, angle, center), pt.rotatePoint(productEnd, angle, center)));
product.getLine().setWidth(1.0);
result.addProduct(product);
result.addReactant(reactant);
return result;
}
@Test
public void testDrawReactionWithRectangle() throws Exception {
try {
for (double angle = 0.0; angle <= Math.PI * 2; angle += 0.1) {
Graphics2D graphics = createGraphicsMock();
ReactionConverter rc = new ReactionConverter(colorExtractor);
Reaction reaction = createUnknownTransitionReaction(angle);
rc.draw(reaction, graphics, new ConverterParams().sbgnFormat(false));
InOrder inOrder = Mockito.inOrder(graphics);
inOrder.verify(graphics).rotate(anyDouble(), anyDouble(), anyDouble());
inOrder.verify(graphics).rotate(anyDouble(), anyDouble(), anyDouble());
inOrder.verify(graphics).drawString(any(String.class), anyInt(), anyInt());
}
} catch (Exception e) {
throw e;
}
}
}
......@@ -465,6 +465,7 @@ table.minerva-window-drug-table td {
.minerva-left-panel {
position: relative;
flex: 0 0 357px;
max-width: 357px;
border: none;
background-color: #ffffff;
height: 100%;
......
......@@ -229,7 +229,7 @@ GuiConnector.prototype.hideProcessing = function () {
/**
*
* @param {string} error
* @param {string|Error} error
* @param {boolean} [redirectIfSecurityError]
*/
GuiConnector.prototype.alert = function (error, redirectIfSecurityError) {
......
......@@ -1796,7 +1796,12 @@ ServerConnector.login = function (login, password) {
} else {
params.login = "anonymous";
}
return self.sendPostRequest(self.loginUrl(), params).then(function () {
return self.sendPostRequest(self.loginUrl(), params).then(function (content) {
var data = JSON.parse(content);
if (data["login"] !== undefined) {
params.login = data["login"];
}
console.log(params.login);
self.getSessionData().setLogin(params.login);
return Promise.resolve(self.getSessionData().getToken());
}, function (error) {
......
......@@ -254,7 +254,7 @@ AddProjectDialog.prototype.createGeneralTabContent = function () {
inputName: "project-annotate-automatically",
elements: [showAnnotatorsButton],
help: 'If this checkbox is checked, elements of the uploaded map will be automatically annotated using built in ' +
'annotators. Behavior of the annotators can be configured by clicking the Advanced button.'
'annotators. Behavior of the annotators can be configured by clicking the Advanced button.'
}));
table.appendChild(self.createCheckboxRow({
labelName: "Verify manual annotations:",
......@@ -262,36 +262,46 @@ AddProjectDialog.prototype.createGeneralTabContent = function () {
inputName: "project-verify-annotations",
elements: [showValidatorsButton],
help: 'If this checkbox is checked, elements and interactions of the uploaded map will be scanned for existing ' +
'annotations; if present these existing annotations will be validated against a set of rules. Verification rules ' +
'can be configured by clicking the Advanced button.'
'annotations; if present these existing annotations will be validated against a set of rules. Verification rules ' +
'can be configured by clicking the Advanced button.'
}));
table.appendChild(self.createCheckboxRow({
labelName: "Cache data:",
defaultValue: false,
inputName: "project-cache-data",
help: 'If this checkbox is checked, all hyperlinks in the project resolved by MIRIAM repository (e.g. cross-links ' +
'to external bioinformatics databases) are resolved and cached.'
'to external bioinformatics databases) are resolved and cached.'
}));
table.appendChild(self.createCheckboxRow({
labelName: "Auto margin:",
defaultValue: true,
inputName: "project-auto-margin",
help: 'If this checkbox is checked, upon generation of the graphics, empty spaces surrounding elements and ' +
'interactions will be cropped.'
'interactions will be cropped.'
}));
table.appendChild(self.createCheckboxRow({
labelName: "Display as SBGN:",
defaultValue: false,
inputName: "project-sbgn-visualization",
help: 'If this checkbox is checked, the uploaded model will be displayed in SBGN format, instead of default ' +
'CellDesigner format.'
'CellDesigner format.'
}));
table.appendChild(self.createCheckboxRow({
labelName: "Custom semantic zooming contains multiple overlays:",
defaultValue: false,
inputName: "project-semantic-zooming-contains-multiple-overlays"
inputName: "project-semantic-zooming-contains-multiple-overlays",
help: "Check this checkbox to display each semantic zoom level in the General Overlays"
}));
$("[name='project-semantic-zooming']", table).change(function () {
var disable = !$("[name='project-semantic-zooming']", table).is(":checked");
$("[name='project-semantic-zooming-contains-multiple-overlays']", table).prop("disabled", disable);
if (disable) {
$("[name='project-semantic-zooming-contains-multiple-overlays']", table).prop("checked", false);
}
});
$("[name='project-semantic-zooming-contains-multiple-overlays']", table).prop("disabled", !$("[name='project-semantic-zooming']", table).is(":checked"));
var saveProjectButton = Functions.createElement({
type: "button",
name: "saveProject",
......
......@@ -10,6 +10,8 @@ var CommentsTab = require('./CommentsAdminPanel');
var GuiConnector = require('../../GuiConnector');
var PrivilegeType = require('../../map/data/PrivilegeType');
var ValidationError = require("../../ValidationError");
var HttpStatus = require('http-status-codes');
var NetworkError = require('../../NetworkError');
var Functions = require('../../Functions');
// noinspection JSUnusedLocalSymbols
......@@ -256,7 +258,14 @@ EditProjectDialog.prototype.createGeneralTabContent = function () {
return self.callListeners("onSave");
}).then(function () {
return self.close();
}).catch(GuiConnector.alert);
}).catch(function (error) {
if ((error instanceof NetworkError && error.statusCode === HttpStatus.BAD_REQUEST)) {
GuiConnector.alert(error.content.reason);
} else {
GuiConnector.alert(error);
}
});
},
xss: false
});
......@@ -543,7 +552,7 @@ EditProjectDialog.prototype.refresh = function () {
var element = self.getElement();
var project = self.getProject();
$("[name='projectName']", element).val(xss(project.getName()));
$("[name='projectId']", element).val(project.getProjectId());
$("[name='projectId']", element).html(xss(project.getProjectId()));
$("[name='projectVersion']", element).val(xss(project.getVersion()));
var disease = "";
......
......@@ -693,7 +693,10 @@ EditUserDialog.prototype.getSurname = function () {
*/
EditUserDialog.prototype.close = function () {
var self = this;
$(self.getElement()).dialog("close");
if ($(self.getElement()).hasClass("ui-dialog-content")) {
//close it only if it wasn't destroyed
$(self.getElement()).dialog("close");
}
};
/**
......
......@@ -187,6 +187,12 @@ MapsAdminPanel.prototype.init = function () {
var self = this;
return self.getServerConnector().getProjects().then(function (projects) {
return self.setProjects(projects);
}).then(function () {
return self.getServerConnector().getLoggedUser();
}).then(function (user) {
var configuration = self.getConfiguration();
var canAddProject = user.hasPrivilege(configuration.getPrivilegeType(PrivilegeType.PROJECT_MANAGEMENT));
$("[name='addProject']", self.getElement()).attr("disabled", !canAddProject);
});
};
......
......@@ -69,7 +69,7 @@ UsersAdminPanel.prototype._createMenuRow = function () {
var addUserButton = Functions.createElement({
type: "button",
name: "addProject",
name: "addUser",
content: '<span class="ui-icon ui-icon-circle-plus"></span>&nbsp;ADD USER',
onclick: function () {
return self.onAddClicked().then(null, GuiConnector.alert);
......@@ -78,7 +78,7 @@ UsersAdminPanel.prototype._createMenuRow = function (