From 3ddcd2522def539b09e1ce169a9151642f9cd41c Mon Sep 17 00:00:00 2001
From: vkovtun <viktor.kovtun@id.ethz.ch>
Date: Sat, 11 Mar 2023 09:06:09 +0100
Subject: [PATCH] SSDM-13256: Added Registration Date, Registrator,
 Modification Date and Modifier export support for samples.

---
 .../export/helper/XLSSampleExportHelper.java  |  24 +++++++++++-
 .../server/xls/export/SampleExpectations.java |  36 ++++++++++++++++++
 .../server/xls/export/XLSExportTest.java      |  28 +++++++-------
 .../xls/export/resources/export-sample.xlsx   | Bin 6222 -> 6497 bytes
 4 files changed, 72 insertions(+), 16 deletions(-)

diff --git a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/xls/export/helper/XLSSampleExportHelper.java b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/xls/export/helper/XLSSampleExportHelper.java
index 89c334d66be..c1a4f3469af 100644
--- a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/xls/export/helper/XLSSampleExportHelper.java
+++ b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/xls/export/helper/XLSSampleExportHelper.java
@@ -72,7 +72,7 @@ public class XLSSampleExportHelper extends AbstractXLSEntityExportHelper<Sample,
     protected String[] getAttributeNames(final Sample entity)
     {
         return new String[] { "$", "Identifier", "Code", "Space", "Project", "Experiment",
-                "Auto generate code", "Parents", "Children" };
+                "Auto generate code", "Parents", "Children", "Registrator", "Registration Date", "Modifier", "Modification Date" };
     }
 
     @Override
@@ -89,6 +89,8 @@ public class XLSSampleExportHelper extends AbstractXLSEntityExportHelper<Sample,
         fetchOptions.withChildren();
         fetchOptions.withType().withPropertyAssignments().withPropertyType();
         fetchOptions.withProperties();
+        fetchOptions.withRegistrator();
+        fetchOptions.withModifier();
         return api.getSamples(sessionToken, samplePermIds, fetchOptions).values();
     }
 
@@ -136,6 +138,22 @@ public class XLSSampleExportHelper extends AbstractXLSEntityExportHelper<Sample,
                         .map(child -> child.getIdentifier().getIdentifier())
                         .collect(Collectors.joining("\n"));
             }
+            case "Registrator":
+            {
+                return sample.getRegistrator().getUserId();
+            }
+            case "Registration Date":
+            {
+                return DATE_FORMAT.format(sample.getRegistrationDate());
+            }
+            case "Modifier":
+            {
+                return sample.getModifier().getUserId();
+            }
+            case "Modification Date":
+            {
+                return DATE_FORMAT.format(sample.getModificationDate());
+            }
             default:
             {
                 return null;
@@ -155,7 +173,9 @@ public class XLSSampleExportHelper extends AbstractXLSEntityExportHelper<Sample,
                         .collect(Collectors.joining("\n")),
                 sample.getChildren() == null ? "" : sample.getChildren().stream()
                         .map(child -> child.getIdentifier().getIdentifier())
-                        .collect(Collectors.joining("\n")));
+                        .collect(Collectors.joining("\n")), sample.getRegistrator().getUserId(),
+                DATE_FORMAT.format(sample.getRegistrationDate()), sample.getModifier().getUserId(),
+                DATE_FORMAT.format(sample.getModificationDate()));
     }
 
     @Override
diff --git a/server-application-server/sourceTest/java/ch/ethz/sis/openbis/generic/server/xls/export/SampleExpectations.java b/server-application-server/sourceTest/java/ch/ethz/sis/openbis/generic/server/xls/export/SampleExpectations.java
index 0edee9c4f93..882d3b87e66 100644
--- a/server-application-server/sourceTest/java/ch/ethz/sis/openbis/generic/server/xls/export/SampleExpectations.java
+++ b/server-application-server/sourceTest/java/ch/ethz/sis/openbis/generic/server/xls/export/SampleExpectations.java
@@ -16,6 +16,8 @@
 package ch.ethz.sis.openbis.generic.server.xls.export;
 
 import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Date;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.function.Function;
@@ -30,6 +32,7 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.entitytype.EntityKind;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.entitytype.id.EntityTypePermId;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.Experiment;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.id.ExperimentIdentifier;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.person.Person;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.project.Project;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.project.id.ProjectIdentifier;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.DataType;
@@ -106,6 +109,19 @@ class SampleExpectations extends Expectations
                 defaultExperiment.setCode("DEFAULT");
                 defaultExperiment.setIdentifier(new ExperimentIdentifier("/DEFAULT/DEFAULT/DEFAULT"));
 
+                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");
+
+                final Person modifier = new Person();
+                modifier.setUserId("test");
+
                 final Sample[] samples = new Sample[5];
 
                 samples[0] = new Sample();
@@ -119,6 +135,10 @@ class SampleExpectations extends Expectations
                 samples[0].setExperiment(experiment);
                 samples[0].setProperty("$NAME", "Bench");
                 samples[0].setProperty("$STORAGE.BOX_NUM", "9999");
+                samples[0].setRegistrator(registrator);
+                samples[0].setModifier(modifier);
+                samples[0].setRegistrationDate(registrationDate);
+                samples[0].setModificationDate(modificationDate);
 
                 samples[1] = new Sample();
                 samples[1].setType(sampleType);
@@ -132,6 +152,10 @@ class SampleExpectations extends Expectations
                 samples[1].setExperiment(experiment);
                 samples[1].setProperty("$NAME", "Default Storage");
                 samples[1].setProperty("$STORAGE.BOX_NUM", "1111");
+                samples[1].setRegistrator(registrator);
+                samples[1].setModifier(modifier);
+                samples[1].setRegistrationDate(registrationDate);
+                samples[1].setModificationDate(modificationDate);
 
                 samples[2] = new Sample();
                 samples[2].setType(defaultSampleType);
@@ -144,6 +168,10 @@ class SampleExpectations extends Expectations
                 samples[2].setProject(defaultProject);
                 samples[2].setExperiment(defaultExperiment);
                 samples[2].setProperty("$NAME", "Default");
+                samples[2].setRegistrator(registrator);
+                samples[2].setModifier(modifier);
+                samples[2].setRegistrationDate(registrationDate);
+                samples[2].setModificationDate(modificationDate);
 
                 samples[3] = new Sample();
                 samples[3].setType(sampleType);
@@ -157,6 +185,10 @@ class SampleExpectations extends Expectations
                 samples[3].setExperiment(experiment);
                 samples[3].setProperty("$NAME", "Child 1");
                 samples[3].setProperty("$STORAGE.BOX_NUM", "1");
+                samples[3].setRegistrator(registrator);
+                samples[3].setModifier(modifier);
+                samples[3].setRegistrationDate(registrationDate);
+                samples[3].setModificationDate(modificationDate);
 
                 samples[4] = new Sample();
                 samples[4].setType(sampleType);
@@ -170,6 +202,10 @@ class SampleExpectations extends Expectations
                 samples[4].setExperiment(experiment);
                 samples[4].setProperty("$NAME", "Child 2");
                 samples[4].setProperty("$STORAGE.BOX_NUM", "2");
+                samples[4].setRegistrator(registrator);
+                samples[4].setModifier(modifier);
+                samples[4].setRegistrationDate(registrationDate);
+                samples[4].setModificationDate(modificationDate);
 
                 samples[0].setChildren(List.of(samples[3], samples[4]));
                 samples[1].setChildren(List.of(samples[3], samples[4]));
diff --git a/server-application-server/sourceTest/java/ch/ethz/sis/openbis/generic/server/xls/export/XLSExportTest.java b/server-application-server/sourceTest/java/ch/ethz/sis/openbis/generic/server/xls/export/XLSExportTest.java
index aab49b71d3d..39e86a4679a 100644
--- a/server-application-server/sourceTest/java/ch/ethz/sis/openbis/generic/server/xls/export/XLSExportTest.java
+++ b/server-application-server/sourceTest/java/ch/ethz/sis/openbis/generic/server/xls/export/XLSExportTest.java
@@ -90,26 +90,26 @@ public class XLSExportTest
     public static final Map<String, Map<String, List<Map<String, String>>>> EXPORT_FIELDS =
             Map.of(
                     DATASET.toString(), Map.of(
-                        "ATTACHMENT", List.of(
-                                Map.of(FIELD_TYPE_KEY, FieldType.ATTRIBUTE.toString(), FIELD_ID_KEY, "Code"),
-                                Map.of(FIELD_TYPE_KEY, FieldType.ATTRIBUTE.toString(), FIELD_ID_KEY, "Sample"),
-                                Map.of(FIELD_TYPE_KEY, FieldType.PROPERTY.toString(), FIELD_ID_KEY, "$ATTACHMENT")),
-                        "RAW_DATA", List.of(
+                            "ATTACHMENT", List.of(
+                                    Map.of(FIELD_TYPE_KEY, FieldType.ATTRIBUTE.toString(), FIELD_ID_KEY, "Code"),
+                                    Map.of(FIELD_TYPE_KEY, FieldType.ATTRIBUTE.toString(), FIELD_ID_KEY, "Sample"),
+                                    Map.of(FIELD_TYPE_KEY, FieldType.PROPERTY.toString(), FIELD_ID_KEY, "$ATTACHMENT")),
+                            "RAW_DATA", List.of(
                                     Map.of(FIELD_TYPE_KEY, FieldType.PROPERTY.toString(), FIELD_ID_KEY, "$NAME"),
                                     Map.of(FIELD_TYPE_KEY, FieldType.ATTRIBUTE.toString(), FIELD_ID_KEY, "Experiment"),
                                     Map.of(FIELD_TYPE_KEY, FieldType.ATTRIBUTE.toString(), FIELD_ID_KEY, "Code"),
                                     Map.of(FIELD_TYPE_KEY, FieldType.PROPERTY.toString(), FIELD_ID_KEY, "NOTES"))
                     ),
                     EXPERIMENT.toString(), Map.of(
-                        "COLLECTION", List.of(
-                                Map.of(FIELD_TYPE_KEY, FieldType.ATTRIBUTE.toString(), FIELD_ID_KEY, "Identifier"),
-                                Map.of(FIELD_TYPE_KEY, FieldType.ATTRIBUTE.toString(), FIELD_ID_KEY, "Project"),
-                                Map.of(FIELD_TYPE_KEY, FieldType.PROPERTY.toString(),
-                                        FIELD_ID_KEY, "$DEFAULT_OBJECT_TYPE")),
-                        "DEFAULT_EXPERIMENT", List.of(Map.of(FIELD_TYPE_KEY, FieldType.PROPERTY.toString(),
-                                            FIELD_ID_KEY, "FINISHED_FLAG"),
-                            Map.of(FIELD_TYPE_KEY, FieldType.ATTRIBUTE.toString(), FIELD_ID_KEY, "Identifier"),
-                            Map.of(FIELD_TYPE_KEY, FieldType.ATTRIBUTE.toString(), FIELD_ID_KEY, "Code"))
+                            "COLLECTION", List.of(
+                                    Map.of(FIELD_TYPE_KEY, FieldType.ATTRIBUTE.toString(), FIELD_ID_KEY, "Identifier"),
+                                    Map.of(FIELD_TYPE_KEY, FieldType.ATTRIBUTE.toString(), FIELD_ID_KEY, "Project"),
+                                    Map.of(FIELD_TYPE_KEY, FieldType.PROPERTY.toString(),
+                                            FIELD_ID_KEY, "$DEFAULT_OBJECT_TYPE")),
+                            "DEFAULT_EXPERIMENT", List.of(Map.of(FIELD_TYPE_KEY, FieldType.PROPERTY.toString(),
+                                                FIELD_ID_KEY, "FINISHED_FLAG"),
+                                    Map.of(FIELD_TYPE_KEY, FieldType.ATTRIBUTE.toString(), FIELD_ID_KEY, "Identifier"),
+                                    Map.of(FIELD_TYPE_KEY, FieldType.ATTRIBUTE.toString(), FIELD_ID_KEY, "Code"))
                     ),
                     SAMPLE.toString(), Map.of(
                             "DEFAULT", List.of(
diff --git a/server-application-server/sourceTest/java/ch/ethz/sis/openbis/generic/server/xls/export/resources/export-sample.xlsx b/server-application-server/sourceTest/java/ch/ethz/sis/openbis/generic/server/xls/export/resources/export-sample.xlsx
index 68a3239cd91e3ef2a78a60237b8d55c707bf8bb7..cb9c9457ec2a77c15e536c8cd0b40bccd9b05901 100644
GIT binary patch
delta 4345
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
I_!sj30dYvwdjJ3c

delta 3976
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
P1A;^Fh|`idf1~~bE_oNV

-- 
GitLab