diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/SampleRegistrationPanel.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/SampleRegistrationPanel.java index a63ae6fb5231c02cd79124a207a75b2509250836..739ee11fa495b74abb3ad2d7046ef13b41af1b7f 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/SampleRegistrationPanel.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/SampleRegistrationPanel.java @@ -16,11 +16,11 @@ package ch.systemsx.cisd.openbis.generic.client.web.client.application.ui; +import com.extjs.gxt.ui.client.Style.Scroll; import com.extjs.gxt.ui.client.data.ModelData; import com.extjs.gxt.ui.client.event.SelectionChangedEvent; import com.extjs.gxt.ui.client.event.SelectionChangedListener; import com.extjs.gxt.ui.client.widget.ContentPanel; -import com.extjs.gxt.ui.client.widget.layout.FitLayout; import com.extjs.gxt.ui.client.widget.toolbar.AdapterToolItem; import com.extjs.gxt.ui.client.widget.toolbar.LabelToolItem; import com.extjs.gxt.ui.client.widget.toolbar.ToolBar; @@ -44,7 +44,7 @@ public class SampleRegistrationPanel extends ContentPanel setHeading("Sample registration"); setHeaderVisible(false); setBodyBorder(false); - setLayout(new FitLayout()); + setScrollMode(Scroll.AUTO); sampleTypeSelection = new SampleTypeSelectionWidget(viewContext, true); sampleTypeSelection.addSelectionChangedListener(new SelectionChangedListener<ModelData>() { @@ -56,7 +56,6 @@ public class SampleRegistrationPanel extends ContentPanel { removeAll(); add(new GenericSampleRegistrationForm(viewContext, selectedType)); - layout(); } } diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/SampleBO.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/SampleBO.java index c5936119ab9f4c78ce03d0390e85531a4cd12914..e34eafcb9d13644e7119c90cbd7c3ac8089b65a5 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/SampleBO.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/SampleBO.java @@ -126,6 +126,7 @@ public final class SampleBO extends AbstractSampleIdentifierBusinessObject imple sample.setTop(contained.getTop() == null ? contained : contained.getTop()); } } + SampleGenericBusinessRules.assertValidParents(sample); dataChanged = true; } diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/SampleGenericBusinessRules.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/SampleGenericBusinessRules.java new file mode 100644 index 0000000000000000000000000000000000000000..466d77abfc68070bb49e35cb81eab9700ce49a3c --- /dev/null +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/SampleGenericBusinessRules.java @@ -0,0 +1,74 @@ +/* + * Copyright 2008 ETH Zuerich, CISD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.systemsx.cisd.openbis.generic.server.business.bo; + +import ch.systemsx.cisd.common.exceptions.UserFailureException; +import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePE; +import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.SampleIdentifier; + +/** + * Sample related business rules. + * <ul> + * <li>A database instance sample can be derived from a database instance sample. + * <li>A group sample can be derived from a group sample of the same group only. + * <li>A group sample can be derived from a database instance sample. + * </ul> + * + * @author Izabela Adamczyk + */ +public class SampleGenericBusinessRules +{ + + static private void assertValidParentRelation(final SamplePE parent, final SamplePE child) + throws UserFailureException + { + if (parent == null || child == null) + return; + + SampleIdentifier parentId = parent.getSampleIdentifier(); + SampleIdentifier childId = child.getSampleIdentifier(); + + if (parentId.isGroupLevel()) + { + if (childId.isDatabaseInstanceLevel()) + { + throwUserFailureException("The database instance sample '%s' " + + "can not be derived from the group sample '%s'.", child, parent); + } + if (parentId.getGroupLevel().equals(childId.getGroupLevel()) == false) + { + throwUserFailureException("The sample '%s' has to be in the same group as " + + "the sample '%s' from which it is derived.", child, parent); + } + } + } + + static public void assertValidParents(SamplePE sample) + { + if (sample == null) + return; + assertValidParentRelation(sample.getContainer(), sample); + assertValidParentRelation(sample.getGeneratedFrom(), sample); + } + + static private void throwUserFailureException(String messageTemplate, SamplePE sample1, + SamplePE sample2) + { + throw UserFailureException.fromTemplate(messageTemplate, sample1.getSampleIdentifier(), + sample2.getSampleIdentifier()); + } +} diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/application/sample/GenericSampleRegistrationForm.java b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/application/sample/GenericSampleRegistrationForm.java index f01aad116e2c2aef43252ca5c6cc1ee13d1348b0..19a6a2a4500a719d1db4e7a7a7366ede138df21c 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/application/sample/GenericSampleRegistrationForm.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/application/sample/GenericSampleRegistrationForm.java @@ -25,7 +25,6 @@ import com.extjs.gxt.ui.client.event.BaseEvent; import com.extjs.gxt.ui.client.event.ComponentEvent; import com.extjs.gxt.ui.client.event.Listener; import com.extjs.gxt.ui.client.event.SelectionListener; -import com.extjs.gxt.ui.client.widget.MessageBox; import com.extjs.gxt.ui.client.widget.button.Button; import com.extjs.gxt.ui.client.widget.button.ButtonBar; import com.extjs.gxt.ui.client.widget.form.AdapterField; @@ -33,6 +32,7 @@ import com.extjs.gxt.ui.client.widget.form.CheckBox; import com.extjs.gxt.ui.client.widget.form.DateField; import com.extjs.gxt.ui.client.widget.form.Field; import com.extjs.gxt.ui.client.widget.form.FormPanel; +import com.extjs.gxt.ui.client.widget.form.LabelField; import com.extjs.gxt.ui.client.widget.form.MultiField; import com.extjs.gxt.ui.client.widget.form.TextField; import com.extjs.gxt.ui.client.widget.form.Validator; @@ -44,6 +44,7 @@ import ch.systemsx.cisd.openbis.generic.client.web.client.application.AbstractAs import ch.systemsx.cisd.openbis.generic.client.web.client.application.GenericConstants; import ch.systemsx.cisd.openbis.generic.client.web.client.application.IViewContext; import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.sample_browser.GroupSelectionWidget; +import ch.systemsx.cisd.openbis.generic.client.web.client.application.util.StringUtils; import ch.systemsx.cisd.openbis.generic.client.web.client.dto.DataTypes; import ch.systemsx.cisd.openbis.generic.client.web.client.dto.Group; import ch.systemsx.cisd.openbis.generic.client.web.client.dto.SampleProperty; @@ -82,8 +83,14 @@ public final class GenericSampleRegistrationForm extends FormPanel private MultiField<Field<?>> groupMultiField; + private InfoBox infoBox; + private static final String MANDATORY_LABEL_SEPARATOR = ": *"; + private static final int LABEL_WIDTH = 150; + + private static final int FIELD_WIDTH = 400; + public GenericSampleRegistrationForm( final IViewContext<IGenericClientServiceAsync> viewContext, SampleType sampleType) { @@ -95,7 +102,17 @@ public final class GenericSampleRegistrationForm extends FormPanel private void createFormFields() { + infoBox = new InfoBox(); + code = new CodeField("Sample code", true); + code.addListener(Events.Focus, new Listener<BaseEvent>() + { + + public void handleEvent(BaseEvent be) + { + infoBox.fade(); + } + }); groupSelectionWidget = new GroupSelectionWidget(viewContext); groupSelectionWidget.setEnabled(SELECT_GROUP_BY_DEFAULT); @@ -143,24 +160,26 @@ public final class GenericSampleRegistrationForm extends FormPanel private void configureForm() { - setHScrollPosition(20); setHeaderVisible(false); setBodyBorder(false); - setLabelWidth(150); - setFieldWidth(350); + + setLabelWidth(LABEL_WIDTH); + setFieldWidth(FIELD_WIDTH); setButtonAlign(HorizontalAlignment.LEFT); final ButtonBar bb = new ButtonBar(); bb.setCellSpacing(20); - Button resetButton = new Button("Reset"); - resetButton.addSelectionListener(new SelectionListener<ComponentEvent>() - { - @Override - public void componentSelected(ComponentEvent ce) - { - resetForm(); - } - }); - bb.add(resetButton); + // Reset functionality - uncomment if needed + // + // Button resetButton = new Button("Reset"); + // resetButton.addSelectionListener(new SelectionListener<ComponentEvent>() + // { + // @Override + // public void componentSelected(ComponentEvent ce) + // { + // resetForm(""); + // } + // }); + // bb.add(resetButton); Button saveButton = new Button("Save"); saveButton.addSelectionListener(new SelectionListener<ComponentEvent>() @@ -193,6 +212,19 @@ public final class GenericSampleRegistrationForm extends FormPanel return sb.toString().toUpperCase(); } + static String createSuccessfullRegistrationInfo(boolean shared, String code, Group group) + { + if (shared) + { + return "Shared sample <b>" + code + "</b> successfully registered"; + + } else + { + return "Sample <b>" + code + "</b> successfully registered in group <b>" + + group.getCode() + "</b>"; + } + } + private void submitForm() { if (isValid()) @@ -214,28 +246,26 @@ public final class GenericSampleRegistrationForm extends FormPanel viewContext.getService().registerSample(sampleToRegister, new AbstractAsyncCallback<Void>(viewContext) { - @Override protected void process(Void result) { - MessageBox mb = new MessageBox(); - mb.setIcon(MessageBox.INFO); - mb.setButtons(MessageBox.OK); - mb.setModal(true); - mb.setTitle("Registration successfull"); - mb.setMessage("Sample '" + createSampleIdentifier() - + "' successfully registered."); - mb.show(); - resetForm(); + + String message = + createSuccessfullRegistrationInfo( + sharedCheckbox.getValue(), code.getValue(), + groupSelectionWidget.tryGetSelected()); + resetForm(message); + } }); } } - private void resetForm() + private void resetForm(String info) { createFormFields(); addFormFields(); + infoBox.display(info); layout(); } @@ -249,6 +279,7 @@ public final class GenericSampleRegistrationForm extends FormPanel public void addFormFields() { removeAll(); + add(infoBox); add(code); add(groupMultiField); add(parentGenerator); @@ -382,6 +413,7 @@ public final class GenericSampleRegistrationForm extends FormPanel super(label, mandatory); setRegex(GenericConstants.CODE_PATTERN); } + } static class RealField extends BasicTextField<Double> @@ -430,4 +462,43 @@ public final class GenericSampleRegistrationForm extends FormPanel } } + static class InfoBox extends LabelField + { + + public InfoBox() + { + setVisible(false); + setStyleAttribute("text-align", "center !important"); + setPosition(-2, 0); + } + + private void setStrongStyle() + { + setStyleAttribute("background-color", "#feffbe !important"); + setStyleAttribute("border", "1px solid #edee8b !important"); + } + + private void setLightStyle() + { + setStyleAttribute("background-color", "#feffdf !important"); + setStyleAttribute("color", "gray !important"); + setStyleAttribute("border", "1px solid #e7e7e7 !important"); + } + + public void fade() + { + setLightStyle(); + } + + public void display(String text) + { + if (StringUtils.isBlank(text) == false) + { + setStrongStyle(); + setVisible(true); + setText(text); + } + } + } + } diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/SampleBOTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/SampleBOTest.java index 9b3e0e050f483fc92bf3552045e843cdf06b6c50..8e2968c175ce105e893af50513e1784d834fca30 100644 --- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/SampleBOTest.java +++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/SampleBOTest.java @@ -49,6 +49,7 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.SampleToRegisterDTO; import ch.systemsx.cisd.openbis.generic.shared.dto.SampleTypePE; import ch.systemsx.cisd.openbis.generic.shared.dto.SampleTypePropertyTypePE; import ch.systemsx.cisd.openbis.generic.shared.dto.SimpleEntityProperty; +import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.DatabaseInstanceIdentifier; import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.IdentifierHelper; import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.SampleIdentifier; import ch.systemsx.cisd.openbis.generic.shared.dto.properties.EntityDataType; @@ -61,6 +62,8 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.types.SampleTypeCode; */ public final class SampleBOTest { + private static final String DB = "DB"; + private static final String DILUTION_PLATE = SampleTypeCode.DILUTION_PLATE.getCode(); private static final String MASTER_PLATE = SampleTypeCode.MASTER_PLATE.getCode(); @@ -135,11 +138,68 @@ public final class SampleBOTest return sample; } + private final static SampleIdentifier getSharedSampleIdentifier(final String code) + { + return new SampleIdentifier(new DatabaseInstanceIdentifier(DB), code); + } + private final static SampleIdentifier getSampleIdentifier(final String code) { return new SampleIdentifier(IdentifierHelper.createIdentifier(EXAMPLE_GROUP), code); } + @Test(expectedExceptions = UserFailureException.class) + public final void testFailToDefineSharedSampleWithParentInAGroup() + { + final SampleIdentifier sharedSampleIdentifier = + getSharedSampleIdentifier(DEFAULT_SAMPLE_CODE); + final SampleToRegisterDTO newSharedSample = new SampleToRegisterDTO(); + newSharedSample.setSampleIdentifier(sharedSampleIdentifier); + newSharedSample.setSampleTypeCode(SampleTypeCode.DILUTION_PLATE.getCode()); + + final SampleIdentifier parentGroupIdentifier = getSampleIdentifier("SAMPLE_GENERATOR"); + newSharedSample.setGeneratorParent(parentGroupIdentifier); + + newSharedSample.setProperties(SimpleEntityProperty.EMPTY_ARRAY); + + context.checking(new Expectations() + { + { + one(databaseInstanceDAO).tryFindDatabaseInstanceByCode(DB); + will(returnValue(ManagerTestTool.EXAMPLE_DATABASE_INSTANCE)); + + allowing(daoFactory).getSampleDAO(); + will(returnValue(sampleDAO)); + + ManagerTestTool.prepareFindGroup(this, daoFactory, groupDAO, + databaseInstanceDAO); + + final SamplePE groupParent = new SamplePE(); + groupParent.setRegistrator(EXAMPLE_PERSON); + groupParent.setGroup(EXAMPLE_GROUP); + groupParent.setCode("SAMPLE_GENERATOR"); + one(sampleDAO).tryFindByCodeAndGroup(parentGroupIdentifier.getSampleCode(), + EXAMPLE_GROUP); + will(returnValue(groupParent)); + + final SampleTypePE sampleType = new SampleTypePE(); + sampleType.setCode(DILUTION_PLATE); + + one(daoFactory).getSampleTypeDAO(); + will(returnValue(sampleTypeDAO)); + one(sampleTypeDAO).tryFindSampleTypeByCode(DILUTION_PLATE); + will(returnValue(sampleType)); + + one(propertiesConverter).convertProperties(newSharedSample.getProperties(), + DILUTION_PLATE, EXAMPLE_PERSON); + will(returnValue(new ArrayList<SamplePropertyPE>())); + } + }); + + final SampleBO sampleBO = createSampleBO(); + sampleBO.define(newSharedSample); + } + @Test public final void testDefineSampleHappyCase() { diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/SampleGenericBusinessRulesTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/SampleGenericBusinessRulesTest.java new file mode 100644 index 0000000000000000000000000000000000000000..80fdd3c32e84f8d9172894839d027a4d76ed6565 --- /dev/null +++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/SampleGenericBusinessRulesTest.java @@ -0,0 +1,166 @@ +/* + * Copyright 2008 ETH Zuerich, CISD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.systemsx.cisd.openbis.generic.server.business.bo; + +import org.testng.annotations.Test; + +import ch.systemsx.cisd.common.exceptions.UserFailureException; +import ch.systemsx.cisd.openbis.generic.shared.dto.DatabaseInstancePE; +import ch.systemsx.cisd.openbis.generic.shared.dto.GroupPE; +import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePE; + +/** + * Test cases for corresponding {@link SampleGenericBusinessRules} class. + * + * @author Izabela Adamczyk + */ +public final class SampleGenericBusinessRulesTest +{ + + private static final String DB = "db"; + + private static final String GROUP_1 = "group-1"; + + private static final String GROUP_2 = "group-2"; + + private static final String SAMPLE_1 = "sample-1"; + + private static final String SAMPLE_2 = "sample-2"; + + private static final String SAMPLE_3 = "sample-3"; + + private static DatabaseInstancePE createDatabaseInstance(String code) + { + DatabaseInstancePE d = new DatabaseInstancePE(); + d.setCode(code); + return d; + } + + private static GroupPE createGroup(DatabaseInstancePE db, String code) + { + GroupPE g = new GroupPE(); + g.setCode(code); + g.setDatabaseInstance(db); + return g; + } + + private static SamplePE createGroupSample(GroupPE g, String code) + { + SamplePE s = new SamplePE(); + s.setCode(code); + s.setGroup(g); + return s; + } + + private static SamplePE createSharedSample(DatabaseInstancePE db, String code) + { + SamplePE s = new SamplePE(); + s.setCode(code); + s.setDatabaseInstance(db); + return s; + } + + @Test + public void testGroupSampleCanHaveParentFromTheSameGroup() throws Exception + { + DatabaseInstancePE databaseInstance = createDatabaseInstance(DB); + SamplePE newSample = createGroupSample(createGroup(databaseInstance, GROUP_1), SAMPLE_1); + + SamplePE generator = createGroupSample(createGroup(databaseInstance, GROUP_1), SAMPLE_2); + newSample.setGeneratedFrom(generator); + + SamplePE container = createGroupSample(createGroup(databaseInstance, GROUP_1), SAMPLE_3); + newSample.setContainer(container); + + SampleGenericBusinessRules.assertValidParents(newSample); + } + + @Test + public void testGroupSampleCanHaveSharedParent() throws Exception + { + DatabaseInstancePE databaseInstance = createDatabaseInstance(DB); + SamplePE newSample = createGroupSample(createGroup(databaseInstance, GROUP_1), SAMPLE_1); + + SamplePE parent = createSharedSample(databaseInstance, SAMPLE_2); + newSample.setGeneratedFrom(parent); + + SamplePE container = createSharedSample(databaseInstance, SAMPLE_3); + newSample.setContainer(container); + + SampleGenericBusinessRules.assertValidParents(newSample); + } + + @Test(expectedExceptions = UserFailureException.class) + public void testGroupSampleCannotBeContainedByParentFromDifferentGroup() throws Exception + { + DatabaseInstancePE databaseInstance = createDatabaseInstance(DB); + SamplePE newSample = createGroupSample(createGroup(databaseInstance, GROUP_1), SAMPLE_1); + + SamplePE parent = createGroupSample(createGroup(databaseInstance, GROUP_2), SAMPLE_2); + newSample.setContainer(parent); + + SampleGenericBusinessRules.assertValidParents(newSample); + } + + @Test(expectedExceptions = UserFailureException.class) + public void testGroupSampleCannotBeDerivedFromParentFromDifferentGroup() throws Exception + { + DatabaseInstancePE databaseInstance = createDatabaseInstance(DB); + SamplePE newSample = createGroupSample(createGroup(databaseInstance, GROUP_1), SAMPLE_1); + + SamplePE parent = createGroupSample(createGroup(databaseInstance, GROUP_2), SAMPLE_2); + newSample.setGeneratedFrom(parent); + + SampleGenericBusinessRules.assertValidParents(newSample); + } + + @Test + public void testInstanceSampleCanHaveInstanceSampleParents() throws Exception + { + DatabaseInstancePE databaseInstance = createDatabaseInstance(DB); + SamplePE newSample = createSharedSample(databaseInstance, SAMPLE_1); + + SamplePE generator = createSharedSample(databaseInstance, SAMPLE_2); + newSample.setGeneratedFrom(generator); + + SamplePE container = createSharedSample(databaseInstance, SAMPLE_3); + newSample.setContainer(container); + + SampleGenericBusinessRules.assertValidParents(newSample); + } + + @Test(expectedExceptions = UserFailureException.class) + public void testInstanceSampleCannotBeContainedByGroupSample() throws Exception + { + DatabaseInstancePE databaseInstance = createDatabaseInstance(DB); + SamplePE newSample = createSharedSample(databaseInstance, SAMPLE_1); + SamplePE parent = createGroupSample(createGroup(databaseInstance, GROUP_1), SAMPLE_2); + newSample.setContainer(parent); + SampleGenericBusinessRules.assertValidParents(newSample); + } + + @Test(expectedExceptions = UserFailureException.class) + public void testInstanceSampleCannotBeDerivedFromGroupSample() throws Exception + { + DatabaseInstancePE databaseInstance = createDatabaseInstance(DB); + SamplePE newSample = createSharedSample(databaseInstance, SAMPLE_1); + SamplePE parent = createGroupSample(createGroup(databaseInstance, GROUP_1), SAMPLE_2); + newSample.setGeneratedFrom(parent); + SampleGenericBusinessRules.assertValidParents(newSample); + } + +} \ No newline at end of file