From 6b19fab08f5e48f71b091dcb1671ba6f2eec1a6f Mon Sep 17 00:00:00 2001 From: vkovtun <viktor.kovtun@id.ethz.ch> Date: Fri, 31 Mar 2023 12:38:59 +0200 Subject: [PATCH] SSDM-13256: Added missing columns for vocabulary type. --- .../helper/XLSVocabularyExportHelper.java | 22 ++++++++++++++---- .../xls/export/VocabularyExpectations.java | 17 ++++++++++++++ .../export/resources/export-vocabulary.xlsx | Bin 5385 -> 5562 bytes 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/xls/export/helper/XLSVocabularyExportHelper.java b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/xls/export/helper/XLSVocabularyExportHelper.java index 457a39ad488..39cc75f1741 100644 --- a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/xls/export/helper/XLSVocabularyExportHelper.java +++ b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/xls/export/helper/XLSVocabularyExportHelper.java @@ -15,6 +15,8 @@ */ package ch.ethz.sis.openbis.generic.server.xls.export.helper; +import static ch.ethz.sis.openbis.generic.server.xls.export.Attribute.*; + import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -115,7 +117,7 @@ public class XLSVocabularyExportHelper extends AbstractXLSExportHelper<IEntityTy { if (FieldType.valueOf(attribute.get(FIELD_TYPE_KEY)) == FieldType.ATTRIBUTE) { - return Attribute.valueOf(attribute.get(FIELD_ID_KEY)).getName(); + return valueOf(attribute.get(FIELD_ID_KEY)).getName(); } else { throw new IllegalArgumentException(); @@ -126,7 +128,7 @@ public class XLSVocabularyExportHelper extends AbstractXLSExportHelper<IEntityTy .toArray(Attribute[]::new); final Set<Attribute> selectedAttributes = selectedExportAttributes.stream() .filter(map -> map.get(FIELD_TYPE_KEY).equals(FieldType.ATTRIBUTE.toString())) - .map(map -> Attribute.valueOf(map.get(FIELD_ID_KEY))) + .map(map -> valueOf(map.get(FIELD_ID_KEY))) .collect(Collectors.toCollection(() -> EnumSet.noneOf(Attribute.class))); final Stream<String> requiredForImportAttributeNameStream = compatibleWithImport ? Arrays.stream(requiredForImportAttributes) @@ -152,7 +154,7 @@ public class XLSVocabularyExportHelper extends AbstractXLSExportHelper<IEntityTy { if (FieldType.valueOf(field.get(FIELD_TYPE_KEY)) == FieldType.ATTRIBUTE) { - return getAttributeValue(vocabulary, Attribute.valueOf(field.get(FIELD_ID_KEY))); + return getAttributeValue(vocabulary, valueOf(field.get(FIELD_ID_KEY))); } else { throw new IllegalArgumentException(); @@ -188,7 +190,7 @@ public class XLSVocabularyExportHelper extends AbstractXLSExportHelper<IEntityTy protected Attribute[] getAttributes() { - return new Attribute[] { Attribute.VERSION, Attribute.CODE, Attribute.DESCRIPTION }; + return new Attribute[] { VERSION, CODE, DESCRIPTION, REGISTRATOR, REGISTRATION_DATE, MODIFICATION_DATE }; } protected String getAttributeValue(final Vocabulary vocabulary, final Attribute attribute) @@ -207,6 +209,18 @@ public class XLSVocabularyExportHelper extends AbstractXLSExportHelper<IEntityTy { return vocabulary.getDescription(); } + case REGISTRATOR: + { + return vocabulary.getRegistrator().getUserId(); + } + case REGISTRATION_DATE: + { + return DATE_FORMAT.format(vocabulary.getRegistrationDate()); + } + case MODIFICATION_DATE: + { + return DATE_FORMAT.format(vocabulary.getModificationDate()); + } default: { return null; diff --git a/server-application-server/sourceTest/java/ch/ethz/sis/openbis/generic/server/xls/export/VocabularyExpectations.java b/server-application-server/sourceTest/java/ch/ethz/sis/openbis/generic/server/xls/export/VocabularyExpectations.java index c3bd23e763d..2d0130a5b2d 100644 --- a/server-application-server/sourceTest/java/ch/ethz/sis/openbis/generic/server/xls/export/VocabularyExpectations.java +++ b/server-application-server/sourceTest/java/ch/ethz/sis/openbis/generic/server/xls/export/VocabularyExpectations.java @@ -16,7 +16,9 @@ package ch.ethz.sis.openbis.generic.server.xls.export; import java.util.Arrays; +import java.util.Calendar; import java.util.Collections; +import java.util.Date; import java.util.List; import org.jmock.Expectations; @@ -25,6 +27,7 @@ import org.jmock.lib.action.CustomAction; import ch.ethz.sis.openbis.generic.asapi.v3.IApplicationServerApi; import ch.ethz.sis.openbis.generic.asapi.v3.dto.entitytype.id.EntityTypePermId; +import ch.ethz.sis.openbis.generic.asapi.v3.dto.person.Person; import ch.ethz.sis.openbis.generic.asapi.v3.dto.vocabulary.Vocabulary; import ch.ethz.sis.openbis.generic.asapi.v3.dto.vocabulary.VocabularyTerm; import ch.ethz.sis.openbis.generic.asapi.v3.dto.vocabulary.fetchoptions.VocabularyFetchOptions; @@ -37,6 +40,16 @@ class VocabularyExpectations extends Expectations public VocabularyExpectations(final IApplicationServerApi api, final boolean exportReferred) { + final Calendar calendar = Calendar.getInstance(); + calendar.set(2023, Calendar.MARCH, 10, 17, 23, 44); + final Date registrationDate = calendar.getTime(); + + calendar.set(2023, Calendar.MARCH, 11, 17, 23, 44); + final Date modificationDate = calendar.getTime(); + + final Person registrator = new Person(); + registrator.setUserId("system"); + allowing(api).getVocabularies(with(XLSExportTest.SESSION_TOKEN), with(new CollectionMatcher<>( Collections.singletonList(new VocabularyPermId("ANTIBODY.DETECTION")))), with(any(VocabularyFetchOptions.class))); @@ -48,11 +61,15 @@ class VocabularyExpectations extends Expectations public Object invoke(final Invocation invocation) throws Throwable { final VocabularyFetchOptions fetchOptions = (VocabularyFetchOptions) invocation.getParameter(2); + fetchOptions.withRegistrator(); final Vocabulary vocabulary = new Vocabulary(); vocabulary.setFetchOptions(fetchOptions); vocabulary.setCode("ANTIBODY.DETECTION"); vocabulary.setDescription("Protein detection system"); + vocabulary.setRegistrator(registrator); + vocabulary.setRegistrationDate(registrationDate); + vocabulary.setModificationDate(modificationDate); vocabulary.setTerms(getVocabularyTerms(fetchOptions)); diff --git a/server-application-server/sourceTest/java/ch/ethz/sis/openbis/generic/server/xls/export/resources/export-vocabulary.xlsx b/server-application-server/sourceTest/java/ch/ethz/sis/openbis/generic/server/xls/export/resources/export-vocabulary.xlsx index fc3e697b1d6025a5f1cc80308b333a37aee525f6..59b389334173acaf4b8db18f10be6908c067c6f9 100644 GIT binary patch delta 3260 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 delta 3111 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 R12Y1_q_7H5-D5u8{SUV>bYcJi -- GitLab