Commit 85db0ad2 authored by Piotr Gawron's avatar Piotr Gawron
Browse files

Merge branch 'release-14.0.8-issues' into 'devel_14.0.x'

bug fixes for 14.0.8

See merge request !1046
parents a4409a4c 0e377ab0
Pipeline #20433 passed with stage
in 13 minutes and 21 seconds
...@@ -318,6 +318,8 @@ build_debian_package: ...@@ -318,6 +318,8 @@ build_debian_package:
dependencies: dependencies:
- build_war - build_war
artifacts: artifacts:
# you cannot enforce never here yet
expire_in: 1000 yrs
paths: paths:
- "debian/*.deb" - "debian/*.deb"
script: script:
...@@ -432,6 +434,8 @@ build_rpm: ...@@ -432,6 +434,8 @@ build_rpm:
tags: tags:
- privileged - privileged
artifacts: artifacts:
# you cannot enforce never here yet
expire_in: 1000 yrs
paths: paths:
- "rpm/rpmbuildtemp/RPMS/noarch/minerva-*.rpm" - "rpm/rpmbuildtemp/RPMS/noarch/minerva-*.rpm"
script: script:
......
minerva (14.0.8) stable; urgency=medium
* Bug fix: API didn't allow to upload pluins with local url (#1084,
regression 14.0.7)
* Bug fix: upload of data overlay with two entries having different uniprot
identifier but pointing to the same gene crashed (#1083)
* Bug fix: don't allow to remove plugin twice (#1081)
* Bug fix: upload of SBML file with very short reaction length could produce
a map that couldn't be exported to SBML anymore (#1078)
* Bug fix: import of structural state from SBGN-ML PD that cannot be
normalized to residue is changed into structural state without losing
information (#1062)
* Bug fix: import of stimulation modifiers from SBGN-ML crashed converter
(#1063)
* Bug fix: export to SBGN files created from newt source produced corrupted
disconnected reactions
* Bug fix: upload from zip file allowed empty overlay name (#1065)
* Bug fix: genome browser slider didn't work properly in Safari on
non-default Browser zoom level (#1048)
* Bug fix: project without background overlay but with some custom data
overlays crashed with an error (#1066)
* Bug fix: when session expires updating options in configuration panel threw
unhandled error (#1068)
-- Piotr Gawron <piotr.gawron@uni.lu> Tue, 28 Jan 2020 11:00:00 +0200
minerva (14.0.7) stable; urgency=high minerva (14.0.7) stable; urgency=high
* Bug fix: API allowed to provide malformed plugin url that could be used for * Bug fix: API allowed to provide malformed plugin url that could be used for
xss attack (#1073) xss attack (#1073)
......
...@@ -10,8 +10,8 @@ import org.apache.logging.log4j.LogManager; ...@@ -10,8 +10,8 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.sbgn.*; import org.sbgn.*;
import org.sbgn.bindings.*; import org.sbgn.bindings.*;
import org.sbgn.bindings.Map;
import org.sbgn.bindings.Arc.*; import org.sbgn.bindings.Arc.*;
import org.sbgn.bindings.Map;
import lcsb.mapviewer.common.comparator.DoubleComparator; import lcsb.mapviewer.common.comparator.DoubleComparator;
import lcsb.mapviewer.common.exception.InvalidArgumentException; import lcsb.mapviewer.common.exception.InvalidArgumentException;
...@@ -192,7 +192,7 @@ public class SbgnmlXmlExporter { ...@@ -192,7 +192,7 @@ public class SbgnmlXmlExporter {
} }
} }
sourceTargetMap.put(newGlyph.getId(), newGlyph); sourceTargetMap.put(element.getElementId(), newGlyph);
return newGlyph; return newGlyph;
} }
...@@ -202,7 +202,13 @@ public class SbgnmlXmlExporter { ...@@ -202,7 +202,13 @@ public class SbgnmlXmlExporter {
glyph.setClazz(GlyphClazz.STATE_VARIABLE.getClazz()); glyph.setClazz(GlyphClazz.STATE_VARIABLE.getClazz());
Glyph.State state = new Glyph.State(); Glyph.State state = new Glyph.State();
state.setValue(structuralState); if (structuralState.indexOf("@") >= 0) {
state.setValue(structuralState.substring(0, structuralState.indexOf("@")));
state.setVariable(structuralState.substring(structuralState.indexOf("@") + 1));
} else {
state.setValue(structuralState);
}
glyph.setState(state); glyph.setState(state);
Bbox bbox = new Bbox(); Bbox bbox = new Bbox();
...@@ -838,6 +844,7 @@ public class SbgnmlXmlExporter { ...@@ -838,6 +844,7 @@ public class SbgnmlXmlExporter {
if (!node.getLine().getEndAtd().getArrowType().equals(ArrowType.NONE)) { if (!node.getLine().getEndAtd().getArrowType().equals(ArrowType.NONE)) {
arc.setTarget(sourceTargetMap.get(node.getReaction().getIdReaction())); arc.setTarget(sourceTargetMap.get(node.getReaction().getIdReaction()));
} else { } else {
boolean found = false;
for (NodeOperator operator : node.getReaction().getOperators()) { for (NodeOperator operator : node.getReaction().getOperators()) {
if (operator.getInputs().contains(node)) { if (operator.getInputs().contains(node)) {
if (!parsedOperators.contains(operator)) { if (!parsedOperators.contains(operator)) {
...@@ -845,8 +852,13 @@ public class SbgnmlXmlExporter { ...@@ -845,8 +852,13 @@ public class SbgnmlXmlExporter {
glyphList.add(newOperator); glyphList.add(newOperator);
} }
arc.setTarget(sourceTargetMap.get(operatorIds.get(operator).concat(".1"))); arc.setTarget(sourceTargetMap.get(operatorIds.get(operator).concat(".1")));
found = true;
} }
} }
if (!found) {
throw new InvalidArgumentException(
new ElementUtils().getElementTag((Modifier) node) + "Problem with exportin modifier");
}
} }
} else if (node instanceof NodeOperator) { } else if (node instanceof NodeOperator) {
if ((node instanceof DissociationOperator) || (node instanceof AssociationOperator)) { if ((node instanceof DissociationOperator) || (node instanceof AssociationOperator)) {
...@@ -867,7 +879,7 @@ public class SbgnmlXmlExporter { ...@@ -867,7 +879,7 @@ public class SbgnmlXmlExporter {
} }
} }
} }
List<Point2D> arcPoints = node.getLine().getPoints(); List<Point2D> arcPoints = node.getLine().getPoints();
Start start = new Start(); Start start = new Start();
if ((node instanceof Product) || (node instanceof NodeOperator)) { if ((node instanceof Product) || (node instanceof NodeOperator)) {
......
package lcsb.mapviewer.converter.model.sbgnml; package lcsb.mapviewer.converter.model.sbgnml;
import java.awt.*; import java.awt.Color;
import java.awt.geom.Line2D; import java.awt.geom.Line2D;
import java.awt.geom.Point2D; import java.awt.geom.Point2D;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.*;
import java.util.List;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.sbgn.*; import org.sbgn.*;
import org.sbgn.bindings.*; import org.sbgn.bindings.*;
import org.sbgn.bindings.Arc.*; import org.sbgn.bindings.Arc.*;
import org.sbgn.bindings.Map;
import lcsb.mapviewer.common.comparator.DoubleComparator; import lcsb.mapviewer.common.comparator.DoubleComparator;
import lcsb.mapviewer.common.exception.InvalidArgumentException; import lcsb.mapviewer.common.exception.InvalidArgumentException;
...@@ -390,8 +390,9 @@ public class SbgnmlXmlParser { ...@@ -390,8 +390,9 @@ public class SbgnmlXmlParser {
* arc to be parsed * arc to be parsed
* @param model * @param model
* model to be updated * model to be updated
* @throws InvalidInputDataExecption
*/ */
private void parseArc(Arc a, Model model) { private void parseArc(Arc a, Model model) throws InvalidInputDataExecption {
switch (ArcClazz.fromClazz(a.getClazz())) { switch (ArcClazz.fromClazz(a.getClazz())) {
case CONSUMPTION: case CONSUMPTION:
Port arcTargetPort = (Port) a.getTarget(); Port arcTargetPort = (Port) a.getTarget();
...@@ -422,35 +423,56 @@ public class SbgnmlXmlParser { ...@@ -422,35 +423,56 @@ public class SbgnmlXmlParser {
case MODULATION: case MODULATION:
case NECESSARY_STIMULATION: case NECESSARY_STIMULATION:
case STIMULATION: case STIMULATION:
Glyph targetGlyph = (Glyph) a.getTarget(); if (a.getTarget() instanceof Glyph) {
if (GlyphClazz.fromClazz(targetGlyph.getClazz()).equals(GlyphClazz.PHENOTYPE)) { Glyph targetGlyph = (Glyph) a.getTarget();
if (!(a.getSource() instanceof Glyph)) { if (GlyphClazz.fromClazz(targetGlyph.getClazz()).equals(GlyphClazz.PHENOTYPE)) {
logger.warn("Invalid phenotype arc: " + a.getId()); if (!(a.getSource() instanceof Glyph)) {
break; logger.warn("Invalid phenotype arc: " + a.getId());
} break;
try { }
parsePhenotypeArc(a, model);
} catch (Exception e) {
logger.warn(e.getMessage(), e);
}
break;
}
if (a.getSource() instanceof Glyph) {
Glyph sourceGlyph = (Glyph) a.getSource();
if (GlyphClazz.fromClazz(sourceGlyph.getClazz()).equals(GlyphClazz.PHENOTYPE)
&& (model.getElementByElementId(targetGlyph.getId()) instanceof Species)) {
try { try {
parsePhenotypeArc(a, model); parsePhenotypeArc(a, model);
} catch (InvalidArgumentException ex) { } catch (Exception e) {
logger.warn("The arc " + a.getId() + " of class " + a.getClazz() + " is not a valid reduced notation arc."); logger.warn(e.getMessage(), e);
} }
break; break;
} }
} if (a.getSource() instanceof Glyph) {
for (Process p : processes) { Glyph sourceGlyph = (Glyph) a.getSource();
if (p.getCentralPoint().getId().equals(targetGlyph.getId())) { if (GlyphClazz.fromClazz(sourceGlyph.getClazz()).equals(GlyphClazz.PHENOTYPE)
p.addInfluenceArc(a); && (model.getElementByElementId(targetGlyph.getId()) instanceof Species)) {
break; try {
parsePhenotypeArc(a, model);
} catch (InvalidArgumentException ex) {
logger
.warn("The arc " + a.getId() + " of class " + a.getClazz() + " is not a valid reduced notation arc.");
}
break;
}
}
for (Process p : processes) {
if (p.getCentralPoint().getId().equals(targetGlyph.getId())) {
p.addInfluenceArc(a);
break;
}
}
} else {
Port targetPort = (Port) a.getTarget();
boolean found = false;
for (Process p : processes) {
Set<String> ids = new HashSet<>();
ids.add(p.getCentralPoint().getId());
for (Port port : p.getCentralPoint().getPort()) {
ids.add(port.getId());
}
if (ids.contains(targetPort.getId())) {
p.addInfluenceArc(a);
found = true;
break;
}
}
if (!found) {
throw new InvalidInputDataExecption("Problem with processing arc: " + a.getId());
} }
} }
break; break;
...@@ -584,19 +606,42 @@ public class SbgnmlXmlParser { ...@@ -584,19 +606,42 @@ public class SbgnmlXmlParser {
for (Glyph child : children) { for (Glyph child : children) {
if (GlyphClazz.fromClazz(child.getClazz()).equals(GlyphClazz.STATE_VARIABLE)) { if (GlyphClazz.fromClazz(child.getClazz()).equals(GlyphClazz.STATE_VARIABLE)) {
if (child.getState() == null || child.getState().getVariable() != null) { if (child.getState() == null || child.getState().getVariable() != null) {
try { Residue residue = stateVariableToResidue(child);
parseStateVariable(child, newSpecies); if (residue != null) {
} catch (Exception ex) { if (newSpecies instanceof Protein) {
logger.warn(ex.getMessage()); ((Protein) newSpecies).addResidue(residue);
} else if (newSpecies instanceof Complex) {
String state = createStructuralState(child);
if (state != null) {
Complex complex = (Complex) newSpecies;
complex.setStructuralState(state);
}
} else {
logger.warn("Only macromolecule elements can have state variables.");
}
} else {
String state = createStructuralState(child);
if (state != null) {
if (newSpecies instanceof Protein) {
((Protein) newSpecies).setStructuralState(state);
} else if (newSpecies instanceof Complex) {
Complex complex = (Complex) newSpecies;
complex.setStructuralState(state);
} else {
logger.warn("Only macromolecule elements can have state.");
}
}
} }
} else { } else {
String structuralState = child.getState().getValue(); String structuralState = createStructuralState(child);
if (newSpecies instanceof Protein) { if (structuralState != null) {
Protein protein = (Protein) newSpecies; if (newSpecies instanceof Protein) {
protein.setStructuralState(structuralState); Protein protein = (Protein) newSpecies;
} else if (newSpecies instanceof Complex) { protein.setStructuralState(structuralState);
Complex complex = (Complex) newSpecies; } else if (newSpecies instanceof Complex) {
complex.setStructuralState(structuralState); Complex complex = (Complex) newSpecies;
complex.setStructuralState(structuralState);
}
} }
} }
} }
...@@ -608,6 +653,63 @@ public class SbgnmlXmlParser { ...@@ -608,6 +653,63 @@ public class SbgnmlXmlParser {
} }
/**
* Method used to parse state variable.
*
* @param glyph
* unit of information glyph from sbgn-ml file
* @throws Exception
* Exception is thrown if state variable is parsed for species other
* than Protein
*/
private Residue stateVariableToResidue(Glyph glyph) {
if (glyph.getState() != null && (glyph.getState().getVariable() == null
|| glyph.getState().getVariable().trim().isEmpty())) {
return null;
}
Residue mr = new Residue();
mr.setIdModificationResidue(glyph.getId());
if (glyph.getState() != null) {
// If State variable consists of value and variable
mr.setName(glyph.getState().getVariable());
for (ModificationState ms : ModificationState.values()) {
if (ms.getAbbreviation().equals(glyph.getState().getValue())) {
mr.setState(ms);
}
}
}
if (mr.getState() == null) {
return null;
}
// Compute the angle from coordinates and dimensions
double x = glyph.getBbox().getX() + glyph.getBbox().getW() / 2;
double y = glyph.getBbox().getY() + glyph.getBbox().getH() / 2;
mr.setPosition(new Point2D.Double(x, y));
return mr;
}
private String createStructuralState(Glyph glyph) {
if (glyph.getState() == null) {
return null;
}
String result = "";
if (glyph.getState().getValue() != null) {
result += glyph.getState().getValue();
}
if (glyph.getState().getVariable() != null && !glyph.getState().getVariable().isEmpty()) {
result += "@";
result += glyph.getState().getVariable();
}
return result;
}
/** /**
* {@link ModificationResidue} in element might have slightly off coordinates * {@link ModificationResidue} in element might have slightly off coordinates
* (due to different symbol shapes). For that we need to align them to match our * (due to different symbol shapes). For that we need to align them to match our
...@@ -751,45 +853,6 @@ public class SbgnmlXmlParser { ...@@ -751,45 +853,6 @@ public class SbgnmlXmlParser {
return rna; return rna;
} }
/**
* Method used to parse state variable.
*
* @param unitOfInformationGlyph
* unit of information glyph from sbgn-ml file
* @param species
* species that the unit of information considers
* @throws Exception
* Exception is thrown if state variable is parsed for species other
* than Protein
*/
private void parseStateVariable(Glyph unitOfInformationGlyph, Species species) {
if (!(species instanceof Protein)) {
throw new InvalidArgumentException("Only macromolecule elements can have state variables.");
}
Protein protein = (Protein) species;
Residue mr = new Residue();
mr.setSpecies(protein);
mr.setIdModificationResidue(unitOfInformationGlyph.getId());
if (unitOfInformationGlyph.getState() != null) {
// If State variable consists of value and variable
mr.setName(unitOfInformationGlyph.getState().getVariable());
for (ModificationState ms : ModificationState.values()) {
if (ms.getAbbreviation().equals(unitOfInformationGlyph.getState().getValue())) {
mr.setState(ms);
}
}
}
// Compute the angle from coordinates and dimensions
double x = unitOfInformationGlyph.getBbox().getX() + unitOfInformationGlyph.getBbox().getW() / 2;
double y = unitOfInformationGlyph.getBbox().getY() + unitOfInformationGlyph.getBbox().getH() / 2;
mr.setPosition(new Point2D.Double(x, y));
protein.addResidue(mr);
}
/** /**
* Method used for parsing units of information. * Method used for parsing units of information.
* *
...@@ -1144,6 +1207,7 @@ public class SbgnmlXmlParser { ...@@ -1144,6 +1207,7 @@ public class SbgnmlXmlParser {
throw new InvalidArgumentException( throw new InvalidArgumentException(
p.getCentralPoint().getId() + ": The process must have at least one outgoing arc."); p.getCentralPoint().getId() + ": The process must have at least one outgoing arc.");
} }
p.setProductsPort((Port) p.getProductArcs().get(0).getSource()); p.setProductsPort((Port) p.getProductArcs().get(0).getSource());
for (Arc productArc : p.getProductArcs()) { for (Arc productArc : p.getProductArcs()) {
if (!((Port) productArc.getSource()).equals(p.getProductsPort())) { if (!((Port) productArc.getSource()).equals(p.getProductsPort())) {
......
...@@ -3,6 +3,7 @@ package lcsb.mapviewer.converter.model.sbgnml; ...@@ -3,6 +3,7 @@ package lcsb.mapviewer.converter.model.sbgnml;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import java.awt.geom.Point2D; import java.awt.geom.Point2D;
import java.io.InputStream;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
...@@ -105,4 +106,45 @@ public class SbgnmlXmlParserTest2 extends SbgnmlTestFunctions { ...@@ -105,4 +106,45 @@ public class SbgnmlXmlParserTest2 extends SbgnmlTestFunctions {
assertEquals(p2.getLine().getBeginPoint(), outputOperator.getLine().getBeginPoint()); assertEquals(p2.getLine().getBeginPoint(), outputOperator.getLine().getBeginPoint());
} }
@Test
public void testImportExportStateWithVariable() throws Exception {
Converter converter = new SbgnmlXmlConverter();
Model model = converter.createModel(
new ConverterParams().filename("testFiles/sbgnmlParserTestFiles/sbgnmlFiles/state_with_variable.sbgn"));
String sbgn = converter.model2String(model);
assertTrue("State variable is missing", sbgn.contains("state variable"));
assertTrue("State variable doesn't contain variable", sbgn.contains("variable=\"g\""));
}
@Test
public void testStimulationModification() throws Exception {
Converter converter = new SbgnmlXmlConverter();
Model model = converter.createModel(
new ConverterParams().filename("testFiles/sbgnmlParserTestFiles/sbgnmlFiles/stimulation_modification.sbgn"));
for (Reaction r : model.getReactions()) {
assertEquals(1, r.getModifiers().size());
}
}
@Test
public void testExportStimulationModification() throws Exception {
Converter converter = new SbgnmlXmlConverter();
Model model = converter.createModel(
new ConverterParams().filename("testFiles/sbgnmlParserTestFiles/sbgnmlFiles/stimulation_modification.sbgn"));
InputStream is = converter.model2InputStream(model);
Model model2 = converter.createModel(new ConverterParams().inputStream(is));
for (Reaction r : model2.getReactions()) {
assertEquals(1, r.getModifiers().size());
}
}
} }
<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
<sbgn xmlns="http://sbgn.org/libsbgn/0.2">
<map language="process description">
<glyph id="nwtN_a529363c-d32c-4b03-bce7-1b79e6066b2d" class="complex">
<label text="complex"/>
<bbox x="1001.671875" y="148.375" w="63.25" h="33.25"/>
<glyph id="nwtSV_439e2238-3d15-46da-aa67-77a1119a3552" class="state variable">
<state value="xxx" variable="g"/>
<bbox x="1049.1945435615057" y="142.375" w="22.39697265625" h="12"/>
</glyph>
<glyph id="nwtN_d5109825-20cd-40f4-a9ba-1fd707c6b2a1" class="macromolecule">
<label text="macromolecule"/>
<bbox x="1018.296875" y="165" w="60" h="30"/>
</glyph>
</glyph>
</map>
</sbgn>
\ No newline at end of file
<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
<sbgn xmlns="http://sbgn.org/libsbgn/0.2">