Skip to content

Commit

Permalink
Add collection-level graphs
Browse files Browse the repository at this point in the history
  • Loading branch information
ml-evs committed Jul 10, 2023
1 parent e7a831c commit b2934d0
Show file tree
Hide file tree
Showing 10 changed files with 130 additions and 26 deletions.
1 change: 0 additions & 1 deletion pydatalab/pydatalab/models/traits.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ def add_missing_collection_relationships(cls, values):
if len([d for d in values.get("relationships", []) if d.type == "collections"]) != len(
values.get("collections", [])
):
breakpoint()
raise RuntimeError("Relationships and collections mismatch")

return values
6 changes: 3 additions & 3 deletions pydatalab/pydatalab/routes/v0_1/collections.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
collection = Blueprint("collections", __name__)


@collection.route("/collections/")
@collection.route("/collections")
def get_collections():

collections = flask_mongo.db.collections.aggregate(
Expand Down Expand Up @@ -87,7 +87,7 @@ def get_collection(collection_id):
)


@collection.route("/collections/", methods=["PUT"])
@collection.route("/collections", methods=["PUT"])
def create_collection():
request_json = request.get_json() # noqa: F821 pylint: disable=undefined-variable
data = request_json.get("data", {})
Expand Down Expand Up @@ -301,7 +301,7 @@ def delete_collection(collection_id: str):
)


@collection.route("/search-collections/", methods=["GET"])
@collection.route("/search-collections", methods=["GET"])
def search_collections():
query = request.args.get("query", type=str)
nresults = request.args.get("nresults", default=100, type=int)
Expand Down
32 changes: 24 additions & 8 deletions pydatalab/pydatalab/routes/v0_1/graphs.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,33 @@
from typing import Callable, Dict, Optional, Set

from flask import jsonify
from flask import jsonify, request

from pydatalab.mongo import flask_mongo
from pydatalab.routes.utils import get_default_permissions


def get_graph_cy_format(item_id: Optional[str] = None):
def get_graph_cy_format(item_id: Optional[str] = None, collection_id: Optional[str] = None):

collection_id = request.args.get("collection_id", type=str)

if item_id is None:
if collection_id is not None:
collection_immutable_id = flask_mongo.db.collections.find_one(
{"collection_id": collection_id}, projection={"_id": 1}
)
if not collection_immutable_id:
raise RuntimeError("No collection {collection_id=} found.")
collection_immutable_id = collection_immutable_id["_id"]
query = {
"$and": [
{"relationships.immutable_id": collection_immutable_id},
{"relationships.type": "collections"},
]
}
else:
query = {}
all_documents = flask_mongo.db.items.find(
get_default_permissions(user_only=False),
{**query, **get_default_permissions(user_only=False)},
projection={"item_id": 1, "name": 1, "type": 1, "relationships": 1},
)
node_ids: Set[str] = {document["item_id"] for document in all_documents}
Expand All @@ -33,11 +50,11 @@ def get_graph_cy_format(item_id: Optional[str] = None):
for relationship in document.get("relationships", [])
}
if len(node_ids) > 1:
query = [{"item_id": id} for id in node_ids if id != item_id]
or_query = [{"item_id": id} for id in node_ids if id != item_id]
# query.extend([{"relationships.item_id": id} for id in node_ids if id != item_id])
next_shell = flask_mongo.db.items.find(
{
"$or": query,
"$or": or_query,
**get_default_permissions(user_only=False),
},
projection={"item_id": 1, "name": 1, "type": 1, "relationships": 1},
Expand All @@ -55,8 +72,8 @@ def get_graph_cy_format(item_id: Optional[str] = None):

node_collections = set()
for relationship in document.get("relationships", []):
# only considering child-parent relationships:
if relationship.get("type") == "collections":
# only considering child-parent relationships
if relationship.get("type") == "collections" and not collection_id:
collection_data = flask_mongo.db.collections.find_one(
{
"_id": relationship["immutable_id"],
Expand Down Expand Up @@ -120,7 +137,6 @@ def get_graph_cy_format(item_id: Optional[str] = None):
"id": document["item_id"],
"name": document["name"],
"type": document["type"],
"collections": list(node_collections),
}
}
)
Expand Down
24 changes: 24 additions & 0 deletions pydatalab/tests/routers/test_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,27 @@ def test_simple_graph(client):
graph = client.get("/item-graph/parent").json
assert len(graph["nodes"]) == 4
assert len(graph["edges"]) == 3

collection_id = "testcoll"
collection_json = {
"data": {
"collection_id": collection_id,
"title": "Test title",
"starting_members": [
{"item_id": "parent"},
{"item_id": "child_1"},
{"item_id": "child_2"},
],
}
}
response = client.put("/collections", json=collection_json)
assert response.status_code == 201
assert response.json["status"] == "success"

graph = client.get(f"/item-graph?collection_id={collection_id}").json
assert len(graph["nodes"]) == 3
assert len(graph["edges"]) == 2

graph = client.get("/item-graph/parent").json
assert len(graph["nodes"]) == 4
assert len(graph["edges"]) == 3
10 changes: 5 additions & 5 deletions pydatalab/tests/routers/test_samples.py
Original file line number Diff line number Diff line change
Expand Up @@ -394,19 +394,19 @@ def test_create_cell(client, default_cell):
def test_create_collections(client, default_collection):

# Check no collections initially
response = client.get("/collections/")
response = client.get("/collections")
assert len(response.json["data"]) == 0, response.json
assert response.status_code == 200

# Create an empty collection
response = client.put("/collections/", json={"data": json.loads(default_collection.json())})
response = client.put("/collections", json={"data": json.loads(default_collection.json())})
assert response.status_code == 201, response.json
assert response.json["status"] == "success"
assert response.json["data"]["collection_id"] == "test_collection"
assert response.json["data"]["title"] == "My Test Collection"
assert response.json["data"]["num_items"] == 0

response = client.get("/collections/")
response = client.get("/collections")
assert response.status_code == 200
assert len(response.json["data"]) == 1
assert response.json["data"][0]["collection_id"] == "test_collection"
Expand All @@ -426,7 +426,7 @@ def test_create_collections(client, default_collection):
]
}
)
response = client.put("/collections/", json={"data": data})
response = client.put("/collections", json={"data": data})
assert response.status_code == 201, response.json
assert response.json["status"] == "success"
assert response.json["data"]["collection_id"] == "test_collection_2"
Expand Down Expand Up @@ -461,7 +461,7 @@ def test_create_collections(client, default_collection):
assert len(response.json["item_data"]["collections"]) == 0

# remake it for the next test
response = client.put("/collections/", json={"data": data})
response = client.put("/collections", json={"data": data})
assert response.status_code == 201, response.json
assert response.json["status"] == "success"
assert response.json["data"]["collection_id"] == "test_collection_2"
Expand Down
6 changes: 3 additions & 3 deletions webapp/src/components/CollectionInformation.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
v-model="CollectionDescription"
></TinyMceInline>

<!-- <CollectionRelationshipVisualization :collection_id="collection_id" /> -->
<CollectionRelationshipVisualization :collection_id="collection_id" />
<FancyCollectionSampleTable :collection_id="collection_id" />
</div>
</template>
Expand All @@ -30,7 +30,7 @@ import { createComputedSetterForCollectionField } from "@/field_utils.js";
import FancyCollectionSampleTable from "@/components/FancyCollectionSampleTable";
import TinyMceInline from "@/components/TinyMceInline";
import Creators from "@/components/Creators";
// import CollectionRelationshipVisualization from "@/components/CollectionRelationshipVisualization";
import CollectionRelationshipVisualization from "@/components/CollectionRelationshipVisualization";
export default {
props: {
Expand All @@ -47,7 +47,7 @@ export default {
TinyMceInline,
FancyCollectionSampleTable,
Creators,
// CollectionRelationshipVisualization,
CollectionRelationshipVisualization,
},
};
</script>
58 changes: 58 additions & 0 deletions webapp/src/components/CollectionRelationshipVisualization.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<template>
<label class="mr-2">Relationships</label>
<div>
<ItemGraph
:graphData="graphData"
style="height: 150px"
:defaultGraphStyle="elk - layered - down"
/>
</div>
</template>

<script>
// import FormattedItemName from "@/components/FormattedItemName"
import ItemGraph from "@/components/ItemGraph";
import { getItemGraph } from "@/server_fetch_utils.js";
export default {
computed: {
graphData() {
return this.$store.state.itemGraphData;
},
},
props: {
collection_id: String,
},
async mounted() {
await getItemGraph({ item_id: null, collection_id: this.collection_id });
},
components: {
ItemGraph,
},
};
</script>

<style scoped>
.nav-link {
cursor: pointer;
}
.contents-item {
cursor: pointer;
}
.contents-blocktype {
font-style: italic;
color: gray;
margin-right: 1rem;
}
.contents-blocktitle {
color: #004175;
}
#contents-ol {
margin-bottom: 0rem;
padding-left: 1rem;
}
</style>
6 changes: 5 additions & 1 deletion webapp/src/components/ItemGraph.vue
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,14 @@ const layoutOptions = {
export default {
props: {
graphData: Object,
defaultGraphStyle: {
type: String,
default: "elk-stress",
},
},
data() {
return {
graphStyle: "elk-stress",
graphStyle: this.defaultGraphStyle,
};
},
methods: {
Expand Down
2 changes: 1 addition & 1 deletion webapp/src/components/RelationshipVisualization.vue
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export default {
item_id: String,
},
async mounted() {
await getItemGraph(this.item_id);
await getItemGraph({ item_id: this.item_id });
},
components: {
// FormattedItemName
Expand Down
11 changes: 7 additions & 4 deletions webapp/src/server_fetch_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ export function createNewSamples(newSampleDatas, copyFromItemIds = null) {
}

export function createNewCollection(collection_id, title, startingData = {}, copyFrom = null) {
return fetch_put(`${API_URL}/collections/`, {
return fetch_put(`${API_URL}/collections`, {
copy_from_collection_id: copyFrom,
data: {
collection_id: collection_id,
Expand Down Expand Up @@ -179,7 +179,7 @@ export function getCollectionSampleList(collection_id) {
}

export function getCollectionList() {
return fetch_get(`${API_URL}/collections/`)
return fetch_get(`${API_URL}/collections`)
.then(function (response_json) {
store.commit("setCollectionList", response_json.data);
})
Expand Down Expand Up @@ -498,11 +498,14 @@ export async function addRemoteFileToSample(file_entry, item_id) {
.catch((error) => `addRemoteFilesToSample unsuccessful. Error: ${error}`);
}

export async function getItemGraph(item_id = "") {
export async function getItemGraph({ item_id = null, collection_id = null } = {}) {
let url = `${API_URL}/item-graph`;
if (item_id != "") {
if (item_id != null) {
url = url + "/" + item_id;
}
if (collection_id != null) {
url = url + "?collection_id=" + collection_id;
}
return fetch_get(url)
.then(function (response_json) {
console.log("received graph");
Expand Down

0 comments on commit b2934d0

Please sign in to comment.