Skip to content

Commit

Permalink
Merge ccb72ee into 29af490
Browse files Browse the repository at this point in the history
  • Loading branch information
BenjaminCharmes committed Sep 10, 2024
2 parents 29af490 + ccb72ee commit 21c6290
Show file tree
Hide file tree
Showing 6 changed files with 308 additions and 14 deletions.
54 changes: 54 additions & 0 deletions pydatalab/pydatalab/routes/v0_1/collections.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import datetime
import json

from bson import ObjectId
from flask import Blueprint, jsonify, request
from flask_login import current_user
from pydantic import ValidationError
Expand Down Expand Up @@ -325,3 +326,56 @@ def search_collections():
]

return jsonify({"status": "success", "data": list(cursor)}), 200


@COLLECTIONS.route("/collections/<collection_id>", methods=["POST"])
def add_items_to_collection(collection_id):
data = request.get_json()
refcodes = data.get("data", {}).get("refcodes", [])

collection = flask_mongo.db.collections.find_one(
{"collection_id": collection_id, **get_default_permissions()}
)

if not collection:
return jsonify({"error": "Collection not found"}), 404

if not refcodes:
return jsonify({"error": "No item provided"}), 400

item_count = flask_mongo.db.items.count_documents(
{"refcode": {"$in": refcodes}, **get_default_permissions()}
)

if item_count == 0:
return jsonify({"error": "No matching items found"}), 404

update_result = flask_mongo.db.items.update_many(
{"refcode": {"$in": refcodes}, **get_default_permissions()},
{
"$addToSet": {
"relationships": {
"description": "Is a member of",
"relation": None,
"type": "collections",
"immutable_id": ObjectId(collection["_id"]),
}
}
},
)

if update_result.matched_count == 0:
return (jsonify({"status": "error", "message": "Unable to add to collection."}), 400)

if update_result.modified_count == 0:
return (
jsonify(
{
"status": "success",
"message": "No update was performed",
}
),
200,
)

return (jsonify({"status": "success"}), 200)
67 changes: 58 additions & 9 deletions pydatalab/tests/server/test_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,21 @@ def test_simple_graph(admin_client):

child_2 = Sample(
item_id="child_2",
refcode="test:TEST02",
synthesis_constituents=[
Constituent(item={"type": "samples", "item_id": "parent"}, quantity=None),
],
)

child_3 = Sample(
item_id="child_3",
synthesis_constituents=[
Constituent(item={"type": "samples", "item_id": "parent"}, quantity=None),
],
)

child_4 = Sample(
item_id="child_4",
synthesis_constituents=[
Constituent(item={"type": "samples", "item_id": "parent"}, quantity=None),
],
Expand All @@ -45,25 +60,30 @@ def test_simple_graph(admin_client):
cell_format="swagelok",
)

new_samples = [json.loads(d.json()) for d in [parent, child_1, child_2, missing_child, cell]]
new_samples = [
json.loads(d.json())
for d in [parent, child_1, child_2, child_3, child_4, missing_child, cell]
]

response = admin_client.post(
sample_list = admin_client.post(
"/new-samples/",
json={"new_sample_datas": new_samples},
)

assert response.status_code == 207
assert all(d == 201 for d in response.json["http_codes"])
assert sample_list.status_code == 207
assert all(d == 201 for d in sample_list.json["http_codes"])

graph = admin_client.get("/item-graph").json
assert {n["data"]["id"] for n in graph["nodes"]} == {
"parent",
"child_1",
"child_2",
"child_3",
"child_4",
"missing_child",
"abcd-1-2-3",
}
assert len(graph["edges"]) == 4
assert len(graph["edges"]) == 6

response = admin_client.post("/delete-sample/", json={"item_id": "missing_child"})
assert response
Expand All @@ -73,18 +93,20 @@ def test_simple_graph(admin_client):
"parent",
"child_1",
"child_2",
"child_3",
"child_4",
"abcd-1-2-3",
}

assert len(graph["edges"]) == 3
assert len(graph["edges"]) == 5

graph = admin_client.get("/item-graph/child_1").json
assert len(graph["nodes"]) == 2
assert len(graph["edges"]) == 1

graph = admin_client.get("/item-graph/parent").json
assert len(graph["nodes"]) == 4
assert len(graph["edges"]) == 3
assert len(graph["nodes"]) == 6
assert len(graph["edges"]) == 5

collection_id = "testcoll"
collection_json = {
Expand All @@ -107,5 +129,32 @@ def test_simple_graph(admin_client):
assert len(graph["edges"]) == 2

graph = admin_client.get("/item-graph/parent").json
assert len(graph["nodes"]) == 7
assert len(graph["edges"]) == 10

samples = sample_list.json["responses"]

refcode_child_3 = None
refcode_child_4 = None

for item in samples:
if item["item_id"] == "child_3":
refcode_child_3 = item["sample_list_entry"].get("refcode")
elif item["item_id"] == "child_4":
refcode_child_4 = item["sample_list_entry"].get("refcode")

response = admin_client.post(
f"/collections/{collection_id}",
json={"data": {"refcodes": [refcode_child_3, refcode_child_4]}},
)

assert response.status_code == 200
assert response.json["status"] == "success"

graph = admin_client.get(f"/item-graph?collection_id={collection_id}").json
assert len(graph["nodes"]) == 5
assert len(graph["edges"]) == 8
assert len(graph["edges"]) == 4

graph = admin_client.get("/item-graph/parent").json
assert len(graph["nodes"]) == 7
assert len(graph["edges"]) == 14
53 changes: 53 additions & 0 deletions pydatalab/tests/server/test_samples.py
Original file line number Diff line number Diff line change
Expand Up @@ -642,3 +642,56 @@ def test_items_added_to_existing_collection(client, default_collection, default_
len([d for d in response.json["item_data"]["relationships"] if d["type"] == "collections"])
== 1
)


@pytest.mark.dependency()
def test_add_items_to_collection_not_found(client):
collection_id = "invalid_collection_id"

response = client.post(f"/collections/{collection_id}", json={"data": {"refcodes": []}})
assert response.status_code == 404
assert response.json["error"] == "Collection not found"


@pytest.mark.dependency(depends=["test_add_items_to_collection_not_found"])
def test_add_items_to_collection_no_items(client, default_collection):
response = client.post(
f"/collections/{default_collection.collection_id}", json={"data": {"refcodes": []}}
)

assert response.status_code == 400
assert response.json["error"] == "No item provided"


@pytest.mark.dependency(depends=["test_add_items_to_collection_no_items"])
def test_add_items_to_collection_no_matching_items(client, default_collection):
refcodes = ["item123", "item456"]

response = client.post(
f"/collections/{default_collection.collection_id}", json={"data": {"refcodes": refcodes}}
)
assert response.status_code == 404
assert response.json["error"] == "No matching items found"


@pytest.mark.dependency(depends=["test_add_items_to_collection_no_matching_items"])
def test_add_items_to_collection_success(client, default_collection, example_items):
refcodes = [
item["refcode"] for item in example_items if item["item_id"] in {"12345", "sample_1"}
]

response = client.post(
f"/collections/{default_collection.collection_id}",
json={"data": {"refcodes": refcodes}},
)

assert response.status_code == 200
assert response.json["status"] == "success"

response = client.get(f"/collections/{default_collection.collection_id}")
assert response.status_code == 200

collection_data = response.json
child_refcodes = [item["refcode"] for item in collection_data["child_items"]]

assert all(refcode in child_refcodes for refcode in refcodes)
95 changes: 95 additions & 0 deletions webapp/src/components/AddToCollectionModal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<template>
<form class="modal-enclosure" data-testid="create-equipment-form" @submit.prevent="submitForm">
<Modal :model-value="modelValue" @update:model-value="$emit('update:modelValue', $event)">
<template #header> Add to collections </template>
<template #body>
<div class="form-row">
<div class="form-group col-md-8">
<label for="items-selected" class="col-form-label">Items Selected:</label>
<div id="items-selected" class="dynamic-input">
<FormattedItemName
v-for="(item, index) in itemsSelected"
:key="index"
:item_id="item.item_id"
:item-type="item.type"
enable-click
/>
</div>
</div>
</div>
<div class="form-row">
<div class="col-md-12 form-group">
<label for="collection-select">Insert into collection:</label>
<CollectionSelect
id="collection-select"
v-model="startInCollection"
aria-labelledby="startInCollection"
multiple
/>
</div>
</div>
</template>
</Modal>
</form>
</template>

<script>
import Modal from "@/components/Modal.vue";
import FormattedItemName from "@/components/FormattedItemName";
import CollectionSelect from "@/components/CollectionSelect.vue";
import { addItemsToCollection } from "@/server_fetch_utils";
export default {
name: "AddToCollectionsModal",
components: {
Modal,
FormattedItemName,
CollectionSelect,
},
props: {
modelValue: Boolean,
itemsSelected: {
type: Array,
required: true,
},
},
emits: ["update:modelValue", "itemsUpdated"],
data() {
return {
startInCollection: [],
};
},
methods: {
async submitForm() {
try {
const collectionIds = this.startInCollection.map((collection) => collection.collection_id);
const refcodes = this.itemsSelected.map((item) => item.refcode);
for (const collectionId of collectionIds) {
await addItemsToCollection(collectionId, refcodes);
}
this.$emit("itemsUpdated");
console.log("Items added successfully.");
this.$emit("update:modelValue", false);
} catch (error) {
console.error("Error adding items to collections:", error);
}
},
},
};
</script>

<style scoped>
.dynamic-input {
display: flex;
flex-wrap: wrap;
border: 1px solid #ced4da;
padding: 0.375rem 0.75rem;
border-radius: 0.25rem;
max-width: 100%;
box-sizing: border-box;
gap: 0.2em;
}
</style>
Loading

0 comments on commit 21c6290

Please sign in to comment.