diff --git a/CHANGELOG b/CHANGELOG index 78678e8a57f3d3ac835b7ee1db5b13313db117a6..619737659da8ef4cd3c21a798cbbf784ec44b50d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,6 +2,7 @@ minerva (19.0.0~alpha.0) stable; urgency=medium * Backward incompatible: copying annotations into complex children moved to a dedicated annotator (#2216) * Backward incompatible: caching should be done in the background (#1656) + * Backward incompatible: annotations are performed asynchronously (#1657) * Backward incompatible: MiRNA search has been removed (#2102) * Backward incompatible: degraded does not have a name (#2116) * Backward incompatible: custom annotation validation on project upload diff --git a/annotation/src/main/java/lcsb/mapviewer/annotation/services/ModelAnnotator.java b/annotation/src/main/java/lcsb/mapviewer/annotation/services/ModelAnnotator.java index 0861b2a4170de7ef671a11d5dbca878372226d7e..a00cad55195a4d25b71f710aa45f4e32acf0c084 100644 --- a/annotation/src/main/java/lcsb/mapviewer/annotation/services/ModelAnnotator.java +++ b/annotation/src/main/java/lcsb/mapviewer/annotation/services/ModelAnnotator.java @@ -4,15 +4,21 @@ import lcsb.mapviewer.annotation.services.annotators.AnnotatorException; import lcsb.mapviewer.annotation.services.annotators.ElementAnnotator; import lcsb.mapviewer.annotation.services.annotators.IElementAnnotator; import lcsb.mapviewer.common.IProgressUpdater; -import lcsb.mapviewer.common.exception.InvalidArgumentException; +import lcsb.mapviewer.model.Project; import lcsb.mapviewer.model.map.BioEntity; import lcsb.mapviewer.model.map.MiriamType; -import lcsb.mapviewer.model.map.model.Model; +import lcsb.mapviewer.model.map.reaction.Reaction; +import lcsb.mapviewer.model.map.species.Element; import lcsb.mapviewer.model.user.UserAnnotationSchema; import lcsb.mapviewer.model.user.UserClassAnnotators; import lcsb.mapviewer.model.user.annotator.AnnotatorData; import lcsb.mapviewer.modelutils.map.ClassTreeNode; import lcsb.mapviewer.modelutils.map.ElementUtils; +import lcsb.mapviewer.persist.DbUtils; +import lcsb.mapviewer.persist.dao.map.ReactionDao; +import lcsb.mapviewer.persist.dao.map.ReactionProperty; +import lcsb.mapviewer.persist.dao.map.species.ElementDao; +import lcsb.mapviewer.persist.dao.map.species.ElementProperty; import lcsb.mapviewer.persist.dao.user.UserAnnotationSchemaDao; import org.apache.commons.collections4.ListUtils; import org.apache.logging.log4j.LogManager; @@ -21,9 +27,9 @@ import org.springframework.aop.framework.Advised; import org.springframework.aop.support.AopUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; @@ -31,19 +37,11 @@ import java.util.List; import java.util.Map; import java.util.Queue; import java.util.Set; +import java.util.stream.Collectors; -/** - * This class annotates all elements in the model. - * - * @author Piotr Gawron - */ @Service -@Transactional public class ModelAnnotator { - /** - * Default class logger. - */ private static final Logger logger = LogManager.getLogger(); /** @@ -51,37 +49,22 @@ public class ModelAnnotator { */ private final List<IElementAnnotator> availableAnnotators; - /** - * List of all available {@link ElementAnnotator} objects. - */ - private final List<IElementAnnotator> defaultAnnotators; - private final UserAnnotationSchemaDao userAnnotationSchemaDao; + private final ElementDao elementDao; + private final ReactionDao reactionDao; + private final DbUtils dbUtils; - /** - * Constructor - */ @Autowired public ModelAnnotator(final List<IElementAnnotator> availableAnnotators, - final UserAnnotationSchemaDao userAnnotationSchemaDao) { + final UserAnnotationSchemaDao userAnnotationSchemaDao, + final ElementDao elementDao, + final ReactionDao reactionDao, + final DbUtils dbUtils) { this.availableAnnotators = availableAnnotators; this.userAnnotationSchemaDao = userAnnotationSchemaDao; - defaultAnnotators = new ArrayList<>(); - for (final IElementAnnotator annotator : availableAnnotators) { - if (annotator.isDefault()) { - defaultAnnotators.add(annotator); - } - } - } - - /** - * Performs all possible and automatic annotations on the model. - * - * @param model model to update - * @param progressUpdater callback function used for updating progress of the function - */ - public void performAnnotations(final Model model, final IProgressUpdater progressUpdater) { - performAnnotations(model, progressUpdater, new UserAnnotationSchema()); + this.dbUtils = dbUtils; + this.elementDao = elementDao; + this.reactionDao = reactionDao; } /** @@ -89,39 +72,69 @@ public class ModelAnnotator { * * @param inputAnnotationSchema information about {@link ElementAnnotator} objects that should be * used for a given classes - * @param model model to update + * @param project project to update * @param progressUpdater callback function used for updating progress of the function */ - public void performAnnotations(final Model model, final IProgressUpdater progressUpdater, final UserAnnotationSchema inputAnnotationSchema) { - + public void performAnnotations(final Project project, final IProgressUpdater progressUpdater, final UserAnnotationSchema inputAnnotationSchema) { UserAnnotationSchema annotationSchema = inputAnnotationSchema; if (annotationSchema != null && annotationSchema.getId() > 0) { annotationSchema = userAnnotationSchemaDao.getById(annotationSchema.getId()); } + if (annotationSchema == null) { + logger.error("Annotation Schema does not exist"); + annotationSchema = new UserAnnotationSchema(); + } + ElementUtils elementUtils = new ElementUtils(); progressUpdater.setProgress(0); - List<Model> models = new ArrayList<>(); - models.add(model); - models.addAll(model.getSubmodels()); - final double size = models.size(); + double counter = 0; - for (final Model m : models) { - final double ratio = counter / size; + Map<ElementProperty, Object> elementFilter = new HashMap<>(); + elementFilter.put(ElementProperty.PROJECT_ID, Collections.singletonList(project.getProjectId())); - annotateModel(m, progress -> progressUpdater.setProgress(ratio * IProgressUpdater.MAX_PROGRESS / size), annotationSchema); - counter++; - } - } + List<Integer> elementIds = dbUtils.callInTransactionWithResult(() -> elementDao.getAll(elementFilter)) + .stream() + .map(Element::getId) + .collect(Collectors.toList()); - protected void annotateModel(final Model model, final IProgressUpdater progressUpdater, final UserAnnotationSchema annotationSchema) { - progressUpdater.setProgress(0); - ElementUtils elementUtils = new ElementUtils(); + Map<ReactionProperty, Object> reactionFilter = new HashMap<>(); + reactionFilter.put(ReactionProperty.PROJECT_ID, Collections.singletonList(project.getProjectId())); - double counter = 0; - double amount = model.getElements().size() + model.getReactions().size(); + List<Integer> reactionIds = dbUtils.callInTransactionWithResult(() -> reactionDao.getAll(reactionFilter)) + .stream() + .map(Reaction::getId) + .collect(Collectors.toList()); + + double amount = elementIds.size() + reactionIds.size(); // annotate all elements - for (final BioEntity element : model.getBioEntities()) { + for (final Integer reactionId : reactionIds) { + Reaction reaction = dbUtils.callInTransactionWithResult(() -> reactionDao.getById(reactionId)); + + List<AnnotatorData> list = annotationSchema.getAnnotatorsForClass(reaction.getClass()); + if (list == null) { + list = getDefaultAnnotators(reaction.getClass()); + } + for (final AnnotatorData annotatorData : list) { + IElementAnnotator elementAnnotator = null; + try { + elementAnnotator = getAnnotator(annotatorData); + elementAnnotator.annotateElement(reaction, annotatorData); + dbUtils.callInTransaction(() -> { + reactionDao.update(reaction); + return null; + }); + } catch (final AnnotatorException e) { + logger.warn("{} annotation problem.", elementUtils.getElementTag(reaction, elementAnnotator), e); + } + } + counter++; + progressUpdater.setProgress(IProgressUpdater.MAX_PROGRESS * counter / amount); + } + + for (final Integer elementId : elementIds) { + Element element = dbUtils.callInTransactionWithResult(() -> elementDao.getById(elementId)); + List<AnnotatorData> list = annotationSchema.getAnnotatorsForClass(element.getClass()); if (list == null) { list = getDefaultAnnotators(element.getClass()); @@ -131,35 +144,30 @@ public class ModelAnnotator { try { elementAnnotator = getAnnotator(annotatorData); elementAnnotator.annotateElement(element, annotatorData); + dbUtils.callInTransaction(() -> { + elementDao.update(element); + return null; + }); } catch (final AnnotatorException e) { - logger.warn(elementUtils.getElementTag(element, elementAnnotator) + " annotation problem.", e); + logger.warn("{} annotation problem.", elementUtils.getElementTag(element, elementAnnotator), e); } } counter++; progressUpdater.setProgress(IProgressUpdater.MAX_PROGRESS * counter / amount); } + } private IElementAnnotator getAnnotator(final AnnotatorData annotatorData) throws AnnotatorException { for (final IElementAnnotator proxiedElementAnnotator : availableAnnotators) { IElementAnnotator elementAnnotator = (IElementAnnotator) unwrapProxy(proxiedElementAnnotator); if (annotatorData.getAnnotatorClassName().isAssignableFrom(elementAnnotator.getClass())) { - return elementAnnotator; + return proxiedElementAnnotator; } } throw new AnnotatorException("Annotator not supported: " + annotatorData.getAnnotatorClassName()); } - /** - * Returns list of default annotators ({@link ElementAnnotator} with - * {@link ElementAnnotator#isDefault()} flag). - * - * @return list of default annotators - */ - List<IElementAnnotator> getDefaultAnnotators() { - return defaultAnnotators; - } - /** * Returns list of default {@link ElementAnnotator} names that are available * for class given in the parameter. @@ -215,30 +223,6 @@ public class ModelAnnotator { return result; } - /** - * Converts list of strings into list of {@link ElementAnnotator}. Strings - * must be valid {@link ElementAnnotator} common names. - * - * @param list list of {@link ElementAnnotator#getCommonName()}. - * @return list of {@link ElementAnnotator annotators} - */ - public List<IElementAnnotator> getAnnotatorsFromCommonNames(final List<AnnotatorData> list) { - List<IElementAnnotator> result = new ArrayList<>(); - for (final AnnotatorData annotatorData : list) { - boolean added = false; - for (final IElementAnnotator annotator : availableAnnotators) { - if (annotatorData.getAnnotatorClassName().isAssignableFrom(unwrapProxy(annotator).getClass())) { - added = true; - result.add(annotator); - } - } - if (!added) { - throw new InvalidArgumentException("Unknown annotator name: " + annotatorData.getAnnotatorClassName()); - } - } - return result; - } - /** * Returns map with information about default valid {@link MiriamType miriam * types } for {@link BioEntity} class type. @@ -247,15 +231,15 @@ public class ModelAnnotator { */ @SuppressWarnings("unchecked") public Map<Class<? extends BioEntity>, Set<MiriamType>> getDefaultValidClasses() { - Map<Class<? extends BioEntity>, Set<MiriamType>> result = new HashMap<Class<? extends BioEntity>, Set<MiriamType>>(); + Map<Class<? extends BioEntity>, Set<MiriamType>> result = new HashMap<>(); ElementUtils eu = new ElementUtils(); ClassTreeNode tree = eu.getAnnotatedElementClassTree(); - Queue<ClassTreeNode> nodes = new LinkedList<ClassTreeNode>(); + Queue<ClassTreeNode> nodes = new LinkedList<>(); nodes.add(tree); while (!nodes.isEmpty()) { ClassTreeNode node = nodes.poll(); - Set<MiriamType> set = new HashSet<MiriamType>(); + Set<MiriamType> set = new HashSet<>(); Class<? extends BioEntity> clazz = (Class<? extends BioEntity>) node.getClazz(); for (final MiriamType mt : MiriamType.values()) { for (final Class<?> clazz2 : mt.getValidClass()) { @@ -265,9 +249,7 @@ public class ModelAnnotator { } } result.put(clazz, set); - for (final ClassTreeNode child : node.getChildren()) { - nodes.add(child); - } + nodes.addAll(node.getChildren()); } return result; } @@ -302,9 +284,7 @@ public class ModelAnnotator { } } result.put(clazz, set); - for (final ClassTreeNode child : node.getChildren()) { - nodes.add(child); - } + nodes.addAll(node.getChildren()); } return result; } diff --git a/annotation/src/main/java/lcsb/mapviewer/annotation/services/annotators/CopyMissingCellDesignerMiriamDataAnnotatorImpl.java b/annotation/src/main/java/lcsb/mapviewer/annotation/services/annotators/CopyMissingCellDesignerMiriamDataAnnotatorImpl.java index d6557cb24d6c272abcab765e5952211b35789a89..92589a43d4667ef868c136169c9d53929e4cfae6 100644 --- a/annotation/src/main/java/lcsb/mapviewer/annotation/services/annotators/CopyMissingCellDesignerMiriamDataAnnotatorImpl.java +++ b/annotation/src/main/java/lcsb/mapviewer/annotation/services/annotators/CopyMissingCellDesignerMiriamDataAnnotatorImpl.java @@ -14,6 +14,7 @@ import lcsb.mapviewer.persist.dao.map.species.ElementProperty; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; import java.util.Collections; @@ -22,6 +23,7 @@ import java.util.List; import java.util.Map; @Service +@Transactional public class CopyMissingCellDesignerMiriamDataAnnotatorImpl extends ElementAnnotator implements CopyMissingCellDesignerMiriamDataAnnotator { private final ElementDao elementDao; diff --git a/annotation/src/main/java/lcsb/mapviewer/annotation/services/annotators/ElementAnnotator.java b/annotation/src/main/java/lcsb/mapviewer/annotation/services/annotators/ElementAnnotator.java index f20fbaed811e371cf0fd3deaf862546e8cbb4048..c9725246dd9fd85d4ce8e429ac83a1ca629f898b 100644 --- a/annotation/src/main/java/lcsb/mapviewer/annotation/services/annotators/ElementAnnotator.java +++ b/annotation/src/main/java/lcsb/mapviewer/annotation/services/annotators/ElementAnnotator.java @@ -6,7 +6,6 @@ import lcsb.mapviewer.common.comparator.SetComparator; import lcsb.mapviewer.common.comparator.StringComparator; import lcsb.mapviewer.common.exception.InvalidArgumentException; import lcsb.mapviewer.common.exception.NotImplementedException; -import lcsb.mapviewer.converter.model.celldesigner.structure.CellDesignerChemical; import lcsb.mapviewer.model.LogMarker; import lcsb.mapviewer.model.ProjectLogEntryType; import lcsb.mapviewer.model.map.BioEntity; @@ -101,7 +100,7 @@ public abstract class ElementAnnotator extends CachableInterface implements IEle * * @param bioEntity object to be annotated * @param parameters list of parameters passed to the annotator which is expected to be - * in the same order as its {@link this#parameterDefs} + * in the same order as its {@link this#paramsDefs} * @throws AnnotatorException thrown when there is a problem with annotating not related to data */ @Override @@ -109,10 +108,10 @@ public abstract class ElementAnnotator extends CachableInterface implements IEle if (isAnnotatable(bioEntity)) { final BioEntityProxy proxy = new BioEntityProxy(bioEntity, parameters); List<AnnotatorInputParameter> inputParameters = parameters.getInputParameters(); - if (inputParameters.size() == 0) { + if (inputParameters.isEmpty()) { inputParameters = getAvailableInputParameters(); } - if (parameters.getOutputParameters().size() == 0) { + if (parameters.getOutputParameters().isEmpty()) { parameters.addAnnotatorParameters(this.getAvailableOuputProperties()); } final List<Set<Object>> inputs = getInputsParameters(bioEntity, inputParameters); @@ -164,8 +163,7 @@ public abstract class ElementAnnotator extends CachableInterface implements IEle * the parameter. * * @param object object to be tested if can be annotated - * @return <code>true</code> if object can be annotated by this annotator, - * <code>false</code> otherwise + * @return <code>true</code> if object can be annotated by this annotator, <code>false</code> otherwise */ @Override public boolean isAnnotatable(final BioEntity object) { @@ -183,8 +181,7 @@ public abstract class ElementAnnotator extends CachableInterface implements IEle * class type. * * @param clazz class to be tested if can be annotated - * @return <code>true</code> if class can be annotated by this annotator, - * <code>false</code> otherwise + * @return <code>true</code> if class can be annotated by this annotator, <code>false</code> otherwise */ @Override public boolean isAnnotatable(final Class<?> clazz) { @@ -327,8 +324,6 @@ public abstract class ElementAnnotator extends CachableInterface implements IEle /** * Creates default {@link AnnotatorData} for this {@link ElementAnnotator}. - * - * @return */ @Override public AnnotatorData createAnnotatorData() { @@ -456,9 +451,9 @@ public abstract class ElementAnnotator extends CachableInterface implements IEle if (!parameters.hasOutputField(field)) { return false; } - if (oldCollection == null || oldCollection.size() == 0) { + if (oldCollection == null || oldCollection.isEmpty()) { return true; - } else if (newCollection == null || newCollection.size() == 0) { + } else if (newCollection == null || newCollection.isEmpty()) { return false; } else { final SetComparator<String> stringSetComparator = new SetComparator<>(new StringComparator()); @@ -526,13 +521,13 @@ public abstract class ElementAnnotator extends CachableInterface implements IEle public void setFormerSymbols(final Collection<String> formerSymbols) { if (originalBioEntity instanceof Element) { final Element element = (Element) originalBioEntity; - if (canAssignStringSet(formerSymbols, element.getFormerSymbols(), BioEntityField.SYMBOL)) { + if (canAssignStringSet(formerSymbols, element.getFormerSymbols(), BioEntityField.PREVIOUS_SYMBOLS)) { final List<String> sorted = new ArrayList<>(formerSymbols); Collections.sort(sorted); element.setFormerSymbols(sorted); } } else { - logger.warn("Cannot assign " + BioEntityField.SYMBOL.getCommonName() + " to " + logger.warn("Cannot assign " + BioEntityField.PREVIOUS_SYMBOLS.getCommonName() + " to " + originalBioEntity.getClass().getSimpleName()); } } @@ -571,7 +566,7 @@ public abstract class ElementAnnotator extends CachableInterface implements IEle } /** - * Sets {@link CellDesignerChemical#smiles}. + * Sets {@link Chemical#getSmiles()}. * * @param smile value to set */ @@ -588,7 +583,7 @@ public abstract class ElementAnnotator extends CachableInterface implements IEle } /** - * Sets {@link Species#charge}. + * Sets {@link Species#getCharge()}. * * @param charge value to set */ @@ -605,7 +600,7 @@ public abstract class ElementAnnotator extends CachableInterface implements IEle } /** - * Sets {@link Reaction#subsystem}. + * Sets {@link Reaction#getSubsystem()}. * * @param subsystem value to set */ diff --git a/annotation/src/main/java/lcsb/mapviewer/annotation/services/annotators/HgncAnnotatorImpl.java b/annotation/src/main/java/lcsb/mapviewer/annotation/services/annotators/HgncAnnotatorImpl.java index f361ea9493f22e1a7eaf1eb2a84d6627762b8c74..a50a6452f080558ad6d76d563cf7f69d46d99959 100644 --- a/annotation/src/main/java/lcsb/mapviewer/annotation/services/annotators/HgncAnnotatorImpl.java +++ b/annotation/src/main/java/lcsb/mapviewer/annotation/services/annotators/HgncAnnotatorImpl.java @@ -66,7 +66,7 @@ public class HgncAnnotatorImpl extends ElementAnnotator implements HgncAnnotator proteinAlias.addMiriamData(getExampleValidAnnotation()); annotateElement(proteinAlias); - if (proteinAlias.getFullName() == null || proteinAlias.getFullName().equals("")) { + if (proteinAlias.getFullName() == null || proteinAlias.getFullName().isEmpty()) { status.setStatus(ExternalServiceStatusType.CHANGED); } else { status.setStatus(ExternalServiceStatusType.OK); @@ -98,7 +98,7 @@ public class HgncAnnotatorImpl extends ElementAnnotator implements HgncAnnotator Node response = XmlParser.getNode("response", xml.getChildNodes()); Node result = XmlParser.getNode("result", response.getChildNodes()); String count = XmlParser.getNodeAttr("numFound", result); - if (count == null || count.equals("") || count.equals("0")) { + if (count == null || count.isEmpty() || count.equals("0")) { // print warn info when we have only hgnc identifiers (or no // identifiers at all) logger.warn(element.getLogMarker(ProjectLogEntryType.CANNOT_FIND_INFORMATION), @@ -124,7 +124,7 @@ public class HgncAnnotatorImpl extends ElementAnnotator implements HgncAnnotator element.addMiriamData(MiriamType.ENTREZ, XmlParser.getNodeValue(node)); } else if (type.equals("symbol")) { if (!identifier.getDataType().equals(MiriamType.HGNC_SYMBOL) || !element.contains(identifier)) { - // add hgnc symbol annnotation only when there was no + // add hgnc symbol annotation only when there was no // hgnc_symbol in the element element.addMiriamData(MiriamType.HGNC_SYMBOL, XmlParser.getNodeValue(node)); } @@ -214,6 +214,7 @@ public class HgncAnnotatorImpl extends ElementAnnotator implements HgncAnnotator new AnnotatorOutputParameter(MiriamType.REFSEQ), new AnnotatorOutputParameter(MiriamType.UNIPROT), new AnnotatorOutputParameter(BioEntityField.SYMBOL), + new AnnotatorOutputParameter(BioEntityField.PREVIOUS_SYMBOLS), new AnnotatorOutputParameter(BioEntityField.SYNONYMS), new AnnotatorOutputParameter(BioEntityField.NAME), new AnnotatorOutputParameter(BioEntityField.FULL_NAME)); @@ -273,7 +274,7 @@ public class HgncAnnotatorImpl extends ElementAnnotator implements HgncAnnotator Node response = XmlParser.getNode("response", xml.getChildNodes()); Node resultNode = XmlParser.getNode("result", response.getChildNodes()); String count = XmlParser.getNodeAttr("numFound", resultNode); - if (count == null || count.equals("") || count.equals("0")) { + if (count == null || count.isEmpty() || count.equals("0")) { return result; } else { Node entry = XmlParser.getNode("doc", resultNode.getChildNodes()); @@ -332,7 +333,7 @@ public class HgncAnnotatorImpl extends ElementAnnotator implements HgncAnnotator Node response = XmlParser.getNode("response", xml.getChildNodes()); Node resultNode = XmlParser.getNode("result", response.getChildNodes()); String count = XmlParser.getNodeAttr("numFound", resultNode); - if (count == null || count.equals("") || count.equals("0")) { + if (count == null || count.isEmpty() || count.equals("0")) { return null; } else { Node entry = XmlParser.getNode("doc", resultNode.getChildNodes()); @@ -400,7 +401,7 @@ public class HgncAnnotatorImpl extends ElementAnnotator implements HgncAnnotator Node response = XmlParser.getNode("response", xml.getChildNodes()); Node result = XmlParser.getNode("result", response.getChildNodes()); String count = XmlParser.getNodeAttr("numFound", result); - if (count == null || count.equals("") || count.equals("0")) { + if (count == null || count.isEmpty() || count.equals("0")) { return null; } else { Node entry = XmlParser.getNode("doc", result.getChildNodes()); @@ -420,9 +421,7 @@ public class HgncAnnotatorImpl extends ElementAnnotator implements HgncAnnotator } } return null; - } catch (final IOException e) { - throw new AnnotatorException(e); - } catch (final InvalidXmlSchemaException e) { + } catch (final IOException | InvalidXmlSchemaException e) { throw new AnnotatorException(e); } } diff --git a/annotation/src/test/java/lcsb/mapviewer/annotation/AnnotationNoTransactionTestFunctions.java b/annotation/src/test/java/lcsb/mapviewer/annotation/AnnotationNoTransactionTestFunctions.java new file mode 100644 index 0000000000000000000000000000000000000000..43ecc94b43f82eccf880b0b854fd3b8a17afb516 --- /dev/null +++ b/annotation/src/test/java/lcsb/mapviewer/annotation/AnnotationNoTransactionTestFunctions.java @@ -0,0 +1,123 @@ +package lcsb.mapviewer.annotation; + +import lcsb.mapviewer.annotation.cache.GeneralCacheInterface; +import lcsb.mapviewer.common.tests.TestUtils; +import lcsb.mapviewer.common.tests.UnitTestFailedWatcher; +import lcsb.mapviewer.converter.ConverterParams; +import lcsb.mapviewer.converter.model.celldesigner.CellDesignerXmlParser; +import lcsb.mapviewer.model.Project; +import lcsb.mapviewer.model.graphics.HorizontalAlign; +import lcsb.mapviewer.model.graphics.LineType; +import lcsb.mapviewer.model.graphics.PolylineData; +import lcsb.mapviewer.model.graphics.VerticalAlign; +import lcsb.mapviewer.model.map.model.Model; +import lcsb.mapviewer.model.map.model.ModelData; +import lcsb.mapviewer.model.map.reaction.Reaction; +import lcsb.mapviewer.model.map.species.GenericProtein; +import lcsb.mapviewer.model.map.species.Ion; +import lcsb.mapviewer.model.map.species.Species; +import lcsb.mapviewer.model.user.User; +import lcsb.mapviewer.persist.dao.cache.CacheTypeDao; +import org.junit.Rule; +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 java.util.HashMap; +import java.util.Map; + +@ContextConfiguration(classes = SpringAnnotationTestConfig.class) +@RunWith(SpringJUnit4ClassRunner.class) +public abstract class AnnotationNoTransactionTestFunctions extends TestUtils { + + private static final Map<String, Model> models = new HashMap<>(); + + @Rule + public UnitTestFailedWatcher unitTestFailedWatcher = new UnitTestFailedWatcher(); + + @Autowired + protected GeneralCacheInterface cache; + + @Autowired + protected CacheTypeDao cacheTypeDao; + + private static int idCounter = 0; + + protected Model getModelForFile(final String fileName, final boolean fromCache) throws Exception { + if (!fromCache) { + logger.debug("File without cache: {}", fileName); + return new CellDesignerXmlParser().createModel(new ConverterParams().filename(fileName)); + } + Model result = AnnotationNoTransactionTestFunctions.models.get(fileName); + if (result == null) { + logger.debug("File to cache: {}", fileName); + + CellDesignerXmlParser parser = new CellDesignerXmlParser(); + result = parser.createModel(new ConverterParams().filename(fileName).sizeAutoAdjust(false)); + AnnotationNoTransactionTestFunctions.models.put(fileName, result); + } + return result; + } + + public GenericProtein createProtein() { + GenericProtein result = new GenericProtein("s" + (idCounter++)); + populateRandomData(result); + + return result; + } + + private static void populateRandomData(final Species result) { + result.setNameX(faker.number().numberBetween(10, 100)); + result.setNameY(faker.number().numberBetween(10, 100)); + result.setZ(faker.number().numberBetween(10, 100)); + result.setNameWidth(faker.number().numberBetween(10, 100)); + result.setNameHeight(faker.number().numberBetween(10, 100)); + result.setHomodimer(faker.number().numberBetween(1, 10)); + result.setNameHorizontalAlign(faker.options().option(HorizontalAlign.class)); + result.setNameVerticalAlign(faker.options().option(VerticalAlign.class)); + result.setHypothetical(faker.bool().bool()); + result.setName(faker.name().fullName()); + } + + public Ion createIon() { + Ion result = new Ion("s" + (idCounter++)); + populateRandomData(result); + + return result; + } + + public ModelData createModelData() { + ModelData model = new ModelData(); + model.setWidth(faker.number().numberBetween(1, 1000)); + model.setHeight(faker.number().numberBetween(1, 1000)); + + return model; + } + + public Project createProject() { + User admin = new User(); + admin.setId(BUILT_IN_TEST_ADMIN_ID); + + Project project = new Project(); + project.setProjectId(faker.numerify("T######")); + project.setOwner(admin); + + return project; + } + + protected Reaction createEmptyReaction() { + Reaction reaction = new Reaction(faker.numerify("re######")); + reaction.setLine(createLine()); + reaction.setZ(faker.number().numberBetween(1, 10)); + + return reaction; + } + + private PolylineData createLine() { + PolylineData line = new PolylineData(); + line.setType(faker.options().option(LineType.class)); + return line; + } + +} diff --git a/annotation/src/test/java/lcsb/mapviewer/annotation/AnnotationTestFunctions.java b/annotation/src/test/java/lcsb/mapviewer/annotation/AnnotationTestFunctions.java index 773ea51aca3896bd6bfa673b7273b961d0b6e4a7..1b385167c0db6ac5b6614c20a45e7401c9133534 100644 --- a/annotation/src/test/java/lcsb/mapviewer/annotation/AnnotationTestFunctions.java +++ b/annotation/src/test/java/lcsb/mapviewer/annotation/AnnotationTestFunctions.java @@ -1,95 +1,13 @@ package lcsb.mapviewer.annotation; -import lcsb.mapviewer.annotation.cache.GeneralCacheInterface; -import lcsb.mapviewer.common.tests.TestUtils; -import lcsb.mapviewer.common.tests.UnitTestFailedWatcher; -import lcsb.mapviewer.converter.ConverterParams; -import lcsb.mapviewer.converter.model.celldesigner.CellDesignerXmlParser; -import lcsb.mapviewer.model.graphics.HorizontalAlign; -import lcsb.mapviewer.model.graphics.VerticalAlign; -import lcsb.mapviewer.model.map.model.Model; -import lcsb.mapviewer.model.map.model.ModelData; -import lcsb.mapviewer.model.map.species.GenericProtein; -import lcsb.mapviewer.model.map.species.Ion; -import lcsb.mapviewer.model.map.species.Species; -import lcsb.mapviewer.persist.dao.cache.CacheTypeDao; -import org.junit.Rule; 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.transaction.annotation.Transactional; -import java.util.HashMap; -import java.util.Map; - @Transactional @ContextConfiguration(classes = SpringAnnotationTestConfig.class) @RunWith(SpringJUnit4ClassRunner.class) -public abstract class AnnotationTestFunctions extends TestUtils { - - private static final Map<String, Model> models = new HashMap<>(); - - @Rule - public UnitTestFailedWatcher unitTestFailedWatcher = new UnitTestFailedWatcher(); - - @Autowired - protected GeneralCacheInterface cache; - - @Autowired - protected CacheTypeDao cacheTypeDao; - - private static int idCounter = 0; - - protected Model getModelForFile(final String fileName, final boolean fromCache) throws Exception { - if (!fromCache) { - logger.debug("File without cache: {}", fileName); - return new CellDesignerXmlParser().createModel(new ConverterParams().filename(fileName)); - } - Model result = AnnotationTestFunctions.models.get(fileName); - if (result == null) { - logger.debug("File to cache: {}", fileName); - - CellDesignerXmlParser parser = new CellDesignerXmlParser(); - result = parser.createModel(new ConverterParams().filename(fileName).sizeAutoAdjust(false)); - AnnotationTestFunctions.models.put(fileName, result); - } - return result; - } - - public GenericProtein createProtein() { - GenericProtein result = new GenericProtein("s" + (idCounter++)); - populateRandomData(result); - - return result; - } - - private static void populateRandomData(final Species result) { - result.setNameX(faker.number().numberBetween(10, 100)); - result.setNameY(faker.number().numberBetween(10, 100)); - result.setZ(faker.number().numberBetween(10, 100)); - result.setNameWidth(faker.number().numberBetween(10, 100)); - result.setNameHeight(faker.number().numberBetween(10, 100)); - result.setHomodimer(faker.number().numberBetween(1, 10)); - result.setNameHorizontalAlign(faker.options().option(HorizontalAlign.class)); - result.setNameVerticalAlign(faker.options().option(VerticalAlign.class)); - result.setHypothetical(faker.bool().bool()); - result.setName(faker.name().fullName()); - } - - public Ion createIon() { - Ion result = new Ion("s" + (idCounter++)); - populateRandomData(result); - - return result; - } - - public ModelData createModelData() { - ModelData model = new ModelData(); - model.setWidth(faker.number().numberBetween(1, 1000)); - model.setHeight(faker.number().numberBetween(1, 1000)); - - return model; - } +public abstract class AnnotationTestFunctions extends AnnotationNoTransactionTestFunctions { } diff --git a/annotation/src/test/java/lcsb/mapviewer/annotation/services/ModelAnnotatorTest.java b/annotation/src/test/java/lcsb/mapviewer/annotation/services/ModelAnnotatorTest.java index 4c0b8d8d3b740145734cf83af26ac7ec29461195..ee55829d8da801de411ff11c1b4c98622c79bc04 100644 --- a/annotation/src/test/java/lcsb/mapviewer/annotation/services/ModelAnnotatorTest.java +++ b/annotation/src/test/java/lcsb/mapviewer/annotation/services/ModelAnnotatorTest.java @@ -1,16 +1,17 @@ package lcsb.mapviewer.annotation.services; -import lcsb.mapviewer.annotation.AnnotationTestFunctions; +import lcsb.mapviewer.annotation.AnnotationNoTransactionTestFunctions; import lcsb.mapviewer.annotation.services.annotators.IElementAnnotator; import lcsb.mapviewer.common.IProgressUpdater; +import lcsb.mapviewer.model.Project; import lcsb.mapviewer.model.map.BioEntity; import lcsb.mapviewer.model.map.MiriamData; import lcsb.mapviewer.model.map.MiriamType; import lcsb.mapviewer.model.map.model.Model; +import lcsb.mapviewer.model.map.model.ModelData; import lcsb.mapviewer.model.map.model.ModelFullIndexed; import lcsb.mapviewer.model.map.model.ModelSubmodelConnection; import lcsb.mapviewer.model.map.model.SubmodelType; -import lcsb.mapviewer.model.map.reaction.Reaction; import lcsb.mapviewer.model.map.species.Element; import lcsb.mapviewer.model.map.species.GenericProtein; import lcsb.mapviewer.model.map.species.Ion; @@ -18,6 +19,8 @@ import lcsb.mapviewer.model.map.species.Protein; import lcsb.mapviewer.model.map.species.Species; import lcsb.mapviewer.model.user.UserAnnotationSchema; import lcsb.mapviewer.model.user.UserClassAnnotators; +import lcsb.mapviewer.persist.DbUtils; +import lcsb.mapviewer.persist.dao.ProjectDao; import lcsb.mapviewer.persist.dao.map.ModelDao; import org.apache.commons.lang3.mutable.MutableDouble; import org.junit.After; @@ -38,11 +41,8 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -public class ModelAnnotatorTest extends AnnotationTestFunctions { - private final IProgressUpdater updater = new IProgressUpdater() { - @Override - public void setProgress(final double progress) { - } +public class ModelAnnotatorTest extends AnnotationNoTransactionTestFunctions { + private final IProgressUpdater updater = progress -> { }; @Autowired @@ -51,50 +51,72 @@ public class ModelAnnotatorTest extends AnnotationTestFunctions { @Autowired private ModelDao modelDao; + @Autowired + private ProjectDao projectDao; + + @Autowired + private DbUtils dbUtils; + + private List<Project> projects = new ArrayList<>(); + @Before public void setUp() throws Exception { + projects = new ArrayList<>(); } @After public void tearDown() throws Exception { + for (Project project : projects) { + dbUtils.callInTransaction(() -> { + projectDao.delete(projectDao.getById(project.getId())); + return null; + }); + } } @Test public void testAnnotateModel() { - Model model = new ModelFullIndexed(null); - - model.addReaction(new Reaction("re")); + ModelData modelData = createModelData(); + modelData.addReaction(createEmptyReaction()); - Species protein1 = new GenericProtein("a1"); + Species protein1 = createProtein(); protein1.setName("SNCA"); - Species protein2 = new GenericProtein("a2"); + Species protein2 = createProtein(); protein2.setName("PDK1"); protein2.addMiriamData(new MiriamData(MiriamType.CAS, "c")); - model.addElement(protein1); - model.addElement(protein2); + modelData.addElement(protein1); + modelData.addElement(protein2); + Project project = createProject(); + project.addModel(modelData); + this.persistProject(project); - modelAnnotator.annotateModel(model, updater, modelAnnotator.createDefaultAnnotatorSchema()); + modelAnnotator.performAnnotations(project, updater, modelAnnotator.createDefaultAnnotatorSchema()); - assertTrue(model.getElementByElementId("a1").getMiriamData().size() > 0); - assertTrue(model.getElementByElementId("a2").getMiriamData().size() >= 1); + dbUtils.callInTransaction(() -> { + Model model = new ModelFullIndexed(modelDao.getById(modelData.getId())); + assertFalse(model.getElementByElementId(protein1.getElementId()).getMiriamData().isEmpty()); + assertFalse(model.getElementByElementId(protein2.getElementId()).getMiriamData().isEmpty()); - modelAnnotator.annotateModel(model, updater, modelAnnotator.createDefaultAnnotatorSchema()); + return null; + }); + modelAnnotator.performAnnotations(project, updater, modelAnnotator.createDefaultAnnotatorSchema()); } + @Test - public void testAnnotateModelWithGivenAnnotators() throws Exception { - Model model = new ModelFullIndexed(null); - GenericProtein species = new GenericProtein("id1"); + public void testAnnotateModelWithGivenAnnotators() { + ModelData modelData = createModelData(); + GenericProtein species = createProtein(); species.setName("SNCA"); - model.addElement(species); + modelData.addElement(species); - Ion species2 = new Ion("id2"); + Ion species2 = createIon(); species2.setName("h2o"); species2.addMiriamData(new MiriamData(MiriamType.CHEBI, "12345")); - model.addElement(species2); - model.addReaction(new Reaction("re")); + modelData.addElement(species2); + modelData.addReaction(createEmptyReaction()); UserAnnotationSchema annotationSchema = new UserAnnotationSchema(); @@ -106,140 +128,196 @@ public class ModelAnnotatorTest extends AnnotationTestFunctions { ionAnnotators.setAnnotators(modelAnnotator.getDefaultAnnotators(Ion.class)); annotationSchema.addClassAnnotator(ionAnnotators); - modelAnnotator.annotateModel(model, updater, annotationSchema); + Project project = createProject(); + project.addModel(modelData); + this.persistProject(project); - // we didn't annotate protein - assertEquals(0, species.getMiriamData().size()); - // but we should annotate ion - assertFalse(species2.getFullName().isEmpty()); + modelAnnotator.performAnnotations(project, updater, annotationSchema); + + dbUtils.callInTransaction(() -> { + Model model = new ModelFullIndexed(modelDao.getById(modelData.getId())); + + Element protein = model.getElementByElementId(species.getElementId()); + Element ion = model.getElementByElementId(species2.getElementId()); + // we didn't annotate protein + assertEquals(0, protein.getMiriamData().size()); + // but we should annotate ion + assertFalse(ion.getFullName().isEmpty()); + + return null; + }); } @Test public void testDuplicateAnnotations() throws Exception { - int counter = 0; - Model model = getModelForFile("testFiles/annotation/duplicate.xml", false); + ModelData modelData = getModelForFile("testFiles/annotation/duplicate.xml", false).getModelData(); - Set<String> knowAnnotations = new HashSet<String>(); + Project project = createProject(); + project.addModel(modelData); + this.persistProject(project); - modelAnnotator.annotateModel(model, updater, modelAnnotator.createDefaultAnnotatorSchema()); - for (final MiriamData md : model.getElementByElementId("sa1").getMiriamData()) { - knowAnnotations.add(md.getDataType() + ":" + md.getRelationType() + ":" + md.getResource()); - } - counter = 0; - for (final String string : knowAnnotations) { - if (string.contains("29108")) { - counter++; + modelAnnotator.performAnnotations(project, updater, modelAnnotator.createDefaultAnnotatorSchema()); + + dbUtils.callInTransaction(() -> { + Model model = new ModelFullIndexed(modelDao.getById(project.getTopModel().getId())); + Set<String> knowAnnotations = new HashSet<>(); + for (final MiriamData md : model.getElementByElementId("sa1").getMiriamData()) { + knowAnnotations.add(md.getDataType() + ":" + md.getRelationType() + ":" + md.getResource()); } - } - assertEquals(1, counter); + int counter = 0; + for (final String string : knowAnnotations) { + if (string.contains("29108")) { + counter++; + } + } + assertEquals(1, counter); + return null; + }); - modelAnnotator.annotateModel(model, updater, modelAnnotator.createDefaultAnnotatorSchema()); + modelAnnotator.performAnnotations(project, updater, modelAnnotator.createDefaultAnnotatorSchema()); - knowAnnotations.clear(); - for (final MiriamData md : model.getElementByElementId("sa1").getMiriamData()) { - knowAnnotations.add(md.getDataType() + ":" + md.getRelationType() + ":" + md.getResource()); - } - counter = 0; - for (final String string : knowAnnotations) { - if (string.contains("29108")) { - counter++; + dbUtils.callInTransaction(() -> { + Model model = new ModelFullIndexed(modelDao.getById(project.getTopModel().getId())); + Set<String> knowAnnotations = new HashSet<>(); + for (final MiriamData md : model.getElementByElementId("sa1").getMiriamData()) { + knowAnnotations.add(md.getDataType() + ":" + md.getRelationType() + ":" + md.getResource()); } - } - assertEquals(1, counter); + int counter = 0; + for (final String string : knowAnnotations) { + if (string.contains("29108")) { + counter++; + } + } + assertEquals(1, counter); + return null; + }); } @Test public void testGetAnnotationsForSYN() throws Exception { - Model model = getModelForFile("testFiles/annotation/emptyAnnotationsSyn1.xml", true); - Element sa1 = model.getElementByElementId("sa1"); - Element sa2 = model.getElementByElementId("sa2"); - - assertFalse(sa1.getNotes().contains("Symbol")); - assertFalse(sa2.getNotes().contains("Symbol")); - modelAnnotator.annotateModel(model, updater, modelAnnotator.createDefaultAnnotatorSchema()); - assertFalse(sa2.getNotes().contains("Symbol")); - assertNotNull(sa1.getSymbol()); - assertNotEquals("", sa1.getSymbol()); - // modelAnnotator.removeIncorrectAnnotations(model, updater); - assertNotNull(sa1.getSymbol()); - assertNotEquals("", sa1.getSymbol()); - assertFalse(sa1.getNotes().contains("Symbol")); - assertFalse(sa2.getNotes().contains("Symbol")); - assertNull(sa2.getSymbol()); - - for (final Species el : model.getSpeciesList()) { - if (el.getNotes() != null) { - assertFalse("Invalid notes: " + el.getNotes(), el.getNotes().contains("Symbol")); - assertFalse("Invalid notes: " + el.getNotes(), el.getNotes().contains("HGNC")); + ModelData modelData = getModelForFile("testFiles/annotation/emptyAnnotationsSyn1.xml", true).getModelData(); + + Project project = createProject(); + project.addModel(modelData); + this.persistProject(project); + modelAnnotator.performAnnotations(project, updater, modelAnnotator.createDefaultAnnotatorSchema()); + + dbUtils.callInTransaction(() -> { + Project p = projectDao.getProjectByProjectId(project.getProjectId()); + Model model = new ModelFullIndexed(modelDao.getById(p.getTopModel().getId())); + + Element sa1 = model.getElementByElementId("sa1"); + Element sa2 = model.getElementByElementId("sa2"); + + assertFalse(sa2.getNotes().contains("Symbol")); + assertNotNull(sa1.getSymbol()); + assertNotEquals("", sa1.getSymbol()); + // modelAnnotator.removeIncorrectAnnotations(model, updater); + assertNotNull(sa1.getSymbol()); + assertNotEquals("", sa1.getSymbol()); + assertFalse(sa1.getNotes().contains("Symbol")); + assertFalse(sa2.getNotes().contains("Symbol")); + assertNull(sa2.getSymbol()); + + for (final Species el : model.getSpeciesList()) { + if (el.getNotes() != null) { + assertFalse("Invalid notes: " + el.getNotes(), el.getNotes().contains("Symbol")); + assertFalse("Invalid notes: " + el.getNotes(), el.getNotes().contains("HGNC")); + } } - } + return null; + }); + } @Test public void testAnnotateModelWithDrugMolecule() throws Exception { - Model model = super.getModelForFile("testFiles/annotation/problematic.xml", false); + ModelData modelData = super.getModelForFile("testFiles/annotation/problematic.xml", false).getModelData(); + + Project project = createProject(); + project.addModel(modelData); + this.persistProject(project); - modelAnnotator.annotateModel(model, updater, modelAnnotator.createDefaultAnnotatorSchema()); - modelDao.add(model); + modelAnnotator.performAnnotations(project, updater, modelAnnotator.createDefaultAnnotatorSchema()); - modelDao.delete(model); } + @Test - public void testGetDefaultRequired() throws Exception { + public void testGetDefaultRequired() { Map<Class<? extends BioEntity>, Set<MiriamType>> map1 = modelAnnotator.getDefaultRequiredClasses(); assertNotNull(map1); } @Test - public void testGetDefaultValid() throws Exception { + public void testGetDefaultValid() { Map<Class<? extends BioEntity>, Set<MiriamType>> map2 = modelAnnotator.getDefaultValidClasses(); assertNotNull(map2); } @Test - public void testPerformAnnotationAndCheckProgress() throws Exception { - Model model = new ModelFullIndexed(null); - Model submodel = new ModelFullIndexed(null); - Model submodel2 = new ModelFullIndexed(null); + public void testPerformAnnotationAndCheckProgress() { + dbUtils.callInTransaction(() -> { + Project project = createProject(); + Model model = new ModelFullIndexed(null); + Model submodel = new ModelFullIndexed(null); + Model submodel2 = new ModelFullIndexed(null); - GenericProtein protein = new GenericProtein("el"); + GenericProtein protein = createProtein(); - model.addSubmodelConnection(new ModelSubmodelConnection(submodel, SubmodelType.UNKNOWN)); - model.addSubmodelConnection(new ModelSubmodelConnection(submodel2, SubmodelType.UNKNOWN)); + model.addSubmodelConnection(new ModelSubmodelConnection(submodel, SubmodelType.UNKNOWN)); + model.addSubmodelConnection(new ModelSubmodelConnection(submodel2, SubmodelType.UNKNOWN)); - model.addElement(protein); - submodel.addElement(protein); - submodel2.addElement(protein); + model.addElement(protein); + submodel.addElement(protein); + submodel2.addElement(protein); - final MutableDouble maxProgress = new MutableDouble(0.0); + final MutableDouble maxProgress = new MutableDouble(0.0); - modelAnnotator.performAnnotations(model, new IProgressUpdater() { - @Override - public void setProgress(final double progress) { - maxProgress.setValue(Math.max(progress, maxProgress.getValue())); - } - }); - assertTrue(maxProgress.getValue() <= IProgressUpdater.MAX_PROGRESS); + project.addModel(model); + project.addModel(submodel); + project.addModel(submodel2); + projectDao.add(project); + + + modelAnnotator.performAnnotations(project, + progress -> maxProgress.setValue(Math.max(progress, maxProgress.getValue())), new UserAnnotationSchema()); + assertTrue(maxProgress.getValue() <= IProgressUpdater.MAX_PROGRESS); + projectDao.delete(project); + + return null; + }); } @Test public void testGetAvailableAnnotators() { List<IElementAnnotator> list = modelAnnotator.getAvailableAnnotators(Protein.class); - assertTrue(list.size() > 0); + assertFalse(list.isEmpty()); } @Test public void testGetAvailableAnnotators2() { List<IElementAnnotator> list = modelAnnotator.getAvailableAnnotators(); - assertTrue(list.size() > 0); + assertFalse(list.isEmpty()); } @Test public void testGetAvailableDefaultAnnotators() { List<IElementAnnotator> list = modelAnnotator.getAvailableDefaultAnnotators(Protein.class); - assertTrue(list.size() > 0); + assertFalse(list.isEmpty()); + } + + protected Project persistProject(final Project project) { + Project projectData = dbUtils.callInTransactionWithResult(() -> { + try { + return projectDao.add(project); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + projects.add(projectData); + return projectData; } } diff --git a/annotation/src/test/java/lcsb/mapviewer/annotation/services/annotators/HgncAnnotatorTest.java b/annotation/src/test/java/lcsb/mapviewer/annotation/services/annotators/HgncAnnotatorTest.java index d0b2c8083a06fe301aa485c9dda95ff0cf7a4c97..513373614491627c0adec42d51e30c57d0a48dcb 100644 --- a/annotation/src/test/java/lcsb/mapviewer/annotation/services/annotators/HgncAnnotatorTest.java +++ b/annotation/src/test/java/lcsb/mapviewer/annotation/services/annotators/HgncAnnotatorTest.java @@ -1,24 +1,5 @@ package lcsb.mapviewer.annotation.services.annotators; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.nullable; -import static org.mockito.Mockito.when; - -import java.io.IOException; -import java.util.List; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.test.util.ReflectionTestUtils; - import lcsb.mapviewer.annotation.AnnotationTestFunctions; import lcsb.mapviewer.annotation.cache.GeneralCacheInterface; import lcsb.mapviewer.annotation.cache.WebPageDownloader; @@ -28,6 +9,24 @@ import lcsb.mapviewer.model.map.MiriamData; import lcsb.mapviewer.model.map.MiriamType; import lcsb.mapviewer.model.map.species.GenericProtein; import lcsb.mapviewer.model.map.species.Species; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.util.ReflectionTestUtils; + +import java.io.IOException; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.Mockito.when; public class HgncAnnotatorTest extends AnnotationTestFunctions { @@ -37,7 +36,7 @@ public class HgncAnnotatorTest extends AnnotationTestFunctions { @Autowired private GeneralCacheInterface cache; - private WebPageDownloader webPageDownloader = new WebPageDownloader(); + private final WebPageDownloader webPageDownloader = new WebPageDownloader(); @Before public void setUp() { @@ -112,10 +111,10 @@ public class HgncAnnotatorTest extends AnnotationTestFunctions { proteinAlias.addMiriamData(nsmf); hgncAnnotator.annotateElement(proteinAlias); assertNotNull(proteinAlias.getSymbol()); - assertTrue(proteinAlias.getFormerSymbols().size() > 0); + assertFalse(proteinAlias.getFormerSymbols().isEmpty()); assertNotNull(proteinAlias.getFullName()); assertTrue(proteinAlias.getMiriamData().size() > 1); - assertTrue(proteinAlias.getSynonyms().size() > 0); + assertFalse(proteinAlias.getSynonyms().isEmpty()); boolean ensemble = false; boolean hgncId = false; @@ -314,13 +313,13 @@ public class HgncAnnotatorTest extends AnnotationTestFunctions { MiriamData data1 = new MiriamData(MiriamType.HGNC_SYMBOL, "PTGS1"); MiriamData entrez = hgncAnnotator.hgncToEntrez(data1); assertNotNull(entrez); - assertTrue(new MiriamData(MiriamType.ENTREZ, "5742").equals(entrez)); + assertEquals(new MiriamData(MiriamType.ENTREZ, "5742"), entrez); // check by id data1 = new MiriamData(MiriamType.HGNC, "11138"); entrez = hgncAnnotator.hgncToEntrez(data1); assertNotNull(entrez); - assertTrue(new MiriamData(MiriamType.ENTREZ, "6622").equals(entrez)); + assertEquals(new MiriamData(MiriamType.ENTREZ, "6622"), entrez); } @Test diff --git a/commons/src/main/java/lcsb/mapviewer/common/tests/TestUtils.java b/commons/src/main/java/lcsb/mapviewer/common/tests/TestUtils.java index 13abad157870eb17f7a4e97a1b2e252b7c15f469..9b7ee6fecb51e8095505a0712b77d5aa6d8a9af4 100644 --- a/commons/src/main/java/lcsb/mapviewer/common/tests/TestUtils.java +++ b/commons/src/main/java/lcsb/mapviewer/common/tests/TestUtils.java @@ -61,6 +61,8 @@ public class TestUtils { protected static final String BUILT_IN_TEST_ADMIN_PASSWORD = "admin"; protected static final String BUILT_IN_TEST_ADMIN_LOGIN = "admin"; + protected static final int BUILT_IN_TEST_ADMIN_ID = 1; + protected static final String BUILT_IN_PROJECT = "empty"; protected static final int BUILT_IN_MAP_ID = 3; diff --git a/converter-graphics/src/main/java/lcsb/mapviewer/converter/graphics/MapGenerator.java b/converter-graphics/src/main/java/lcsb/mapviewer/converter/graphics/MapGenerator.java index 6a153c0cdd3fc754a1f0c164583df9d66217427a..eb8e9b4f61bfbd83213b3026e80f23c8c5532f5b 100644 --- a/converter-graphics/src/main/java/lcsb/mapviewer/converter/graphics/MapGenerator.java +++ b/converter-graphics/src/main/java/lcsb/mapviewer/converter/graphics/MapGenerator.java @@ -1,25 +1,24 @@ package lcsb.mapviewer.converter.graphics; -import java.io.File; -import java.io.IOException; - -import org.apache.commons.io.FileUtils; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - import lcsb.mapviewer.common.Configuration; import lcsb.mapviewer.common.IProgressUpdater; import lcsb.mapviewer.converter.graphics.AbstractImageGenerator.Params; import lcsb.mapviewer.model.map.layout.ProjectBackground; import lcsb.mapviewer.model.map.layout.ProjectBackgroundImageLayer; import lcsb.mapviewer.model.map.model.Model; +import lcsb.mapviewer.model.map.model.ModelData; +import org.apache.commons.io.FileUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.File; +import java.io.IOException; /** * This class allows to generate set of images that could be later on used by * Google Maps API. - * + * * @author Piotr Gawron - * */ public class MapGenerator { @@ -42,17 +41,14 @@ public class MapGenerator { /** * Default class logger. */ - private static Logger logger = LogManager.getLogger(); + private static final Logger logger = LogManager.getLogger(); /** * This method generates PNG images used by GoogleMaps API. * - * @param params - * {@link MapGeneratorParams params} used for generating images - * @throws IOException - * exception thrown when there are some problems with creating images - * @throws DrawingException - * thrown when there was a problem with drawing a map + * @param params {@link MapGeneratorParams params} used for generating images + * @throws IOException exception thrown when there are some problems with creating images + * @throws DrawingException thrown when there was a problem with drawing a map */ public void generateMapImages(final MapGeneratorParams params) throws IOException, DrawingException { @@ -137,32 +133,28 @@ public class MapGenerator { /** * Computes how many zoom levels should exists for the map model. * - * @param model - * map model + * @param model map model * @return zoom levels for the model */ - public int computeZoomLevels(final Model model) { + public int computeZoomLevels(final ModelData model) { return (int) Math.ceil(Math.log(computeZoomFactor(model)) / Math.log(2)); } /** * Computes the scale that should be used on the top zoom level. * - * @param model - * model for which computation is done + * @param model model for which computation is done * @return scale on the top zoom level */ - public double computeZoomFactor(final Model model) { + public double computeZoomFactor(final ModelData model) { return Math.max(model.getHeight(), model.getWidth()) / (TILE_SIZE); } /** * Removes files that were generated for the layout. * - * @param background - * layout object for which images will be removed - * @throws IOException - * thrown when there are some problems with removing files + * @param background layout object for which images will be removed + * @throws IOException thrown when there are some problems with removing files */ public void removeProjectBackground(final ProjectBackground background) throws IOException { removeProjectBackground(background, null); @@ -171,12 +163,9 @@ public class MapGenerator { /** * Removes files that were generated for the layout. * - * @param background - * background for which images will be removed - * @param homeDir - * determines the directory where images are stored (it's optional) - * @throws IOException - * thrown when there are some problems with removing files + * @param background background for which images will be removed + * @param homeDir determines the directory where images are stored (it's optional) + * @throws IOException thrown when there are some problems with removing files */ public void removeProjectBackground(final ProjectBackground background, final String homeDir) throws IOException { for (final ProjectBackgroundImageLayer imageLayer : background.getProjectBackgroundImageLayer()) { @@ -205,36 +194,29 @@ public class MapGenerator { * <li>{@link #levels}</li>. * </ul> * - * * @author Piotr Gawron - * */ public class MapGeneratorParams { /** * Information about map to be generated. - * */ private Model model; /** * Where we want to put the files. The structure of the directory will be as * follows: directory/subDirA/subDirB/file.PNG. subDirA is a level of zooming. * subDirB is x coordinate of the image. file is y coordinate of the image. - * */ private String directory; /** * Do we want to generate images in hierarchical view or normal. - * */ private boolean nested = false; /** * Should we remove empty elements in nested view. - * */ private boolean removeEmpty = false; /** * Callback function that update progress information. - * */ private IProgressUpdater updater = new IProgressUpdater() { @Override @@ -258,27 +240,25 @@ public class MapGenerator { private boolean sbgn; /** - * @param model - * the model to set - * @see #model + * @param model the model to set * @return full {@link MapGeneratorParams} object + * @see #model */ public MapGeneratorParams model(final Model model) { this.model = model; if (zoomFactor == null) { - zoomFactor = computeZoomFactor(model); + zoomFactor = computeZoomFactor(model.getModelData()); } if (levels == null) { - levels = computeZoomLevels(model); + levels = computeZoomLevels(model.getModelData()); } return this; } /** - * @param directory - * the directory to set - * @see #directory + * @param directory the directory to set * @return full {@link MapGeneratorParams} object + * @see #directory */ public MapGeneratorParams directory(final String directory) { this.directory = directory; @@ -294,10 +274,9 @@ public class MapGenerator { } /** - * @param nested - * the nested to set - * @see #nested + * @param nested the nested to set * @return full {@link MapGeneratorParams} object + * @see #nested */ public MapGeneratorParams nested(final boolean nested) { this.nested = nested; @@ -313,10 +292,9 @@ public class MapGenerator { } /** - * @param removeEmpty - * the removeEmpty to set - * @see #removeEmpty + * @param removeEmpty the removeEmpty to set * @return full {@link MapGeneratorParams} object + * @see #removeEmpty */ public MapGeneratorParams removeEmpty(final boolean removeEmpty) { this.removeEmpty = removeEmpty; @@ -332,10 +310,9 @@ public class MapGenerator { } /** - * @param updater - * the updater to set - * @see #updater + * @param updater the updater to set * @return full {@link MapGeneratorParams} object + * @see #updater */ public MapGeneratorParams updater(final IProgressUpdater updater) { this.updater = updater; @@ -351,10 +328,9 @@ public class MapGenerator { } /** - * @param zoomFactor - * the zoomFactor to set - * @see #zoomFactor + * @param zoomFactor the zoomFactor to set * @return full {@link MapGeneratorParams} object + * @see #zoomFactor */ public MapGeneratorParams zoomFactor(final Double zoomFactor) { this.zoomFactor = zoomFactor; @@ -370,10 +346,9 @@ public class MapGenerator { } /** - * @param levels - * the levels to set - * @see #levels + * @param levels the levels to set * @return full {@link MapGeneratorParams} object + * @see #levels */ public MapGeneratorParams levels(final Integer levels) { this.levels = levels; @@ -389,10 +364,9 @@ public class MapGenerator { } /** - * @param sbgn - * the sbgn to set - * @see #sbgn + * @param sbgn the sbgn to set * @return object with all parameters + * @see #sbgn */ public MapGeneratorParams sbgn(final boolean sbgn) { this.sbgn = sbgn; diff --git a/frontend-js/src/main/js/ServerConnector.js b/frontend-js/src/main/js/ServerConnector.js index 9e6b35a1bd898e7935c56b20e785814eda2c2749..8965fe3a575b33146f5be0df4bea5ae4b33aa85b 100644 --- a/frontend-js/src/main/js/ServerConnector.js +++ b/frontend-js/src/main/js/ServerConnector.js @@ -474,6 +474,20 @@ ServerConnector.getApiUrl = function (paramObj) { return result; }; +ServerConnector.getNewApiUrl = function (paramObj) { + var type = paramObj.type; + var params = this.createGetParams(paramObj.params); + + var result = paramObj.url; + if (result === undefined) { + result = this.getNewApiBaseUrl() + type; + } + if (params !== "") { + result += "?" + params; + } + return result; +}; + /** * * @param [queryParams] @@ -487,6 +501,19 @@ ServerConnector.getProjectsUrl = function (queryParams, filterParams) { }); }; +/** + * + * @param [queryParams] + * @param [filterParams] + * @return {string} + */ +ServerConnector.getNewApiProjectsUrl = function (queryParams, filterParams) { + return this.getNewApiUrl({ + type: "projects/", + params: filterParams + }); +}; + ServerConnector.getPluginsUrl = function (queryParams, filterParams) { return this.getApiUrl({ type: "plugins/", @@ -538,6 +565,14 @@ ServerConnector.getProjectUrl = function (queryParams, filterParams) { }); }; +ServerConnector.getNewApiProjectUrl = function (queryParams, filterParams) { + var id = this.getIdOrAsterisk(queryParams.projectId); + return this.getNewApiUrl({ + url: this.getNewApiProjectsUrl(queryParams) + id + "/", + params: filterParams + }); +}; + ServerConnector.getMoveProjectUrl = function (queryParams, filterParams) { var id = this.getIdOrAsterisk(queryParams.projectId); return this.getNewApiBaseUrl() + 'projects/' + id + ':move'; @@ -620,6 +655,17 @@ ServerConnector.getProjectLogsUrl = function (queryParams, filterParams) { params: filterParams }); }; + +ServerConnector.getProjectJobsUrl = function (queryParams, filterParams) { + filterParams.page = filterParams.page || 0; + filterParams.size = filterParams.size || 10; + + return this.getNewApiUrl({ + url: this.getNewApiProjectUrl(queryParams) + "job/", + params: filterParams + }); +}; + ServerConnector.getMeshUrl = function (queryParams, filterParams) { return this.getApiUrl({ type: "mesh/" + queryParams.id, @@ -3301,6 +3347,33 @@ ServerConnector.getProjectLogs = function (params) { return JSON.parse(content); }); }; +/** + * + * @param {Object} [params] + * @param {number} [params.start] + * @param {number} [params.length] + * @param {string} [params.projectId] + * @return {Promise} + */ +ServerConnector.getProjectJobs = function (params) { + var self = this; + if (params === undefined) { + params = {}; + } + + var queryParams = {}; + var filterParams = { + page: params.start, + size: params.length + }; + return self.getProjectId(params.projectId).then(function (result) { + queryParams.projectId = result; + return self.sendGetRequest(self.getProjectJobsUrl(queryParams, filterParams)); + }).then(function (content) { + return JSON.parse(content); + }); +}; + /** * diff --git a/frontend-js/src/main/js/gui/admin/JobListDialog.js b/frontend-js/src/main/js/gui/admin/JobListDialog.js new file mode 100644 index 0000000000000000000000000000000000000000..77716074b01af11f18a8ca844206efa8463b7d99 --- /dev/null +++ b/frontend-js/src/main/js/gui/admin/JobListDialog.js @@ -0,0 +1,216 @@ +"use strict"; + +var Promise = require("bluebird"); +var $ = require('jquery'); + +/* exported logger */ + +var AbstractGuiElement = require('../AbstractGuiElement'); + +var Functions = require('../../Functions'); +var GuiConnector = require('../../GuiConnector'); + + +/** + * + * @param {Object} params + * @param {HTMLElement} params.element + * @param {CustomMap} [params.customMap] + * @param {Configuration} params.configuration + * @param {Project} [params.project] + * @param {string} params.projectId + * @param {ServerConnector} params.serverConnector + * + * @constructor + * @extends AbstractGuiElement + */ +function JobListDialog(params) { + AbstractGuiElement.call(this, params); + var self = this; + self.createJobListDialogGui(); + + /** + * @type {string} + * @private + */ + self._projectId = params.projectId; +} + +JobListDialog.prototype = Object.create(AbstractGuiElement.prototype); +JobListDialog.prototype.constructor = JobListDialog; + +/** + * + */ +JobListDialog.prototype.createJobListDialogGui = function () { + var self = this; + var head = Functions.createElement({ + type: "thead", + content: "<tr>" + + "<th>ID</th>" + + "<th>Type</th>" + + "<th>Status</th>" + + "<th>Progress</th>" + + "<th>Created at</th>" + + "<th>Finished at</th>" + + "<th>Priority</th>" + + "</tr>" + }); + var body = Functions.createElement({ + type: "tbody" + }); + var tableElement = Functions.createElement({ + type: "table", + className: "minerva-logs-table", + style: "width: 100%" + }); + + tableElement.appendChild(head); + tableElement.appendChild(body); + + self.tableElement = tableElement; + self.getElement().appendChild(tableElement); + + var menuRow = Functions.createElement({ + type: "div", + className: "minerva-menu-row", + style: "display:table-row; margin:10px" + }); + self.getElement().appendChild(menuRow); + +}; + +function removeNull(object) { + if (object === null || object === undefined) { + return ""; + } + return object; +} + +function entryToRow(entry) { + var row = []; + row[0] = removeNull(entry.id); + row[1] = removeNull(entry.jobType); + row[2] = removeNull(entry.jobStatus); + row[3] = removeNull(entry.progress); + row[4] = removeNull(entry.jobStarted); + row[5] = removeNull(entry.jobFinished); + row[6] = removeNull(entry.priority); + return row; +} + +/** + * + * @param {Object} data + * @param {number} data.start + * @param {number} data.length + * @param {Object} data.search + * @param {Object} data.draw + * @param {Array} data.order + * @param {function} callback + * @returns {Promise} + * @private + */ +JobListDialog.prototype._dataTableAjaxCall = function (data, callback) { + var self = this; + return self.getServerConnector().getProjectJobs({ + start: data.start / data.length, + length: data.length, + projectId: self._projectId + }).then(function (jobEntries) { + var out = []; + + for (var i = 0; i < jobEntries.content.length; i++) { + var entry = jobEntries.content[i]; + + var row = entryToRow(entry); + out.push(row); + } + callback({ + draw: data.draw, + recordsTotal: jobEntries.totalElements, + recordsFiltered: jobEntries.totalElements, + data: out + }); + }); +}; + +JobListDialog.prototype.open = function () { + var self = this; + if (!$(self.getElement()).hasClass("ui-dialog-content")) { + $(self.getElement()).dialog({ + dialogClass: 'minerva-logs-dialog', + title: "Job list", + autoOpen: false, + resizable: false, + width: Math.max(window.innerWidth / 2, window.innerWidth - 100), + height: Math.max(window.innerHeight / 2, window.innerHeight - 100) + }); + } + + $(self.getElement()).dialog("open"); + + if (!$.fn.DataTable.isDataTable(self.tableElement)) { + return new Promise(function (resolve) { + $(self.tableElement).dataTable({ + serverSide: true, + ordering: false, + searching: false, + ajax: function (data, callback) { + resolve(self._dataTableAjaxCall(data, callback)); + }, + columns: self.getColumnsDefinition() + }); + }); + } else { + $(self.tableElement).dataTable().api(true).draw(); + return Promise.resolve(); + } + +}; + +/** + * + * @returns {Array} + */ +JobListDialog.prototype.getColumnsDefinition = function () { + return [{ + name: "id" + }, { + name: "jobType" + }, { + name: "jobStatus" + }, { + name: "progress" + }, { + name: "jobStarted" + }, { + name: "jobFinished" + }, { + name: "priority" + }]; +}; + +/** + * + * @returns {Promise} + */ +JobListDialog.prototype.init = function () { + return Promise.resolve(); +}; + +/** + * + */ +JobListDialog.prototype.destroy = function () { + var self = this; + var div = self.getElement(); + if ($(div).hasClass("ui-dialog-content")) { + $(div).dialog("destroy"); + } + if ($.fn.DataTable.isDataTable(self.tableElement)) { + $(self.tableElement).DataTable().destroy(); + } +}; + +module.exports = JobListDialog; diff --git a/frontend-js/src/main/js/gui/admin/MapsAdminPanel.js b/frontend-js/src/main/js/gui/admin/MapsAdminPanel.js index 5179db7a0a50eb3726b5e4bec6a112ff21f9b65e..aa8a38d6adb45a1cdbbbda81f118bfb3caab449d 100644 --- a/frontend-js/src/main/js/gui/admin/MapsAdminPanel.js +++ b/frontend-js/src/main/js/gui/admin/MapsAdminPanel.js @@ -7,6 +7,7 @@ var AbstractAdminPanel = require('./AbstractAdminPanel'); var AddProjectDialog = require('./AddProjectDialog'); var EditProjectDialog = require('./EditProjectDialog'); var LogListDialog = require('./LogListDialog'); +var JobListDialog = require('./JobListDialog'); var PrivilegeType = require('../../map/data/PrivilegeType'); var ConfigurationType = require('../../ConfigurationType'); var NetworkError = require('../../NetworkError'); @@ -242,6 +243,11 @@ MapsAdminPanel.prototype._createProjectTableRow = function () { return self.showLogs($(button).attr("data")).catch(GuiConnector.alert); }); + $(projectsTable).on("click", "[name='showJobs']", function () { + var button = this; + return self.showJobs($(button).attr("data")).catch(GuiConnector.alert); + }); + $(projectsTable).on("click", "[name='minerva-shared']", function () { var field = this; var projectId = $(field).attr('data'); @@ -448,6 +454,14 @@ MapsAdminPanel.prototype.projectToTableRow = function (project, row, user) { } } + if (isAdmin) { + icon = "<i class='fa fa-info' style='font-size:18px; padding-right:10px;color:#337ab7'></i>"; + status += " <a name='showJobs' href='#' data='" + project.getProjectId() + "'>" + icon + "</a>"; + } else { + status += icon; + } + + row[0] = formattedProjectId; var date = project.getCreationDate(); if (date === undefined) { @@ -768,6 +782,36 @@ MapsAdminPanel.prototype.getLogDialog = function (projectId) { } }; +/** + * + * @param {string} projectId + * @returns {Promise<JobListDialog>} + */ +MapsAdminPanel.prototype.getJobDialog = function (projectId) { + var self = this; + if (self._jobDialogs === undefined) { + self._jobDialogs = []; + } + var dialog = self._jobDialogs[projectId]; + if (dialog === undefined) { + dialog = new JobListDialog({ + element: Functions.createElement({ + type: "div" + }), + configuration: self.getConfiguration(), + projectId: projectId, + serverConnector: self.getServerConnector(), + customMap: null + }); + self._jobDialogs[projectId] = dialog; + return dialog.init().then(function () { + return dialog; + }); + } else { + return Promise.resolve(dialog); + } +}; + /** * * @param {string} id projectId @@ -804,6 +848,21 @@ MapsAdminPanel.prototype.showLogs = function (id) { }); }; +/** + * + * @param {string} id projectId + * @returns {Promise<JobListDialog>} + */ +MapsAdminPanel.prototype.showJobs = function (id) { + var self = this; + GuiConnector.showProcessing(); + return self.getJobDialog(id).then(function (dialog) { + return dialog.open(); + }).finally(function () { + GuiConnector.hideProcessing(); + }); +}; + /** * * @param {string} id projectId diff --git a/frontend-js/src/test/js/ServerConnector-mock.js b/frontend-js/src/test/js/ServerConnector-mock.js index 1a1a1f8e86ec676c98c60fadb7e4b31855dc76b2..dd895873a048190ef7d75968c74d7a6a29b4c588 100644 --- a/frontend-js/src/test/js/ServerConnector-mock.js +++ b/frontend-js/src/test/js/ServerConnector-mock.js @@ -97,16 +97,39 @@ ServerConnectorMock._sendRequest = function (params) { }); }; -ServerConnectorMock.getApiBaseUrl = function () { - return "./testFiles/apiCalls/"; -}; +ServerConnectorMock.Original = { + getApiBaseUrl: ServerConnectorMock.getApiBaseUrl, + getNewApiBaseUrl: ServerConnectorMock.getNewApiBaseUrl, + getApiUrl: ServerConnectorMock.getApiUrl +} -var originalGetApiUrl = OriginalServerConnector.getApiUrl; +ServerConnectorMock.Mock = { + getApiBaseUrl: function () { + return "./testFiles/apiCalls/"; + }, + getNewApiBaseUrl: function () { + return "./testFiles/apiCallsNew/"; + }, + getApiUrl: function (paramObj) { + // replace '?' (or '/?') with '/' + // the call is done on ServerConnectorObject (so 'this' is set properly) + return ServerConnectorMock.Original.getApiUrl.call(this, paramObj).replace(/\/?\?/g, '/'); + } +} -ServerConnectorMock.getApiUrl = function (paramObj) { - // replace '?' (or '/?') with '/' - // the call is done on ServerConnectorObject (so 'this' is set properly) - return originalGetApiUrl.call(this, paramObj).replace(/\/?\?/g, '/'); -}; +ServerConnectorMock.useMock = function () { + ServerConnectorMock.getApiBaseUrl = ServerConnectorMock.Mock.getApiBaseUrl; + ServerConnectorMock.getApiUrl = ServerConnectorMock.Mock.getApiUrl; + ServerConnectorMock.getNewApiBaseUrl = ServerConnectorMock.Mock.getNewApiBaseUrl; + +} + +ServerConnectorMock.useOriginal = function () { + ServerConnectorMock.getApiBaseUrl = ServerConnectorMock.Original.getApiBaseUrl; + ServerConnectorMock.getApiUrl = ServerConnectorMock.Original.getApiUrl; + ServerConnectorMock.getNewApiBaseUrl = ServerConnectorMock.Original.getNewApiBaseUrl; +} + +ServerConnectorMock.useMock(); module.exports = ServerConnectorMock; diff --git a/frontend-js/src/test/js/ServerConnector-test.js b/frontend-js/src/test/js/ServerConnector-test.js index e677b8bf178c0e389b66290bb5186e551fa29fbd..628c01c574a057bb3a3cf9e6b9e98fa3f42881f7 100644 --- a/frontend-js/src/test/js/ServerConnector-test.js +++ b/frontend-js/src/test/js/ServerConnector-test.js @@ -467,5 +467,14 @@ describe('ServerConnector', function () { }); }); + it('getProjectJobsUrl', function () { + try { + ServerConnector.useOriginal(); + var url = ServerConnector.getProjectJobsUrl("test", {}); + assert.isTrue(url.includes('new_api'), url); + } finally { + ServerConnector.useMock(); + } + }); }); diff --git a/model/src/main/java/lcsb/mapviewer/model/LogMarker.java b/model/src/main/java/lcsb/mapviewer/model/LogMarker.java index 57c34da00a960c61d7d244207aead11fc34f1e56..c6f57a9d0a427c7f9ee377d8739a651f7aefea76 100644 --- a/model/src/main/java/lcsb/mapviewer/model/LogMarker.java +++ b/model/src/main/java/lcsb/mapviewer/model/LogMarker.java @@ -1,25 +1,25 @@ package lcsb.mapviewer.model; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.Marker; - import lcsb.mapviewer.common.exception.NotImplementedException; import lcsb.mapviewer.model.map.BioEntity; import lcsb.mapviewer.model.map.layout.graphics.LayerText; import lcsb.mapviewer.model.map.reaction.ReactionNode; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.Marker; +import org.hibernate.proxy.HibernateProxy; public class LogMarker implements Marker { private static final Logger logger = LogManager.getLogger(LogMarker.class); /** - * + * */ private static final long serialVersionUID = 1L; public static final Marker IGNORE = new IgnoredLogMarker(); - private ProjectLogEntry entry; + private final ProjectLogEntry entry; public LogMarker(final ProjectLogEntryType type, final BioEntity bioEntity) { entry = new ProjectLogEntry(); @@ -27,8 +27,8 @@ public class LogMarker implements Marker { if (bioEntity != null) { entry.setObjectIdentifier(bioEntity.getElementId()); entry.setObjectClass(bioEntity.getClass().getSimpleName()); - if (bioEntity.getModel() != null) { - entry.setMapName(bioEntity.getModel().getName()); + if (bioEntity.getModelData() != null && !(bioEntity.getModelData() instanceof HibernateProxy)) { + entry.setMapName(bioEntity.getModelData().getName()); } } } diff --git a/model/src/main/java/lcsb/mapviewer/model/Project.java b/model/src/main/java/lcsb/mapviewer/model/Project.java index 81e8d53a14feed3a516e0d9d922d9ddd1a38c672..300e8294fa1357153f178ea4831633e146ba96d2 100644 --- a/model/src/main/java/lcsb/mapviewer/model/Project.java +++ b/model/src/main/java/lcsb/mapviewer/model/Project.java @@ -375,7 +375,7 @@ public class Project implements Serializable, MinervaEntity { } - public void addLogEntries(final Set<ProjectLogEntry> createLogEntries) { + public void addLogEntries(final Collection<ProjectLogEntry> createLogEntries) { for (final ProjectLogEntry projectLogEntry : createLogEntries) { addLogEntry(projectLogEntry); } diff --git a/model/src/main/java/lcsb/mapviewer/model/ProjectStatus.java b/model/src/main/java/lcsb/mapviewer/model/ProjectStatus.java index b2a0c9a1eb29d46154eb1108c29a6b7afc38eacc..36e6d8858c6f94049498980f5202c56820fc3dcd 100644 --- a/model/src/main/java/lcsb/mapviewer/model/ProjectStatus.java +++ b/model/src/main/java/lcsb/mapviewer/model/ProjectStatus.java @@ -17,11 +17,6 @@ public enum ProjectStatus { */ PARSING_DATA("Parsing data"), - /** - * Model is annotated by the annotation tool. - */ - ANNOTATING("Annotating"), - /** * Model is being uploaded to the database. */ diff --git a/model/src/main/java/lcsb/mapviewer/model/job/MinervaJob.java b/model/src/main/java/lcsb/mapviewer/model/job/MinervaJob.java index 835cd5dd426f0cbeffe1d5b2bdb1070cf3fc3535..d878ed679186da961660f27e67c7167c4003e1f0 100644 --- a/model/src/main/java/lcsb/mapviewer/model/job/MinervaJob.java +++ b/model/src/main/java/lcsb/mapviewer/model/job/MinervaJob.java @@ -49,6 +49,12 @@ public class MinervaJob implements MinervaEntity { private Calendar jobFinished; + private Double progress; + + private Class<?> externalObjectClass; + + private Integer externalObjectId; + @JsonIgnore private String logs; @@ -128,4 +134,35 @@ public class MinervaJob implements MinervaEntity { public EntityType getEntityType() { throw new NotImplementedException(); } + + public Double getProgress() { + return progress; + } + + public void setProgress(final Double progress) { + this.progress = progress; + } + + public Class<?> getExternalObjectClass() { + return externalObjectClass; + } + + public void setExternalObjectClass(final Class<?> externalObjectClass) { + this.externalObjectClass = externalObjectClass; + } + + public Integer getExternalObjectId() { + return externalObjectId; + } + + public void setExternalObjectId(final Integer externalObjectId) { + this.externalObjectId = externalObjectId; + } + + public void setExternalObject(final MinervaEntity entity) { + if (entity != null) { + this.externalObjectClass = entity.getClass(); + this.externalObjectId = entity.getId(); + } + } } diff --git a/model/src/main/java/lcsb/mapviewer/model/job/MinervaJobType.java b/model/src/main/java/lcsb/mapviewer/model/job/MinervaJobType.java index 58050aaa7d03229d12da7fc44da1f1e231076974..95fb89e72483a69dac5a359d25480465e411b784 100644 --- a/model/src/main/java/lcsb/mapviewer/model/job/MinervaJobType.java +++ b/model/src/main/java/lcsb/mapviewer/model/job/MinervaJobType.java @@ -17,7 +17,10 @@ public enum MinervaJobType { CREATE_PROJECT_FAILURE, DELETE_BACKGROUND, - REVIVE_BACKGROUNDS; + REVIVE_BACKGROUNDS, + + ANNOTATE_PROJECT, + ; private final boolean allowDuplicates; diff --git a/model/src/test/java/lcsb/mapviewer/model/ProjectTest.java b/model/src/test/java/lcsb/mapviewer/model/ProjectTest.java index 6d49dcb99ca85ada289b1d37091d118099872187..8750df2c3639733f121143a1d64c555c58e8c185 100644 --- a/model/src/test/java/lcsb/mapviewer/model/ProjectTest.java +++ b/model/src/test/java/lcsb/mapviewer/model/ProjectTest.java @@ -1,17 +1,5 @@ package lcsb.mapviewer.model; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - -import java.util.Calendar; -import java.util.HashSet; -import java.util.Set; - -import org.apache.commons.lang3.SerializationUtils; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - import lcsb.mapviewer.ModelTestFunctions; import lcsb.mapviewer.common.Configuration; import lcsb.mapviewer.model.cache.UploadedFileEntry; @@ -19,6 +7,17 @@ import lcsb.mapviewer.model.map.MiriamData; import lcsb.mapviewer.model.map.model.Model; import lcsb.mapviewer.model.map.model.ModelData; import lcsb.mapviewer.model.map.model.ModelFullIndexed; +import org.apache.commons.lang3.SerializationUtils; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.util.Calendar; +import java.util.HashSet; +import java.util.Set; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; public class ProjectTest extends ModelTestFunctions { @@ -60,13 +59,13 @@ public class ProjectTest extends ModelTestFunctions { @Test public void testGetters() { MiriamData organism = new MiriamData(); - int id = 5; - String projectId = "str"; - ProjectStatus status = ProjectStatus.ANNOTATING; - double progress = 4.5; + int id = faker.number().numberBetween(1, Integer.MAX_VALUE); + String projectId = faker.numerify("P####"); + ProjectStatus status = faker.options().option(ProjectStatus.class); + double progress = faker.number().randomDouble(2, 1, 100); Set<ModelData> models = new HashSet<>(); - String directory = "dir"; - String name = "name3"; + String directory = faker.numerify("dir-####"); + String name = faker.name().fullName(); MiriamData disease = new MiriamData(); Project project = new Project("str"); boolean sbgn = true; @@ -107,7 +106,7 @@ public class ProjectTest extends ModelTestFunctions { } @Test - public void testsetCreationDate() { + public void testSetCreationDate() { Project project = new Project(); Calendar creationDate = Calendar.getInstance(); project.setCreationDate(creationDate); diff --git a/persist/src/main/java/lcsb/mapviewer/persist/DbUtils.java b/persist/src/main/java/lcsb/mapviewer/persist/DbUtils.java index 8d3e13e38f43574315cdaea02021b9cc2d337abe..5863891af7caa866ccb58f11cf46f19ad3fca532 100644 --- a/persist/src/main/java/lcsb/mapviewer/persist/DbUtils.java +++ b/persist/src/main/java/lcsb/mapviewer/persist/DbUtils.java @@ -1,11 +1,5 @@ package lcsb.mapviewer.persist; -import java.sql.Connection; -import java.sql.SQLException; -import java.util.HashMap; -import java.util.Map; -import java.util.Observable; - import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.hibernate.SessionFactory; @@ -15,6 +9,14 @@ import org.hibernate.internal.SessionFactoryImpl; import org.hibernate.jdbc.Work; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; +import java.util.Observable; +import java.util.function.Supplier; /** * This class contains a set of methods that allow to manage hibernate sessions @@ -26,9 +28,8 @@ import org.springframework.stereotype.Service; * </ul> * Class extends {@link Observable}. Whenever session is created or closed * observers are notified. - * + * * @author Piotr Gawron - * */ @SuppressWarnings("deprecation") @Service @@ -37,7 +38,7 @@ public class DbUtils extends Observable { /** * Default class logger. */ - private static Logger logger = LogManager.getLogger(); + private static final Logger logger = LogManager.getLogger(); /** * This determines if every add/update/delete operation should be flushed (for @@ -49,7 +50,7 @@ public class DbUtils extends Observable { * might be some problems with long transactions.</li> * </ol> */ - private static Map<Long, Boolean> autoFlushForThread = new HashMap<>(); + private static final Map<Long, Boolean> autoFlushForThread = new HashMap<>(); /** * Hibernate session factory. @@ -70,8 +71,7 @@ public class DbUtils extends Observable { } /** - * @param sessionFactory - * the sessionFactory to set + * @param sessionFactory the sessionFactory to set * @see #sessionFactory */ public void setSessionFactory(final SessionFactory sessionFactory) { @@ -81,9 +81,9 @@ public class DbUtils extends Observable { /** * Returns info if the flush is automatically done or not in current * {@link Thread}. - * + * * @return <code>true</code> if flush is automatically done, - * <code>false</code> otherwise + * <code>false</code> otherwise * @see #autoFlushForThread */ public boolean isAutoFlush() { @@ -104,9 +104,8 @@ public class DbUtils extends Observable { /** * Set autoFlush for current {@link Thread}. - * - * @param autoFlush - * the new autoflush value + * + * @param autoFlush the new autoflush value * @see #autoFlushForThread */ public void setAutoFlush(final boolean autoFlush) { @@ -143,4 +142,15 @@ public class DbUtils extends Observable { public void dumpDbToFile(final String filename) { sessionFactory.getCurrentSession().createNativeQuery("SCRIPT '" + filename + "'").executeUpdate(); } + + @Transactional + public void callInTransaction(final Supplier<Void> function) { + function.get(); + } + + @Transactional + public <T> T callInTransactionWithResult(final Supplier<T> function) { + return function.get(); + } + } diff --git a/persist/src/main/java/lcsb/mapviewer/persist/dao/BaseDao.java b/persist/src/main/java/lcsb/mapviewer/persist/dao/BaseDao.java index b27d5bafdc5cb28290ae9fc44ea88a6a81ae5430..ac96b749dde60d3b5d6e1d0eef1bbe8ef79da870 100644 --- a/persist/src/main/java/lcsb/mapviewer/persist/dao/BaseDao.java +++ b/persist/src/main/java/lcsb/mapviewer/persist/dao/BaseDao.java @@ -360,6 +360,11 @@ public abstract class BaseDao<T extends MinervaEntity, S extends MinervaEntityPr return new PageImpl<>(sublist, pageable, count); } + public List<T> getAll(final Map<S, Object> filterOptions) { + return getAll(filterOptions, Pageable.unpaged()).getContent(); + } + + protected List<Order> getOrder(final Sort sort, final CriteriaBuilder criteriaBuilder, final Root<T> root) { for (Iterator<Sort.Order> iterator = sort.iterator(); iterator.hasNext(); ) { Sort.Order order = iterator.next(); diff --git a/persist/src/main/java/lcsb/mapviewer/persist/dao/MinervaJobDao.java b/persist/src/main/java/lcsb/mapviewer/persist/dao/MinervaJobDao.java index 18f50b8e8d87d91d534922787254be6812794357..c9fd86bb635549ffa93bffbf012f5abf9372eb4d 100644 --- a/persist/src/main/java/lcsb/mapviewer/persist/dao/MinervaJobDao.java +++ b/persist/src/main/java/lcsb/mapviewer/persist/dao/MinervaJobDao.java @@ -1,5 +1,6 @@ package lcsb.mapviewer.persist.dao; +import lcsb.mapviewer.common.exception.InvalidArgumentException; import lcsb.mapviewer.model.job.MinervaJob; import lcsb.mapviewer.model.job.MinervaJobStatus; import lcsb.mapviewer.model.job.MinervaJobType; @@ -7,14 +8,17 @@ import org.springframework.stereotype.Repository; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; +import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; +import java.util.Collection; import java.util.List; import java.util.Map; @Repository -public class MinervaJobDao extends BaseDao<MinervaJob, MinervaEntityProperty<MinervaJob>> { +public class MinervaJobDao extends BaseDao<MinervaJob, MinervaJobProperty> { public MinervaJobDao() { super(MinervaJob.class); @@ -88,4 +92,35 @@ public class MinervaJobDao extends BaseDao<MinervaJob, MinervaEntityProperty<Min .setParameter("job_type", jobType) .list(); } + + @Override + protected Predicate createPredicate(final Map<MinervaJobProperty, Object> filterOptions, final Root<MinervaJob> root) { + CriteriaBuilder builder = getSession().getCriteriaBuilder(); + List<Predicate> predicates = new ArrayList<>(); + + for (MinervaJobProperty key : filterOptions.keySet()) { + if (key.equals(MinervaJobProperty.EXTERNAL_OBJECT_CLASS)) { + Collection<Class<?>> values = (Collection<Class<?>>) filterOptions.get(key); + + final CriteriaBuilder.In<Class<?>> predicate = builder.in(root.get("externalObjectClass")); + for (final Class<?> object : values) { + predicate.value(object); + } + predicates.add(predicate); + } else if (key.equals(MinervaJobProperty.EXTERNAL_OBJECT_ID)) { + Collection<Integer> values = (Collection<Integer>) filterOptions.get(key); + + final CriteriaBuilder.In<Integer> predicate = builder.in(root.get("externalObjectId")); + for (final Integer object : values) { + predicate.value(object); + } + predicates.add(predicate); + } else { + throw new InvalidArgumentException("Unknown property: " + key); + } + } + Predicate predicate = builder.and(predicates.toArray(new Predicate[0])); + return predicate; + + } } diff --git a/persist/src/main/java/lcsb/mapviewer/persist/dao/MinervaJobProperty.java b/persist/src/main/java/lcsb/mapviewer/persist/dao/MinervaJobProperty.java new file mode 100644 index 0000000000000000000000000000000000000000..4f7bbff5f593cc50b46e74145b95b293c2e75688 --- /dev/null +++ b/persist/src/main/java/lcsb/mapviewer/persist/dao/MinervaJobProperty.java @@ -0,0 +1,8 @@ +package lcsb.mapviewer.persist.dao; + +import lcsb.mapviewer.model.job.MinervaJob; + +public enum MinervaJobProperty implements MinervaEntityProperty<MinervaJob> { + EXTERNAL_OBJECT_CLASS, + EXTERNAL_OBJECT_ID, +} diff --git a/persist/src/main/resources/db/migration/hsql/19.0.0~alpha.0/V19.0.0.20250227.2__add_progress_to_minerva_job.sql b/persist/src/main/resources/db/migration/hsql/19.0.0~alpha.0/V19.0.0.20250227.2__add_progress_to_minerva_job.sql new file mode 100644 index 0000000000000000000000000000000000000000..eb923d432a7c6c918ea92789ec1f3bd6848bdb3a --- /dev/null +++ b/persist/src/main/resources/db/migration/hsql/19.0.0~alpha.0/V19.0.0.20250227.2__add_progress_to_minerva_job.sql @@ -0,0 +1,2 @@ +alter table minerva_job_table + add column progress double precision; \ No newline at end of file diff --git a/persist/src/main/resources/db/migration/hsql/19.0.0~alpha.0/V19.0.0.20250227.3__add_object_connection_to_minerva_job.sql b/persist/src/main/resources/db/migration/hsql/19.0.0~alpha.0/V19.0.0.20250227.3__add_object_connection_to_minerva_job.sql new file mode 100644 index 0000000000000000000000000000000000000000..4a735d4e773c6732a663c02cbe20ba56fcd20c80 --- /dev/null +++ b/persist/src/main/resources/db/migration/hsql/19.0.0~alpha.0/V19.0.0.20250227.3__add_object_connection_to_minerva_job.sql @@ -0,0 +1,5 @@ +alter table minerva_job_table + add column external_object_id integer; + +alter table minerva_job_table + add column external_object_class character varying(512); diff --git a/persist/src/main/resources/db/migration/hsql/19.0.0~alpha.0/V19.0.0.20250227__remove_annotating_project_status.sql b/persist/src/main/resources/db/migration/hsql/19.0.0~alpha.0/V19.0.0.20250227__remove_annotating_project_status.sql new file mode 100644 index 0000000000000000000000000000000000000000..e6de86f9f4de92fc8656ddcc9996aa6d227503b8 --- /dev/null +++ b/persist/src/main/resources/db/migration/hsql/19.0.0~alpha.0/V19.0.0.20250227__remove_annotating_project_status.sql @@ -0,0 +1,3 @@ +update project_table +set status ='UNKNOWN' +where status in ('ANNOTATING'); \ No newline at end of file diff --git a/persist/src/main/resources/db/migration/postgres/19.0.0~alpha.0/V19.0.0.20250227.2__add_progress_to_minerva_job.sql b/persist/src/main/resources/db/migration/postgres/19.0.0~alpha.0/V19.0.0.20250227.2__add_progress_to_minerva_job.sql new file mode 100644 index 0000000000000000000000000000000000000000..eb923d432a7c6c918ea92789ec1f3bd6848bdb3a --- /dev/null +++ b/persist/src/main/resources/db/migration/postgres/19.0.0~alpha.0/V19.0.0.20250227.2__add_progress_to_minerva_job.sql @@ -0,0 +1,2 @@ +alter table minerva_job_table + add column progress double precision; \ No newline at end of file diff --git a/persist/src/main/resources/db/migration/postgres/19.0.0~alpha.0/V19.0.0.20250227.3__add_object_connection_to_minerva_job.sql b/persist/src/main/resources/db/migration/postgres/19.0.0~alpha.0/V19.0.0.20250227.3__add_object_connection_to_minerva_job.sql new file mode 100644 index 0000000000000000000000000000000000000000..4a735d4e773c6732a663c02cbe20ba56fcd20c80 --- /dev/null +++ b/persist/src/main/resources/db/migration/postgres/19.0.0~alpha.0/V19.0.0.20250227.3__add_object_connection_to_minerva_job.sql @@ -0,0 +1,5 @@ +alter table minerva_job_table + add column external_object_id integer; + +alter table minerva_job_table + add column external_object_class character varying(512); diff --git a/persist/src/main/resources/db/migration/postgres/19.0.0~alpha.0/V19.0.0.20250227__remove_annotating_project_status.sql b/persist/src/main/resources/db/migration/postgres/19.0.0~alpha.0/V19.0.0.20250227__remove_annotating_project_status.sql new file mode 100644 index 0000000000000000000000000000000000000000..e6de86f9f4de92fc8656ddcc9996aa6d227503b8 --- /dev/null +++ b/persist/src/main/resources/db/migration/postgres/19.0.0~alpha.0/V19.0.0.20250227__remove_annotating_project_status.sql @@ -0,0 +1,3 @@ +update project_table +set status ='UNKNOWN' +where status in ('ANNOTATING'); \ No newline at end of file diff --git a/persist/src/test/java/lcsb/mapviewer/persist/utils/TransactionalElementDao.java b/persist/src/test/java/lcsb/mapviewer/persist/utils/TransactionalElementDao.java index d173897f9c4c4387e0eea6dcf58305a9aabcf1a8..ca5a1a88f05f6c2b0302ba1926f1a58a05783fd0 100644 --- a/persist/src/test/java/lcsb/mapviewer/persist/utils/TransactionalElementDao.java +++ b/persist/src/test/java/lcsb/mapviewer/persist/utils/TransactionalElementDao.java @@ -1,16 +1,15 @@ package lcsb.mapviewer.persist.utils; -import java.util.Map; - +import lcsb.mapviewer.model.map.species.Element; +import lcsb.mapviewer.persist.dao.map.species.ElementDao; +import lcsb.mapviewer.persist.dao.map.species.ElementProperty; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; -import lcsb.mapviewer.model.map.species.Element; -import lcsb.mapviewer.persist.dao.map.species.ElementDao; -import lcsb.mapviewer.persist.dao.map.species.ElementProperty; +import java.util.Map; @Component @Transactional diff --git a/rest-api/src/main/java/lcsb/mapviewer/api/users/UserController.java b/rest-api/src/main/java/lcsb/mapviewer/api/users/UserController.java index 08d1652c7ce7ff605273ff079e4c22f38757bccf..7cbaf4f9eb6436467c4f75d3eef07b29fb771562 100644 --- a/rest-api/src/main/java/lcsb/mapviewer/api/users/UserController.java +++ b/rest-api/src/main/java/lcsb/mapviewer/api/users/UserController.java @@ -21,8 +21,8 @@ import lcsb.mapviewer.modelutils.serializer.model.security.PrivilegeKeyDeseriali import lcsb.mapviewer.services.InvalidTokenException; import lcsb.mapviewer.services.ObjectExistsException; import lcsb.mapviewer.services.ObjectNotFoundException; +import lcsb.mapviewer.services.interfaces.IAnnotationService; import lcsb.mapviewer.services.interfaces.IConfigurationService; -import lcsb.mapviewer.services.interfaces.IProjectService; import lcsb.mapviewer.services.interfaces.IUserService; import lcsb.mapviewer.services.utils.EmailException; import lcsb.mapviewer.services.utils.EmailSender; @@ -82,7 +82,7 @@ import java.util.stream.Collectors; public class UserController extends BaseController { private final IUserService userService; - private final IProjectService projectService; + private final IAnnotationService annotationService; private final IConfigurationService configurationService; private final PasswordEncoder passwordEncoder; private final EmailSender emailSender; @@ -94,14 +94,14 @@ public class UserController extends BaseController { final PasswordEncoder passwordEncoder, final EmailSender emailSender, final IConfigurationService configurationService, - final IProjectService projectService, + final IAnnotationService annotationService, final SessionRegistry sessionRegistry) { this.userService = userService; this.passwordEncoder = passwordEncoder; this.emailSender = emailSender; - this.projectService = projectService; this.configurationService = configurationService; this.sessionRegistry = sessionRegistry; + this.annotationService = annotationService; } /** @@ -129,7 +129,7 @@ public class UserController extends BaseController { throw new ObjectNotFoundException("User doesn't exist"); } if (user.getAnnotationSchema() == null) { - user.setAnnotationSchema(projectService.prepareUserAnnotationSchema(user, true)); + user.setAnnotationSchema(annotationService.prepareUserAnnotationSchema(user, true)); } Boolean ldapAvailable = false; if (columnSet.contains("ldapAccountAvailable")) { @@ -221,7 +221,7 @@ public class UserController extends BaseController { throw new ObjectNotFoundException("User doesn't exist"); } - final UserAnnotationSchema schema = projectService.prepareUserAnnotationSchema(modifiedUser, true); + final UserAnnotationSchema schema = annotationService.prepareUserAnnotationSchema(modifiedUser, true); if (body.getPreferences().getValidateMiriamTypes() != null) { schema.setValidateMiriamTypes(body.getPreferences().getValidateMiriamTypes()); @@ -305,7 +305,7 @@ public class UserController extends BaseController { data.add(new UserDTO(user, ldapAvailability.get(user.getLogin()), getLastActive(user))); } data = data.stream() - .sorted(Comparator.comparing(user -> user.getLogin(), Comparator.reverseOrder())) + .sorted(Comparator.comparing(User::getLogin, Comparator.reverseOrder())) .collect(Collectors.toList()); return createResponseWithColumns(columns, data); diff --git a/service/src/main/java/lcsb/mapviewer/services/impl/AnnotationService.java b/service/src/main/java/lcsb/mapviewer/services/impl/AnnotationService.java new file mode 100644 index 0000000000000000000000000000000000000000..31c6bdc94320c7f3b48ddeaab8e119ac62f31fbb --- /dev/null +++ b/service/src/main/java/lcsb/mapviewer/services/impl/AnnotationService.java @@ -0,0 +1,132 @@ +package lcsb.mapviewer.services.impl; + +import lcsb.mapviewer.annotation.services.ModelAnnotator; +import lcsb.mapviewer.common.exception.NotImplementedException; +import lcsb.mapviewer.model.Project; +import lcsb.mapviewer.model.job.MinervaJob; +import lcsb.mapviewer.model.job.MinervaJobType; +import lcsb.mapviewer.model.user.User; +import lcsb.mapviewer.model.user.UserAnnotationSchema; +import lcsb.mapviewer.model.user.UserClassAnnotators; +import lcsb.mapviewer.modelutils.map.ClassTreeNode; +import lcsb.mapviewer.modelutils.map.ElementUtils; +import lcsb.mapviewer.services.interfaces.IAnnotationService; +import lcsb.mapviewer.services.interfaces.IMinervaJobService; +import lcsb.mapviewer.services.interfaces.IProjectService; +import lcsb.mapviewer.services.interfaces.IUserService; +import lcsb.mapviewer.services.jobs.AnnotateProjectMinervaJob; +import lcsb.mapviewer.services.search.drug.IDrugService; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; +import java.util.LinkedList; +import java.util.Queue; +import java.util.function.Consumer; + +@Service +public class AnnotationService implements IAnnotationService { + + private final Logger logger = LogManager.getLogger(); + + private final ModelAnnotator modelAnnotator; + + private final IUserService userService; + + private final IMinervaJobService minervaJobService; + + @Autowired + private IAnnotationService self; + + private final IProjectService projectService; + private final IDrugService drugService; + + @Autowired + public AnnotationService(final IMinervaJobService minervaJobService, + final ModelAnnotator modelAnnotator, + final IUserService userService, + final IProjectService projectService, + final IDrugService drugService) { + this.minervaJobService = minervaJobService; + this.modelAnnotator = modelAnnotator; + this.userService = userService; + this.projectService = projectService; + this.drugService = drugService; + } + + @PostConstruct + public void init() { + minervaJobService.registerExecutor(MinervaJobType.ANNOTATE_PROJECT, self); + } + + + @Override + public void execute(final MinervaJob job) throws Exception { + if (job.getJobType() == MinervaJobType.ANNOTATE_PROJECT) { + AnnotateProjectMinervaJob parameters = new AnnotateProjectMinervaJob(job.getJobParameters()); + self.handleAnnotateProjectJob(parameters, (final Double progress) -> minervaJobService.updateJobProgress(job, progress)); + + Project project = projectService.getProjectByProjectId(parameters.getProjectId()); + + projectService.submitRefreshChemicalInfoJobs(parameters.getProjectId()); + drugService.submitRefreshChemblSuggestedQueryList(project.getOrganism(), project); + drugService.submitRefreshDrugBankSuggestedQueryList(project.getOrganism(), project); + + } else { + throw new NotImplementedException(job.getJobType() + " not implemented"); + } + } + + + /** + * Retrieves (or creates) annotation schema for a given user. + * + * @return {@link UserAnnotationSchema} for {@link User} + */ + @Override + public UserAnnotationSchema prepareUserAnnotationSchema(final User user, final boolean initializeLazy) { + UserAnnotationSchema annotationSchema = null; + if (user != null) { + annotationSchema = userService.getById(user.getId()).getAnnotationSchema(); + if (annotationSchema != null && annotationSchema.getClassAnnotators().isEmpty()) { + for (final UserClassAnnotators uca : modelAnnotator.createDefaultAnnotatorSchema().getClassAnnotators()) { + annotationSchema.addClassAnnotator(uca); + } + } + } + if (annotationSchema == null) { + annotationSchema = modelAnnotator.createDefaultAnnotatorSchema(); + + final ElementUtils elementUtils = new ElementUtils(); + + final ClassTreeNode top = elementUtils.getAnnotatedElementClassTree(); + + final Queue<ClassTreeNode> nodes = new LinkedList<>(); + nodes.add(top); + + while (!nodes.isEmpty()) { + final ClassTreeNode element = nodes.poll(); + annotationSchema.addClassAnnotator(new UserClassAnnotators(element.getClazz(), + modelAnnotator.getDefaultAnnotators(element.getClazz()))); + nodes.addAll(element.getChildren()); + } + if (user != null) { + final User dbUser = userService.getById(user.getId()); + dbUser.setAnnotationSchema(annotationSchema); + userService.update(dbUser); + } + } + if (user != null) { + return userService.getUserByLogin(user.getLogin(), initializeLazy).getAnnotationSchema(); + } + return annotationSchema; + } + + @Override + public void handleAnnotateProjectJob(final AnnotateProjectMinervaJob parameters, final Consumer<Double> updateProgress) { + Project project = projectService.getProjectByProjectId(parameters.getProjectId()); + modelAnnotator.performAnnotations(project, updateProgress::accept, parameters.getSchema()); + } +} diff --git a/service/src/main/java/lcsb/mapviewer/services/impl/MinervaJobService.java b/service/src/main/java/lcsb/mapviewer/services/impl/MinervaJobService.java index 21ea237cb907f290a1f32544047eddbf7cef2677..f70ac17dbd094d0d531901469ddb489543503b9a 100644 --- a/service/src/main/java/lcsb/mapviewer/services/impl/MinervaJobService.java +++ b/service/src/main/java/lcsb/mapviewer/services/impl/MinervaJobService.java @@ -3,6 +3,7 @@ package lcsb.mapviewer.services.impl; import lcsb.mapviewer.annotation.cache.CacheQueryMinervaJob; import lcsb.mapviewer.annotation.cache.GeneralCacheInterface; import lcsb.mapviewer.common.MinervaConfigurationHolder; +import lcsb.mapviewer.common.exception.NotImplementedException; import lcsb.mapviewer.model.cache.CacheQuery; import lcsb.mapviewer.model.cache.CacheType; import lcsb.mapviewer.model.job.MinervaJob; @@ -12,6 +13,7 @@ import lcsb.mapviewer.model.job.MinervaJobStatus; import lcsb.mapviewer.model.job.MinervaJobType; import lcsb.mapviewer.persist.CustomDatabasePopulator; import lcsb.mapviewer.persist.dao.MinervaJobDao; +import lcsb.mapviewer.persist.dao.MinervaJobProperty; import lcsb.mapviewer.persist.dao.cache.CacheTypeDao; import lcsb.mapviewer.services.interfaces.IMinervaJobService; import org.apache.commons.lang3.exception.ExceptionUtils; @@ -22,6 +24,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.jdbc.datasource.init.ScriptException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -135,7 +139,7 @@ public class MinervaJobService implements IMinervaJobService, ApplicationContext logger.error("QUEUE interrupted", e); break; } - logger.info("Recovering attempt: " + errorIssues); + logger.info("Recovering attempt: {}", errorIssues); } } if (MinervaJobService.shutdownOnFailure) { @@ -164,7 +168,6 @@ public class MinervaJobService implements IMinervaJobService, ApplicationContext @Override public void setApplicationContext(final ApplicationContext context) throws BeansException { this.context = context; - } @Override @@ -248,6 +251,12 @@ public class MinervaJobService implements IMinervaJobService, ApplicationContext return minervaJobDao.getCount(status); } + @Override + public long getCount(final Map<MinervaJobProperty, Object> filterOptions) { + return minervaJobDao.getCount(filterOptions); + } + + @Override public void registerExecutor(final MinervaJobType jobType, final MinervaJobExecutor executor) { executors.put(jobType, executor); @@ -303,6 +312,17 @@ public class MinervaJobService implements IMinervaJobService, ApplicationContext MinervaJobService.queueEnabled = false; } + @Override + public void updateJobProgress(final MinervaJob job, final Double progress) { + MinervaJob dbJob = self.getJobById(job.getId()); + if (dbJob != null) { + dbJob.setProgress(progress); + minervaJobDao.update(dbJob); + } else { + logger.warn("Job not found: {}", job.getId()); + } + } + private Callable<Object> createJobExecutionWrapper(final MinervaJob job) { return new Callable<Object>() { @Override @@ -312,10 +332,12 @@ public class MinervaJobService implements IMinervaJobService, ApplicationContext MinervaJobExecutor executor = getExecutor(job.getJobType()); if (executor == null) { - logger.error("Cannot find executor for job: " + job.getJobType() + " (job id: " + job.getId() + ")"); - job.setJobStatus(MinervaJobStatus.FAILED); - job.setLogs("Cannot find executor for job: " + job.getJobType() + " (job id: " + job.getId() + ")"); - return self.updateJob(job); + logger.error("Cannot find executor for job: {} (job id: {})", job.getJobType(), job.getId()); + + MinervaJob dbJob = self.getJobById(job.getId()); + dbJob.setJobStatus(MinervaJobStatus.FAILED); + dbJob.setLogs("Cannot find executor for job: " + dbJob.getJobType() + " (job id: " + dbJob.getId() + ")"); + return self.updateJob(dbJob); } executor.execute(job); @@ -356,22 +378,49 @@ public class MinervaJobService implements IMinervaJobService, ApplicationContext } private void startJob(final MinervaJob job) { - logger.debug("Starting job: " + job.getId() + ", " + job.getJobType() + ", priority: " + job.getPriority() + ", " + job.getJobParameters()); - job.setJobStatus(MinervaJobStatus.RUNNING); - self.updateJob(job); + logger.debug("Starting job: {}, {}, priority: {}, {}", job.getId(), job.getJobType(), job.getPriority(), job.getJobParameters()); + MinervaJob dbJob = self.getJobById(job.getId()); + dbJob.setJobStatus(MinervaJobStatus.RUNNING); + self.updateJob(dbJob); } private MinervaJob finishJob(final MinervaJob job) { - logger.debug("Job finished: " + job.getId()); - job.setJobStatus(MinervaJobStatus.DONE); - return self.updateJob(job); + logger.debug("Job finished: {}", job.getId()); + MinervaJob dbJob = self.getJobById(job.getId()); + dbJob.setJobStatus(MinervaJobStatus.DONE); + return self.updateJob(dbJob); } private MinervaJob finishJobWithProblems(final MinervaJob job, final Throwable e) { - logger.error("Problem with executing job: " + job.getJobType() + " (job id: " + job.getId() + ")", e); - job.setJobStatus(MinervaJobStatus.FAILED); - job.setLogs(ExceptionUtils.getStackTrace(e)); - return self.updateJob(job); + logger.error("Problem with executing job: {} (job id: {})", job.getJobType(), job.getId(), e); + MinervaJob dbJob = self.getJobById(job.getId()); + dbJob.setJobStatus(MinervaJobStatus.FAILED); + dbJob.setLogs(ExceptionUtils.getStackTrace(e)); + return self.updateJob(dbJob); + } + + @Override + public Page<MinervaJob> getAll(final Map<MinervaJobProperty, Object> filter, final Pageable pageable) { + return minervaJobDao.getAll(filter, pageable); } + @Override + public MinervaJob getById(final int id) { + return minervaJobDao.getById(id); + } + + @Override + public void update(final MinervaJob object) { + self.updateJob(object); + } + + @Override + public void remove(final MinervaJob object) { + throw new NotImplementedException(); + } + + @Override + public void add(final MinervaJob object) { + self.addJob(object); + } } diff --git a/service/src/main/java/lcsb/mapviewer/services/impl/ProjectService.java b/service/src/main/java/lcsb/mapviewer/services/impl/ProjectService.java index 813e4f2b662119c657f78dc43ae494c8470bd26a..11e01fb110b161a1daef817dfa9260062bbddf80 100644 --- a/service/src/main/java/lcsb/mapviewer/services/impl/ProjectService.java +++ b/service/src/main/java/lcsb/mapviewer/services/impl/ProjectService.java @@ -1,7 +1,6 @@ package lcsb.mapviewer.services.impl; import lcsb.mapviewer.annotation.services.MeSHParser; -import lcsb.mapviewer.annotation.services.ModelAnnotator; import lcsb.mapviewer.annotation.services.TaxonomyBackend; import lcsb.mapviewer.annotation.services.TaxonomySearchException; import lcsb.mapviewer.commands.ClearColorModelCommand; @@ -12,7 +11,6 @@ import lcsb.mapviewer.common.IProgressUpdater; import lcsb.mapviewer.common.MinervaConfigurationHolder; import lcsb.mapviewer.common.MinervaLoggerAppender; import lcsb.mapviewer.common.Pair; -import lcsb.mapviewer.common.exception.InvalidArgumentException; import lcsb.mapviewer.common.exception.NotImplementedException; import lcsb.mapviewer.converter.ColorSchemaReader; import lcsb.mapviewer.converter.ComplexZipConverter; @@ -54,25 +52,25 @@ import lcsb.mapviewer.model.overlay.InvalidDataOverlayException; import lcsb.mapviewer.model.user.ConfigurationElementType; import lcsb.mapviewer.model.user.User; import lcsb.mapviewer.model.user.UserAnnotationSchema; -import lcsb.mapviewer.model.user.UserClassAnnotators; -import lcsb.mapviewer.modelutils.map.ClassTreeNode; -import lcsb.mapviewer.modelutils.map.ElementUtils; import lcsb.mapviewer.persist.DbUtils; import lcsb.mapviewer.persist.ObjectValidator; import lcsb.mapviewer.persist.dao.ProjectDao; import lcsb.mapviewer.persist.dao.ProjectLogEntryDao; import lcsb.mapviewer.persist.dao.ProjectLogEntryProperty; import lcsb.mapviewer.persist.dao.ProjectProperty; +import lcsb.mapviewer.persist.dao.graphics.LayerProperty; import lcsb.mapviewer.persist.dao.map.ModelProperty; -import lcsb.mapviewer.persist.dao.user.UserDao; +import lcsb.mapviewer.persist.dao.user.UserAnnotationSchemaDao; import lcsb.mapviewer.services.interfaces.ICommentService; import lcsb.mapviewer.services.interfaces.IConfigurationService; import lcsb.mapviewer.services.interfaces.IFileService; +import lcsb.mapviewer.services.interfaces.ILayerService; import lcsb.mapviewer.services.interfaces.IMinervaJobService; import lcsb.mapviewer.services.interfaces.IModelService; import lcsb.mapviewer.services.interfaces.IProjectBackgroundService; import lcsb.mapviewer.services.interfaces.IProjectService; import lcsb.mapviewer.services.interfaces.IUserService; +import lcsb.mapviewer.services.jobs.AnnotateProjectMinervaJob; import lcsb.mapviewer.services.jobs.CreateProjectFailureMinervaJob; import lcsb.mapviewer.services.jobs.CreateProjectMinervaJob; import lcsb.mapviewer.services.jobs.DeleteBackgroundMinervaJob; @@ -82,8 +80,6 @@ import lcsb.mapviewer.services.jobs.RefreshChemicalSuggestedQueryListMinervaJob; import lcsb.mapviewer.services.jobs.RefreshDrugInfoMinervaJob; import lcsb.mapviewer.services.jobs.RefreshMiriamInfoMinervaJob; import lcsb.mapviewer.services.jobs.ReviveBackgroundsMinervaJob; -import lcsb.mapviewer.services.search.chemical.IChemicalService; -import lcsb.mapviewer.services.search.drug.IDrugService; import lcsb.mapviewer.services.utils.CreateProjectParams; import lcsb.mapviewer.services.utils.EmailException; import lcsb.mapviewer.services.utils.EmailSender; @@ -99,7 +95,6 @@ import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; -import javax.mail.MessagingException; import javax.persistence.PersistenceException; import java.io.ByteArrayInputStream; import java.io.File; @@ -109,11 +104,9 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Queue; import java.util.Set; /** @@ -140,23 +133,17 @@ public class ProjectService implements IProjectService { /** * Data access object for projects. */ - private ProjectDao projectDao; + private final ProjectDao projectDao; /** * Data access object for models. */ private final ProjectLogEntryDao projectLogEntryDao; - /** - * Data access object for users. - */ - private final UserDao userDao; - - /** - * Service that allows to access and manage models. - */ private final IModelService modelService; + private final ILayerService layerService; + /** * Service that allows to access and manage comments. */ @@ -172,24 +159,9 @@ public class ProjectService implements IProjectService { */ private final IUserService userService; - /** - * Services that access data about chemicals. - */ - private final IChemicalService chemicalService; - - /** - * Services that access data about drugs. - */ - private final IDrugService drugService; - @Autowired private IFileService fileService; - /** - * Module that allows to annotate maps. - */ - private final ModelAnnotator modelAnnotator; - private final IProjectBackgroundService projectBackgroundService; private final IMinervaJobService minervaJobService; @@ -217,8 +189,10 @@ public class ProjectService implements IProjectService { private final MinervaConfigurationHolder minervaConfigurationHolder; + private final UserAnnotationSchemaDao userAnnotationSchemaDao; + /** - * Class that helps to generate images for google maps API. + * Class that helps to generate images for frontend. */ private final MapGenerator generator = new MapGenerator(); @@ -226,35 +200,31 @@ public class ProjectService implements IProjectService { public ProjectService(final MinervaConfigurationHolder minervaConfigurationHolder, final ProjectDao projectDao, final ProjectLogEntryDao projectLogEntryDao, - final UserDao userDao, final IModelService modelService, + final ILayerService layerService, final ICommentService commentService, final IConfigurationService configurationService, final IUserService userService, - final IChemicalService chemicalService, - final IDrugService drugService, - final ModelAnnotator modelAnnotator, final DbUtils dbUtils, final MeSHParser meshParser, final TaxonomyBackend taxonomyBackend, final IProjectBackgroundService projectBackgroundService, - final IMinervaJobService minervaJobService) { + final IMinervaJobService minervaJobService, + final UserAnnotationSchemaDao userAnnotationSchemaDao) { this.minervaConfigurationHolder = minervaConfigurationHolder; this.projectDao = projectDao; this.projectLogEntryDao = projectLogEntryDao; - this.userDao = userDao; this.modelService = modelService; + this.layerService = layerService; this.commentService = commentService; this.configurationService = configurationService; this.userService = userService; - this.chemicalService = chemicalService; - this.drugService = drugService; - this.modelAnnotator = modelAnnotator; this.dbUtils = dbUtils; this.meshParser = meshParser; this.taxonomyBackend = taxonomyBackend; this.projectBackgroundService = projectBackgroundService; this.minervaJobService = minervaJobService; + this.userAnnotationSchemaDao = userAnnotationSchemaDao; } @PostConstruct @@ -278,7 +248,7 @@ public class ProjectService implements IProjectService { @Override public Project getProjectByProjectId(final String projectId, final boolean initializeLazy) { - final Project result = getProjectByProjectId(projectId); + final Project result = self.getProjectByProjectId(projectId); if (result != null && initializeLazy) { initLazy(result); } @@ -287,7 +257,7 @@ public class ProjectService implements IProjectService { @Override public boolean projectExists(final String projectName) { - if (projectName == null || projectName.equals("")) { + if (projectName == null || projectName.isEmpty()) { return false; } return projectDao.isProjectExistsByName(projectName); @@ -297,14 +267,14 @@ public class ProjectService implements IProjectService { public List<Project> getAllProjects() { final List<Project> projects = projectDao.getAll(); for (final Project project : projects) { - project.getLogEntries().size(); + Hibernate.initialize(project.getLogEntries()); } return projects; } @Override public List<Project> getAllProjects(final boolean initializeLazy) { - final List<Project> result = getAllProjects(); + final List<Project> result = self.getAllProjects(); if (initializeLazy) { for (final Project project : result) { initLazy(project); @@ -319,6 +289,7 @@ public class ProjectService implements IProjectService { final DeleteProjectMinervaJob jobParameters = new DeleteProjectMinervaJob(project.getProjectId(), homeDir); final MinervaJob job = new MinervaJob(MinervaJobType.DELETE_PROJECT, MinervaJobPriority.MEDIUM, jobParameters); + job.setExternalObject(project); minervaJobService.addJob(job); return job; } @@ -378,7 +349,7 @@ public class ProjectService implements IProjectService { } } catch (final Exception e) { - self.projectFailure(project.getProjectId(), ProjectLogEntryType.OTHER, + self.projectFailure(projectId, ProjectLogEntryType.OTHER, "Severe problem with removing object. Underlying error:\n" + e.getMessage() + "\nMore information can be found in log file.", e); @@ -414,7 +385,7 @@ public class ProjectService implements IProjectService { self.addLogEntries(params.getProjectId(), createLogEntries(appender)); createRefreshDrugInfoJobs(params.getProjectId()); - createRefreshChemicalInfoJobs(params.getProjectId()); + submitRefreshChemicalInfoJobs(params.getProjectId()); createRefreshMiriamInfoJobs(params.getProjectId()); self.updateProjectStatus(params.getProjectId(), ProjectStatus.DONE, IProgressUpdater.MAX_PROGRESS); @@ -435,10 +406,11 @@ public class ProjectService implements IProjectService { + "You might violated some unhandled constraints or you run out of memory. Underlying error:\n" + e.getMessage() + "\nMore information can be found in log file.", e); - } catch (final OutOfMemoryError oome) { + } catch (final OutOfMemoryError outOfMemoryError) { // release some memory outOfMemoryBuffer = null; - self.projectFailure(params.getProjectId(), ProjectLogEntryType.OUT_OF_MEMORY, "Out of memory: " + oome.getMessage(), oome); + self.projectFailure(params.getProjectId(), ProjectLogEntryType.OUT_OF_MEMORY, + "Out of memory: " + outOfMemoryError.getMessage(), outOfMemoryError); } catch (final Throwable e) { outOfMemoryBuffer = null; self.projectFailure(params.getProjectId(), ProjectLogEntryType.OTHER, @@ -465,6 +437,7 @@ public class ProjectService implements IProjectService { final CreateProjectMinervaJob jobParameters = new CreateProjectMinervaJob(params); final MinervaJob job = new MinervaJob(MinervaJobType.CREATE_PROJECT, MinervaJobPriority.HIGH, jobParameters); + job.setExternalObject(project); minervaJobService.addJob(job); return job; @@ -483,7 +456,7 @@ public class ProjectService implements IProjectService { } } - public void createRefreshChemicalInfoJobs(final String projectId) { + public void submitRefreshChemicalInfoJobs(final String projectId) { Project project = self.getProjectByProjectId(projectId, true); MiriamData disease = project.getDisease(); if (disease != null) { @@ -491,7 +464,9 @@ public class ProjectService implements IProjectService { minervaJobService.addJob(createRefreshChemicalInfoJob(miriamData, disease)); } RefreshChemicalSuggestedQueryListMinervaJob parameters = new RefreshChemicalSuggestedQueryListMinervaJob(disease, project.getId()); - minervaJobService.addJob(new MinervaJob(MinervaJobType.REFRESH_CHEMICAL_SUGGESTED_QUERY_LIST, MinervaJobPriority.LOWEST, parameters)); + MinervaJob job = new MinervaJob(MinervaJobType.REFRESH_CHEMICAL_SUGGESTED_QUERY_LIST, MinervaJobPriority.LOWEST, parameters); + job.setExternalObject(project); + minervaJobService.addJob(job); } } @@ -536,9 +511,9 @@ public class ProjectService implements IProjectService { @Override public void update(final Project project) { projectDao.update(project); - for (final ProjectBackground background : getBackgrounds(project.getProjectId(), false)) { + for (final ProjectBackground background : self.getBackgrounds(project.getProjectId(), false)) { background.setCreator(project.getOwner()); - updateBackground(background); + self.updateBackground(background); } if (project.getOrganism() != null) { minervaJobService.addJob(createRefreshMiriamInfoJob(project.getOrganism())); @@ -548,53 +523,6 @@ public class ProjectService implements IProjectService { } } - /** - * Retrieves (or creates) annotation schema for a given user. - * - * @return {@link UserAnnotationSchema} for {@link User} - */ - @Override - public UserAnnotationSchema prepareUserAnnotationSchema(final User user, final boolean initializeLazy) { - UserAnnotationSchema annotationSchema = null; - if (user != null) { - annotationSchema = userDao.getById(user.getId()).getAnnotationSchema(); - if (annotationSchema != null && annotationSchema.getClassAnnotators().size() == 0) { - for (final UserClassAnnotators uca : modelAnnotator.createDefaultAnnotatorSchema().getClassAnnotators()) { - annotationSchema.addClassAnnotator(uca); - } - } - } - if (annotationSchema == null) { - annotationSchema = modelAnnotator.createDefaultAnnotatorSchema(); - - final ElementUtils elementUtils = new ElementUtils(); - - final ClassTreeNode top = elementUtils.getAnnotatedElementClassTree(); - - final Map<Class<? extends BioEntity>, Set<MiriamType>> validMiriam = modelAnnotator.getDefaultValidClasses(); - final Map<Class<? extends BioEntity>, Set<MiriamType>> requiredMiriam = modelAnnotator.getDefaultRequiredClasses(); - - final Queue<ClassTreeNode> nodes = new LinkedList<ClassTreeNode>(); - nodes.add(top); - - while (!nodes.isEmpty()) { - final ClassTreeNode element = nodes.poll(); - annotationSchema.addClassAnnotator(new UserClassAnnotators(element.getClazz(), - modelAnnotator.getDefaultAnnotators(element.getClazz()))); - nodes.addAll(element.getChildren()); - } - if (user != null) { - final User dbUser = userDao.getById(user.getId()); - dbUser.setAnnotationSchema(annotationSchema); - userDao.update(dbUser); - } - } - if (user != null) { - return userService.getUserByLogin(user.getLogin(), initializeLazy).getAnnotationSchema(); - } - return annotationSchema; - } - /** * This method creates set of images for the model backgrounds. * @@ -609,9 +537,9 @@ public class ProjectService implements IProjectService { if (!params.isImages()) { return; } - final Project project = getProjectByProjectId(params.getProjectId()); + final Project project = self.getProjectByProjectId(params.getProjectId()); project.getTopModel(); - updateProjectStatus(params.getProjectId(), ProjectStatus.GENERATING_IMAGES, 0); + self.updateProjectStatus(params.getProjectId(), ProjectStatus.GENERATING_IMAGES, 0); final int projectSize = project.getProjectBackgrounds().size(); int counter = 0; @@ -619,13 +547,11 @@ public class ProjectService implements IProjectService { final ProjectBackground background = project.getProjectBackgrounds().get(i); final double imgCounter = counter; final double finalSize = projectSize; - final IProgressUpdater updater = new IProgressUpdater() { - @Override - public void setProgress(final double progress) { - updateProjectStatus(params.getProjectId(), ProjectStatus.GENERATING_IMAGES, - IProgressUpdater.MAX_PROGRESS * imgCounter / finalSize + progress / finalSize); - } - }; + final IProgressUpdater updater = progress -> self.updateProjectStatus( + params.getProjectId(), + ProjectStatus.GENERATING_IMAGES, + IProgressUpdater.MAX_PROGRESS * imgCounter / finalSize + progress / finalSize + ); generateImagesForBuiltInBackground(background, updater); counter++; } @@ -663,97 +589,54 @@ public class ProjectService implements IProjectService { @Override public Project createModel(final CreateProjectParams params) - throws InvalidInputDataExecption, ConverterException, IOException, InvalidDataOverlayException, InstantiationException, IllegalAccessException, - CommandExecutionException { - final Project project = getProjectByProjectId(params.getProjectId()); + throws InvalidInputDataExecption, ConverterException, IOException, InvalidDataOverlayException, CommandExecutionException { final User dbUser = userService.getUserByLogin(params.getUserLogin()); UserAnnotationSchema userAnnotationSchema = dbUser.getAnnotationSchema(); if (userAnnotationSchema == null) { userAnnotationSchema = new UserAnnotationSchema(); } - final UploadedFileEntry file = fileService.getById(params.getProjectFileId()); if (params.isComplex()) { - try { - Class<? extends Converter> clazz = CellDesignerXmlParser.class; - if (params.getParser() != null) { - clazz = params.getParser(); - } - final ComplexZipConverter parser = new ComplexZipConverter(clazz); - final ComplexZipConverterParams complexParams; - complexParams = new ComplexZipConverterParams().zipFile(file); - complexParams.visualizationDir(params.getProjectDir()); - for (final ZipEntryFile entry : params.getZipEntries()) { - complexParams.entry(entry); - } - final ProjectFactory projectFactory = new ProjectFactory(parser); - projectFactory.create(complexParams, project); - for (final DataOverlay overlay : project.getDataOverlays()) { - overlay.setCreator(dbUser); - } - } catch (final IOException e) { - throw new InvalidInputDataExecption("Problem with processing input file", e); - } + self.createComplexProject(params); } else { - final Converter parser = params.getParser().newInstance(); - if (parser == null) { - throw new InvalidArgumentException("Parser is undefined"); - } - final InputStream input = new ByteArrayInputStream(file.getFileContent()); - final Model model = parser - .createModel(new ConverterParams().inputStream(input, file.getOriginalFileName()) - .sizeAutoAdjust(params.isAutoResize()).sbgnFormat(project.isSbgnFormat())); - project.addModel(model); + self.createSimpleProject(params); } - final Model topModel = project.getTopModel(); - final Set<Model> models = new HashSet<>(); - models.add(topModel); - for (final ModelSubmodelConnection connection : topModel.getSubmodelConnections()) { - models.add(connection.getSubmodel().getModel()); - } - for (final Model m : models) { - for (final Layer l : m.getLayers()) { - if (!l.getName().equals(CreateHierarchyCommand.TEXT_LAYER_NAME)) { - l.setVisible(false); - } + Map<LayerProperty, Object> layerFilter = new HashMap<>(); + layerFilter.put(LayerProperty.PROJECT_ID, params.getProjectId()); + List<Layer> layers = layerService.getAll(layerFilter, Pageable.unpaged()).getContent(); + for (final Layer layer : layers) { + if (!layer.getName().equals(CreateHierarchyCommand.TEXT_LAYER_NAME)) { + layer.setVisible(false); + layerService.update(layer); } } - fixProjectIssues(project); - assignZoomLevelDataToModel(topModel); + self.updateProjectStatus(params.getProjectId(), ProjectStatus.UPLOADING_TO_DB, 0.0); - updateProjectStatus(params.getProjectId(), ProjectStatus.UPLOADING_TO_DB, 0.0); + self.addBuiltInBackgrounds(params); - addBuiltInBackgrounds(params, project, dbUser); - self.update(project); - validateDataOverlays(project); - if (params.isUpdateAnnotations()) { - logger.debug("Updating annotations"); - modelAnnotator.performAnnotations(topModel, new IProgressUpdater() { + self.validateDataOverlays(params.getProjectId()); - @Override - public void setProgress(final double progress) { - updateProjectStatus(params.getProjectId(), ProjectStatus.ANNOTATING, progress); - } - }, userAnnotationSchema); - logger.debug("Annotations updated"); - } - logger.debug("Model created"); - final Model originalModel = project.getTopModel(); - new CreateHierarchyCommand(originalModel, generator.computeZoomLevels(originalModel), - generator.computeZoomFactor(originalModel)).execute(); - for (final Model model : originalModel.getSubmodels()) { - new CreateHierarchyCommand(model, generator.computeZoomLevels(model), generator.computeZoomFactor(model)) - .execute(); + self.createHierarchicalView(params.getProjectId()); + + self.updateProjectStatus(params.getProjectId(), ProjectStatus.UPLOADING_TO_DB, 100.0); + + logger.trace("Model {} created", params.getProjectId()); + + if (params.isUpdateAnnotations()) { + self.submitAnnotateProjectJob(params.getProjectId(), userAnnotationSchema); } - logger.debug("Hierarchy view added"); - return project; + + return self.getProjectByProjectId(params.getProjectId()); } - private void addBuiltInBackgrounds(final CreateProjectParams params, final Project project, final User dbUser) + @Override + public void addBuiltInBackgrounds(final CreateProjectParams params) throws InvalidInputDataExecption { + User dbUser = userService.getUserByLogin(params.getUserLogin()); + Project project = self.getProjectByProjectId(params.getProjectId()); final List<BuildInBackgrounds> buildInBackgrounds = new ArrayList<>(); if (params.isNetworkBackgroundAsDefault()) { buildInBackgrounds.add(BuildInBackgrounds.NORMAL); @@ -786,7 +669,7 @@ public class ProjectService implements IProjectService { final String directory = params.getProjectDir() + "/" + buildInBackground.getDirectorySuffix() + submodelId + "/"; background.addProjectBackgroundImageLayer(new ProjectBackgroundImageLayer(connection.getSubmodel(), directory)); - assignZoomLevelDataToModel(connection.getSubmodel().getModel()); + assignZoomLevelDataToModel(connection.getSubmodel().getModel().getModelData()); submodelId++; } } @@ -796,7 +679,9 @@ public class ProjectService implements IProjectService { } } - private void validateDataOverlays(final Project project) throws IOException, InvalidDataOverlayException { + @Override + public void validateDataOverlays(final String projectId) throws IOException, InvalidDataOverlayException { + Project project = self.getProjectByProjectId(projectId); for (final DataOverlay l : project.getDataOverlays()) { if (l.getInputData() != null) { final ColorSchemaReader reader = new ColorSchemaReader(); @@ -811,11 +696,79 @@ public class ProjectService implements IProjectService { } } - private void assignZoomLevelDataToModel(final Model topModel) throws InvalidInputDataExecption { - final Integer maxZoomLevels = Integer + @Override + public void createHierarchicalView(final String projectId) throws CommandExecutionException { + Project project = self.getProjectByProjectId(projectId); + final Model originalModel = project.getTopModel(); + new CreateHierarchyCommand(originalModel, generator.computeZoomLevels(originalModel.getModelData()), + generator.computeZoomFactor(originalModel.getModelData())).execute(); + for (final Model model : originalModel.getSubmodels()) { + new CreateHierarchyCommand(model, generator.computeZoomLevels(model.getModelData()), generator.computeZoomFactor(model.getModelData())) + .execute(); + } + self.update(project); + logger.debug("Hierarchy view added"); + } + + @Override + public void createComplexProject(final CreateProjectParams params) throws InvalidInputDataExecption { + try { + final UploadedFileEntry file = fileService.getById(params.getProjectFileId()); + Project project = self.getProjectByProjectId(params.getProjectId()); + User dbUser = userService.getUserByLogin(params.getUserLogin()); + + Class<? extends Converter> clazz = CellDesignerXmlParser.class; + if (params.getParser() != null) { + clazz = params.getParser(); + } + final ComplexZipConverter parser = new ComplexZipConverter(clazz); + final ComplexZipConverterParams complexParams; + complexParams = new ComplexZipConverterParams().zipFile(file); + complexParams.visualizationDir(params.getProjectDir()); + for (final ZipEntryFile entry : params.getZipEntries()) { + complexParams.entry(entry); + } + final ProjectFactory projectFactory = new ProjectFactory(parser); + projectFactory.create(complexParams, project); + for (final DataOverlay overlay : project.getDataOverlays()) { + overlay.setCreator(dbUser); + } + for (ModelData modelData : project.getModels()) { + project.addLogEntries(fixModelIssues(modelData)); + } + self.update(project); + } catch (final IOException | ConverterException e) { + throw new InvalidInputDataExecption("Problem with processing input file", e); + } + } + + @Override + public void createSimpleProject(final CreateProjectParams params) throws ConverterException { + try { + final UploadedFileEntry file = fileService.getById(params.getProjectFileId()); + Project project = self.getProjectByProjectId(params.getProjectId()); + + final Converter parser = params.getParser().newInstance(); + final InputStream input = new ByteArrayInputStream(file.getFileContent()); + final Model model = parser.createModel(new ConverterParams() + .inputStream(input, file.getOriginalFileName()) + .sizeAutoAdjust(params.isAutoResize()) + .sbgnFormat(project.isSbgnFormat())); + project.addLogEntries(fixModelIssues(model.getModelData())); + assignZoomLevelDataToModel(model.getModelData()); + modelService.add(model.getModelData()); + project.setTopModel(model); + self.update(project); + } catch (InvalidInputDataExecption | InstantiationException | IllegalAccessException e) { + throw new ConverterException(e); + } + } + + private void assignZoomLevelDataToModel(final ModelData topModel) throws InvalidInputDataExecption { + final int maxZoomLevels = Integer .parseInt(configurationService.getValue(ConfigurationElementType.MAX_NUMBER_OF_MAP_LEVELS).getValue()); - final Integer zoomLevels = generator.computeZoomLevels(topModel); + final int zoomLevels = generator.computeZoomLevels(topModel); if (zoomLevels > maxZoomLevels) { throw new InvalidInputDataExecption( "Map " + topModel.getName() + " too big. You can change the max number of map zoom levels in configuration."); @@ -834,42 +787,25 @@ public class ProjectService implements IProjectService { */ @Override public void updateProjectStatus(final String projectId, final ProjectStatus status, final double progress) { - final Project project = getProjectByProjectId(projectId); + final Project project = self.getProjectByProjectId(projectId); if (project != null) { if (!status.equals(project.getStatus()) || (Math.abs(progress - project.getProgress()) > IProgressUpdater.PROGRESS_BAR_UPDATE_RESOLUTION)) { project.setStatus(status); project.setProgress(progress); - fixProjectIssues(project); self.update(project); } } else { - logger.debug("status: " + status + ", " + progress); + logger.trace("Update project status: {}, {}", status, progress); } } - /** - * @return the projectDao - * @see #projectDao - */ - public ProjectDao getProjectDao() { - return projectDao; - } - - /** - * @param projectDao the projectDao to set - * @see #projectDao - */ - public void setProjectDao(final ProjectDao projectDao) { - this.projectDao = projectDao; - } - /** * Sends notification email that map was removed. * * @param projectId identifier of the project * @param email notification email - * @throws MessagingException thrown when there is a problem with sending email + * @throws EmailException thrown when there is a problem with sending email */ protected void sendSuccesfullRemoveEmail(final String projectId, final String email) throws EmailException { final EmailSender emailSender = new EmailSender(configurationService); @@ -964,6 +900,23 @@ public class ProjectService implements IProjectService { return result; } + private ProjectLogEntry createLogEntry(final Pair<Object, String> pair) { + final ProjectLogEntry entry = new ProjectLogEntry(); + entry.setContent(pair.getRight()); + final Object left = pair.getLeft(); + if (left instanceof BioEntity) { + final BioEntity bioEntity = (BioEntity) left; + entry.setMapName(bioEntity.getModelData().getName()); + entry.setObjectClass(bioEntity.getClass().getSimpleName()); + entry.setObjectIdentifier(bioEntity.getElementId()); + } else if (left instanceof ModelData) { + entry.setMapName(((ModelData) left).getName()); + } + entry.setSeverity("WARNING"); + entry.setType(ProjectLogEntryType.DATABASE_PROBLEM); + return entry; + } + private ProjectLogEntry createLogEntry(final String severity, final LogEvent event) { if (event.getMarker() instanceof IgnoredLogMarker) { return null; @@ -983,30 +936,31 @@ public class ProjectService implements IProjectService { return entry; } + List<ProjectLogEntry> fixModelIssues(final ModelData model) { + List<ProjectLogEntry> result = new ArrayList<>(); + final ObjectValidator validator = new ObjectValidator(); + final List<Pair<Object, String>> issues = validator.getValidationIssues(model); + if (!issues.isEmpty()) { + for (final Pair<Object, String> pair : issues) { + result.add(createLogEntry(pair)); + } + } + validator.fixValidationIssues(model); + return result; + } + void fixProjectIssues(final Project project) { final ObjectValidator validator = new ObjectValidator(); final List<Pair<Object, String>> issues = validator.getValidationIssues(project); - if (issues.size() > 0) { + if (!issues.isEmpty()) { for (final Pair<Object, String> pair : issues) { - final ProjectLogEntry entry = new ProjectLogEntry(); - entry.setContent(pair.getRight()); - final Object left = pair.getLeft(); - if (left instanceof BioEntity) { - final BioEntity bioEntity = (BioEntity) left; - entry.setMapName(bioEntity.getModelData().getName()); - entry.setObjectClass(bioEntity.getClass().getSimpleName()); - entry.setObjectIdentifier(bioEntity.getElementId()); - } else if (left instanceof ModelData) { - entry.setMapName(((ModelData) left).getName()); - } - entry.setSeverity("WARNING"); - entry.setType(ProjectLogEntryType.DATABASE_PROBLEM); - project.addLogEntry(entry); + project.addLogEntry(createLogEntry(pair)); } } validator.fixValidationIssues(project); } + @Override public void removeBackground(final ProjectBackground projectBackground) { projectBackgroundService.delete(projectBackground); @@ -1053,7 +1007,7 @@ public class ProjectService implements IProjectService { @Override public UploadedFileEntry getFileByProjectId(final String projectId) { - final Project project = getProjectByProjectId(projectId); + final Project project = self.getProjectByProjectId(projectId); if (project == null) { return null; } @@ -1063,7 +1017,7 @@ public class ProjectService implements IProjectService { @Override public List<ProjectBackground> getBackgrounds(final String projectId, final boolean initializeLazy) { - final Project project = getProjectByProjectId(projectId); + final Project project = self.getProjectByProjectId(projectId); if (project != null) { for (final ProjectBackground projectBackground : project.getProjectBackgrounds()) { if (initializeLazy) { @@ -1109,7 +1063,7 @@ public class ProjectService implements IProjectService { public void execute(final MinervaJob job) throws Exception { if (job.getJobType() == MinervaJobType.DELETE_PROJECT) { final DeleteProjectMinervaJob parameters = new DeleteProjectMinervaJob(job.getJobParameters()); - removeProject(parameters.getProjectId(), parameters.getDirectory()); + self.removeProject(parameters.getProjectId(), parameters.getDirectory()); } else if (job.getJobType() == MinervaJobType.CREATE_PROJECT) { final CreateProjectMinervaJob parameters = new CreateProjectMinervaJob(job.getJobParameters()); createProject(parameters.getParameters()); @@ -1146,12 +1100,12 @@ public class ProjectService implements IProjectService { public void addLogEntries(final String projectId, final Set<ProjectLogEntry> createLogEntries) { final Project project = self.getProjectByProjectId(projectId); project.addLogEntries(createLogEntries); - update(project); + self.update(project); } @Override public void submitArchiveProjectJob(final String projectId) { - final Project project = getProjectByProjectId(projectId); + final Project project = self.getProjectByProjectId(projectId); final String homeDir = getProjectHomeDir(project); project.setStatus(ProjectStatus.ARCHIVED); @@ -1159,10 +1113,24 @@ public class ProjectService implements IProjectService { for (final ProjectBackground background : project.getProjectBackgrounds()) { final DeleteBackgroundMinervaJob jobParameters = new DeleteBackgroundMinervaJob(background.getId(), homeDir); final MinervaJob job = new MinervaJob(MinervaJobType.DELETE_BACKGROUND, MinervaJobPriority.LOW, jobParameters); + job.setExternalObject(project); minervaJobService.addJob(job); } } + @Override + public void submitAnnotateProjectJob(final String projectId, final UserAnnotationSchema schema) { + UserAnnotationSchema userAnnotationSchema = schema; + if (schema != null && schema.getId() != 0) { + userAnnotationSchema = userAnnotationSchemaDao.getById(schema.getId()); + } + Project project = self.getProjectByProjectId(projectId); + final AnnotateProjectMinervaJob jobParameters = new AnnotateProjectMinervaJob(projectId, userAnnotationSchema); + final MinervaJob job = new MinervaJob(MinervaJobType.ANNOTATE_PROJECT, MinervaJobPriority.MEDIUM, jobParameters); + job.setExternalObject(project); + minervaJobService.addJob(job); + } + @Override public String getProjectHomeDir(final Project project) { final String homeDir; @@ -1177,26 +1145,28 @@ public class ProjectService implements IProjectService { @Override public void submitReviveProjectJob(final String projectId) { - final Project project = getProjectByProjectId(projectId); + final Project project = self.getProjectByProjectId(projectId); project.setStatus(ProjectStatus.GENERATING_IMAGES); final ReviveBackgroundsMinervaJob jobParameters = new ReviveBackgroundsMinervaJob(projectId); final MinervaJob job = new MinervaJob(MinervaJobType.REVIVE_BACKGROUNDS, MinervaJobPriority.MEDIUM, jobParameters); + job.setExternalObject(project); minervaJobService.addJob(job); } @Override public void reviveBackgrounds(final String projectId) { try { - final Project project = getProjectByProjectId(projectId); + final Project project = self.getProjectByProjectId(projectId); final CreateProjectParams params = new CreateProjectParams() .projectId(projectId) + .setUser(project.getOwner()) .projectDir(getProjectHomeDir(project)) .images(true); - addBuiltInBackgrounds(params, project, project.getOwner()); - createImages(params); + self.addBuiltInBackgrounds(params); + self.createImages(params); project.setStatus(ProjectStatus.DONE); } catch (final Exception e) { diff --git a/service/src/main/java/lcsb/mapviewer/services/interfaces/IAnnotationService.java b/service/src/main/java/lcsb/mapviewer/services/interfaces/IAnnotationService.java new file mode 100644 index 0000000000000000000000000000000000000000..467bb0f88e79b7e7ecdd76021618e2d55932486c --- /dev/null +++ b/service/src/main/java/lcsb/mapviewer/services/interfaces/IAnnotationService.java @@ -0,0 +1,18 @@ +package lcsb.mapviewer.services.interfaces; + +import lcsb.mapviewer.model.job.MinervaJobExecutor; +import lcsb.mapviewer.model.user.User; +import lcsb.mapviewer.model.user.UserAnnotationSchema; +import lcsb.mapviewer.services.jobs.AnnotateProjectMinervaJob; +import org.springframework.transaction.annotation.Transactional; + +import java.util.function.Consumer; + +public interface IAnnotationService extends MinervaJobExecutor { + + @Transactional + UserAnnotationSchema prepareUserAnnotationSchema(final User user, final boolean initializeLazy); + + void handleAnnotateProjectJob(final AnnotateProjectMinervaJob parameters, Consumer<Double> updateProgress); + +} diff --git a/service/src/main/java/lcsb/mapviewer/services/interfaces/IMinervaJobService.java b/service/src/main/java/lcsb/mapviewer/services/interfaces/IMinervaJobService.java index a6da66f3c85933d1f9763ffd165333a8d09a15d8..cf81e304f49afd17b7ed3203020cc430cdb8bd8a 100644 --- a/service/src/main/java/lcsb/mapviewer/services/interfaces/IMinervaJobService.java +++ b/service/src/main/java/lcsb/mapviewer/services/interfaces/IMinervaJobService.java @@ -5,12 +5,13 @@ import lcsb.mapviewer.model.job.MinervaJob; import lcsb.mapviewer.model.job.MinervaJobExecutor; import lcsb.mapviewer.model.job.MinervaJobStatus; import lcsb.mapviewer.model.job.MinervaJobType; +import lcsb.mapviewer.persist.dao.MinervaJobProperty; import org.springframework.transaction.annotation.Transactional; import java.util.Calendar; import java.util.List; -public interface IMinervaJobService { +public interface IMinervaJobService extends CrudService<MinervaJob, MinervaJobProperty> { boolean addJob(MinervaJob job); @@ -51,4 +52,7 @@ public interface IMinervaJobService { void enableQueue(boolean shutdownOnFailure); void disableQueue(); + + @Transactional + void updateJobProgress(MinervaJob job, Double progress); } diff --git a/service/src/main/java/lcsb/mapviewer/services/interfaces/IProjectService.java b/service/src/main/java/lcsb/mapviewer/services/interfaces/IProjectService.java index fc153a2c3724f8c0b80af947b7c0509d53537673..0f73a7bcca41dc17fcdac39724d66f7593159904 100644 --- a/service/src/main/java/lcsb/mapviewer/services/interfaces/IProjectService.java +++ b/service/src/main/java/lcsb/mapviewer/services/interfaces/IProjectService.java @@ -14,7 +14,6 @@ import lcsb.mapviewer.model.job.MinervaJobExecutor; import lcsb.mapviewer.model.map.MiriamData; import lcsb.mapviewer.model.map.layout.ProjectBackground; import lcsb.mapviewer.model.overlay.InvalidDataOverlayException; -import lcsb.mapviewer.model.user.User; import lcsb.mapviewer.model.user.UserAnnotationSchema; import lcsb.mapviewer.persist.dao.ProjectLogEntryProperty; import lcsb.mapviewer.persist.dao.ProjectProperty; @@ -74,12 +73,8 @@ public interface IProjectService extends MinervaJobExecutor, CrudService<Project @Transactional MinervaJob submitCreateProjectJob(final CreateProjectParams params) throws SecurityException; - @Transactional Project createProject(final CreateProjectParams params) throws SecurityException; - @Transactional - UserAnnotationSchema prepareUserAnnotationSchema(final User user, final boolean initializeLazy); - @Transactional void removeBackground(final ProjectBackground projectBackground); @@ -110,7 +105,6 @@ public interface IProjectService extends MinervaJobExecutor, CrudService<Project @Transactional void removeProject(String projectId, String directory); - @Transactional Project createModel(CreateProjectParams params) throws InvalidInputDataExecption, ConverterException, IOException, InvalidDataOverlayException, InstantiationException, IllegalAccessException, CommandExecutionException; @@ -168,4 +162,24 @@ public interface IProjectService extends MinervaJobExecutor, CrudService<Project @Transactional Page<ProjectLogEntry> getLogEntries(Map<ProjectLogEntryProperty, Object> searchFilter, Pageable pageable); + + @Transactional + void addBuiltInBackgrounds(final CreateProjectParams params) throws InvalidInputDataExecption; + + @Transactional + void validateDataOverlays(final String projectId) throws IOException, InvalidDataOverlayException; + + @Transactional + void createHierarchicalView(final String projectId) throws CommandExecutionException; + + @Transactional + void createComplexProject(CreateProjectParams params) throws InvalidInputDataExecption; + + @Transactional + void createSimpleProject(CreateProjectParams params) throws ConverterException; + + @Transactional + void submitAnnotateProjectJob(final String projectId, final UserAnnotationSchema schema); + + void submitRefreshChemicalInfoJobs(final String projectId); } diff --git a/service/src/main/java/lcsb/mapviewer/services/jobs/AnnotateProjectMinervaJob.java b/service/src/main/java/lcsb/mapviewer/services/jobs/AnnotateProjectMinervaJob.java new file mode 100644 index 0000000000000000000000000000000000000000..46dcc914bee8426680d810112a6f22217c794931 --- /dev/null +++ b/service/src/main/java/lcsb/mapviewer/services/jobs/AnnotateProjectMinervaJob.java @@ -0,0 +1,73 @@ +package lcsb.mapviewer.services.jobs; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import lcsb.mapviewer.model.job.InvalidMinervaJobParameters; +import lcsb.mapviewer.model.job.MinervaJobParameters; +import lcsb.mapviewer.model.user.UserAnnotationSchema; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.HashMap; +import java.util.Map; + +public class AnnotateProjectMinervaJob extends MinervaJobParameters { + + private static final Logger logger = LogManager.getLogger(); + + private String projectId; + + private UserAnnotationSchema schema; + + public AnnotateProjectMinervaJob(final String projectId, final UserAnnotationSchema schema) { + this.projectId = projectId; + this.schema = schema; + } + + public AnnotateProjectMinervaJob(final Map<String, Object> jobParameters) throws InvalidMinervaJobParameters { + super(jobParameters); + } + + @Override + protected void assignData(final Map<String, Object> jobParameters) { + ObjectMapper mapper = new ObjectMapper(); + + this.projectId = (String) jobParameters.get("projectId"); + Object schemaData = jobParameters.get("schema"); + if (schemaData != null) { + try { + String serializedSchema = mapper.writeValueAsString(schemaData); + this.schema = mapper.readValue(serializedSchema, new TypeReference<UserAnnotationSchema>() { + }); + } catch (Exception e) { + logger.error("Problem with deserializing task", e); + this.schema = null; + } + } + } + + @Override + protected Map<String, Object> getJobParameters() { + ObjectMapper mapper = new ObjectMapper(); + Map<String, Object> jobParameters = new HashMap<>(); + jobParameters.put("projectId", projectId); + try { + String serializedSchema = mapper.writeValueAsString(schema); + jobParameters.put("schema", mapper.readValue(serializedSchema, new TypeReference<Map<String, Object>>() { + })); + } catch (Exception e) { + logger.error("Problem with serializing task", e); + this.schema = null; + } + + return jobParameters; + } + + public String getProjectId() { + return projectId; + } + + public UserAnnotationSchema getSchema() { + return schema; + } +} diff --git a/service/src/main/java/lcsb/mapviewer/services/search/chemical/ChemicalService.java b/service/src/main/java/lcsb/mapviewer/services/search/chemical/ChemicalService.java index df9615befa8998026fcd0c95ace310c790ba966f..14983096fe746a592c12b8331fa0ea4e64b89198 100644 --- a/service/src/main/java/lcsb/mapviewer/services/search/chemical/ChemicalService.java +++ b/service/src/main/java/lcsb/mapviewer/services/search/chemical/ChemicalService.java @@ -171,7 +171,9 @@ public class ChemicalService extends DbSearchService implements IChemicalService if (result == null) { chemicalParser.setChemicalSuggestedQueryList(project.getId(), disease, Collections.singletonList(" ")); RefreshChemicalSuggestedQueryListMinervaJob parameters = new RefreshChemicalSuggestedQueryListMinervaJob(disease, project.getId()); - minervaJobService.addJob(new MinervaJob(MinervaJobType.REFRESH_CHEMICAL_SUGGESTED_QUERY_LIST, MinervaJobPriority.LOWEST, parameters)); + MinervaJob job = new MinervaJob(MinervaJobType.REFRESH_CHEMICAL_SUGGESTED_QUERY_LIST, MinervaJobPriority.LOWEST, parameters); + job.setExternalObject(project); + minervaJobService.addJob(job); result = new ArrayList<>(); } return result; diff --git a/service/src/main/java/lcsb/mapviewer/services/search/drug/DrugService.java b/service/src/main/java/lcsb/mapviewer/services/search/drug/DrugService.java index 4ca81e45d23318d13df8a401fd01b664de235e92..9be0ef00d02263a522649146a8bf9e090287f041 100644 --- a/service/src/main/java/lcsb/mapviewer/services/search/drug/DrugService.java +++ b/service/src/main/java/lcsb/mapviewer/services/search/drug/DrugService.java @@ -181,7 +181,7 @@ public class DrugService extends DbSearchService implements IDrugService { @Override public Drug getByName(final String name, final DbSearchCriteria searchCriteria) { - if (name.trim().equals("")) { + if (name.trim().isEmpty()) { return null; } DrugAnnotation secondParser = chEMBLParser; @@ -253,7 +253,7 @@ public class DrugService extends DbSearchService implements IDrugService { addProjectTargets(searchCriteria.getProject(), drug); } - Collections.sort(drugList, new Drug.NameComparator()); + drugList.sort(new Drug.NameComparator()); return drugList; } @@ -285,8 +285,7 @@ public class DrugService extends DbSearchService implements IDrugService { List<String> chemblList = chEMBLParser.getSuggestedQueryList(project, organism); if (drugBankList == null) { drugBankParser.setDrugSuggestedQueryList(project.getId(), organism, Collections.singletonList(" ")); - RefreshDrugSuggestedQueryListMinervaJob parameters = new RefreshDrugSuggestedQueryListMinervaJob(organism, project.getId()); - minervaJobService.addJob(new MinervaJob(MinervaJobType.REFRESH_DRUG_BANK_SUGGESTED_QUERY_LIST, MinervaJobPriority.LOWEST, parameters)); + submitRefreshDrugBankSuggestedQueryList(organism, project); } else { for (final String string : drugBankList) { resultSet.add(string.toLowerCase()); @@ -295,8 +294,7 @@ public class DrugService extends DbSearchService implements IDrugService { if (chemblList == null) { chEMBLParser.setDrugSuggestedQueryList(project.getId(), organism, Collections.singletonList(" ")); - RefreshDrugSuggestedQueryListMinervaJob parameters = new RefreshDrugSuggestedQueryListMinervaJob(organism, project.getId()); - minervaJobService.addJob(new MinervaJob(MinervaJobType.REFRESH_CHEMBL_SUGGESTED_QUERY_LIST, MinervaJobPriority.LOWEST, parameters)); + submitRefreshChemblSuggestedQueryList(organism, project); } else { for (final String string : chemblList) { resultSet.add(string.toLowerCase()); @@ -308,6 +306,22 @@ public class DrugService extends DbSearchService implements IDrugService { return result; } + @Override + public void submitRefreshChemblSuggestedQueryList(final MiriamData organism, final Project project) { + RefreshDrugSuggestedQueryListMinervaJob parameters = new RefreshDrugSuggestedQueryListMinervaJob(organism, project.getId()); + MinervaJob job = new MinervaJob(MinervaJobType.REFRESH_CHEMBL_SUGGESTED_QUERY_LIST, MinervaJobPriority.LOWEST, parameters); + job.setExternalObject(project); + minervaJobService.addJob(job); + } + + @Override + public void submitRefreshDrugBankSuggestedQueryList(final MiriamData organism, final Project project) { + RefreshDrugSuggestedQueryListMinervaJob parameters = new RefreshDrugSuggestedQueryListMinervaJob(organism, project.getId()); + MinervaJob job = new MinervaJob(MinervaJobType.REFRESH_DRUG_BANK_SUGGESTED_QUERY_LIST, MinervaJobPriority.LOWEST, parameters); + job.setExternalObject(project); + minervaJobService.addJob(job); + } + @Override public void execute(final MinervaJob job) throws Exception { if (job.getJobType() == MinervaJobType.REFRESH_DRUG_INFO) { diff --git a/service/src/main/java/lcsb/mapviewer/services/search/drug/IDrugService.java b/service/src/main/java/lcsb/mapviewer/services/search/drug/IDrugService.java index 99e3407ece37b96115dab339b6b36aea0ec439b0..6cfe842af73ace439828f5f344f1f7ed55fb5d35 100644 --- a/service/src/main/java/lcsb/mapviewer/services/search/drug/IDrugService.java +++ b/service/src/main/java/lcsb/mapviewer/services/search/drug/IDrugService.java @@ -1,7 +1,5 @@ package lcsb.mapviewer.services.search.drug; -import java.util.List; - import lcsb.mapviewer.annotation.data.Drug; import lcsb.mapviewer.annotation.services.DrugSearchException; import lcsb.mapviewer.model.Project; @@ -9,14 +7,18 @@ import lcsb.mapviewer.model.job.MinervaJobExecutor; import lcsb.mapviewer.model.map.MiriamData; import lcsb.mapviewer.services.search.IDbSearchService; +import java.util.List; + /** * Service for accessing information about drugs. - * + * * @author Piotr Gawron - * */ public interface IDrugService extends IDbSearchService<Drug>, MinervaJobExecutor { List<String> getSuggestedQueryList(final Project project, final MiriamData disease) throws DrugSearchException; + void submitRefreshChemblSuggestedQueryList(final MiriamData organism, final Project project); + + void submitRefreshDrugBankSuggestedQueryList(final MiriamData organism, final Project project); } diff --git a/service/src/test/java/lcsb/mapviewer/services/impl/MinervaJobServiceTest.java b/service/src/test/java/lcsb/mapviewer/services/impl/MinervaJobServiceTest.java index 88d63c13fb13efe4d3074307bb55bbdcec8b6441..cad28d28e52529d650a9ae280191d7e30393e04e 100644 --- a/service/src/test/java/lcsb/mapviewer/services/impl/MinervaJobServiceTest.java +++ b/service/src/test/java/lcsb/mapviewer/services/impl/MinervaJobServiceTest.java @@ -5,6 +5,7 @@ import lcsb.mapviewer.annotation.cache.GeneralCacheInterface; import lcsb.mapviewer.annotation.services.annotators.HgncAnnotator; import lcsb.mapviewer.common.tests.TestUtils; import lcsb.mapviewer.common.tests.UnitTestFailedWatcher; +import lcsb.mapviewer.model.Project; import lcsb.mapviewer.model.cache.CacheQuery; import lcsb.mapviewer.model.cache.CacheType; import lcsb.mapviewer.model.job.MinervaJob; @@ -14,6 +15,7 @@ import lcsb.mapviewer.model.job.MinervaJobStatus; import lcsb.mapviewer.model.job.MinervaJobType; import lcsb.mapviewer.model.map.MiriamData; import lcsb.mapviewer.model.map.MiriamType; +import lcsb.mapviewer.model.user.User; import lcsb.mapviewer.services.SpringServiceTestConfig; import lcsb.mapviewer.services.interfaces.IMinervaJobService; import lcsb.mapviewer.services.interfaces.IMiriamService; @@ -205,4 +207,67 @@ public class MinervaJobServiceTest extends TestUtils { } } + @Test + public void testUpdateProgress() { + minervaJobService.disableQueue(); + try { + RefreshMiriamInfoMinervaJob params = new RefreshMiriamInfoMinervaJob(-1); + MinervaJob job = new MinervaJob(MinervaJobType.REFRESH_MIRIAM_INFO, MinervaJobPriority.MEDIUM, params); + minervaJobService.addJob(job); + + minervaJobService.updateJobProgress(job, 50.0); + + job = minervaJobService.getJobById(job.getId()); + assertEquals(50.0, job.getProgress(), EPSILON); + } finally { + minervaJobService.enableQueue(false); + } + } + + @Test + public void testUpdateProgressInvalidJob() { + minervaJobService.disableQueue(); + try { + RefreshMiriamInfoMinervaJob params = new RefreshMiriamInfoMinervaJob(-1); + MinervaJob job = new MinervaJob(MinervaJobType.REFRESH_MIRIAM_INFO, MinervaJobPriority.MEDIUM, params); + + minervaJobService.updateJobProgress(job, 50.0); + + assertNull(minervaJobService.getJobById(job.getId())); + } finally { + minervaJobService.enableQueue(false); + } + } + + @Test + public void testAddJobWithExternalObject() { + Project project = createProject(); + minervaJobService.disableQueue(); + try { + RefreshMiriamInfoMinervaJob params = new RefreshMiriamInfoMinervaJob(-1); + MinervaJob job = new MinervaJob(MinervaJobType.REFRESH_MIRIAM_INFO, MinervaJobPriority.MEDIUM, params); + job.setExternalObject(project); + + minervaJobService.addJob(job); + + job = minervaJobService.getJobById(job.getId()); + + assertNotNull(job.getExternalObjectClass()); + assertNotNull(job.getExternalObjectId()); + } finally { + minervaJobService.enableQueue(false); + } + } + + public Project createProject() { + User admin = new User(); + admin.setId(BUILT_IN_TEST_ADMIN_ID); + + Project project = new Project(); + project.setProjectId(faker.numerify("T######")); + project.setOwner(admin); + + return project; + } + } diff --git a/service/src/test/java/lcsb/mapviewer/services/impl/ProjectServiceNoTransactionTest.java b/service/src/test/java/lcsb/mapviewer/services/impl/ProjectServiceNoTransactionTest.java index 7a697ee84b6038ec66ea40af8758eb9e5acff678..000a7709de727c9ec0257db9e571669131b4dacb 100644 --- a/service/src/test/java/lcsb/mapviewer/services/impl/ProjectServiceNoTransactionTest.java +++ b/service/src/test/java/lcsb/mapviewer/services/impl/ProjectServiceNoTransactionTest.java @@ -538,7 +538,7 @@ public class ProjectServiceNoTransactionTest extends TestUtils { ProjectService projectService = new ProjectService(null, mockProjectDao, Mockito.mock(ProjectLogEntryDao.class), - null, null, null, null, null, null, null, null, null, null, null, Mockito.mock(ProjectBackgroundService.class), null); + null, null, null, null, null, null, null, null, Mockito.mock(ProjectBackgroundService.class), null, null); ReflectionTestUtils.setField(projectService, "self", projectService); projectService.createImages(params); @@ -637,8 +637,8 @@ public class ProjectServiceNoTransactionTest extends TestUtils { GenericProtein protein = new GenericProtein("1"); project.addModel(model); model.addElement(protein); - service.fixProjectIssues(project); - assertFalse(project.getLogEntries().isEmpty()); + List<ProjectLogEntry> entries = service.fixModelIssues(model); + assertFalse(entries.isEmpty()); } diff --git a/service/src/test/java/lcsb/mapviewer/services/jobs/AnnotateProjectMinervaJobTest.java b/service/src/test/java/lcsb/mapviewer/services/jobs/AnnotateProjectMinervaJobTest.java new file mode 100644 index 0000000000000000000000000000000000000000..0eaeba78fe6c8a441c1f1a68ae96e872a3f15c4a --- /dev/null +++ b/service/src/test/java/lcsb/mapviewer/services/jobs/AnnotateProjectMinervaJobTest.java @@ -0,0 +1,40 @@ +package lcsb.mapviewer.services.jobs; + +import lcsb.mapviewer.annotation.services.ModelAnnotator; +import lcsb.mapviewer.model.user.UserAnnotationSchema; +import lcsb.mapviewer.services.ServiceTestFunctions; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import static org.junit.Assert.assertEquals; + +public class AnnotateProjectMinervaJobTest extends ServiceTestFunctions { + + @Autowired + private ModelAnnotator modelAnnotator; + + @Before + public void setUp() throws Exception { + } + + @After + public void tearDown() throws Exception { + } + + @Test + public void testJobSerialization() { + UserAnnotationSchema schema = modelAnnotator.createDefaultAnnotatorSchema(); + AnnotateProjectMinervaJob job = new AnnotateProjectMinervaJob("test", schema); + + AnnotateProjectMinervaJob job2 = new AnnotateProjectMinervaJob("test", new UserAnnotationSchema()); + + job2.assignData(job.getJobParameters()); + + UserAnnotationSchema serializedSchema = job2.getSchema(); + + assertEquals(schema.getClassAnnotators().size(), serializedSchema.getClassAnnotators().size()); + assertEquals(job.getJobParameters(), job2.getJobParameters()); + } +} diff --git a/web/src/main/java/lcsb/mapviewer/web/api/project/NewProjectJobController.java b/web/src/main/java/lcsb/mapviewer/web/api/project/NewProjectJobController.java new file mode 100644 index 0000000000000000000000000000000000000000..bf9087eb7775120c9e138337818b5f5b895f74cb --- /dev/null +++ b/web/src/main/java/lcsb/mapviewer/web/api/project/NewProjectJobController.java @@ -0,0 +1,73 @@ +package lcsb.mapviewer.web.api.project; + +import lcsb.mapviewer.model.Project; +import lcsb.mapviewer.model.job.MinervaJob; +import lcsb.mapviewer.persist.dao.MinervaJobProperty; +import lcsb.mapviewer.services.FailedDependencyException; +import lcsb.mapviewer.services.ObjectNotFoundException; +import lcsb.mapviewer.services.interfaces.IMinervaJobService; +import lcsb.mapviewer.services.interfaces.IProjectService; +import lcsb.mapviewer.web.api.NewApiResponseSerializer; +import org.hibernate.validator.constraints.NotBlank; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +@RestController +@Validated +@RequestMapping( + value = { + "/minerva/new_api/projects/{projectId:.+}/job", + }, + produces = MediaType.APPLICATION_JSON_VALUE) +public class NewProjectJobController { + + private final IProjectService projectService; + private final IMinervaJobService minervaJobService; + private final NewApiResponseSerializer serializer; + + @Autowired + public NewProjectJobController( + final IProjectService projectService, + final NewApiResponseSerializer serializer, + final IMinervaJobService minervaJobService + ) { + this.projectService = projectService; + this.serializer = serializer; + this.minervaJobService = minervaJobService; + } + + @PreAuthorize("hasAuthority('IS_ADMIN')" + + " or hasAuthority('IS_CURATOR') and hasAuthority('READ_PROJECT:' + #projectId)") + @GetMapping + public ResponseEntity<?> getLogEntries( + final @NotBlank @PathVariable(value = "projectId") String projectId, + final Pageable pageable) + throws ObjectNotFoundException, FailedDependencyException, IOException { + Project project = projectService.getProjectByProjectId(projectId); + if (project == null) { + throw new ObjectNotFoundException(); + } + Map<MinervaJobProperty, Object> searchFilter = new HashMap<>(); + searchFilter.put(MinervaJobProperty.EXTERNAL_OBJECT_CLASS, Collections.singleton(Project.class)); + searchFilter.put(MinervaJobProperty.EXTERNAL_OBJECT_ID, Collections.singleton(project.getId())); + + Page<MinervaJob> page = minervaJobService.getAll(searchFilter, pageable); + + return serializer.prepareResponse(page); + + } +} \ No newline at end of file diff --git a/web/src/main/java/lcsb/mapviewer/web/api/user/NewUserController.java b/web/src/main/java/lcsb/mapviewer/web/api/user/NewUserController.java index f6555a3e5ee7b18488dc0a78bc7740b56b223d70..abefda97ed55655b347da82eae753d350abf9d23 100644 --- a/web/src/main/java/lcsb/mapviewer/web/api/user/NewUserController.java +++ b/web/src/main/java/lcsb/mapviewer/web/api/user/NewUserController.java @@ -18,9 +18,9 @@ import lcsb.mapviewer.persist.dao.user.UserProperty; import lcsb.mapviewer.services.InvalidTokenException; import lcsb.mapviewer.services.ObjectExistsException; import lcsb.mapviewer.services.ObjectNotFoundException; +import lcsb.mapviewer.services.interfaces.IAnnotationService; import lcsb.mapviewer.services.interfaces.IConfigurationService; import lcsb.mapviewer.services.interfaces.ILdapService; -import lcsb.mapviewer.services.interfaces.IProjectService; import lcsb.mapviewer.services.interfaces.IUserService; import lcsb.mapviewer.services.utils.EmailException; import lcsb.mapviewer.services.utils.EmailSender; @@ -71,7 +71,7 @@ public class NewUserController extends BaseController { private final IUserService userService; private final ILdapService ldapService; - private final IProjectService projectService; + private final IAnnotationService annotationService; private final IConfigurationService configurationService; private final PasswordEncoder passwordEncoder; private final EmailSender emailSender; @@ -86,16 +86,16 @@ public class NewUserController extends BaseController { final EmailSender emailSender, final IConfigurationService configurationService, final ILdapService ldapService, - final IProjectService projectService, + final IAnnotationService annotationService, final SessionRegistry sessionRegistry, final NewApiResponseSerializer serializer) { this.userService = userService; this.passwordEncoder = passwordEncoder; this.emailSender = emailSender; - this.projectService = projectService; this.configurationService = configurationService; this.serializer = serializer; this.ldapService = ldapService; + this.annotationService = annotationService; this.userResponseDecorator = new NewUserResponseDecorator(ldapUsernames, sessionRegistry); this.refreshLdapUsernames(); } @@ -128,7 +128,7 @@ public class NewUserController extends BaseController { throw new ObjectNotFoundException("User doesn't exist"); } if (user.getAnnotationSchema() == null) { - user.setAnnotationSchema(projectService.prepareUserAnnotationSchema(user, true)); + user.setAnnotationSchema(annotationService.prepareUserAnnotationSchema(user, true)); } return serializer.prepareResponse(user, this.userResponseDecorator); } diff --git a/web/src/test/java/lcsb/mapviewer/web/ControllerIntegrationTest.java b/web/src/test/java/lcsb/mapviewer/web/ControllerIntegrationTest.java index 4443155aeb282b3024f0ed6e1fb87fffb15b9778..19c48cc114adc7c47e18a2d6a480ac71e8ee9ca5 100644 --- a/web/src/test/java/lcsb/mapviewer/web/ControllerIntegrationTest.java +++ b/web/src/test/java/lcsb/mapviewer/web/ControllerIntegrationTest.java @@ -2,6 +2,7 @@ package lcsb.mapviewer.web; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; +import lcsb.mapviewer.annotation.cache.CacheQueryMinervaJob; import lcsb.mapviewer.annotation.data.serializer.ChemicalSerializer; import lcsb.mapviewer.annotation.services.PubmedSearchException; import lcsb.mapviewer.common.TextFileUtils; @@ -20,6 +21,9 @@ import lcsb.mapviewer.model.graphics.HorizontalAlign; import lcsb.mapviewer.model.graphics.LineType; import lcsb.mapviewer.model.graphics.PolylineData; import lcsb.mapviewer.model.graphics.VerticalAlign; +import lcsb.mapviewer.model.job.MinervaJob; +import lcsb.mapviewer.model.job.MinervaJobPriority; +import lcsb.mapviewer.model.job.MinervaJobType; import lcsb.mapviewer.model.map.Comment; import lcsb.mapviewer.model.map.MiriamData; import lcsb.mapviewer.model.map.MiriamType; @@ -1035,4 +1039,11 @@ public abstract class ControllerIntegrationTest extends TestUtils { return result; } + protected MinervaJob createMinervaJob() { + return new MinervaJob( + MinervaJobType.REFRESH_CACHE, + MinervaJobPriority.LOWEST, + new CacheQueryMinervaJob("https://google.lu", -1)); + } + } diff --git a/web/src/test/java/lcsb/mapviewer/web/ProjectControllerIntegrationTest.java b/web/src/test/java/lcsb/mapviewer/web/ProjectControllerIntegrationTest.java index 41176a4ddac27fe0c0ff93e6cadfe4ff21b7e42c..29819fe5e649b8e86f2b6fb61ca03bc723b7571b 100644 --- a/web/src/test/java/lcsb/mapviewer/web/ProjectControllerIntegrationTest.java +++ b/web/src/test/java/lcsb/mapviewer/web/ProjectControllerIntegrationTest.java @@ -1403,6 +1403,8 @@ public class ProjectControllerIntegrationTest extends ControllerIntegrationTest project.removeProjectBackground(project.getProjectBackgrounds().get(0)); projectService.update(project); + minervaJobService.waitForTasksToFinish(); + final String projectHomeDir = projectService.getProjectHomeDir(project); new File(projectHomeDir).mkdirs(); diff --git a/web/src/test/java/lcsb/mapviewer/web/api/project/NewProjectDiseaseControllerTest.java b/web/src/test/java/lcsb/mapviewer/web/api/project/NewProjectDiseaseControllerTest.java index 82115c106f112e87700899bbf5da8d6cf73e8fbd..5883cab8a302b5fc485e0465d6b1e2e812c33026 100644 --- a/web/src/test/java/lcsb/mapviewer/web/api/project/NewProjectDiseaseControllerTest.java +++ b/web/src/test/java/lcsb/mapviewer/web/api/project/NewProjectDiseaseControllerTest.java @@ -93,6 +93,8 @@ public class NewProjectDiseaseControllerTest extends ControllerIntegrationTest { project.setDisease(null); projectService.update(project); + minervaJobService.waitForTasksToFinish(); + NewMiriamDataDTO data = createMiriamDataDTO(MiriamType.MESH_2012, "D010300"); RequestBuilder request = post("/minerva/new_api/projects/{projectId}/disease", TEST_PROJECT) diff --git a/web/src/test/java/lcsb/mapviewer/web/api/project/NewProjectJobControllerTest.java b/web/src/test/java/lcsb/mapviewer/web/api/project/NewProjectJobControllerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..bb4cf8a6f5a77993c0d19fd09b614de0c4ec9f6d --- /dev/null +++ b/web/src/test/java/lcsb/mapviewer/web/api/project/NewProjectJobControllerTest.java @@ -0,0 +1,70 @@ +package lcsb.mapviewer.web.api.project; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import lcsb.mapviewer.model.Project; +import lcsb.mapviewer.model.job.MinervaJob; +import lcsb.mapviewer.services.interfaces.IMinervaJobService; +import lcsb.mapviewer.web.ControllerIntegrationTest; +import lcsb.mapviewer.web.api.NewApiResponseSerializer; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.mock.web.MockHttpSession; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.web.servlet.RequestBuilder; + +import static org.junit.Assert.assertEquals; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@RunWith(SpringJUnit4ClassRunner.class) +public class NewProjectJobControllerTest extends ControllerIntegrationTest { + + @Autowired + private IMinervaJobService minervaJobService; + + @Autowired + private NewApiResponseSerializer newApiResponseSerializer; + + private ObjectMapper objectMapper; + + @Before + public void setUp() throws Exception { + objectMapper = newApiResponseSerializer.getObjectMapper(); + } + + @After + public void tearDown() throws Exception { + minervaJobService.waitForTasksToFinish(); + removeProject(TEST_PROJECT); + } + + @Test + public void testGetJobsForProject() throws Exception { + Project project = createAndPersistProject(TEST_PROJECT); + + MinervaJob job = createMinervaJob(); + job.setExternalObject(project); + minervaJobService.add(job); + + MinervaJob job2 = createMinervaJob(); + minervaJobService.add(job2); + + MockHttpSession session = createSession(BUILT_IN_TEST_ADMIN_LOGIN, BUILT_IN_TEST_ADMIN_PASSWORD); + + RequestBuilder request = get( + "/minerva/new_api/projects/{projectId}/job/", + TEST_PROJECT).session(session); + + String response = mockMvc.perform(request) + .andExpect(status().is2xxSuccessful()) + .andReturn().getResponse().getContentAsString(); + Page<Object> result = objectMapper.readValue(response, new TypeReference<Page<Object>>() { + }); + assertEquals(1, result.getTotalElements()); + } +}