diff --git a/pom.xml b/pom.xml index 82a3d0d..e80320d 100644 --- a/pom.xml +++ b/pom.xml @@ -82,6 +82,13 @@ 2.4.0 + + + + org.springframework.boot + spring-boot-devtools + true + @@ -91,11 +98,8 @@ spring-boot-maven-plugin - - - spring-snapshots @@ -118,6 +122,4 @@ - - \ No newline at end of file diff --git a/src/main/examples/transcriptional_unit.txt b/src/main/examples/transcriptional_unit.txt index 0106477..370a778 100644 --- a/src/main/examples/transcriptional_unit.txt +++ b/src/main/examples/transcriptional_unit.txt @@ -16,12 +16,12 @@ CREATE (d1)-[:CONTAINS]->(n8) CREATE (d1)-[:CONTAINS]->(n10) CREATE (d1)-[:CONTAINS]->(n13) -CREATE (n1)-[:PRECEDES {componentIDs: ["p1"], componentRoles: ["promoter"], orientation: "inline"}]->(n2) -CREATE (n2)-[:PRECEDES {componentIDs: ["p2"], componentRoles: ["promoter"], orientation: "inline"}]->(n4) -CREATE (n4)-[:PRECEDES {componentIDs: ["rz1"], componentRoles: ["ribozyme"], orientation: "inline"}]->(n6) -CREATE (n6)-[:PRECEDES {componentIDs: ["rb1"], componentRoles: ["ribosome_entry_site"], orientation: "inline"}]->(n8) -CREATE (n8)-[:PRECEDES {componentIDs: ["c1"], componentRoles: ["CDS"], orientation: "inline"}]->(n10) -CREATE (n10)-[:PRECEDES {componentIDs: ["t1"], componentRoles: ["terminator"], orientation: "inline"}]->(n13) +CREATE (n1)-[:PRECEDES {componentIDs: ["p1"], componentRoles: ["promoter"], orientation: "INLINE"}]->(n2) +CREATE (n2)-[:PRECEDES {componentIDs: ["p2"], componentRoles: ["promoter"], orientation: "INLINE"}]->(n4) +CREATE (n4)-[:PRECEDES {componentIDs: ["rz1"], componentRoles: ["ribozyme"], orientation: "INLINE"}]->(n6) +CREATE (n6)-[:PRECEDES {componentIDs: ["rb1"], componentRoles: ["ribosome_entry_site"], orientation: "INLINE"}]->(n8) +CREATE (n8)-[:PRECEDES {componentIDs: ["c1"], componentRoles: ["CDS"], orientation: "INLINE"}]->(n10) +CREATE (n10)-[:PRECEDES {componentIDs: ["t1"], componentRoles: ["terminator"], orientation: "INLINE"}]->(n13) CREATE (d1)-[:ARCHIVES]->(b0:Branch {branchID: "test1"})-[:CONTAINS]->(c0:Commit {commitID: "c0"})-[:CONTAINS]->(s1:Snapshot {nodeIndex: 7}) CREATE (d1)-[:SELECTS]->(b0)-[:LATEST]->(c0) @@ -42,12 +42,12 @@ CREATE (s1)-[:CONTAINS]->(n31) CREATE (s1)-[:CONTAINS]->(n33) CREATE (s1)-[:CONTAINS]->(n36) -CREATE (n23)-[:PRECEDES {componentIDs: ["p1"], componentRoles: ["promoter"], orientation: "inline"}]->(n25) -CREATE (n25)-[:PRECEDES {componentIDs: ["p2"], componentRoles: ["promoter"], orientation: "inline"}]->(n27) -CREATE (n27)-[:PRECEDES {componentIDs: ["rz1"], componentRoles: ["ribozyme"], orientation: "inline"}]->(n29) -CREATE (n29)-[:PRECEDES {componentIDs: ["rb1"], componentRoles: ["ribosome_entry_site"], orientation: "inline"}]->(n31) -CREATE (n31)-[:PRECEDES {componentIDs: ["c1"], componentRoles: ["CDS"], orientation: "inline"}]->(n33) -CREATE (n33)-[:PRECEDES {componentIDs: ["t1"], componentRoles: ["terminator"], orientation: "inline"}]->(n36) +CREATE (n23)-[:PRECEDES {componentIDs: ["p1"], componentRoles: ["promoter"], orientation: "INLINE"}]->(n25) +CREATE (n25)-[:PRECEDES {componentIDs: ["p2"], componentRoles: ["promoter"], orientation: "INLINE"}]->(n27) +CREATE (n27)-[:PRECEDES {componentIDs: ["rz1"], componentRoles: ["ribozyme"], orientation: "INLINE"}]->(n29) +CREATE (n29)-[:PRECEDES {componentIDs: ["rb1"], componentRoles: ["ribosome_entry_site"], orientation: "INLINE"}]->(n31) +CREATE (n31)-[:PRECEDES {componentIDs: ["c1"], componentRoles: ["CDS"], orientation: "INLINE"}]->(n33) +CREATE (n33)-[:PRECEDES {componentIDs: ["t1"], componentRoles: ["terminator"], orientation: "INLINE"}]->(n36) CREATE (d2:DesignSpace {spaceID: "test2", nodeIndex: 5, commitIndex: 1}) @@ -63,10 +63,10 @@ CREATE (d2)-[:CONTAINS]->(n17) CREATE (d2)-[:CONTAINS]->(n19) CREATE (d2)-[:CONTAINS]->(n22) -CREATE (n13b)-[:PRECEDES {componentIDs: ["p1","p2"], componentRoles: ["promoter"], orientation: "inline"}]->(n15) -CREATE (n15)-[:PRECEDES {componentIDs: ["rb1","rb2"], componentRoles: ["ribosome_entry_site"], orientation: "inline"}]->(n17) -CREATE (n17)-[:PRECEDES {componentIDs: ["c1"], componentRoles: ["CDS"], orientation: "inline"}]->(n19) -CREATE (n19)-[:PRECEDES {componentIDs: ["t1"], componentRoles: ["terminator"], orientation: "inline"}]->(n22) +CREATE (n13b)-[:PRECEDES {componentIDs: ["p1","p2"], componentRoles: ["promoter"], orientation: "INLINE"}]->(n15) +CREATE (n15)-[:PRECEDES {componentIDs: ["rb1","rb2"], componentRoles: ["ribosome_entry_site"], orientation: "INLINE"}]->(n17) +CREATE (n17)-[:PRECEDES {componentIDs: ["c1"], componentRoles: ["CDS"], orientation: "INLINE"}]->(n19) +CREATE (n19)-[:PRECEDES {componentIDs: ["t1"], componentRoles: ["terminator"], orientation: "INLINE"}]->(n22) CREATE (d2)-[:ARCHIVES]->(b1:Branch {branchID: "test2"})-[:CONTAINS]->(c1:Commit {commitID: "c0"})-[:CONTAINS]->(s2:Snapshot {nodeIndex: 5}) CREATE (d2)-[:SELECTS]->(b1)-[:LATEST]->(c1) @@ -83,9 +83,9 @@ CREATE (s2)-[:CONTAINS]->(n41) CREATE (s2)-[:CONTAINS]->(n43) CREATE (s2)-[:CONTAINS]->(n46) -CREATE (n37)-[:PRECEDES {componentIDs: ["p1","p2"], componentRoles: ["promoter"], orientation: "inline"}]->(n39) -CREATE (n39)-[:PRECEDES {componentIDs: ["rb1","rb2"], componentRoles: ["ribosome_entry_site"], orientation: "inline"}]->(n41) -CREATE (n41)-[:PRECEDES {componentIDs: ["c1"], componentRoles: ["CDS"], orientation: "inline"}]->(n43) -CREATE (n43)-[:PRECEDES {componentIDs: ["t1"], componentRoles: ["terminator"], orientation: "inline"}]->(n46) +CREATE (n37)-[:PRECEDES {componentIDs: ["p1","p2"], componentRoles: ["promoter"], orientation: "INLINE"}]->(n39) +CREATE (n39)-[:PRECEDES {componentIDs: ["rb1","rb2"], componentRoles: ["ribosome_entry_site"], orientation: "INLINE"}]->(n41) +CREATE (n41)-[:PRECEDES {componentIDs: ["c1"], componentRoles: ["CDS"], orientation: "INLINE"}]->(n43) +CREATE (n43)-[:PRECEDES {componentIDs: ["t1"], componentRoles: ["terminator"], orientation: "INLINE"}]->(n46) ; \ No newline at end of file diff --git a/src/main/java/knox/spring/data/neo4j/controller/KnoxController.java b/src/main/java/knox/spring/data/neo4j/controller/KnoxController.java index 99a1aaf..9fd3148 100644 --- a/src/main/java/knox/spring/data/neo4j/controller/KnoxController.java +++ b/src/main/java/knox/spring/data/neo4j/controller/KnoxController.java @@ -4,10 +4,8 @@ import java.io.InputStream; import java.util.*; -import knox.spring.data.neo4j.domain.DesignSpace; import knox.spring.data.neo4j.exception.*; import knox.spring.data.neo4j.sample.DesignSampler.EnumerateType; -import knox.spring.data.neo4j.sbol.SBOLConversion; import knox.spring.data.neo4j.services.DesignSpaceService; import org.sbolstandard.core2.SBOLConversionException; @@ -522,6 +520,38 @@ public ResponseEntity mergeDesignSpaces(@RequestParam(value = "inputSpac } } + /** + * @api {post} /designSpace/reverse Reverse + * @apiName reverseDesignSpace + * @apiGroup DesignSpace + * + * @apiParam {String} inputSpaceID ID for the input design space to be reversed. + * @apiParam {String} outputSpaceID ID for the output design space resulting from reverse. If omitted, then the result is + * stored in the input design space. + * + * @apiDescription Reverse the edges from the input design space. + */ + @RequestMapping(value = "/designSpace/reverse", method = RequestMethod.POST) + public ResponseEntity reverseDesignSpace(@RequestParam(value = "inputSpaceID", required = true) String inputSpaceID, + @RequestParam(value = "outputSpaceID", required = false) String outputSpaceID) { + try { + long startTime = System.nanoTime(); + + if (outputSpaceID == null) { + designSpaceService.reverseDesignSpace(inputSpaceID); + } else { + designSpaceService.reverseDesignSpace(inputSpaceID, outputSpaceID); + } + + return new ResponseEntity("{\"message\": \"Design space was successfully reversed after " + + (System.nanoTime() - startTime) + " ns.\"}", HttpStatus.NO_CONTENT); + } catch (ParameterEmptyException | DesignSpaceNotFoundException | + DesignSpaceConflictException | DesignSpaceBranchesConflictException ex) { + return new ResponseEntity("{\"message\": \"" + ex.getMessage() + "\"}", + HttpStatus.BAD_REQUEST); + } + } + /** * @api {post} /designSpace/or OR * @apiName orDesignSpaces diff --git a/src/main/java/knox/spring/data/neo4j/domain/Edge.java b/src/main/java/knox/spring/data/neo4j/domain/Edge.java index ec5547e..1248fc9 100644 --- a/src/main/java/knox/spring/data/neo4j/domain/Edge.java +++ b/src/main/java/knox/spring/data/neo4j/domain/Edge.java @@ -555,7 +555,6 @@ public boolean hasOrientation() { return isInline() || isReverseComplement(); } - public boolean hasOrientation(Orientation orientation) { return hasOrientation() && this.orientation.equals(orientation); } diff --git a/src/main/java/knox/spring/data/neo4j/operations/ReverseOperator.java b/src/main/java/knox/spring/data/neo4j/operations/ReverseOperator.java new file mode 100644 index 0000000..875380e --- /dev/null +++ b/src/main/java/knox/spring/data/neo4j/operations/ReverseOperator.java @@ -0,0 +1,22 @@ +package knox.spring.data.neo4j.operations; + +import knox.spring.data.neo4j.domain.Edge; +import knox.spring.data.neo4j.domain.NodeSpace; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +public class ReverseOperator { + public static void apply(NodeSpace inputSpace, NodeSpace outputSpace) { + //copy input space to a new output space + outputSpace.copyNodeSpace(inputSpace); + + Set allEdges = outputSpace.getEdges(); + + //traverse all edges of input space and flip the orientation attribute + for(Edge edge: allEdges){ + edge.reverseOrientation(); + } + } +} \ No newline at end of file diff --git a/src/main/java/knox/spring/data/neo4j/services/DesignSpaceService.java b/src/main/java/knox/spring/data/neo4j/services/DesignSpaceService.java index a106b58..70baa56 100644 --- a/src/main/java/knox/spring/data/neo4j/services/DesignSpaceService.java +++ b/src/main/java/knox/spring/data/neo4j/services/DesignSpaceService.java @@ -9,14 +9,11 @@ import knox.spring.data.neo4j.domain.Snapshot; import knox.spring.data.neo4j.exception.*; import knox.spring.data.neo4j.operations.ANDOperator; -import knox.spring.data.neo4j.operations.Concatenation; import knox.spring.data.neo4j.operations.JoinOperator; import knox.spring.data.neo4j.operations.MergeOperator; import knox.spring.data.neo4j.operations.OROperator; -import knox.spring.data.neo4j.operations.Product; +import knox.spring.data.neo4j.operations.ReverseOperator; import knox.spring.data.neo4j.operations.RepeatOperator; -import knox.spring.data.neo4j.operations.Star; -import knox.spring.data.neo4j.operations.Union; import knox.spring.data.neo4j.repositories.BranchRepository; import knox.spring.data.neo4j.repositories.CommitRepository; import knox.spring.data.neo4j.repositories.DesignSpaceRepository; @@ -325,7 +322,31 @@ public void mergeBranches(String targetSpaceID, List inputBranchIDs, saveDesignSpace(targetSpace); } - + + public void reverseDesignSpace(String inputSpaceID) { + reverseDesignSpace(inputSpaceID, inputSpaceID); + } + + public void reverseDesignSpace(String inputSpaceID, String outputSpaceID) { + List inputSpaceIDs = new ArrayList<>(); + inputSpaceIDs.add(inputSpaceID); + List inputSpaces = new ArrayList(inputSpaceIDs.size()); + + DesignSpace outputSpace = loadIOSpaces(inputSpaceIDs, outputSpaceID, inputSpaces); + + ReverseOperator.apply(inputSpaces.get(0), outputSpace); + + + List inputSnaps = new ArrayList(inputSpaces.size()); + + NodeSpace outputSnap = mergeVersionHistories(castNodeSpacesToDesignSpaces(inputSpaces), + outputSpace, inputSnaps); + + ReverseOperator.apply(inputSnaps.get(0), outputSnap); + + saveDesignSpace(outputSpace); + } + private DesignSpace loadIOSpaces(List inputSpaceIDs, String outputSpaceID, List inputSpaces) { for (String inputSpaceID : inputSpaceIDs) { @@ -863,7 +884,8 @@ private DesignSpace findDesignSpace(String targetSpaceID) { } private DesignSpace loadDesignSpace(String targetSpaceID) { - DesignSpace targetSpace = designSpaceRepository.findOne(getDesignSpaceGraphID(targetSpaceID), 3); + Long keyID = getDesignSpaceGraphID(targetSpaceID); + DesignSpace targetSpace = designSpaceRepository.findOne(keyID, 3); for (Commit commit : targetSpace.getCommits()) { commit.setSnapshot(reloadSnapshot(commit.getSnapshot())); diff --git a/src/main/resources/static/js/endpoints.js b/src/main/resources/static/js/endpoints.js index 912b74f..346377b 100644 --- a/src/main/resources/static/js/endpoints.js +++ b/src/main/resources/static/js/endpoints.js @@ -30,7 +30,8 @@ export const operators = { OR: 'or', AND: 'and', MERGE: 'merge', - REPEAT: 'repeat' + REPEAT: 'repeat', + REVERSE: 'reverse' }; @@ -288,4 +289,19 @@ export function designSpaceMerge(inputSpaces, outputSpace, tolerance){ } else { swalError(request.response); } +} + +export function designSpaceReverse(inputSpace, outputSpace){ + let query = "?"; + query += encodeQueryParameter("inputSpaceID", inputSpace, query); + query += encodeQueryParameter("outputSpaceID", outputSpace, query); + + let request = new XMLHttpRequest(); + request.open("POST", endpoints.DESIGN + "/" + operators.REVERSE + query, false); + request.send(null); + if (request.status >= 200 && request.status < 300) { + swalSuccess(); + } else { + swalError(request.response); + } } \ No newline at end of file diff --git a/src/main/resources/static/js/knox.js b/src/main/resources/static/js/knox.js index fea12f9..849376c 100644 --- a/src/main/resources/static/js/knox.js +++ b/src/main/resources/static/js/knox.js @@ -517,6 +517,15 @@ $('#apply-operators-tooltip').click(() => { } div.appendChild(tolDiv); } + if(this.value === endpoint.operators.REVERSE){ + if(div.contains(inputDiv)){ + div.removeChild(inputDiv); + } + const guidance = document.createElement("P"); + const text = document.createTextNode("The current designSpace will be reversed."); + guidance.appendChild(text); + div.appendChild(guidance); + } }); swal({ @@ -557,6 +566,11 @@ $('#apply-operators-tooltip').click(() => { case endpoint.operators.MERGE: endpoint.designSpaceMerge(inputSpaces, outputSpace, tolerance); break; + + case endpoint.operators.REVERSE: + // the reverse operator would only reverse one inputSpace + endpoint.designSpaceReverse(inputSpaces[0], outputSpace); + break; } } });