diff --git a/CHANGELOG b/CHANGELOG index 7a2e1be8e6452293b915ef2e9d543831d5e2db57..340eaf27502bae64aa903223a4c397cc16d0885a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,28 +1,66 @@ minerva (14.0.0~alpha.0) unstable; urgency=low * Feature: log4j is replaced with log4j2 logging mechanism (#291) - * Feature: database installed via debian package is done via dbconfig-commons + * Feature: database installed via debian package is done via dbconfig-commons (#469) - * Feature: Replaced connection pool manager C3P0 with better maintained + * Feature: Replaced connection pool manager C3P0 with better maintained Hikari (#564) * Feature removal: BioCompendium annotator removed (#32) - * Bug fix: export to CellDesigner of reaction with two modifiers connected - with boolean operator resulted was skipping some layout information - * Bug fix: reaction in SBGNML file containing two products was improperly + * Bug fix: export to CellDesigner of reaction with two modifiers connected + with boolean operator resulted was skipping some layout information + * Bug fix: reaction in SBGNML file containing two products was improperly drawn * Bug fix: color handling in CellDesigner did not work properly for modifiers - inside boolean gateway - * Bug fix: CellDesigner file exported from minerva was not fully compliant + inside boolean gateway + * Bug fix: CellDesigner file exported from minerva was not fully compliant with SBML standard (#831) -- Sascha Herzinger Wed, 22 May 2019 10:30:00 +0200 -minerva (13.1.0~alpha.0) unstable; urgency=low +minerva (13.1.0~beta.1) unstable; urgency=low + * Small improvement: model_name renamed to map_name in data overlay columns + (#827) + * Small improvement: name, type, reference_genome_type and + reference_genome_version column in genetic variant data overlay are + deprecated (#827) + * Bug fix: deprecated columns in data overlay are not visible to the end user + (#827) + * Bug fix: invalid color in data overlay provides proper feedback to the user + (#822) + * Bug fix: genetic variant overlay without name caused problems (#832) + * Bug fix: tair locus identifiers were used improperly - instead of id the + name was used + * Bug fix: plugin tab header wasn't properly resized after adding plugins + that introduced second line for tab selection (#758) + * Bug fix: invisible layer shouldn't be shown in the on th map (#813) + * Bug fix: list of availbale annotators is sorted alphabetically (#815) + * Bug fix: redirect url from export panel is fixed (#819) + * Bug fix: allow to reupload the same file without closing add overlay dialog + (#833) + * Bug fix: gene_name column is allowed only in the genetic variant data + overlay + * Bug fix: protein types are sorted properly in "Select valid annotations" + dialog (#815) + * Bug fix: if there is a description of (sub)map then it is available in + info/submap panel (#824) + * Bug fix: when changing data in edit user dialog there was a need to click + close button twice (#818) + * Bug fix: empty type for data overlay is allowed (#827) + * Bug fix: genetic variants data overlay was ignoring color parameter (#827) + * Bug fix: pathways can be drawn using glyphs (#825) + * Bug fix: empty background overlay doesn't show glyphs, instead standard + visualization is used (#826) + * Bug fix: asynchronous cals on showing/hiding data overlays might cause + problems due to network latency (#830) + + -- Piotr Gawron Fri, 7 Jun 2019 17:00:00 +0200 + +minerva (13.1.0~beta.0) unstable; urgency=low * Feature: annotators are more flexible - you can define set of input and outputs used by annotator (#617) * Feature: changes in admin panel doesn't require saving - they are saved automatically (#676) * Feature: elements can have custom glyphs used for visualization (#487) - * Small improvement: all bio entities have z-index associated with them + * Small improvement: all bio entities have z-index associated with them (#487) * Small improvement: validation of the organism and disease id on map upload added (#618) @@ -32,7 +70,7 @@ minerva (13.1.0~alpha.0) unstable; urgency=low comment dialog (#680) * Small improvement: Left logo is configurable (#731) * Small improvement: list of element types in choose annotator dialog is - sorted (#758) + sorted (#798) * Small improvement: Plugin API provides list of overview images (#702) * Small improvement: Plugin API allows to show/hide overview images (#702) * Small improvement: Plugin API allows to trigger search on the map (#702) @@ -69,7 +107,7 @@ minerva (13.1.0~alpha.0) unstable; urgency=low * Small improvement: allow admin to disable CORS check (#802) * Small improvement: TransparencyZoomLevelVisibility parameter renamed to SemanticZoomLevelTransparency (#801) - * Small improvement: export/import from SBML support z-index in LAYOUT + * Small improvement: export/import from SBML support z-index in LAYOUT extension * Bug fix: progress bar of gene genome mapping upload is refreshing properly (#728) @@ -85,11 +123,11 @@ minerva (13.1.0~alpha.0) unstable; urgency=low having the aa change information * Bug fix: plugin contect element width is adjusted when link to tabs are wrapped in more than one line (#758) - * Bug fix: export to CellDesigner preserve font size (#803) - * Bug fix: layout data was ignored for some reactions when improting from + * Bug fix: export to CellDesigner preserve font size (#798) + * Bug fix: layout data was ignored for some reactions when importing from SBML (#812) - -- Piotr Gawron Tue, 7 May 2019 15:00:00 +0200 + -- Piotr Gawron Mon, 13 May 2019 19:00:00 +0200 minerva (13.0.0) stable; urgency=medium * Bug fix: Since Oracle Java cannot be installed as debian dependency we use diff --git a/annotation/src/main/java/lcsb/mapviewer/annotation/services/ChemicalParser.java b/annotation/src/main/java/lcsb/mapviewer/annotation/services/ChemicalParser.java index 014ea5719388f497de52dd87301540f39827fd61..e6c76cf0d37b877f65516e06f27f11a7ab35f897 100644 --- a/annotation/src/main/java/lcsb/mapviewer/annotation/services/ChemicalParser.java +++ b/annotation/src/main/java/lcsb/mapviewer/annotation/services/ChemicalParser.java @@ -70,7 +70,7 @@ public class ChemicalParser extends CachableInterface implements IExternalServic /** * Home page of ctd database. */ - static final String URL = "http://ctdbase.org/"; + static final String URL = "https://ctdbase.org/"; /** * URL to get a list of chemicals by disease id. diff --git a/annotation/src/main/java/lcsb/mapviewer/annotation/services/annotators/KeggAnnotator.java b/annotation/src/main/java/lcsb/mapviewer/annotation/services/annotators/KeggAnnotator.java index f1c2e73927928f6b017092b19e9fd5279ea45886..3d53b59497dfeb5e2e034cebdfb333a832381721 100644 --- a/annotation/src/main/java/lcsb/mapviewer/annotation/services/annotators/KeggAnnotator.java +++ b/annotation/src/main/java/lcsb/mapviewer/annotation/services/annotators/KeggAnnotator.java @@ -152,6 +152,9 @@ public class KeggAnnotator extends ElementAnnotator implements IExternalService logger.warn("Cannot find kegg data for id: " + identifier); } catch (IOException exception) { throw new AnnotatorException(exception); + } catch (UniprotSearchException e) { + logger.warn(e, e); + return false; } } object.addMiriamData(annotations); @@ -181,8 +184,9 @@ public class KeggAnnotator extends ElementAnnotator implements IExternalService * organisms names. If the value has not been set by the user, null * will be passed. * @return {@link MiriamType#PUBMED}s found on the page + * @throws UniprotSearchException */ - private Collection parseKegg(String pageContent, AnnotatorData params) { + private Collection parseKegg(String pageContent, AnnotatorData params) throws UniprotSearchException { // Retrieve Pubmeds Collection result = new HashSet<>(); @@ -201,10 +205,13 @@ public class KeggAnnotator extends ElementAnnotator implements IExternalService } else { m = athOrthologMatcher.matcher(pageContent); if (m.find()) { - String[] tairCodes = m.group(1).trim().split(" "); - for (String tairCode : tairCodes) { - tairCode = tairCode.split("\\(")[0]; // some codes are in the form AT1G08510(FATB) - result.add(new MiriamData(MiriamType.TAIR_LOCUS, tairCode)); + String[] tairLocusNames = m.group(1).trim().split(" "); + for (String tairLocusName : tairLocusNames) { + tairLocusName = tairLocusName.split("\\(")[0]; // some codes are in the form AT1G08510(FATB) + MiriamData md = uniprotAnnotator.uniprotTairLocusNameToId(tairLocusName); + if (!md.equals(new MiriamData())){ + result.add(md); + } } } } diff --git a/annotation/src/main/java/lcsb/mapviewer/annotation/services/annotators/StringAnnotator.java b/annotation/src/main/java/lcsb/mapviewer/annotation/services/annotators/StringAnnotator.java index 7fd79cc71b873ae13497ceecced8c76cba69c786..f32e7904ef57ea7659b6c3299caa90bfd21edc91 100644 --- a/annotation/src/main/java/lcsb/mapviewer/annotation/services/annotators/StringAnnotator.java +++ b/annotation/src/main/java/lcsb/mapviewer/annotation/services/annotators/StringAnnotator.java @@ -32,6 +32,12 @@ public class StringAnnotator extends ElementAnnotator implements IExternalServic /** * Service used for annotation of entities using {@link MiriamType#TAIR_LOCUS * TAIR}. + * Note that STRING annotation process will annotate only + * records which have a TAIR ID assigned by a human annotator. + * Otherwise, it would generate UniProt miriam records also for + * TAIR IDs generated from, e.g., KEGG annotator, i.e. for homologues + * and these UniProt IDs would be indistinguishable from the + * UniProt IDs describing the molecule. */ private TairAnnotator tairAnnotator; @@ -54,7 +60,9 @@ public class StringAnnotator extends ElementAnnotator implements IExternalServic throws AnnotatorException { List mdUniprots = new ArrayList<>(); if (identifier.getDataType().equals(MiriamType.TAIR_LOCUS)) { - mdUniprots.addAll(tairAnnotator.tairToUniprot(identifier)); + if (identifier.getAnnotator() == null) { + mdUniprots.addAll(tairAnnotator.tairToUniprot(identifier)); + } } else if (identifier.getDataType().equals(MiriamType.UNIPROT)) { mdUniprots.add(identifier); } else { diff --git a/annotation/src/main/java/lcsb/mapviewer/annotation/services/annotators/TairAnnotator.java b/annotation/src/main/java/lcsb/mapviewer/annotation/services/annotators/TairAnnotator.java index 3bcaeca897bc38c42d20ecb6b427a2c06dda8c65..cf28f3f73446b0f7e0c265106badfedc05f48433 100644 --- a/annotation/src/main/java/lcsb/mapviewer/annotation/services/annotators/TairAnnotator.java +++ b/annotation/src/main/java/lcsb/mapviewer/annotation/services/annotators/TairAnnotator.java @@ -5,6 +5,8 @@ import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.apache.logging.log4j.*; import org.springframework.stereotype.Service; @@ -28,7 +30,14 @@ import lcsb.mapviewer.model.user.annotator.AnnotatorOutputParameter; /** * This is a class that implements a backend to TAIR. - * + * Note that TAIR annotation process will annotate only + * records which have a TAIR ID assigned by a human annotator. + * Otherwise, it would generate UniProt miriam records also for + * TAIR IDs generated from, e.g., KEGG annotator, i.e. for homologues + * and these UniProt IDs would be indistinguishable from the + * UniProt IDs describing the molecule. + * + * * @author David Hoksza * */ @@ -40,6 +49,14 @@ public class TairAnnotator extends ElementAnnotator implements IExternalService */ private static Logger logger = LogManager.getLogger(TairAnnotator.class); + /** + * Pattern used for getting Tair Locus ID symbol from UniProt result page. + */ + private Pattern getUniprotIdParsePattern(String tairId) { + return Pattern.compile("(\\w*)\\tlocus:" + tairId); + } + + /** * Default constructor. */ @@ -74,7 +91,13 @@ public class TairAnnotator extends ElementAnnotator implements IExternalService @Override public boolean annotateElement(BioEntityProxy object, MiriamData identifier, AnnotatorData parameters) throws AnnotatorException { + + if (identifier.getAnnotator() != null) { + return false; + } if (identifier.getDataType().equals(MiriamType.TAIR_LOCUS)) { + //UniProt are only obained from TAIR's which were provided by the annotator (otherwise we would get + //also UniProt IDs for, e.g., homologous genes' TAIR IDs obtained from KEGG Collection collection = tairToUniprot(identifier); if (collection.size() > 0) { object.addMiriamData(collection); @@ -97,8 +120,8 @@ public class TairAnnotator extends ElementAnnotator implements IExternalService * @return URL to UniProt result page with the TAIR mapping */ private String getUniProtUrl(String tairId) { - return "https://www.uniprot.org/uniprot/?query=database%3A%28type%3Atair%29+database%3A%28type%3Atair+" + tairId - + "%29&format=list&columns=id"; + return "https://www.uniprot.org/uniprot/?query=database%3A%28type%3Atair%29+" + tairId + "&format=tab&columns=id,database(tair)"; +// return "https://www.uniprot.org/uniprot/?query=database%3A%28type%3Atair+"+tairId+"%29&format=list&columns=id"; } /** @@ -110,11 +133,14 @@ public class TairAnnotator extends ElementAnnotator implements IExternalService * uniprot REST API result page * @return uniprot identifier found on the page */ - private Collection parseUniprotUniprot(String pageContent) { + private Collection parseUniprotUniprot(String pageContent, String tairId) { Collection result = new HashSet(); if (!pageContent.isEmpty()) { - String[] sPageContent = pageContent.split("\\R"); - result.add(new MiriamData(MiriamType.UNIPROT, sPageContent[0])); + //the query returns a list of possible matches which needs to be pruned + Matcher m = getUniprotIdParsePattern(tairId).matcher(pageContent); + if (m.find()) { + result.add(new MiriamData(MiriamType.UNIPROT, m.group(1))); + } } return result; } @@ -144,7 +170,7 @@ public class TairAnnotator extends ElementAnnotator implements IExternalService try { String accessUrl = getUniProtUrl(tair.getResource()); String pageContent = getWebPageContent(accessUrl); - return parseUniprotUniprot(pageContent); + return parseUniprotUniprot(pageContent, tair.getResource()); } catch (WrongResponseCodeIOException exception) { logger.warn("Wrong reponse code when accessing tair data with id: " + tair.getResource()); return null; @@ -185,7 +211,7 @@ public class TairAnnotator extends ElementAnnotator implements IExternalService @Override public MiriamData getExampleValidAnnotation() { - return new MiriamData(MiriamType.TAIR_LOCUS, "AT1G01030"); + return new MiriamData(MiriamType.TAIR_LOCUS, "2200950"); } } diff --git a/annotation/src/main/java/lcsb/mapviewer/annotation/services/annotators/UniprotAnnotator.java b/annotation/src/main/java/lcsb/mapviewer/annotation/services/annotators/UniprotAnnotator.java index f44e4029821ee5c146f6ff6548c581545c10f27c..58aa9b9366f24515f3854be711261973f80b6264 100644 --- a/annotation/src/main/java/lcsb/mapviewer/annotation/services/annotators/UniprotAnnotator.java +++ b/annotation/src/main/java/lcsb/mapviewer/annotation/services/annotators/UniprotAnnotator.java @@ -59,6 +59,12 @@ public class UniprotAnnotator extends ElementAnnotator implements IExternalServi */ private Pattern uniprotToEC = Pattern .compile("EC=((\\d+\\.-\\.-\\.-)|(\\d+\\.\\d+\\.-\\.-)|(\\d+\\.\\d+\\.\\d+\\.-)|(\\d+\\.\\d+\\.\\d+\\.\\d+))"); + + /** + * Pattern used for getting Tair Locus ID symbol from UniProt result page. + */ + private Pattern uniprotTairLocusToId = Pattern + .compile("locus:(\\d*)"); /** * Default constructor. @@ -136,6 +142,18 @@ public class UniprotAnnotator extends ElementAnnotator implements IExternalServi private String getUniprotUrl(String uniprotId) { return "https://www.uniprot.org/uniprot/" + uniprotId + ".txt"; } + + /** + * Returns URL to UniProt result containing mapped UniProtIds for submitted TAIR + * entry. + * + * @param tairId + * TAIR identifier + * @return URL to UniProt result page with the TAIR mapping + */ + private String getUniProtTairLocus2IdUrl(String tairLocusName) { + return "https://www.uniprot.org/uniprot/?query=database%3A%28type%3Atair+" + tairLocusName + "%29&format=tab&columns=id,database(tair)"; + } /** * Parse uniprot webpage to find information about {@link MiriamType#ENTREZ} and @@ -250,7 +268,34 @@ public class UniprotAnnotator extends ElementAnnotator implements IExternalServi } } - + + /** + * Transform TAIR Locus name into TAIR Locus identifier. + * UniProt is used for this task because TAIR i) does not have + * an API and ii) restricts the number of accesses. + * + * @param tairLocus + * String with the TAIR Locus name. + * @return {@link MiriamData} with TAIR Locus ID + * @throws UniprotSearchException + * thrown when there is a problem with accessing external database + */ + public MiriamData uniprotTairLocusNameToId(String tairLocus) throws UniprotSearchException { + String accessUrl = getUniProtTairLocus2IdUrl(tairLocus); + try { + String pageContent = getWebPageContent(accessUrl); + Matcher m = uniprotTairLocusToId.matcher(pageContent); + if (m.find()) { + return new MiriamData(MiriamType.TAIR_LOCUS, m.group(1)); + } else { + logger.warn("No TAIR ID found for locus: " + tairLocus); + return new MiriamData(); + } + } catch (IOException e) { + throw new UniprotSearchException("Problem with accessing uniprot webpage", e); + } + } + @Override public String getCommonName() { return MiriamType.UNIPROT.getCommonName(); diff --git a/annotation/src/test/java/lcsb/mapviewer/annotation/services/annotators/KeggAnnotatorTest.java b/annotation/src/test/java/lcsb/mapviewer/annotation/services/annotators/KeggAnnotatorTest.java index 6a44ecd37d94aff66dbaa5497fbeafa07bdfe620..7db8c0f41618fa6a71d99deabf967b901d8ffc17 100644 --- a/annotation/src/test/java/lcsb/mapviewer/annotation/services/annotators/KeggAnnotatorTest.java +++ b/annotation/src/test/java/lcsb/mapviewer/annotation/services/annotators/KeggAnnotatorTest.java @@ -189,7 +189,7 @@ public class KeggAnnotatorTest extends AnnotationTestFunctions { try { Species protein = new GenericProtein("id"); protein.setName("bla"); - protein.addMiriamData(new MiriamData(MiriamType.TAIR_LOCUS, "AT3G25110")); + protein.addMiriamData(new MiriamData(MiriamType.TAIR_LOCUS, "2090285")); //TAIR locus AT3G25110 keggAnnotator.annotateElement(protein); diff --git a/annotation/src/test/java/lcsb/mapviewer/annotation/services/annotators/StringAnnotatorTest.java b/annotation/src/test/java/lcsb/mapviewer/annotation/services/annotators/StringAnnotatorTest.java index 4c1f91dd1fc7a3938e5cf8299be28f4d200095ee..2c9b90664fb837d23fc1d94df617baee44039810 100644 --- a/annotation/src/test/java/lcsb/mapviewer/annotation/services/annotators/StringAnnotatorTest.java +++ b/annotation/src/test/java/lcsb/mapviewer/annotation/services/annotators/StringAnnotatorTest.java @@ -57,12 +57,12 @@ public class StringAnnotatorTest extends AnnotationTestFunctions { } @Test - @Ignore("TAIR DB restricts queries by IP") +// @Ignore("TAIR DB restricts queries by IP") public void testAnnotateTair() throws Exception { try { Species bioEntity = new GenericProtein("id"); - bioEntity.addMiriamData(new MiriamData(MiriamType.TAIR_LOCUS, "AT1G01030")); + bioEntity.addMiriamData(new MiriamData(MiriamType.TAIR_LOCUS, "2200950")); testedAnnotator.annotateElement(bioEntity); @@ -70,18 +70,35 @@ public class StringAnnotatorTest extends AnnotationTestFunctions { for (MiriamData md : bioEntity.getMiriamData()) { if (md.getDataType().equals(MiriamType.STRING)) { - mdString = md; // there should be only one EC number for that TAIR<->UNIPROT record + mdString = md; } } assertTrue("No STRING annotation extracted from STRING annotator", mdString != null); - assertTrue("Wrong number of annotations extract from STRING annotator", bioEntity.getMiriamData().size() == 3); + assertTrue("Wrong number of annotations extract from STRING annotator", bioEntity.getMiriamData().size() == 2); } catch (Exception e) { e.printStackTrace(); throw e; } } + + @Test +//@Ignore("TAIR DB restricts queries by IP") +public void testAnnotateTairOnlyFromHumanAnnotator() throws Exception { + try { + + Species bioEntity = new GenericProtein("id"); + bioEntity.addMiriamData(new MiriamData(MiriamType.TAIR_LOCUS, "2200950", KeggAnnotator.class)); + + testedAnnotator.annotateElement(bioEntity); + + assertTrue(bioEntity.getMiriamData().size() == 1); + } catch (Exception e) { + e.printStackTrace(); + throw e; + } +} @Test public void testAnnotateInvalidEmpty() throws Exception { diff --git a/annotation/src/test/java/lcsb/mapviewer/annotation/services/annotators/TairAnnotatorTest.java b/annotation/src/test/java/lcsb/mapviewer/annotation/services/annotators/TairAnnotatorTest.java index 75d58d8fd13c14e6ba6407ddfb010a2a9ab7d9b7..8c2b7fb4b1607dd9d87d15adaa4ad2a7c5f93219 100644 --- a/annotation/src/test/java/lcsb/mapviewer/annotation/services/annotators/TairAnnotatorTest.java +++ b/annotation/src/test/java/lcsb/mapviewer/annotation/services/annotators/TairAnnotatorTest.java @@ -1,6 +1,7 @@ package lcsb.mapviewer.annotation.services.annotators; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -45,7 +46,7 @@ public class TairAnnotatorTest extends AnnotationTestFunctions { Species protein = new GenericProtein("id"); protein.setName("bla"); - protein.addMiriamData(new MiriamData(MiriamType.TAIR_LOCUS, "AT1G01030")); + protein.addMiriamData(new MiriamData(MiriamType.TAIR_LOCUS, "2200950")); //AT1G01030 tairAnnotator.annotateElement(protein); @@ -74,7 +75,7 @@ public class TairAnnotatorTest extends AnnotationTestFunctions { Species protein = new GenericProtein("id"); protein.setName("bla"); - protein.addMiriamData(new MiriamData(MiriamType.TAIR_LOCUS, "AT1G15950")); + protein.addMiriamData(new MiriamData(MiriamType.TAIR_LOCUS, "2200427")); protein.addMiriamData(new MiriamData(MiriamType.UNIPROT, "P32246")); // Human version of the protein tairAnnotator.annotateElement(protein); @@ -156,13 +157,29 @@ public class TairAnnotatorTest extends AnnotationTestFunctions { // @Ignore("TAIR DB restricts queries by IP") public void testTairToUniprot() throws Exception { try { - assertTrue(tairAnnotator.tairToUniprot(new MiriamData(MiriamType.TAIR_LOCUS, "AT1G01030")) + assertTrue(tairAnnotator.tairToUniprot(new MiriamData(MiriamType.TAIR_LOCUS, "2200950")) // TAIR locus AT1G01030 .contains(new MiriamData(MiriamType.UNIPROT, "Q9MAN1"))); } catch (Exception e) { e.printStackTrace(); throw e; } } + + @Test + public void testTairToUniprotFromKEGG() throws Exception { + //TAIR Loci comming from annotators should be ignored by TAIR (only TAIR LOCI provided by the human annotator should be considered) + try { + Species protein = new GenericProtein("id"); + protein.setName("bla"); + protein.addMiriamData(new MiriamData(MiriamType.TAIR_LOCUS, "2200427", KeggAnnotator.class)); + tairAnnotator.annotateElement(protein); + assertTrue( protein.getMiriamData().size() == 1); + } catch (Exception e) { + e.printStackTrace(); + throw e; + } + } + @Test // @Ignore("TAIR DB restricts queries by IP") diff --git a/annotation/src/test/java/lcsb/mapviewer/annotation/services/annotators/UniprotAnnotatorTest.java b/annotation/src/test/java/lcsb/mapviewer/annotation/services/annotators/UniprotAnnotatorTest.java index 04e04bd7de640567a3d4b59661d81559de13c997..aaa118d44c5e90d99be8d5e35b6290c479c14f2a 100644 --- a/annotation/src/test/java/lcsb/mapviewer/annotation/services/annotators/UniprotAnnotatorTest.java +++ b/annotation/src/test/java/lcsb/mapviewer/annotation/services/annotators/UniprotAnnotatorTest.java @@ -278,6 +278,30 @@ public class UniprotAnnotatorTest extends AnnotationTestFunctions { throw e; } } + + @Test + public void testUniprotTairLocusToId() throws Exception { + try { + assertEquals(new MiriamData(MiriamType.TAIR_LOCUS, "2201786"), + uniprotAnnotator.uniprotTairLocusNameToId("AT1G08510") ); + } catch (Exception e) { + e.printStackTrace(); + throw e; + } + } + + @Test + public void testUniprotWrongTairLocusToId() throws Exception { + try { + assertEquals(new MiriamData(), + uniprotAnnotator.uniprotTairLocusNameToId("bla")); + } catch (Exception e) { + e.printStackTrace(); + throw e; + } + } + + @Test public void testStatus() throws Exception { diff --git a/commons/src/main/java/lcsb/mapviewer/common/geometry/ColorParser.java b/commons/src/main/java/lcsb/mapviewer/common/geometry/ColorParser.java index 2af7761b62851fc049fcee5ef9c8f71dbdff5cad..d9bc3ab0bb8014b74686b9dd5826a12facd31a16 100644 --- a/commons/src/main/java/lcsb/mapviewer/common/geometry/ColorParser.java +++ b/commons/src/main/java/lcsb/mapviewer/common/geometry/ColorParser.java @@ -47,6 +47,10 @@ public class ColorParser { * @return {@link Color} obtained from input text */ public Color parse(String string) { + if (string==null || string.isEmpty()) { + throw new InvalidArgumentException( + "Invalid color value: " + string + ". Correct format: #xxxxxx (where x is a hex value)"); + } if (string.charAt(0) != '#') { string = "#" + string; } diff --git a/commons/src/test/java/lcsb/mapviewer/common/geometry/ColorParserTest.java b/commons/src/test/java/lcsb/mapviewer/common/geometry/ColorParserTest.java index 710cfa15116f68a3a2373e47e387a702a6b41dd3..4b927108fbb9285709a30cc13d0643cd5bd45be5 100644 --- a/commons/src/test/java/lcsb/mapviewer/common/geometry/ColorParserTest.java +++ b/commons/src/test/java/lcsb/mapviewer/common/geometry/ColorParserTest.java @@ -64,6 +64,18 @@ public class ColorParserTest { } } + @Test(expected=InvalidArgumentException.class) + public void testParseNull() throws Exception { + ColorParser parser = new ColorParser(); + parser.parse(null); + } + + @Test(expected=InvalidArgumentException.class) + public void testParseEmpty() throws Exception { + ColorParser parser = new ColorParser(); + parser.parse(""); + } + @Test public void testSetColorToHtmlString() throws Exception { try { diff --git a/converter-graphics/src/main/java/lcsb/mapviewer/converter/graphics/AbstractImageGenerator.java b/converter-graphics/src/main/java/lcsb/mapviewer/converter/graphics/AbstractImageGenerator.java index 082553eaec8d08e7d5ff738d8c5693dd0ccc6018..0bfed2214fc27bb09dcedd73aefd0349bceeb871 100644 --- a/converter-graphics/src/main/java/lcsb/mapviewer/converter/graphics/AbstractImageGenerator.java +++ b/converter-graphics/src/main/java/lcsb/mapviewer/converter/graphics/AbstractImageGenerator.java @@ -706,7 +706,9 @@ public abstract class AbstractImageGenerator { List bioEntities = new ArrayList<>(); bioEntities.addAll(params.getModel().getBioEntities()); for (Layer layer : params.getModel().getLayers()) { - bioEntities.addAll(layer.getDrawables()); + if (layer.isVisible()) { + bioEntities.addAll(layer.getDrawables()); + } } bioEntities.sort(BioEntity.Z_INDEX_COMPARATOR); diff --git a/converter-graphics/src/test/java/lcsb/mapviewer/converter/graphics/AbstractImageGeneratorTest.java b/converter-graphics/src/test/java/lcsb/mapviewer/converter/graphics/AbstractImageGeneratorTest.java index 4fc57b5b08a0f9052f3a2e16c25c266e613b6b47..18164cfa01a730176b3784eddb97e58b3ec616f9 100644 --- a/converter-graphics/src/test/java/lcsb/mapviewer/converter/graphics/AbstractImageGeneratorTest.java +++ b/converter-graphics/src/test/java/lcsb/mapviewer/converter/graphics/AbstractImageGeneratorTest.java @@ -13,6 +13,8 @@ import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; +import lcsb.mapviewer.model.map.layout.graphics.Layer; +import lcsb.mapviewer.model.map.layout.graphics.LayerRect; import lcsb.mapviewer.model.map.model.Model; import lcsb.mapviewer.model.map.model.ModelFullIndexed; import lcsb.mapviewer.model.map.reaction.Reaction; @@ -68,6 +70,33 @@ public class AbstractImageGeneratorTest extends GraphicsTestFunctions { } } + @Test + public void testDrawMapWithInvisibleLayerNesting() throws Exception { + try { + Graphics2D graphics = createGraphicsMock(); + + Model model = createEmptyModel(); + Layer layer = new Layer(); + layer.setVisible(false); + LayerRect rect = new LayerRect(); + rect.setX(10.0); + rect.setX(10.0); + rect.setWidth(20.); + rect.setHeight(20.); + layer.addLayerRect(rect); + model.addLayer(layer); + + AbstractImageGenerator gen = createAbstractImageGeneratorMock(graphics, model); + gen.setParams(new AbstractImageGenerator.Params().model(model).nested(true)); + gen.draw(); + + // 3 times for proteins and 4 times for reaction + verify(graphics, times(0)).draw(any()); + } catch (Exception e) { + throw e; + } + } + @Test public void testDrawSimpleMapWithWhenNestingHidesElement() throws Exception { try { @@ -88,9 +117,7 @@ public class AbstractImageGeneratorTest extends GraphicsTestFunctions { } private Model createSimpleModel() { - Model model = new ModelFullIndexed(null); - model.setWidth(100); - model.setHeight(100); + Model model = createEmptyModel(); GenericProtein protein1 = createProtein(); model.addElement(protein1); @@ -110,6 +137,13 @@ public class AbstractImageGeneratorTest extends GraphicsTestFunctions { return model; } + private Model createEmptyModel() { + Model model = new ModelFullIndexed(null); + model.setWidth(100); + model.setHeight(100); + return model; + } + private AbstractImageGenerator createAbstractImageGeneratorMock(Graphics2D graphics, Model model) throws Exception { AbstractImageGenerator result = Mockito.mock(AbstractImageGenerator.class, Mockito.CALLS_REAL_METHODS); result.setGraphics(graphics); diff --git a/converter/src/main/java/lcsb/mapviewer/converter/ProjectFactory.java b/converter/src/main/java/lcsb/mapviewer/converter/ProjectFactory.java index ef7b2e596128795c93ed6a75f4a3e0798887206e..869980f1b986cc45768b7e4b8816f75f3dc89787 100644 --- a/converter/src/main/java/lcsb/mapviewer/converter/ProjectFactory.java +++ b/converter/src/main/java/lcsb/mapviewer/converter/ProjectFactory.java @@ -18,8 +18,11 @@ import lcsb.mapviewer.converter.zip.LayoutZipEntryFile; import lcsb.mapviewer.converter.zip.ZipEntryFile; import lcsb.mapviewer.model.Project; import lcsb.mapviewer.model.map.layout.graphics.Glyph; +import lcsb.mapviewer.model.map.layout.graphics.Layer; +import lcsb.mapviewer.model.map.layout.graphics.LayerText; import lcsb.mapviewer.model.map.model.Model; import lcsb.mapviewer.model.map.model.ModelData; +import lcsb.mapviewer.model.map.model.ModelSubmodelConnection; import lcsb.mapviewer.model.map.species.Element; public class ProjectFactory { @@ -90,7 +93,15 @@ public class ProjectFactory { } private void assignGlyphsToElements(Project project) throws InvalidGlyphFile { + Set models = new HashSet<>(); + models.addAll(project.getModels()); + for (ModelData model : project.getModels()) { + for (ModelSubmodelConnection connection : model.getSubmodels()) { + models.add(connection.getSubmodel()); + } + } + for (ModelData model : models) { for (Element element : model.getElements()) { Glyph glyph = extractGlyph(project, element.getNotes()); if (glyph != null) { @@ -98,6 +109,15 @@ public class ProjectFactory { element.setGlyph(glyph); } } + for (Layer layer : model.getLayers()) { + for (LayerText text : layer.getTexts()) { + Glyph glyph = extractGlyph(project, text.getNotes()); + if (glyph != null) { + text.setNotes(removeGlyph(text.getNotes())); + text.setGlyph(glyph); + } + } + } } } diff --git a/converter/src/test/java/lcsb/mapviewer/converter/ProjectFactoryTest.java b/converter/src/test/java/lcsb/mapviewer/converter/ProjectFactoryTest.java index 1d982d7fd24058b2939839855596fb5462c8d6dd..058b30e58e613415648c14cbfcb56ea368e1698d 100644 --- a/converter/src/test/java/lcsb/mapviewer/converter/ProjectFactoryTest.java +++ b/converter/src/test/java/lcsb/mapviewer/converter/ProjectFactoryTest.java @@ -23,6 +23,8 @@ import lcsb.mapviewer.model.Project; import lcsb.mapviewer.model.map.OverviewImage; import lcsb.mapviewer.model.map.OverviewLink; import lcsb.mapviewer.model.map.OverviewModelLink; +import lcsb.mapviewer.model.map.layout.graphics.Layer; +import lcsb.mapviewer.model.map.layout.graphics.LayerText; import lcsb.mapviewer.model.map.model.Model; import lcsb.mapviewer.model.map.model.ModelData; import lcsb.mapviewer.model.map.model.ModelFullIndexed; @@ -211,6 +213,53 @@ public class ProjectFactoryTest extends ConverterTestFunctions { } + @Test + public void testParseGlyphsAndPutThemAsTextGlyphs() throws Exception { + try { + Model model = new ModelFullIndexed(null); + Layer layer = new Layer(); + + LayerText text = new LayerText(); + text.setNotes("Glyph: glyphs/g1.png"); + layer.addLayerText(text); + model.addLayer(layer); + + MockConverter.modelToBeReturned = model; + + ComplexZipConverter converter = new ComplexZipConverter(MockConverter.class); + ProjectFactory projectFactory = new ProjectFactory(converter); + + ComplexZipConverterParams params = new ComplexZipConverterParams(); + params.zipFile(new ZipFile("testFiles/complex_with_glyphs.zip")); + params.entry(new ModelZipEntryFile("main.xml", "main", true, false, null)); + + ZipFile zipFile = new ZipFile("testFiles/complex_with_glyphs.zip"); + + ZipEntryFileFactory factory = new ZipEntryFileFactory(); + Enumeration entries = zipFile.entries(); + while (entries.hasMoreElements()) { + ZipEntry entry = entries.nextElement(); + if (!entry.isDirectory()) { + params.entry(factory.createZipEntryFile(entry, zipFile)); + } + } + + Project project = projectFactory.create(params); + assertNotNull(project); + model = project.getModels().iterator().next().getModel(); + + LayerText fetchedProtein = model.getLayers().iterator().next().getTexts().get(0); + + assertFalse("Glyph field should be removed from notes", fetchedProtein.getNotes().contains("Glyph")); + assertNotNull("Glyph in the protein is not defined but should", fetchedProtein.getGlyph()); + + } catch (Exception e) { + e.printStackTrace(); + throw e; + } + + } + private GenericProtein createProtein() { GenericProtein result = new GenericProtein("s" + elementCounter++); return result; diff --git a/frontend-js/package-lock.json b/frontend-js/package-lock.json index 788f09ef1ebbcdab5f28fab7cfbf0e425707cd92..a29cdf91a1b7f4f6a8e047cfea4c7a714afc2ac6 100644 --- a/frontend-js/package-lock.json +++ b/frontend-js/package-lock.json @@ -87,6 +87,7 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", + "optional": true, "requires": { "kind-of": "3.2.2", "longest": "1.0.1", @@ -3309,7 +3310,8 @@ "longest": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", - "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=" + "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", + "optional": true }, "loose-envify": { "version": "1.3.1", diff --git a/frontend-js/src/main/js/Configuration.js b/frontend-js/src/main/js/Configuration.js index cb3ffc766e7e2aafbefc2f758128c9358501be09..2b9f2ce4514f799c0d79cf3762552f4e714370da 100644 --- a/frontend-js/src/main/js/Configuration.js +++ b/frontend-js/src/main/js/Configuration.js @@ -704,6 +704,16 @@ Configuration.prototype.getElementTypeTree = function () { }; } + var sortFunction = function (entryA, entryB) { + if (entryA.text < entryB.text) { + return -1; + } + if (entryA.text > entryB.text) { + return 1; + } + return 0; + }; + for (var treeNodeName in treeNodes) { if (treeNodes.hasOwnProperty(treeNodeName)) { var treeNode = treeNodes[treeNodeName]; @@ -712,6 +722,7 @@ Configuration.prototype.getElementTypeTree = function () { //prevent compartment subclass specific nodes and reaction subclass specific nodes if (parentNode.data === undefined || (parentNode.data.name !== "Compartment" && parentNode.data.name !== "Generic Reaction")) { parentNode.children.push(treeNode); + parentNode.children.sort(sortFunction); } } } diff --git a/frontend-js/src/main/js/gui/AddOverlayDialog.js b/frontend-js/src/main/js/gui/AddOverlayDialog.js index 5fff0a74a24f93d0e8dedc1227d98d7af8aa1459..f860127ded2f141821f87ad83edc94ec3b74a559 100644 --- a/frontend-js/src/main/js/gui/AddOverlayDialog.js +++ b/frontend-js/src/main/js/gui/AddOverlayDialog.js @@ -94,7 +94,9 @@ AddOverlayDialog.prototype.createGui = function () { name: "overlay-file" }); fileInput.addEventListener("change", function () { - return self.processFile(fileInput.files[0]).then(null, GuiConnector.alert); + return self.processFile(fileInput.files[0]).then(function () { + $(fileInput).val(""); + }).catch(GuiConnector.alert); }, false); content.appendChild(fileInput); content.appendChild(guiUtils.createNewLine()); diff --git a/frontend-js/src/main/js/gui/Header.js b/frontend-js/src/main/js/gui/Header.js index 531b2c3ff488676028418777882a1bc0d5d20d6e..6597b83641f731cdfdfd9eb9930027c5b5fc3706 100644 --- a/frontend-js/src/main/js/gui/Header.js +++ b/frontend-js/src/main/js/gui/Header.js @@ -111,7 +111,7 @@ Header.prototype._createHeaderGui = function (guiParams) { content: ' ' + projectName, xss: false }); - homeLink.href = self.getServerConnector().getServerBaseUrl() + "?id=" + projectId; + homeLink.href = self.getServerConnector().getServerBaseUrl() + "index.xhtml?id=" + projectId; self.getElement().appendChild(homeLink); }; diff --git a/frontend-js/src/main/js/gui/admin/ChooseAnnotatorsDialog.js b/frontend-js/src/main/js/gui/admin/ChooseAnnotatorsDialog.js index 570935cda219eddf32e637fc60ecf2ae654a2ec4..9f5d5795694076bfccdfc213cc661e745cd7227b 100644 --- a/frontend-js/src/main/js/gui/admin/ChooseAnnotatorsDialog.js +++ b/frontend-js/src/main/js/gui/admin/ChooseAnnotatorsDialog.js @@ -197,6 +197,15 @@ ChooseAnnotatorsDialog.prototype.setElementType = function (elementType) { } entries.push(entry); } + entries.sort(function (entryA, entryB) { + if (entryA.name < entryB.name) { + return -1; + } + if (entryA.name > entryB.name) { + return 1; + } + return 0; + }); var checkboxList = new MultiCheckboxList(selectElement, { entries: entries, listTitle: "Available annotators", diff --git a/frontend-js/src/main/js/gui/admin/ConfigurationAdminPanel.js b/frontend-js/src/main/js/gui/admin/ConfigurationAdminPanel.js index c2357d11fcf7df0234ec5282594c99f6a75ddd74..7c7fe4c9cd50d83ea20e24b2518511d96a2582ef 100644 --- a/frontend-js/src/main/js/gui/admin/ConfigurationAdminPanel.js +++ b/frontend-js/src/main/js/gui/admin/ConfigurationAdminPanel.js @@ -6,9 +6,11 @@ var ConfigurationType = require('../../ConfigurationType'); var Functions = require('../../Functions'); var GuiConnector = require('../../GuiConnector'); +var NetworkError = require('../../NetworkError'); var ValidationError = require('../../ValidationError'); var logger = require('../../logger'); +var HttpStatus = require('http-status-codes'); var Promise = require("bluebird"); var xss = require("xss"); @@ -253,9 +255,11 @@ ConfigurationAdminPanel.prototype.saveOption = function (type) { value = element.val(); } + var oldVal; GuiConnector.showProcessing(); return self.getServerConnector().getConfiguration().then(function (configuration) { option = configuration.getOption(type); + oldVal = option.getValue(); if (option === undefined) { return Promise.reject(new ValidationError("Unknown configuration type: " + type)); } @@ -317,7 +321,16 @@ ConfigurationAdminPanel.prototype.saveOption = function (type) { } }); } - }).catch(GuiConnector.alert).finally(GuiConnector.hideProcessing); + }).catch(function (e) { + if (e instanceof NetworkError && e.statusCode === HttpStatus.BAD_REQUEST) { + var content = e.content; + GuiConnector.alert(new ValidationError(content.reason)); + option.setValue(oldVal); + element.val(oldVal); + } else { + GuiConnector.alert(e); + } + }).finally(GuiConnector.hideProcessing); }; /** diff --git a/frontend-js/src/main/js/gui/admin/EditProjectDialog.js b/frontend-js/src/main/js/gui/admin/EditProjectDialog.js index 12d1f3bfaf5c9011eb2e78b0bc5a71eae780c7d8..8826c677c6dff7f3254c2ad2b6e8057d134a4df4 100644 --- a/frontend-js/src/main/js/gui/admin/EditProjectDialog.js +++ b/frontend-js/src/main/js/gui/admin/EditProjectDialog.js @@ -1018,6 +1018,10 @@ EditProjectDialog.prototype.open = function () { width: window.innerWidth * 0.75, height: window.innerHeight / 2 }); + $(".ui-dialog-titlebar-close", $(div).dialog().siblings('.ui-dialog-titlebar')).on("mousedown", function () { + //we need to close dialog on mouse down, because processing modal dialogs prevents the click to finish happening + $(div).dialog("close"); + }); } $(div).dialog("open"); }; diff --git a/frontend-js/src/main/js/gui/admin/EditUserDialog.js b/frontend-js/src/main/js/gui/admin/EditUserDialog.js index 81f3b8b4c2bc2d3fb70f010d118ba72978e2f7fb..beec0a2cce4a768fc81b7bc6c855b6ec674ef3ef 100644 --- a/frontend-js/src/main/js/gui/admin/EditUserDialog.js +++ b/frontend-js/src/main/js/gui/admin/EditUserDialog.js @@ -625,6 +625,10 @@ EditUserDialog.prototype.open = function () { width: window.innerWidth / 2, height: window.innerHeight / 2 }); + $(".ui-dialog-titlebar-close", $(div).dialog().siblings('.ui-dialog-titlebar')).on("mousedown", function () { + //we need to close dialog on mouse down, because processing modal dialogs prevents the click to finish happening + $(div).dialog("close"); + }); } $(div).dialog("open"); }; diff --git a/frontend-js/src/main/js/gui/leftPanel/ProjectInfoPanel.js b/frontend-js/src/main/js/gui/leftPanel/ProjectInfoPanel.js index 4c61b833f0c665c2eaba220ceb8477ab2db4ba1c..0ed1dc7a4c51b4d495ad274ddda3460f60a9b464 100644 --- a/frontend-js/src/main/js/gui/leftPanel/ProjectInfoPanel.js +++ b/frontend-js/src/main/js/gui/leftPanel/ProjectInfoPanel.js @@ -235,6 +235,22 @@ ProjectInfoPanel.prototype._createInfoPanelGui = function () { authorsTab.appendChild(guiUtils.createAuthorsList(self.getProject().getModels()[0].getAuthors())); } + if (self.getProject().getModels()[0].getDescription() !== null && self.getProject().getModels()[0].getDescription() !== "") { + var descriptionTab = Functions.createElement({ + type: "div" + }); + infoDiv.appendChild(descriptionTab); + + descriptionTab.appendChild(Functions.createElement({ + type: "h4", + content: 'Description:' + })); + descriptionTab.appendChild(Functions.createElement({ + type: "p", + content: self.getProject().getModels()[0].getDescription() + })); + } + }; diff --git a/frontend-js/src/main/js/gui/leftPanel/SubmapPanel.js b/frontend-js/src/main/js/gui/leftPanel/SubmapPanel.js index 150c09f77133a1f6aebb220e8b1dfdf941203590..dfe1ea7c4d00cb842a7ae455df6736c5b079ca77 100644 --- a/frontend-js/src/main/js/gui/leftPanel/SubmapPanel.js +++ b/frontend-js/src/main/js/gui/leftPanel/SubmapPanel.js @@ -64,7 +64,8 @@ SubmapPanel.prototype.createRow = function (model) { var nameTd = Functions.createElement({type: "td", content: model.getName(), style: "position:relative"}); - if (model.getReferences().length > 0 || model.getAuthors().length > 0) { + if (model.getReferences().length > 0 || model.getAuthors().length > 0 || + (model.getDescription() !== null && model.getDescription() !== "")) { var referencesTab = Functions.createElement({ type: "div", className: "minerva-search-data-hidden" @@ -88,6 +89,16 @@ SubmapPanel.prototype.createRow = function (model) { referencesTab.appendChild(authors); referencesTab.appendChild(guiUtils.createAuthorsList(model.getAuthors())); } + if (model.getDescription() !== null && model.getDescription() !== "") { + referencesTab.appendChild(Functions.createElement({ + type: "div", + content: 'Description:' + })); + referencesTab.appendChild(Functions.createElement({ + type: "p", + content: model.getDescription() + })); + } var expandButton = Functions.createElement({ type: "button", diff --git a/frontend-js/src/main/js/map/data/MapModel.js b/frontend-js/src/main/js/map/data/MapModel.js index 0149eed1f912ed013393b045ab521cfe01cc0d9d..ed7ff8b9c8e7ddfabd5133ad26f3e458d0333f92 100644 --- a/frontend-js/src/main/js/map/data/MapModel.js +++ b/frontend-js/src/main/js/map/data/MapModel.js @@ -23,6 +23,7 @@ var Reaction = require('./Reaction'); * @typedef {Object} MapModelOptions * @property {number} idObject * @property {string} name + * @property {string} description * @property {number} tileSize * @property {number} width * @property {number} height @@ -90,6 +91,7 @@ function MapModel(configuration) { this.setAuthors(configuration.getAuthors()); this.setModificationDates(configuration.getModificationDates()); this.setCreationDate(configuration.getCreationDate()); + this.setDescription(configuration.getDescription()); } else { this.setId(configuration.idObject); this.setName(configuration.name); @@ -106,6 +108,7 @@ function MapModel(configuration) { this.setAuthors(configuration.authors); this.setModificationDates(configuration.modificationDates); this.setCreationDate(configuration.creationDate); + this.setDescription(configuration.description); } } } @@ -980,4 +983,21 @@ MapModel.prototype.setCreationDate = function (creationDate) { this._creationDate = creationDate; }; +/** + * + * @returns {string|null} + */ +MapModel.prototype.getDescription = function () { + return this._description; +}; + +/** + * + * @param {string} description + */ +MapModel.prototype.setDescription = function (description) { + this._description = description; +}; + + module.exports = MapModel; diff --git a/frontend-js/src/main/js/map/surface/AliasSurface.js b/frontend-js/src/main/js/map/surface/AliasSurface.js index b78839878d8233640946ddbbfdf366bd09ad3567..c0f5ae6654e3b9b06e07385c98cead852ab04062 100644 --- a/frontend-js/src/main/js/map/surface/AliasSurface.js +++ b/frontend-js/src/main/js/map/surface/AliasSurface.js @@ -177,7 +177,11 @@ AliasSurface.prototype.setBoundsForAlias = function (startX, endX) { var pointB = new Point(alias.getX() + endX * alias.getWidth(), alias.getY() + alias.getHeight()); var bounds = new Bounds(pointA, pointB); - this.getMapCanvasObjects()[0].setBounds(bounds); + + var mapCanvasObjects = this.getMapCanvasObjects(); + for (var i = 0; i < mapCanvasObjects.length; i++) { + mapCanvasObjects[i].setBounds(bounds); + } }; /** diff --git a/frontend-js/src/main/js/minerva.js b/frontend-js/src/main/js/minerva.js index aa67ae90295b06cce560ae39c1a4dc8738fff324..036f1f1eb6a8e985f5f921421a649b9896eae39d 100644 --- a/frontend-js/src/main/js/minerva.js +++ b/frontend-js/src/main/js/minerva.js @@ -772,7 +772,7 @@ function createLoginDiv(configuration) { }); $('#go_to_map_button', result).click(function () { return ServerConnector.getProjectId().then(function (projectId) { - window.location.href = ServerConnector.getServerBaseUrl() + '?id=' + projectId; + window.location.href = ServerConnector.getServerBaseUrl() + 'index.xhtml?id=' + projectId; }); }); $('#register_button', result).click(function () { diff --git a/frontend-js/src/main/js/plugin/PluginManager.js b/frontend-js/src/main/js/plugin/PluginManager.js index 8fbf00c05f97baa358dc20cc1b2d3b6a7d0633c9..a04aa1628d816b381b5c08bec12e2bcf2c7e57ad 100644 --- a/frontend-js/src/main/js/plugin/PluginManager.js +++ b/frontend-js/src/main/js/plugin/PluginManager.js @@ -70,6 +70,7 @@ PluginManager.prototype.getGuiUtils = function () { */ PluginManager.prototype.addPlugin = function (options) { var self = this; + var oldLinkHeight = $(".nav-tabs", self.getElement()).height(); $(self.getElement()).show(); if (self._panels === undefined) { self.getGuiUtils().initTabContent(self); @@ -78,7 +79,6 @@ PluginManager.prototype.addPlugin = function (options) { self.getGuiUtils().addTab(self, {name: "PLUGIN", content: element}); - var oldLinkHeight = $(".nav-tabs", self.getElement()).height(); var plugin; return Promise.resolve().then(function () { if (options instanceof Plugin) { diff --git a/frontend-js/src/test/js/Configuration-test.js b/frontend-js/src/test/js/Configuration-test.js index 2398e8fd372a19b1f8c45fe866f2d09ecdab704d..d847f89f02c83d0ed216fb595d98b5ad57b36b5a 100644 --- a/frontend-js/src/test/js/Configuration-test.js +++ b/frontend-js/src/test/js/Configuration-test.js @@ -155,16 +155,40 @@ describe('Configuration', function () { }); }); - it('getElementTypeTree', function () { + describe('getElementTypeTree', function () { + it('default', function () { - return ServerConnector.getConfiguration().then(function (configuration) { - var treeData = configuration.getElementTypeTree(); + return ServerConnector.getConfiguration().then(function (configuration) { + var treeData = configuration.getElementTypeTree(); - assert.ok(treeData); - assert.ok(treeData.text); - assert.ok(treeData.children); - assert.equal(2, treeData.children.length); - }) + assert.ok(treeData); + assert.ok(treeData.text); + assert.ok(treeData.children); + assert.equal(2, treeData.children.length); + }) + }); + it('check sorting', function () { + /** + * + * @param {BioEntityTypeTreeNode[]} children + */ + function checkChildrenSorted(children) { + var i; + for (i = 1; i < children.length; i++) { + assert.ok(children[i - 1].text < children[i].text, "children are not sorted: " + children[i - 1].text + " ; " + children[i].text); + } + for (i = 0; i < children.length; i++) { + if (children[i].children != null) { + checkChildrenSorted(children[i].children); + } + } + } + + return ServerConnector.getConfiguration().then(function (configuration) { + var treeData = configuration.getElementTypeTree(); + checkChildrenSorted(treeData.children); + }) + }); }); diff --git a/frontend-js/src/test/js/gui/admin/EditUserDialog-test.js b/frontend-js/src/test/js/gui/admin/EditUserDialog-test.js index 7650f567f5a6460ba1b36749521b4176234a676d..47919a1c861711471d40bfd0b1c55d63cde328f8 100644 --- a/frontend-js/src/test/js/gui/admin/EditUserDialog-test.js +++ b/frontend-js/src/test/js/gui/admin/EditUserDialog-test.js @@ -150,7 +150,7 @@ describe('EditUserDialog', function () { }).then(function () { var element = $("[name=privilege-int]", dialog.getElement())[0]; $(element).val("101010"); - return helper.triggerJqueryEvent(element,"change"); + return helper.triggerJqueryEvent(element, "change"); }).then(function () { expect(serializedPrivileges).not.to.deep.equal(user.privilegesToExport(helper.getConfiguration())); dialog.destroy(); diff --git a/frontend-js/src/test/js/map/surface/AliasSurface-test.js b/frontend-js/src/test/js/map/surface/AliasSurface-test.js index 4c98a6eb0db1934f67c8ddf5868744e2b0377a93..db8483335b0aba7ed009b2cca0bbcec92166b6fd 100644 --- a/frontend-js/src/test/js/map/surface/AliasSurface-test.js +++ b/frontend-js/src/test/js/map/surface/AliasSurface-test.js @@ -61,11 +61,30 @@ describe('AliasSurface', function () { var bounds = surface.getBounds(); surface.setBoundsForAlias(1, 3); var bounds2 = surface.getBounds(); - assert.equal(bounds.getRightBottom().y, bounds2.getRightBottom().y); + assert.equal(bounds.getRightBottom().y, bounds2.getRightBottom().y, helper.EPSILON); assert.ok(bounds.getRightBottom().x !== bounds2.getRightBottom().x); }); }); + it("before alias init", function () { + var map; + var alias, surface; + return ServerConnector.getProject().then(function (project) { + map = helper.createCustomMap(project); + return map.getModel().getAliasById(329171); + }).then(function (result) { + alias = result; + surface = new AliasSurface({ + alias: result, + overlayData: [helper.createLayoutAlias(alias)], + map: map + }); + surface.setBoundsForAlias(1, 3); + var bounds = surface.getBounds(); + assert.ok(bounds); + }); + }); + it("from element with data overlay", function () { var map = helper.createCustomMap(); var alias = helper.createAlias(map); diff --git a/model-command/src/main/java/lcsb/mapviewer/commands/ColorModelCommand.java b/model-command/src/main/java/lcsb/mapviewer/commands/ColorModelCommand.java index 37cc33ce058c2543bfdf70999127599680474610..9cce0d6982a8e563c4b9235cd35b769be1782d86 100644 --- a/model-command/src/main/java/lcsb/mapviewer/commands/ColorModelCommand.java +++ b/model-command/src/main/java/lcsb/mapviewer/commands/ColorModelCommand.java @@ -337,6 +337,7 @@ public class ColorModelCommand extends ModelCommand { for (Element element : result.getElements()) { element.setColor(Color.WHITE); + element.setGlyph(null); } for (Reaction reaction : result.getReactions()) { for (AbstractNode node : reaction.getNodes()) { diff --git a/model-command/src/main/java/lcsb/mapviewer/commands/CreateHierarchyCommand.java b/model-command/src/main/java/lcsb/mapviewer/commands/CreateHierarchyCommand.java index 8939d7a06e4144c12cc5b8b797b3ff22c80e2169..06f8c1c02dfa20602b59ebf23b7ad127a6a796b6 100644 --- a/model-command/src/main/java/lcsb/mapviewer/commands/CreateHierarchyCommand.java +++ b/model-command/src/main/java/lcsb/mapviewer/commands/CreateHierarchyCommand.java @@ -43,11 +43,11 @@ public class CreateHierarchyCommand extends ModelCommand { private static final double LOG_4 = Math.log(4); /** - * Top left corner x coordinate of the text associated with compratment. + * Top left corner x coordinate of the text associated with compartment. */ private static final double DEFAULT_TITLE_X_COORD_IN_ARTIFITIAL_COMPARTMENT = 10; /** - * Top left corner y coordinate of the text associated with compratment. + * Top left corner y coordinate of the text associated with compartment. */ private static final double DEFAULT_TITLE_Y_COORD_IN_ARTIFITIAL_COMPARTMENT = 10; @@ -180,6 +180,7 @@ public class CreateHierarchyCommand extends ModelCommand { compartment.setName(extractNameFromText(text.getNotes())); compartment.setNotes(extractNotesFromText(text.getNotes())); compartment.setZ(text.getZ()); + compartment.setGlyph(text.getGlyph()); rap.processNotes(compartment); compartment.setNamePoint(new Point2D.Double(text.getX() + DEFAULT_TITLE_X_COORD_IN_ARTIFITIAL_COMPARTMENT, diff --git a/model/src/main/java/lcsb/mapviewer/model/map/layout/graphics/LayerText.java b/model/src/main/java/lcsb/mapviewer/model/map/layout/graphics/LayerText.java index 0e64330431cd0e83aab853ddc444e83cc41ff194..72e9575eba37846d7391ec2faa28b39ae64774a6 100644 --- a/model/src/main/java/lcsb/mapviewer/model/map/layout/graphics/LayerText.java +++ b/model/src/main/java/lcsb/mapviewer/model/map/layout/graphics/LayerText.java @@ -5,9 +5,11 @@ import java.awt.geom.Rectangle2D; import java.io.Serializable; import javax.persistence.Entity; +import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; +import javax.persistence.ManyToOne; import org.apache.logging.log4j.*; @@ -92,6 +94,12 @@ public class LayerText implements Serializable, Drawable { */ private Double fontSize = DEFAULT_LAYER_FONT_SIZE; + /** + * Glyph used for drawing (instead of text). + */ + @ManyToOne(fetch = FetchType.LAZY) + private Glyph glyph = null; + /** * Default constructor. */ @@ -130,6 +138,9 @@ public class LayerText implements Serializable, Drawable { height = layerText.getHeight(); notes = layerText.getNotes(); fontSize = layerText.getFontSize(); + if (layerText.getGlyph() != null) { + setGlyph(new Glyph(layerText.getGlyph())); + } } /** @@ -377,4 +388,12 @@ public class LayerText implements Serializable, Drawable { return "x=" + x + ";y=" + y + "; w=" + width + ", h=" + height; } + public Glyph getGlyph() { + return glyph; + } + + public void setGlyph(Glyph glyph) { + this.glyph = glyph; + } + } diff --git a/persist/src/main/resources/db/migration/13.1.0~beta.1/V13.1.0.20190607__text_contains_glyph.sql b/persist/src/main/resources/db/migration/13.1.0~beta.1/V13.1.0.20190607__text_contains_glyph.sql new file mode 100644 index 0000000000000000000000000000000000000000..8c1f1ac837d55e41bb92f36d8b072bc82d18f449 --- /dev/null +++ b/persist/src/main/resources/db/migration/13.1.0~beta.1/V13.1.0.20190607__text_contains_glyph.sql @@ -0,0 +1,5 @@ +alter table layer_text_table add column glyph_id integer; +alter table layer_text_table add constraint layer_text_table_glyph_fk FOREIGN KEY (glyph_id) + REFERENCES glyph_table (id) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION; + diff --git a/rest-api/src/main/java/lcsb/mapviewer/api/BaseController.java b/rest-api/src/main/java/lcsb/mapviewer/api/BaseController.java index d81c965c5fbd82a2e87a1de494af62fde927b9c6..922c6d0af330d5d2cb2ec3ec294cb4c8053a9154 100644 --- a/rest-api/src/main/java/lcsb/mapviewer/api/BaseController.java +++ b/rest-api/src/main/java/lcsb/mapviewer/api/BaseController.java @@ -3,8 +3,9 @@ package lcsb.mapviewer.api; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; -import java.util.TreeMap; +import java.util.HashMap; import java.util.Map; +import java.util.TreeMap; import org.apache.logging.log4j.*; import org.springframework.http.HttpHeaders; @@ -37,7 +38,8 @@ public abstract class BaseController { } else if (e instanceof ObjectExistsException) { return createErrorResponse("Object already exists.", e.getMessage(), new HttpHeaders(), HttpStatus.CONFLICT); } else if (e instanceof OperationNotAllowedException) { - return createErrorResponse("Operation not allowed.", e.getMessage(), new HttpHeaders(), HttpStatus.METHOD_NOT_ALLOWED); + return createErrorResponse("Operation not allowed.", e.getMessage(), new HttpHeaders(), + HttpStatus.METHOD_NOT_ALLOWED); } else if (e instanceof QueryException) { logger.error(e, e); return createErrorResponse("Query server error.", e.getMessage(), new HttpHeaders(), HttpStatus.BAD_REQUEST); @@ -53,8 +55,10 @@ public abstract class BaseController { private ResponseEntity createErrorResponse(String errorMessage, String error, HttpHeaders httpHeaders, HttpStatus status) { - return new ResponseEntity( - "{\"error\" : \"" + errorMessage + "\",\"reason\":" + new Gson().toJson(error) + "}", httpHeaders, status); + Map response = new HashMap<>(); + response.put("error", errorMessage); + response.put("reason", error); + return new ResponseEntity(new Gson().toJson(response), httpHeaders, status); } public Map parseBody(String body) throws IOException, JsonParseException, JsonMappingException { diff --git a/rest-api/src/main/java/lcsb/mapviewer/api/configuration/ConfigurationController.java b/rest-api/src/main/java/lcsb/mapviewer/api/configuration/ConfigurationController.java index e9a8bf9d2fcebbb0ccb697fa763925d2e2f8d931..949e03b59310bf5153ecffe55ac4cacc15323328 100644 --- a/rest-api/src/main/java/lcsb/mapviewer/api/configuration/ConfigurationController.java +++ b/rest-api/src/main/java/lcsb/mapviewer/api/configuration/ConfigurationController.java @@ -73,7 +73,7 @@ public class ConfigurationController extends BaseController { @RequestMapping(value = "/configuration/options/{option}", method = { RequestMethod.PATCH }, produces = { MediaType.APPLICATION_JSON_VALUE }) - public Map getOption( + public Map updateOption( @RequestBody String body, @CookieValue(value = Configuration.AUTH_TOKEN) String token, @PathVariable(value = "option") String option diff --git a/rest-api/src/main/java/lcsb/mapviewer/api/configuration/ConfigurationRestImpl.java b/rest-api/src/main/java/lcsb/mapviewer/api/configuration/ConfigurationRestImpl.java index ddc14e7cb4d95e286be5505e841211c20d04d331..6d026ef7bffeb7744fca51b054322e2307592a3b 100644 --- a/rest-api/src/main/java/lcsb/mapviewer/api/configuration/ConfigurationRestImpl.java +++ b/rest-api/src/main/java/lcsb/mapviewer/api/configuration/ConfigurationRestImpl.java @@ -293,7 +293,7 @@ public class ConfigurationRestImpl extends BaseRestImpl { try { configurationService.setConfigurationValue(type, value); } catch (InvalidArgumentException e) { - throw new QueryException(e); + throw new QueryException(e.getMessage(), e); } return optionToMap(configurationService.getValue(type)); } diff --git a/rest-api/src/main/java/lcsb/mapviewer/api/projects/models/ModelRestImpl.java b/rest-api/src/main/java/lcsb/mapviewer/api/projects/models/ModelRestImpl.java index 789f997cf71fd886f7688fedb06f15821e99f1e5..d4019b4d2e5b3b1dc37a7af0dc6cb153595375f0 100644 --- a/rest-api/src/main/java/lcsb/mapviewer/api/projects/models/ModelRestImpl.java +++ b/rest-api/src/main/java/lcsb/mapviewer/api/projects/models/ModelRestImpl.java @@ -116,6 +116,7 @@ public class ModelRestImpl extends BaseRestImpl { result.put("authors", submodel.getAuthors()); result.put("creationDate", super.prepareDate(submodel.getCreationDate())); result.put("modificationDates", super.prepareDates(submodel.getModificationDates())); + result.put("description", submodel.getNotes()); return result; } } diff --git a/rest-api/src/test/java/lcsb/mapviewer/api/configuration/AllConfigurationTests.java b/rest-api/src/test/java/lcsb/mapviewer/api/configuration/AllConfigurationTests.java index 14956fb65efabe9e05334116fda4b860e91c2387..1a95c60136830a015cfb50afa1056eb965f98a27 100644 --- a/rest-api/src/test/java/lcsb/mapviewer/api/configuration/AllConfigurationTests.java +++ b/rest-api/src/test/java/lcsb/mapviewer/api/configuration/AllConfigurationTests.java @@ -5,7 +5,9 @@ import org.junit.runners.Suite; import org.junit.runners.Suite.SuiteClasses; @RunWith(Suite.class) -@SuiteClasses({ ConfigurationRestImplTest.class }) +@SuiteClasses({ + ConfigurationControllerTest.class, + ConfigurationRestImplTest.class }) public class AllConfigurationTests { } diff --git a/rest-api/src/test/java/lcsb/mapviewer/api/configuration/ConfigurationControllerTest.java b/rest-api/src/test/java/lcsb/mapviewer/api/configuration/ConfigurationControllerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..40eee3082bb1c3b1fbe2995e920096111109fa6e --- /dev/null +++ b/rest-api/src/test/java/lcsb/mapviewer/api/configuration/ConfigurationControllerTest.java @@ -0,0 +1,56 @@ +package lcsb.mapviewer.api.configuration; + +import static org.junit.Assert.fail; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +import com.google.gson.Gson; + +import lcsb.mapviewer.api.QueryException; +import lcsb.mapviewer.api.SpringRestApiTestConfig; +import lcsb.mapviewer.model.user.ConfigurationElementType; +import lcsb.mapviewer.services.interfaces.IUserService; + +@ContextConfiguration(classes = SpringRestApiTestConfig.class) +@WebAppConfiguration +@RunWith(SpringJUnit4ClassRunner.class) +public class ConfigurationControllerTest { + + @Autowired + ConfigurationController configurationController; + + @Autowired + IUserService userService; + + @Test + public void testSetSmtpPortToInvalid() throws Exception { + try { + // assume that we have admin account with all the privileges + String adminToken = userService.login("admin", "admin"); + + Map option= new HashMap<>(); + option.put("value", "not a number"); + option.put("type", "not a number"); + Map data= new HashMap<>(); + data.put("option", option); + + String body = new Gson().toJson(data); + configurationController.updateOption(body, adminToken, ConfigurationElementType.MAX_NUMBER_OF_MAP_LEVELS.name()); + fail("Exception expected"); + } catch (QueryException e) { + } catch (Exception e) { + e.printStackTrace(); + throw e; + } + } + + +} diff --git a/service/src/main/java/lcsb/mapviewer/services/impl/ConfigurationService.java b/service/src/main/java/lcsb/mapviewer/services/impl/ConfigurationService.java index 3156a0fa8ab86aff07fda752c34684e0fee1b20a..5bad3794530be0cff88c009e49327353fd15d21c 100644 --- a/service/src/main/java/lcsb/mapviewer/services/impl/ConfigurationService.java +++ b/service/src/main/java/lcsb/mapviewer/services/impl/ConfigurationService.java @@ -65,6 +65,7 @@ public class ConfigurationService implements IConfigurationService { } @Override + @Transactional(noRollbackFor = InvalidArgumentException.class) public void setConfigurationValue(ConfigurationElementType type, String value) { if (value == null) { throw new InvalidArgumentException("null is not a proper value"); diff --git a/service/src/main/java/lcsb/mapviewer/services/impl/LayoutService.java b/service/src/main/java/lcsb/mapviewer/services/impl/LayoutService.java index 3d69391137e630106ee5cc9024ed1d3f95aba6f2..b236177bd6f3984b957331c8bd99dcd60aeef561 100644 --- a/service/src/main/java/lcsb/mapviewer/services/impl/LayoutService.java +++ b/service/src/main/java/lcsb/mapviewer/services/impl/LayoutService.java @@ -672,7 +672,7 @@ public class LayoutService implements ILayoutService { sb.append("\t"); } else if (column.equals(ColorSchemaColumn.NAME)) { sb.append(schema.getName() + "\t"); - } else if (column.equals(ColorSchemaColumn.MODEL_NAME)) { + } else if (column.equals(ColorSchemaColumn.MAP_NAME)) { sb.append(schema.getModelName() + "\t"); } else if (column.equals(ColorSchemaColumn.VALUE)) { sb.append(schema.getValue() + "\t"); @@ -727,7 +727,7 @@ public class LayoutService implements ILayoutService { sb.append("\t"); } else if (column.equals(ColorSchemaColumn.NAME)) { sb.append(schema.getName() + "\t"); - } else if (column.equals(ColorSchemaColumn.MODEL_NAME)) { + } else if (column.equals(ColorSchemaColumn.MAP_NAME)) { sb.append(schema.getModelName() + "\t"); } else if (column.equals(ColorSchemaColumn.VALUE)) { sb.append(schema.getValue() + "\t"); diff --git a/service/src/main/java/lcsb/mapviewer/services/utils/ColorSchemaReader.java b/service/src/main/java/lcsb/mapviewer/services/utils/ColorSchemaReader.java index deb15c6ab0824a9b344e8b523a6b3328981374c1..2a5c865956ce891bcb9583f64c899670016e061f 100644 --- a/service/src/main/java/lcsb/mapviewer/services/utils/ColorSchemaReader.java +++ b/service/src/main/java/lcsb/mapviewer/services/utils/ColorSchemaReader.java @@ -7,6 +7,7 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -162,8 +163,11 @@ public class ColorSchemaReader { Integer nameColumn = schemaColumns.get(ColorSchemaColumn.NAME); if (nameColumn == null) { nameColumn = schemaColumns.get(ColorSchemaColumn.GENE_NAME); + } else if (schemaColumns.get(ColorSchemaColumn.GENE_NAME) != null) { + throw new InvalidColorSchemaException(ColorSchemaColumn.GENE_NAME + " and " + ColorSchemaColumn.NAME + + " column cannot appear in the same dataset"); } - Integer modelNameColumn = schemaColumns.get(ColorSchemaColumn.MODEL_NAME); + Integer modelNameColumn = schemaColumns.get(ColorSchemaColumn.MAP_NAME); Integer identifierColumn = schemaColumns.get(ColorSchemaColumn.IDENTIFIER); Integer variantIdentifierColumn = schemaColumns.get(ColorSchemaColumn.VARIANT_IDENTIFIER); Integer allelFrequencyColumn = schemaColumns.get(ColorSchemaColumn.ALLEL_FREQUENCY); @@ -229,7 +233,11 @@ public class ColorSchemaReader { } } if (colorColumn != null) { - schema.setColor(colorParser.parse(values[colorColumn])); + try { + schema.setColor(colorParser.parse(values[colorColumn])); + } catch (InvalidArgumentException e) { + throw new InvalidColorSchemaException(errorPrefix + e.getMessage(), e); + } } if (identifierColumn != null && !values[identifierColumn].equals("")) { processGeneralIdentifier(values[identifierColumn], schema, errorPrefix); @@ -288,7 +296,7 @@ public class ColorSchemaReader { gv.setContig(values[contigColumn]); schema.addGeneVariation(gv); - if (schema.getName().contains(";")) { + if (schema.getName() != null && schema.getName().contains(";")) { String[] names = schema.getName().split(";"); for (String string : names) { if (!string.trim().isEmpty()) { @@ -428,7 +436,7 @@ public class ColorSchemaReader { } } for (ColorSchema colorSchema : schemasByName.values()) { - if (colorSchema instanceof GeneVariationColorSchema) { + if (colorSchema instanceof GeneVariationColorSchema && colorSchema.getColor() == null) { colorSchema.setColor(getGeneVariantsColor(((GeneVariationColorSchema) colorSchema).getGeneVariations())); } } @@ -439,7 +447,7 @@ public class ColorSchemaReader { * Gets color that should be assigned to {@link GeneVariationColorSchema}. * * @param geneVariations - * list of viariants + * list of variants * @return {@link Color} that should be assigned to * {@link GeneVariationColorSchema} */ @@ -486,7 +494,7 @@ public class ColorSchemaReader { Integer valueColumn = schemaColumns.get(ColorSchemaColumn.VALUE); Integer colorColumn = schemaColumns.get(ColorSchemaColumn.COLOR); Integer nameColumn = schemaColumns.get(ColorSchemaColumn.NAME); - Integer modelNameColumn = schemaColumns.get(ColorSchemaColumn.MODEL_NAME); + Integer modelNameColumn = schemaColumns.get(ColorSchemaColumn.MAP_NAME); Integer identifierColumn = schemaColumns.get(ColorSchemaColumn.IDENTIFIER); Integer elementIdentifierColumn = schemaColumns.get(ColorSchemaColumn.ELEMENT_IDENTIFIER); if (elementIdentifierColumn == null) { @@ -535,7 +543,11 @@ public class ColorSchemaReader { schema.setValue(parseValueColumn(values[valueColumn], errorPrefix)); } if (colorColumn != null && !values[colorColumn].isEmpty()) { - schema.setColor(colorParser.parse(values[colorColumn])); + try { + schema.setColor(colorParser.parse(values[colorColumn])); + } catch (InvalidArgumentException e) { + throw new InvalidColorSchemaException(errorPrefix + e.getMessage(), e); + } } if (schema.getValue() != null && schema.getColor() != null) { throw new InvalidColorSchemaException(errorPrefix + "Either color or value can be defined but found both"); @@ -580,21 +592,23 @@ public class ColorSchemaReader { return result; } - private List> parseSpeciesTypes(String typesString, String errorPrefix) + List> parseSpeciesTypes(String typesString, String errorPrefix) throws InvalidColorSchemaException { List> result = new ArrayList<>(); String[] types = typesString.split(","); for (String string : types) { - SpeciesMapping mapping = SpeciesMapping.getMappingByString(string); - if (mapping != null) { - result.add(mapping.getModelClazz()); - } else { - String validStrings = ""; - for (SpeciesMapping speciesMapping : SpeciesMapping.values()) { - validStrings += speciesMapping.getCellDesignerString() + ", "; + if (!string.isEmpty()) { + SpeciesMapping mapping = SpeciesMapping.getMappingByString(string); + if (mapping != null) { + result.add(mapping.getModelClazz()); + } else { + String validStrings = ""; + for (SpeciesMapping speciesMapping : SpeciesMapping.values()) { + validStrings += speciesMapping.getCellDesignerString() + ", "; + } + throw new InvalidColorSchemaException( + errorPrefix + "Unknown class type: " + string + ". Valid values are: " + validStrings); } - throw new InvalidColorSchemaException( - errorPrefix + "Unknown class type: " + string + ". Valid values are: " + validStrings); } } return result; @@ -707,7 +721,15 @@ public class ColorSchemaReader { } else { String columnNames = ""; for (ColorSchemaColumn schemaColumn : ColorSchemaColumn.values()) { - if (schemaColumn.getTypes().contains(type)) { + boolean deprecated = false; + try { + Field f = ColorSchemaColumn.class.getField(schemaColumn.name()); + if (f.isAnnotationPresent(Deprecated.class)) + deprecated = true; + } catch (NoSuchFieldException | SecurityException e) { + } + + if (schemaColumn.getTypes().contains(type) && !deprecated) { columnNames += schemaColumn.getColumnName() + ", "; } } @@ -776,7 +798,7 @@ public class ColorSchemaReader { result.add(ColorSchemaColumn.NAME); } if (schema.getModelName() != null) { - result.add(ColorSchemaColumn.MODEL_NAME); + result.add(ColorSchemaColumn.MAP_NAME); } if (schema.getElementId() != null) { result.add(ColorSchemaColumn.ELEMENT_IDENTIFIER); diff --git a/service/src/main/java/lcsb/mapviewer/services/utils/data/ColorSchemaColumn.java b/service/src/main/java/lcsb/mapviewer/services/utils/data/ColorSchemaColumn.java index 8fd9276dfe5579067dca98d62c7c731ece156f82..fd9e21311645329bcab595ed843b596908abf843 100644 --- a/service/src/main/java/lcsb/mapviewer/services/utils/data/ColorSchemaColumn.java +++ b/service/src/main/java/lcsb/mapviewer/services/utils/data/ColorSchemaColumn.java @@ -19,14 +19,14 @@ public enum ColorSchemaColumn { /** * Name of the element. */ - NAME(new ColorSchemaType[] { ColorSchemaType.GENERIC, ColorSchemaType.GENETIC_VARIANT }), + NAME(new ColorSchemaType[] { ColorSchemaType.GENERIC}), - GENE_NAME(new ColorSchemaType[] { ColorSchemaType.GENERIC, ColorSchemaType.GENETIC_VARIANT }), + GENE_NAME(new ColorSchemaType[] { ColorSchemaType.GENETIC_VARIANT }), /** - * Name of the element. + * Name of the map. */ - MODEL_NAME(new ColorSchemaType[] { ColorSchemaType.GENERIC, ColorSchemaType.GENETIC_VARIANT }), + MAP_NAME("model_name",new ColorSchemaType[] { ColorSchemaType.GENERIC, ColorSchemaType.GENETIC_VARIANT }), /** * Value that will be transformed into new color. @@ -43,7 +43,7 @@ public enum ColorSchemaColumn { /** * Class type of the element. */ - TYPE(new ColorSchemaType[] { ColorSchemaType.GENERIC, ColorSchemaType.GENETIC_VARIANT }), + TYPE(new ColorSchemaType[] { ColorSchemaType.GENERIC}), /** * New element/reaction color. @@ -99,11 +99,13 @@ public enum ColorSchemaColumn { /** * What's the {@link ReferenceGenomeType}. */ + @Deprecated REFERENCE_GENOME_TYPE(new ColorSchemaType[] { ColorSchemaType.GENETIC_VARIANT }), /** * {@link ReferenceGenome#version Version} of the reference genome. */ + @Deprecated REFERENCE_GENOME_VERSION(new ColorSchemaType[] { ColorSchemaType.GENETIC_VARIANT }), /** diff --git a/service/src/test/java/lcsb/mapviewer/services/utils/ColorSchemaReaderTest.java b/service/src/test/java/lcsb/mapviewer/services/utils/ColorSchemaReaderTest.java index d2a89adac64d2c41e57ae662448a0d11c1a32b22..632082e18be8265c196f9a8ea002d3bc69394a55 100644 --- a/service/src/test/java/lcsb/mapviewer/services/utils/ColorSchemaReaderTest.java +++ b/service/src/test/java/lcsb/mapviewer/services/utils/ColorSchemaReaderTest.java @@ -11,10 +11,13 @@ import java.awt.Color; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.util.Collection; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.apache.commons.io.output.ByteArrayOutputStream; @@ -26,19 +29,24 @@ import org.junit.Test; import lcsb.mapviewer.commands.ColorExtractor; import lcsb.mapviewer.commands.ColorModelCommand; import lcsb.mapviewer.common.TextFileUtils; +import lcsb.mapviewer.model.map.BioEntity; import lcsb.mapviewer.model.map.MiriamData; import lcsb.mapviewer.model.map.layout.ColorSchema; import lcsb.mapviewer.model.map.layout.GeneVariation; import lcsb.mapviewer.model.map.layout.GeneVariationColorSchema; import lcsb.mapviewer.model.map.layout.InvalidColorSchemaException; import lcsb.mapviewer.model.map.model.Model; +import lcsb.mapviewer.model.map.species.Element; import lcsb.mapviewer.services.ServiceTestFunctions; public class ColorSchemaReaderTest extends ServiceTestFunctions { Logger logger = LogManager.getLogger(ColorSchemaReaderTest.class); + ColorSchemaReader reader; + @Before public void setUp() throws Exception { + reader = new ColorSchemaReader(); } @After @@ -48,8 +56,6 @@ public class ColorSchemaReaderTest extends ServiceTestFunctions { @Test public void testReadSchema() throws Exception { try { - ColorSchemaReader reader = new ColorSchemaReader(); - Collection schemas = reader.readColorSchema("testFiles/enricoData/ageing.txt"); assertNotNull(schemas); @@ -75,26 +81,10 @@ public class ColorSchemaReaderTest extends ServiceTestFunctions { public void testReadGeneVariantsSchema() throws Exception { try { File f = new File("testFiles/coloring/gene_variants.txt"); - InputStream in = new FileInputStream(f); - - byte[] buff = new byte[8000]; - - int bytesRead = 0; - - ByteArrayOutputStream bao = new ByteArrayOutputStream(); - - while ((bytesRead = in.read(buff)) != -1) { - bao.write(buff, 0, bytesRead); - } - in.close(); - bao.close(); - - byte[] data = bao.toByteArray(); + byte[] data = fileToByteArray(f); ByteArrayInputStream bin = new ByteArrayInputStream(data); - ColorSchemaReader reader = new ColorSchemaReader(); - Collection schemas = reader.readColorSchema(bin, TextFileUtils.getHeaderParametersFromFile(new ByteArrayInputStream(data))); assertNotNull(schemas); @@ -106,28 +96,49 @@ public class ColorSchemaReaderTest extends ServiceTestFunctions { } @Test - public void testReadGeneVariantsWithAminoAcidChange() throws Exception { + public void testReadGeneVariantByIdSchema() throws Exception { try { - File f = new File("testFiles/coloring/gene_variants_aa_change.txt"); - InputStream in = new FileInputStream(f); + File f = new File("testFiles/coloring/gene_variants_id.txt"); + byte[] data = fileToByteArray(f); + + ByteArrayInputStream bin = new ByteArrayInputStream(data); - byte[] buff = new byte[8000]; + Collection schemas = reader.readColorSchema(bin, + TextFileUtils.getHeaderParametersFromFile(new ByteArrayInputStream(data))); + assertNotNull(schemas); + assertEquals(1, schemas.size()); + } catch (Exception e) { + e.printStackTrace(); + throw e; + } + } - int bytesRead = 0; + private byte[] fileToByteArray(File f) throws FileNotFoundException, IOException { + InputStream in = new FileInputStream(f); - ByteArrayOutputStream bao = new ByteArrayOutputStream(); + byte[] buff = new byte[8000]; - while ((bytesRead = in.read(buff)) != -1) { - bao.write(buff, 0, bytesRead); - } - in.close(); - bao.close(); + int bytesRead = 0; - byte[] data = bao.toByteArray(); + ByteArrayOutputStream bao = new ByteArrayOutputStream(); - ByteArrayInputStream bin = new ByteArrayInputStream(data); + while ((bytesRead = in.read(buff)) != -1) { + bao.write(buff, 0, bytesRead); + } + in.close(); + bao.close(); - ColorSchemaReader reader = new ColorSchemaReader(); + byte[] data = bao.toByteArray(); + return data; + } + + @Test + public void testReadGeneVariantsWithAminoAcidChange() throws Exception { + try { + File f = new File("testFiles/coloring/gene_variants_aa_change.txt"); + byte[] data = fileToByteArray(f); + + ByteArrayInputStream bin = new ByteArrayInputStream(data); Collection schemas = reader.readColorSchema(bin, TextFileUtils.getHeaderParametersFromFile(new ByteArrayInputStream(data))); @@ -151,26 +162,10 @@ public class ColorSchemaReaderTest extends ServiceTestFunctions { public void testReadGeneVariantsWithNewNamingSchema() throws Exception { try { File f = new File("testFiles/coloring/gene_variants_new_naming.txt"); - InputStream in = new FileInputStream(f); - - byte[] buff = new byte[8000]; - - int bytesRead = 0; - - ByteArrayOutputStream bao = new ByteArrayOutputStream(); - - while ((bytesRead = in.read(buff)) != -1) { - bao.write(buff, 0, bytesRead); - } - in.close(); - bao.close(); - - byte[] data = bao.toByteArray(); + byte[] data = fileToByteArray(f); ByteArrayInputStream bin = new ByteArrayInputStream(data); - ColorSchemaReader reader = new ColorSchemaReader(); - Collection schemas = reader.readColorSchema(bin, TextFileUtils.getHeaderParametersFromFile(new ByteArrayInputStream(data))); assertNotNull(schemas); @@ -185,26 +180,10 @@ public class ColorSchemaReaderTest extends ServiceTestFunctions { public void testReadGeneVariantsSchemaWithAF() throws Exception { try { File f = new File("testFiles/coloring/gene_variants_all_freq.txt"); - InputStream in = new FileInputStream(f); - - byte[] buff = new byte[8000]; - - int bytesRead = 0; - - ByteArrayOutputStream bao = new ByteArrayOutputStream(); - - while ((bytesRead = in.read(buff)) != -1) { - bao.write(buff, 0, bytesRead); - } - in.close(); - bao.close(); - - byte[] data = bao.toByteArray(); + byte[] data = fileToByteArray(f); ByteArrayInputStream bin = new ByteArrayInputStream(data); - ColorSchemaReader reader = new ColorSchemaReader(); - Collection schemas = reader.readColorSchema(bin, TextFileUtils.getHeaderParametersFromFile(new ByteArrayInputStream(data))); assertNotNull(schemas); @@ -218,8 +197,6 @@ public class ColorSchemaReaderTest extends ServiceTestFunctions { @Test public void testReadInvalidGeneVariantsSchema() throws Exception { try { - ColorSchemaReader reader = new ColorSchemaReader(); - reader.readColorSchema("testFiles/coloring/gene_variants_invalid_genome.txt"); fail("Exception expected"); } catch (InvalidColorSchemaException e) { @@ -232,8 +209,6 @@ public class ColorSchemaReaderTest extends ServiceTestFunctions { @Test public void testReadSchema2() throws Exception { try { - ColorSchemaReader reader = new ColorSchemaReader(); - Collection schemas = reader.readColorSchema("testFiles/coloring/goodSchema.txt"); assertNotNull(schemas); @@ -249,7 +224,6 @@ public class ColorSchemaReaderTest extends ServiceTestFunctions { public void testReadSchema3() throws Exception { try { try { - ColorSchemaReader reader = new ColorSchemaReader(); reader.readColorSchema("testFiles/coloring/wrongSchema.txt"); fail("Excepion expected"); } catch (InvalidColorSchemaException e) { @@ -263,7 +237,6 @@ public class ColorSchemaReaderTest extends ServiceTestFunctions { @Test public void testProblematicStephanSchema3() throws Exception { try { - ColorSchemaReader reader = new ColorSchemaReader(); reader.readColorSchema("testFiles/coloring/problematicSchema.txt"); } catch (Exception e) { e.printStackTrace(); @@ -274,7 +247,6 @@ public class ColorSchemaReaderTest extends ServiceTestFunctions { @Test public void testReadReactionSchema() throws Exception { try { - ColorSchemaReader reader = new ColorSchemaReader(); Collection collection = reader.readColorSchema("testFiles/coloring/reactionSchema.txt"); assertEquals(1, collection.size()); ColorSchema schema = collection.iterator().next(); @@ -290,8 +262,6 @@ public class ColorSchemaReaderTest extends ServiceTestFunctions { @Test(timeout = 15000) public void testNextVersionReadSchema() throws Exception { try { - ColorSchemaReader reader = new ColorSchemaReader(); - Collection schemas = reader.readColorSchema("testFiles/coloring/goodLayout.v=1.0.txt"); assertNotNull(schemas); @@ -310,8 +280,6 @@ public class ColorSchemaReaderTest extends ServiceTestFunctions { Model model = getModelForFile("testFiles/coloring/protein_to_color.xml", false); - ColorSchemaReader reader = new ColorSchemaReader(); - Collection schemas = reader.readColorSchema("testFiles/coloring/problematicSchema.txt"); ColorModelCommand factory = new ColorModelCommand(model, schemas, colorExtractor); factory.execute(); @@ -327,7 +295,6 @@ public class ColorSchemaReaderTest extends ServiceTestFunctions { @Test public void testColoringWithValueOrColor() throws Exception { try { - ColorSchemaReader reader = new ColorSchemaReader(); Map params = new HashMap<>(); params.put(TextFileUtils.COLUMN_COUNT_PARAM, "3"); @@ -346,7 +313,6 @@ public class ColorSchemaReaderTest extends ServiceTestFunctions { @Test public void testElementsByType() throws Exception { try { - ColorSchemaReader reader = new ColorSchemaReader(); Map params = new HashMap<>(); params.put(TextFileUtils.COLUMN_COUNT_PARAM, "3"); @@ -364,7 +330,6 @@ public class ColorSchemaReaderTest extends ServiceTestFunctions { @Test public void testColoringWithInvalidValueAndColor() throws Exception { try { - ColorSchemaReader reader = new ColorSchemaReader(); Map params = new HashMap<>(); params.put(TextFileUtils.COLUMN_COUNT_PARAM, "3"); @@ -378,10 +343,25 @@ public class ColorSchemaReaderTest extends ServiceTestFunctions { } } + @Test + public void testColoringWithEmptyColor() throws Exception { + try { + Map params = new HashMap<>(); + params.put(TextFileUtils.COLUMN_COUNT_PARAM, "3"); + + String input = "name\tcolor\ns1\tx"; + reader.readColorSchema(new ByteArrayInputStream(input.getBytes(StandardCharsets.UTF_8)), params); + fail("Exception expected"); + } catch (InvalidColorSchemaException e) { + } catch (Exception e) { + e.printStackTrace(); + throw e; + } + } + @Test public void testColoringWithInvalidValueAndColor2() throws Exception { try { - ColorSchemaReader reader = new ColorSchemaReader(); Map params = new HashMap<>(); params.put(TextFileUtils.COLUMN_COUNT_PARAM, "3"); @@ -396,8 +376,6 @@ public class ColorSchemaReaderTest extends ServiceTestFunctions { @Test public void testSchemasWithId() throws Exception { try { - ColorSchemaReader reader = new ColorSchemaReader(); - Collection schemas = reader.readColorSchema("testFiles/coloring/schemaWithIdentifiers.txt"); for (ColorSchema colorSchema : schemas) { for (MiriamData md : colorSchema.getMiriamData()) { @@ -415,9 +393,8 @@ public class ColorSchemaReaderTest extends ServiceTestFunctions { @Test public void testSchemasWithIdSnakeCase() throws Exception { try { - ColorSchemaReader reader = new ColorSchemaReader(); - - Collection schemas = reader.readColorSchema("testFiles/coloring/schema_with_identifiers_in_snake_case.txt"); + Collection schemas = reader + .readColorSchema("testFiles/coloring/schema_with_identifiers_in_snake_case.txt"); for (ColorSchema colorSchema : schemas) { for (MiriamData md : colorSchema.getMiriamData()) { assertNotNull(md.getResource()); @@ -434,8 +411,6 @@ public class ColorSchemaReaderTest extends ServiceTestFunctions { @Test public void testSchemasWithEmptyNameAndId() throws Exception { try { - ColorSchemaReader reader = new ColorSchemaReader(); - Collection schemas = reader.readColorSchema("testFiles/coloring/schemaIdWithoutName.txt"); for (ColorSchema colorSchema : schemas) { for (MiriamData md : colorSchema.getMiriamData()) { @@ -453,7 +428,6 @@ public class ColorSchemaReaderTest extends ServiceTestFunctions { @Test public void testSimpleNameSchemas() throws Exception { try { - ColorSchemaReader reader = new ColorSchemaReader(); String input = "#header\ns1\ns2\n"; Collection schemas = reader .readSimpleNameColorSchema(new ByteArrayInputStream(input.getBytes(StandardCharsets.UTF_8))); @@ -464,4 +438,47 @@ public class ColorSchemaReaderTest extends ServiceTestFunctions { } } + @Test + public void testParseSpeciesTypesForEmptyString() throws Exception { + List> classes = reader.parseSpeciesTypes("", null); + assertEquals(0, classes.size()); + } + + @Test + public void testColoringWithModelName() throws Exception { + try { + String input = "name\tcolor\tmodel_name\n" + + "s1\t#ff0000\txxx\n" ; + Map params = new HashMap<>(); + params.put(TextFileUtils.COLUMN_COUNT_PARAM, "3"); + + Collection schemas = reader + .readColorSchema(new ByteArrayInputStream(input.getBytes(StandardCharsets.UTF_8)), params); + assertEquals(1, schemas.size()); + assertNotNull(schemas.iterator().next().getModelName()); + } catch (Exception e) { + e.printStackTrace(); + throw e; + } + } + + @Test + public void testColoringWithMapName() throws Exception { + try { + String input = "name\tcolor\tmap_name\n" + + "s1\t#ff0000\txxx\n" ; + Map params = new HashMap<>(); + params.put(TextFileUtils.COLUMN_COUNT_PARAM, "3"); + + Collection schemas = reader + .readColorSchema(new ByteArrayInputStream(input.getBytes(StandardCharsets.UTF_8)), params); + assertEquals(1, schemas.size()); + assertNotNull(schemas.iterator().next().getModelName()); + } catch (Exception e) { + e.printStackTrace(); + throw e; + } + } + + } diff --git a/service/testFiles/coloring/gene_variants_id.txt b/service/testFiles/coloring/gene_variants_id.txt new file mode 100644 index 0000000000000000000000000000000000000000..d2ee602490c763ccd85f629550d4a0eeaf965bd6 --- /dev/null +++ b/service/testFiles/coloring/gene_variants_id.txt @@ -0,0 +1,5 @@ +#TYPE=GENETIC_VARIANT +#GENOME_TYPE=UCSC +#GENOME_VERSION=hg38 +position original_dna alternative_dna Entrez gene description color contig +10146 AC A 123456 upstream #ff0000 chr1