diff --git a/rest-api/src/main/java/lcsb/mapviewer/api/QueryException.java b/rest-api/src/main/java/lcsb/mapviewer/api/QueryException.java index a6df0faec0db1460c28c325f6b49f1ec8e643bd2..6e5ea787241dc773cad190cc4bb4875a2b31c01a 100644 --- a/rest-api/src/main/java/lcsb/mapviewer/api/QueryException.java +++ b/rest-api/src/main/java/lcsb/mapviewer/api/QueryException.java @@ -6,4 +6,8 @@ public class QueryException extends Exception { super(message); } + public QueryException(String message, Exception reason) { + super(message, reason); + } + } diff --git a/rest-api/src/main/java/lcsb/mapviewer/api/comment/CommentController.java b/rest-api/src/main/java/lcsb/mapviewer/api/comment/CommentController.java index ce5c9fee4b4b6a2d17b6f1d2d16333e527e51a43..be23be4cb05706cc1a9d79d41aee654a4554202c 100644 --- a/rest-api/src/main/java/lcsb/mapviewer/api/comment/CommentController.java +++ b/rest-api/src/main/java/lcsb/mapviewer/api/comment/CommentController.java @@ -1,5 +1,6 @@ package lcsb.mapviewer.api.comment; +import java.awt.geom.Point2D; import java.util.List; import java.util.Map; @@ -22,11 +23,34 @@ public class CommentController extends BaseController { private CommentRestImpl commentController; @RequestMapping(value = "/getCommentList", method = { RequestMethod.GET, RequestMethod.POST }, produces = { MediaType.APPLICATION_JSON_VALUE }) - public List<Map<String,Object>> getOverlayList(@RequestParam(value = "token") String token, @RequestParam(value = "projectId") String projectId, + public List<Map<String, Object>> getOverlayList(@RequestParam(value = "token") String token, @RequestParam(value = "projectId") String projectId, @RequestParam(value = "columns", defaultValue = "") String columns) throws SecurityException, QueryException { return commentController.getCommentList(token, projectId, columns); } + @RequestMapping(value = "/addComment", method = { RequestMethod.GET, RequestMethod.POST }, produces = { MediaType.APPLICATION_JSON_VALUE }) + public Map<String, Object> addComment(@RequestParam(value = "token") String token, @RequestParam(value = "projectId") String projectId, + @RequestParam(value = "elementType", defaultValue = "POINT") String elementType, @RequestParam(value = "elementId", defaultValue = "") String elementId, + @RequestParam(value = "name") String name, @RequestParam(value = "email") String email, @RequestParam(value = "content") String content, + @RequestParam(value = "pinned", defaultValue = "true") String pinned, @RequestParam(value = "coordinates") String coordinates, + @RequestParam(value = "modelId") String modelId) throws SecurityException, QueryException { + String[] tmp = coordinates.split(","); + if (tmp.length != 2) { + throw new QueryException("Coordinates must be in the format: 'xxx.xx,yyy.yy'"); + } + Double x = null; + Double y = null; + try { + x = Double.valueOf(tmp[0]); + y = Double.valueOf(tmp[1]); + } catch (NumberFormatException e) { + throw new QueryException("Coordinates must be in the format: 'xxx.xx,yyy.yy'", e); + } + Point2D pointCoordinates = new Point2D.Double(x, y); + return commentController + .addComment(token, projectId, elementType, elementId, name, email, content, pinned.toLowerCase().equals("true"), pointCoordinates, modelId); + } + /** * @return the overlayController * @see #commentController diff --git a/rest-api/src/main/java/lcsb/mapviewer/api/comment/CommentRestImpl.java b/rest-api/src/main/java/lcsb/mapviewer/api/comment/CommentRestImpl.java index 4309e028aacde014dd9f51446fa999373c42cfbc..253474b53caefc6fcff1135458f53f0a7c489af3 100644 --- a/rest-api/src/main/java/lcsb/mapviewer/api/comment/CommentRestImpl.java +++ b/rest-api/src/main/java/lcsb/mapviewer/api/comment/CommentRestImpl.java @@ -290,4 +290,46 @@ public class CommentRestImpl { this.elementDao = elementDao; } + public Map<String, Object> addComment(String token, String projectId, String elementType, String elementId, String name, String email, String content, + boolean pinned, Point2D pointCoordinates, String submodelId) throws QueryException, SecurityException { + AuthenticationToken authenticationToken = userService.getToken(token); + Model model = modelService.getLastModelByProjectId(projectId, authenticationToken); + if (model == null) { + throw new QueryException("Project with given id doesn't exist"); + } + + Integer modelId = null; + try { + modelId = Integer.valueOf(submodelId); + } catch (NumberFormatException e) { + throw new QueryException("Invalid model identifier for given project", e); + } + Model submodel = model.getSubmodelById(modelId); + if (submodel == null) { + throw new QueryException("Invalid model identifier for given project"); + } + Object commentedObject; + if (elementType.equals(ElementIdentifierType.ALIAS.getJsName())) { + commentedObject = submodel.getElementByDbId(Integer.valueOf(elementId)); + if (commentedObject == null) { + throw new QueryException("Invalid commented element identifier for given project"); + } + } else if (elementType.equals(ElementIdentifierType.REACTION.getJsName())) { + commentedObject = submodel.getReactionByDbId(Integer.valueOf(elementId)); + if (commentedObject == null) { + throw new QueryException("Invalid commented element identifier for given project"); + } + } else if (elementType.equals(ElementIdentifierType.POINT.getJsName())) { + commentedObject = null; + } else { + throw new QueryException("Unknown type of commented object: " + elementType); + } + + commentService.addComment(name, email, content, model, pointCoordinates, commentedObject, pinned, submodel); + // TODO Auto-generated method stub + Map<String, Object> result = new HashMap<>(); + result.put("status", "OK"); + return result; + } + } diff --git a/rest-api/src/main/java/lcsb/mapviewer/api/overlay/OverlayRestImpl.java b/rest-api/src/main/java/lcsb/mapviewer/api/overlay/OverlayRestImpl.java index b23e1052b6fb6f4a61f7145eeb54e4fd2b0c0cc8..5b567c93f7f12ccdbece39aaa2553f2ab004bc47 100644 --- a/rest-api/src/main/java/lcsb/mapviewer/api/overlay/OverlayRestImpl.java +++ b/rest-api/src/main/java/lcsb/mapviewer/api/overlay/OverlayRestImpl.java @@ -15,6 +15,7 @@ import lcsb.mapviewer.services.SecurityException; import lcsb.mapviewer.services.interfaces.ILayoutService; import lcsb.mapviewer.services.interfaces.IModelService; import lcsb.mapviewer.services.interfaces.IUserService; +import lcsb.mapviewer.services.search.data.ElementIdentifier.ElementIdentifierType; import lcsb.mapviewer.services.search.layout.LightLayoutAliasView; import lcsb.mapviewer.services.search.layout.LightLayoutReactionView; import lcsb.mapviewer.services.view.AuthenticationToken; @@ -103,7 +104,7 @@ public class OverlayRestImpl { List<LightLayoutAliasView> speciesList = layoutService.getAliasesForLayout(model, overlayId, authenticationToken); for (LightLayoutAliasView lightLayoutAliasView : speciesList) { Map<String, Object> element = new HashMap<>(); - element.put("type", "ALIAS"); + element.put("type", ElementIdentifierType.ALIAS); element.put("overlayContent", lightLayoutAliasView); result.add(element); } @@ -111,7 +112,7 @@ public class OverlayRestImpl { List<LightLayoutReactionView> reactions = layoutService.getReactionsForLayout(model, overlayId, authenticationToken); for (LightLayoutReactionView lightReactionView : reactions) { Map<String, Object> element = new HashMap<>(); - element.put("type", "REACTION"); + element.put("type", ElementIdentifierType.REACTION); element.put("overlayContent", lightReactionView); result.add(element); } diff --git a/rest-api/src/main/java/lcsb/mapviewer/api/project/ProjectController.java b/rest-api/src/main/java/lcsb/mapviewer/api/project/ProjectController.java index d68310bbe473ea3b8ee7dcd2dc35fed60cb7ea47..b023eb744c9baa65f6459168484019e1f4024e4d 100644 --- a/rest-api/src/main/java/lcsb/mapviewer/api/project/ProjectController.java +++ b/rest-api/src/main/java/lcsb/mapviewer/api/project/ProjectController.java @@ -1,5 +1,6 @@ package lcsb.mapviewer.api.project; +import java.awt.geom.Point2D; import java.util.List; import java.util.Map; @@ -11,9 +12,8 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import lcsb.mapviewer.api.BaseController; +import lcsb.mapviewer.api.QueryException; import lcsb.mapviewer.services.SecurityException; -import lcsb.mapviewer.services.search.ElementView; -import lcsb.mapviewer.services.search.data.LightAliasView; @RestController @RequestMapping("/project") @@ -39,4 +39,25 @@ public class ProjectController extends BaseController { return projectController.getReactions(projectId, id, columns, token); } + @RequestMapping(value = "/getClosestElementsByCoordinates", method = { RequestMethod.GET, RequestMethod.POST }, + produces = { MediaType.APPLICATION_JSON_VALUE }) + public List<Map<String, Object>> getClosestElementsByCoordinates(@RequestParam(value = "projectId") String projectId, + @RequestParam(value = "modelId") String modelId, @RequestParam(value = "token") String token, @RequestParam(value = "coordinates") String coordinates, + @RequestParam(value = "count", defaultValue = "5") Integer count) throws QueryException, SecurityException { + String[] tmp = coordinates.split(","); + if (tmp.length != 2) { + throw new QueryException("Coordinates must be in the format: 'xxx.xx,yyy.yy'"); + } + Double x = null; + Double y = null; + try { + x = Double.valueOf(tmp[0]); + y = Double.valueOf(tmp[1]); + } catch (NumberFormatException e) { + throw new QueryException("Coordinates must be in the format: 'xxx.xx,yyy.yy'", e); + } + Point2D pointCoordinates = new Point2D.Double(x, y); + return projectController.getClosestElementsByCoordinates(projectId, modelId, token, pointCoordinates, Integer.valueOf(count)); + } + } \ No newline at end of file diff --git a/rest-api/src/main/java/lcsb/mapviewer/api/project/ProjectRestImpl.java b/rest-api/src/main/java/lcsb/mapviewer/api/project/ProjectRestImpl.java index 2c41521cc69a224e217f1ec3c538d4f91368d10b..4b207934092e19dfd3fc062477ad26577080bac6 100644 --- a/rest-api/src/main/java/lcsb/mapviewer/api/project/ProjectRestImpl.java +++ b/rest-api/src/main/java/lcsb/mapviewer/api/project/ProjectRestImpl.java @@ -1,5 +1,6 @@ package lcsb.mapviewer.api.project; +import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.util.ArrayList; import java.util.HashMap; @@ -8,20 +9,27 @@ import java.util.List; import java.util.Map; import java.util.Set; +import org.apache.commons.lang3.StringUtils; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.RequestParam; +import lcsb.mapviewer.common.exception.InvalidStateException; import lcsb.mapviewer.model.Project; import lcsb.mapviewer.model.map.model.Model; +import lcsb.mapviewer.model.map.reaction.Modifier; +import lcsb.mapviewer.model.map.reaction.Product; +import lcsb.mapviewer.model.map.reaction.Reactant; import lcsb.mapviewer.model.map.reaction.Reaction; import lcsb.mapviewer.model.map.species.Element; import lcsb.mapviewer.services.SecurityException; import lcsb.mapviewer.services.UserAccessException; import lcsb.mapviewer.services.interfaces.IModelService; import lcsb.mapviewer.services.interfaces.IProjectService; +import lcsb.mapviewer.services.interfaces.ISearchService; import lcsb.mapviewer.services.interfaces.IUserService; +import lcsb.mapviewer.services.search.data.ElementIdentifier.ElementIdentifierType; import lcsb.mapviewer.services.search.data.LightReactionView; import lcsb.mapviewer.services.view.AnnotationViewFactory; @@ -38,6 +46,9 @@ public class ProjectRestImpl { @Autowired private IModelService modelService; + @Autowired + private ISearchService searchService; + @Autowired AnnotationViewFactory annotationViewFactory; @@ -92,6 +103,36 @@ public class ProjectRestImpl { return result; } + public List<Map<String, Object>> getClosestElementsByCoordinates(String projectId, String modelId, String token, Point2D coordinates, Integer count) + throws UserAccessException, SecurityException { + List<Map<String, Object>> resultMap = new ArrayList<>(); + + Model model = modelService.getLastModelByProjectId(projectId, userService.getToken(token)); + + Model submodel = model.getSubmodelById(modelId); + + List<Object> elements = searchService.getClosestElements(submodel, coordinates, count); + for (Object object : elements) { + Map<String, Object> result = new HashMap<>(); + if (object instanceof Element) { + result.put("type", ElementIdentifierType.ALIAS); + Element element = (Element) object; + result.put("id", element.getId()); + result.put("modelId", element.getModel().getId()); + } else if (object instanceof Reaction) { + result.put("type", ElementIdentifierType.REACTION); + Reaction element = (Reaction) object; + result.put("id", element.getId()); + result.put("modelId", element.getModel().getId()); + + } else { + throw new InvalidStateException("Unknown type of result: " + object.getClass()); + } + resultMap.add(result); + } + return resultMap; + }; + public List<Map<String, Object>> getReactions(String projectId, String id, String columns, String token) throws UserAccessException, SecurityException { Model model = modelService.getLastModelByProjectId(projectId, userService.getToken(token)); Set<Integer> ids = new HashSet<>(); @@ -127,10 +168,30 @@ public class ProjectRestImpl { value = reaction.getId(); } else if (column.equals("modelid")) { value = reaction.getModelData().getId(); + } else if (column.equals("reactionid")) { + value = reaction.getIdReaction(); } else if (column.equals("name")) { value = reaction.getName(); } else if (column.equals("centerpoint")) { value = reaction.getCenterPoint(); + } else if (column.equals("products")) { + List<Integer> ids = new ArrayList<>(); + for (Product product : reaction.getProducts()) { + ids.add(product.getElement().getId()); + } + value = StringUtils.join(ids, ","); + } else if (column.equals("reactants")) { + List<Integer> ids = new ArrayList<>(); + for (Reactant reactant : reaction.getReactants()) { + ids.add(reactant.getElement().getId()); + } + value = StringUtils.join(ids, ","); + } else if (column.equals("modifiers")) { + List<Integer> ids = new ArrayList<>(); + for (Modifier product : reaction.getModifiers()) { + ids.add(product.getElement().getId()); + } + value = StringUtils.join(ids, ","); } else if (column.equals("type")) { value = reaction.getStringType(); } else if (column.equals("lines")) { @@ -212,10 +273,14 @@ public class ProjectRestImpl { Set<String> columnsSet = new HashSet<>(); if (columns.equals("")) { columnsSet.add("id"); + columnsSet.add("reactionId"); columnsSet.add("modelId"); columnsSet.add("type"); columnsSet.add("lines"); columnsSet.add("centerPoint"); + columnsSet.add("products"); + columnsSet.add("reactants"); + columnsSet.add("modifiers"); } else { for (String str : columns.split(",")) { columnsSet.add(str); @@ -241,4 +306,21 @@ public class ProjectRestImpl { this.modelService = modelService; } + /** + * @return the searchService + * @see #searchService + */ + public ISearchService getSearchService() { + return searchService; + } + + /** + * @param searchService + * the searchService to set + * @see #searchService + */ + public void setSearchService(ISearchService searchService) { + this.searchService = searchService; + } + } diff --git a/rest-api/src/test/java/lcsb/mapviewer/api/project/ProjectRestImplTest.java b/rest-api/src/test/java/lcsb/mapviewer/api/project/ProjectRestImplTest.java index 9911c0d844cdb80dc966fbc6ef11cf122e68f708..f3ffe752fd34822f4d89bd2f9e69af0afc49304e 100644 --- a/rest-api/src/test/java/lcsb/mapviewer/api/project/ProjectRestImplTest.java +++ b/rest-api/src/test/java/lcsb/mapviewer/api/project/ProjectRestImplTest.java @@ -1,9 +1,12 @@ package lcsb.mapviewer.api.project; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; +import java.awt.geom.Point2D; +import java.util.List; import java.util.Map; import org.apache.log4j.Logger; @@ -22,10 +25,10 @@ public class ProjectRestImplTest extends RestTestFunctions { Logger logger = Logger.getLogger(ProjectRestImplTest.class); @Autowired - ProjectRestImpl projectRestImpl; + ProjectRestImpl _projectRestImpl; @Autowired - IModelService modelService; + IModelService modelService; @AfterClass public static void tearDownAfterClass() throws Exception { @@ -42,11 +45,8 @@ public class ProjectRestImplTest extends RestTestFunctions { @Test public void testGetElementsProcessAllColumns() throws Exception { try { - Model model = super.getModelForFile("testFiles/model/sample.xml", true); - IModelService mockModelService = Mockito.mock(IModelService.class); - Mockito.when(mockModelService.getLastModelByProjectId(anyString(), any())).thenReturn(model); - projectRestImpl.setModelService(mockModelService); - for (Map<String, Object> element : projectRestImpl.getElements("sample", "", "", token.getId())) { + ProjectRestImpl projectRest = createMockProjectRest("testFiles/model/sample.xml"); + for (Map<String, Object> element : projectRest.getElements("sample", "", "", token.getId())) { for (String paramName : element.keySet()) { Object val = element.get(paramName); String paramValue = ""; @@ -59,9 +59,28 @@ public class ProjectRestImplTest extends RestTestFunctions { } catch (Exception e) { e.printStackTrace(); throw e; - } finally { - projectRestImpl.setModelService(modelService); } } + @Test + public void testGetClosestElementsByCoordinates() throws Exception { + try { + ProjectRestImpl projectRest = createMockProjectRest("testFiles/model/sample.xml"); + int count = 3; + List<Map<String, Object>> result = projectRest.getClosestElementsByCoordinates("sample", "0", token.getId(), new Point2D.Double(1, 2), count); + assertEquals(count, result.size()); + } catch (Exception e) { + e.printStackTrace(); + throw e; + } + } + + private ProjectRestImpl createMockProjectRest(String string) throws Exception { + Model model = super.getModelForFile(string, true); + IModelService mockModelService = Mockito.mock(IModelService.class); + Mockito.when(mockModelService.getLastModelByProjectId(anyString(), any())).thenReturn(model); + _projectRestImpl.setModelService(mockModelService); + return _projectRestImpl; + } + } diff --git a/service/src/main/java/lcsb/mapviewer/services/impl/SearchService.java b/service/src/main/java/lcsb/mapviewer/services/impl/SearchService.java index cec3c35b2c82b2db77c86e476718286966e6c284..05986fdeeb56ffe5b5767a7662ca62c61e30508c 100644 --- a/service/src/main/java/lcsb/mapviewer/services/impl/SearchService.java +++ b/service/src/main/java/lcsb/mapviewer/services/impl/SearchService.java @@ -533,7 +533,7 @@ public class SearchService implements ISearchService { // probably this could be improved algorithmitically, right now all objects // are sorted by distance, and numberOfElements closest are chosen as a list // of results - List<DistanceToObject> tmpList = new ArrayList<DistanceToObject>(); + List<DistanceToObject> tmpList = new ArrayList<>(); for (Reaction reaction : model.getReactions()) { tmpList.add(new DistanceToObject(reaction, point)); }