diff --git a/pydatalab/pydatalab/routes/v0_1/items.py b/pydatalab/pydatalab/routes/v0_1/items.py index 112d586ae..158b3c304 100644 --- a/pydatalab/pydatalab/routes/v0_1/items.py +++ b/pydatalab/pydatalab/routes/v0_1/items.py @@ -396,17 +396,16 @@ def _create_sample( "negative_electrode", "electrolyte", ): - if copied_doc.get(component): - existing_consituent_ids = [ - constituent["item"].get("item_id", None) - for constituent in copied_doc[component] - ] - copied_doc[component] += [ - constituent - for constituent in sample_dict.get(component, []) - if constituent["item"].get("item_id", None) is None - or constituent["item"].get("item_id") not in existing_consituent_ids - ] + existing_consituent_ids = [ + constituent["item"].get("item_id", None) + for constituent in copied_doc[component] + ] + copied_doc[component] += [ + constituent + for constituent in sample_dict.get(component, []) + if constituent["item"].get("item_id", None) is None + or constituent["item"].get("item_id") not in existing_consituent_ids + ] sample_dict = copied_doc diff --git a/pydatalab/tests/server/test_samples.py b/pydatalab/tests/server/test_samples.py index 8b0a86f55..c8c55c2f6 100644 --- a/pydatalab/tests/server/test_samples.py +++ b/pydatalab/tests/server/test_samples.py @@ -421,9 +421,54 @@ def test_create_cell(client, default_cell): } copy_request = {"new_sample_data": copy_doc, "copy_from_item_id": default_cell.item_id} response = client.post("/new-sample/", json=copy_request) - + # Check that the copy retains the old components and the new assert response.status_code == 201, response.json assert response.json["status"] == "success" + response = client.get(f"/get-item-data/{test_id}") + cell = response.json["item_data"] + assert cell["electrolyte"][0]["item"]["name"] == "inlined reference" + assert cell["electrolyte"][1]["item"]["name"] == "salt" + assert cell["electrolyte"][1]["item"]["chemform"] == "NaCl" + + assert ( + cell["positive_electrode"][0]["item"]["name"] + == default_cell.positive_electrode[0].item.name + ) + assert ( + cell["negative_electrode"][0]["item"]["name"] + == default_cell.negative_electrode[0].item.name + ) + + +@pytest.mark.dependency(depends=["test_create_cell"]) +def test_cell_from_scratch(client): + cell = { + "item_id": "test_cell_from_scratch", + "type": "cells", + "negative_electrode": [{"quantity": None, "item": {"name": "inline test"}}], + } + + response = client.post("/new-sample/", json=cell) + assert response.status_code == 201 + + # copy a cell with additional components, where previously there were none + copy_id = "copy_of_scratch" + cell.update( + { + "item_id": copy_id, + "positive_electrode": [{"quantity": None, "item": {"name": "inline cathode"}}], + } + ) + response = client.post( + "/new-sample/", + json={"new_sample_data": cell, "copy_from_item_id": "test_cell_from_scratch"}, + ) + assert response.status_code == 201 + + response = client.get(f"/get-item-data/{copy_id}") + new_cell = response.json["item_data"] + assert new_cell["negative_electrode"][0]["item"]["name"] == "inline test" + assert new_cell["positive_electrode"][0]["item"]["name"] == "inline cathode" @pytest.mark.dependency(depends=["test_create_cell"]) diff --git a/webapp/cypress/e2e/batchSampleFeature.cy.js b/webapp/cypress/e2e/batchSampleFeature.cy.js index 09c7e4c50..c01802dc1 100644 --- a/webapp/cypress/e2e/batchSampleFeature.cy.js +++ b/webapp/cypress/e2e/batchSampleFeature.cy.js @@ -58,6 +58,15 @@ let sample_ids = [ "test103_unique", "test101_unique2", "test101_unique", + "cell_A", + "cell_B", + "cell_C", + "cell_D", + "comp1", + "comp2", + "cell_1", + "cell_2", + "cell_3", ]; before(() => { @@ -75,7 +84,7 @@ describe("Batch sample creation", () => { cy.visit("/"); }); it("Adds 3 valid samples", () => { - cy.contains("Add batch of samples").click(); + cy.contains("Add batch of items").click(); getSubmitButton().should("be.disabled"); getBatchAddCell(1, 1).type("testA"); getBatchAddCell(2, 1).type("testB"); @@ -100,7 +109,7 @@ describe("Batch sample creation", () => { }); it("adds two valid samples", () => { - cy.contains("Add batch of samples").click(); + cy.contains("Add batch of items").click(); cy.findByLabelText("Number of rows:").clear().type(2); cy.get('[data-testid="batch-modal-container"]').findByText("Submit").should("be.disabled"); @@ -119,7 +128,7 @@ describe("Batch sample creation", () => { }); it("adds four base samples", () => { - cy.contains("Add batch of samples").click(); + cy.contains("Add batch of items").click(); cy.findByLabelText("Number of rows:").clear().type(4); cy.get('[data-testid="batch-modal-container"]').findByText("Submit").should("be.disabled"); @@ -180,7 +189,7 @@ describe("Batch sample creation", () => { }); it("makes samples copied from others", () => { - cy.contains("Add batch of samples").click(); + cy.contains("Add batch of items").click(); getBatchAddCell(1, 1).type("baseA_copy"); getBatchAddCell(1, 2).type("a copied sample"); getBatchAddCell(1, 4, ".vs__search").type("BaseA"); @@ -244,7 +253,7 @@ describe("Batch sample creation", () => { }); it("creates samples using components", () => { - cy.contains("Add batch of samples").click(); + cy.contains("Add batch of items").click(); cy.findByLabelText("Number of rows:").clear().type(4); // sample with two components @@ -407,7 +416,7 @@ describe("Batch sample creation", () => { }); it("uses the template id", () => { - cy.contains("Add batch of samples").click(); + cy.contains("Add batch of items").click(); getBatchTemplateCell(1).type("test_{{}#{}}"); // manually type names and a date @@ -434,7 +443,7 @@ describe("Batch sample creation", () => { }); it("uses the template id, name, and date", () => { - cy.contains("Add batch of samples").click(); + cy.contains("Add batch of items").click(); getBatchTemplateCell(1).type("test_{{}#{}}"); getBatchTemplateCell(2).type("this is the test sample #{{}#{}}"); getBatchTemplateCell(3).type("1980-02-01T05:35"); @@ -459,7 +468,7 @@ describe("Batch sample creation", () => { }); it("uses the template id, name, date, copyFrom, and components", () => { - cy.contains("Add batch of samples").click(); + cy.contains("Add batch of items").click(); getBatchTemplateCell(1).type("test_{{}#{}}"); getBatchTemplateCell(2).type("this is the test sample #{{}#{}}"); getBatchTemplateCell(3).type("1980-02-01T23:59"); @@ -539,7 +548,7 @@ describe("Batch sample creation", () => { }); it("plays with the number of rows", () => { - cy.contains("Add batch of samples").click(); + cy.contains("Add batch of items").click(); cy.findByLabelText("Number of rows:").clear().type(3); cy.get("[data-testid=batch-add-table] > tbody > tr").should("have.length", 3); @@ -648,7 +657,7 @@ describe("Batch sample creation", () => { }); it("checks errors on the row", () => { - cy.contains("Add batch of samples").click(); + cy.contains("Add batch of items").click(); getBatchTemplateCell("1").type("test10{{}#{}}"); cy.wait(100); getSubmitButton().should("be.disabled"); @@ -704,3 +713,139 @@ describe("Batch sample creation", () => { cy.verifySample("test101_unique2"); }); }); + +describe("Batch cell creation", () => { + beforeEach(() => { + cy.visit("/"); + }); + + it("creates a simple batch of cells", () => { + cy.contains("Add batch of items").click(); + cy.get("[data-testid=batch-modal-container]").findByLabelText("Type:").select("cell"); + cy.findByLabelText("Number of rows:").clear().type(4); + cy.get("[data-testid=batch-add-table] > tbody > tr").should("have.length", 4); + + getSubmitButton().should("be.disabled"); + getBatchAddCell(1, 1, "input").type("cell_A"); + // set positive electrode for the first cell + getBatchAddCell(1, 5, "input.vs__search").eq(0).type("abcdef"); + cy.get(".vs__dropdown-menu").contains("abcdef").click(); + + getBatchAddCell(2, 1, "input").type("cell_B"); + getBatchAddCell(2, 2, "input").type("this cell has a name"); + + getSubmitButton().should("be.disabled"); + getBatchAddCell(3, 1, "input").type("cell_C"); + getBatchAddCell(3, 3, "input").type("2017-06-01T08:30"); + getBatchAddCell(4, 1, "input").type("cell_D"); + + getSubmitButton().click(); + + cy.verifySample("cell_A"); + cy.verifySample("cell_B", "this cell has a name"); + cy.verifySample("cell_C", null, "2017-06-01"); + cy.verifySample("cell_D"); + }); + + it("adds some component samples to be used for the next tests", () => { + cy.contains("Add batch of items").click(); + cy.findByLabelText("Number of rows:").clear().type(2); + + getBatchAddCell(1, 1).type("comp1"); + getBatchAddCell(1, 2).type("comp1 name"); + getBatchAddCell(2, 1).type("comp2"); + + getSubmitButton().click(); + cy.get("[data-testid=batch-modal-container]").contains("a", "comp1"); + cy.get("[data-testid=batch-modal-container]").contains("a", "comp2"); + }); + + it("creates a batch of cells using the template id, name, date, copyFrom, and components", () => { + cy.contains("Add batch of items").click(); + + cy.get("[data-testid=batch-modal-container]").findByLabelText("Type:").select("cell"); + + getBatchTemplateCell(1, "input").eq(0).type("cell_{{}#{}}"); + getBatchTemplateCell(2, "input").type("this is the test cell #{{}#{}}"); + getBatchTemplateCell(3, "input").type("1980-02-01T23:59"); + + // select copyFrom sample, check that it is applied correctly + getBatchTemplateCell(4, ".vs__search").type("cell_B"); + cy.get(".vs__dropdown-menu").contains(".badge", "cell_B").click(); + + getBatchAddCell(1, 4).contains("cell_B"); + getBatchAddCell(2, 4).contains("cell_B"); + getBatchAddCell(3, 4).contains("cell_B"); + + // change the copyFrom sample, check that it is applied correctly + getBatchTemplateCell(4, ".vs__search").type("cell_A"); + cy.get(".vs__dropdown-menu").contains(".badge", "cell_A").click(); + + getBatchAddCell(1, 4).contains("cell_A"); + getBatchAddCell(2, 4).contains("cell_A"); + getBatchAddCell(3, 4).contains("cell_A"); + + // add a positive electrode, check that it is applied correctly + getBatchTemplateCell(5, ".vs__search").eq(0).type("comp1"); + cy.get(".vs__dropdown-menu").contains(".badge", "comp1").click(); + + getBatchAddCell(1, 5).contains("comp1"); + getBatchAddCell(2, 5).contains("comp1"); + getBatchAddCell(3, 5).contains("comp1"); + + // add another component, this one tagged (i.e., not in the db) check that it is applied correctly + getBatchTemplateCell(5, ".vs__search").eq(0).type("tagged"); + cy.get(".vs__dropdown-menu").eq(0).contains("tagged").click(); + + getBatchAddCell(1, 5).contains("comp1"); + getBatchAddCell(1, 5).contains("tagged"); + getBatchAddCell(2, 5).contains("comp1"); + getBatchAddCell(2, 5).contains("tagged"); + getBatchAddCell(3, 5).contains("comp1"); + getBatchAddCell(3, 5).contains("tagged"); + + // add electrolyte + getBatchTemplateCell(5, ".vs__search").eq(1).type("elyte"); + cy.get(".vs__dropdown-menu").eq(0).contains("elyte").click(); + getBatchAddCell(1, 5).contains("elyte"); + getBatchAddCell(2, 5).contains("elyte"); + getBatchAddCell(3, 5).contains("elyte"); + + // add negative electrode + getBatchTemplateCell(5, ".vs__search").eq(2).type("comp2"); + cy.get(".vs__dropdown-menu").eq(0).contains(".badge", "comp2").click(); + getBatchAddCell(1, 5).contains("comp2"); + getBatchAddCell(2, 5).contains("comp2"); + getBatchAddCell(3, 5).contains("comp2"); + + getSubmitButton().click(); + cy.get("[data-testid=batch-modal-container]").contains("a", "cell_1"); + cy.get("[data-testid=batch-modal-container]").contains("a", "cell_2"); + cy.get("[data-testid=batch-modal-container]").contains("a", "cell_3"); + + cy.findAllByText("Successfully created.").should("have.length", 3); + + cy.get("[data-testid=batch-modal-container]").contains("Close").click(); + + cy.verifySample("cell_1", "this is the test cell #1", "1980-02-01T23:59"); + cy.verifySample("cell_2", "this is the test cell #2", "1980-02-01T23:59"); + cy.verifySample("cell_3", "this is the test cell #3", "1980-02-01T23:59"); + + function checkCreatedCell(item_id) { + cy.contains(item_id).click(); + cy.get("#pos-electrode-table").contains("comp1"); + cy.get("#pos-electrode-table").contains("tagged"); + cy.get("#pos-electrode-table").contains("comp1 name"); + + cy.get("#electrolyte-table").contains("elyte"); + + cy.get("#neg-electrode-table").contains("comp2"); + + cy.findByText("Home").click(); + } + + checkCreatedCell("cell_1"); + checkCreatedCell("cell_2"); + checkCreatedCell("cell_3"); + }); +}); diff --git a/webapp/src/components/BatchCreateSampleModal.vue b/webapp/src/components/BatchCreateItemModal.vue similarity index 58% rename from webapp/src/components/BatchCreateSampleModal.vue rename to webapp/src/components/BatchCreateItemModal.vue index 0f68489ef..473467838 100644 --- a/webapp/src/components/BatchCreateSampleModal.vue +++ b/webapp/src/components/BatchCreateItemModal.vue @@ -3,16 +3,16 @@