Skip to content

Commit 151029c

Browse files
committed
Merge remote-tracking branch 'IQSS/develop' into AWSv2
2 parents 47ead36 + a9872c9 commit 151029c

33 files changed

+648
-124
lines changed

conf/solr/schema.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,8 @@
157157
<field name="dvSubject" type="string" stored="true" indexed="true" multiValued="true"/>
158158

159159
<field name="publicationStatus" type="string" stored="true" indexed="true" multiValued="true"/>
160-
<field name="externalStatus" type="string" stored="true" indexed="true" multiValued="false"/>
160+
<field name="curationStatus" type="string" stored="true" indexed="true" multiValued="false"/>
161+
<field name="curationStatusCreateTime" type="pdate" indexed="true" stored="true"/>
161162
<field name="embargoEndDate" type="plong" stored="true" indexed="true" multiValued="false"/>
162163
<field name="retentionEndDate" type="plong" stored="true" indexed="true" multiValued="false"/>
163164

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
The External/Curation Status Label mechanism has been enhanced:
2+
3+
- adding tracking of who creates the status label and when,
4+
- keeping a history of past statuses
5+
- updating the CSV report to include the creation time and assigner of a status
6+
- updating the getCurationStatus api call to return a JSON object for the status with label, assigner, and create time
7+
- adding an includeHistory query param for these API calls to allow seeing prior statuses
8+
- adding a facet to allow filtering by curation status (for users able to set them)
9+
- adding the creation time to solr as a pdate to support search by time period, e.g. current status set prior to a give date
10+
- standardizing the language around 'curation status' vs 'external status'
11+
- adding a 'curation-status' class to displayed labels to allow styling
12+
- adding a dataverse.ui.show-curation-status-to-all feature flag that allows users who can see a draft but not publish it to also view the curation status
13+
14+
Due to changes in the solr schema, updating the solr schema and reindexing is required. Background reindexing should be OK.

doc/sphinx-guides/source/api/changelog.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ v6.7
1111
----
1212

1313
- An undocumented :doc:`search` parameter called "show_my_data" has been removed. It was never exercised by tests and is believed to be unused. API users should use the :ref:`api-mydata` API instead.
14+
- /api/datasets/{id}/curationStatus API now includes a JSON object with curation label, createtime, and assigner rather than a string 'label' and it supports a new boolean includeHistory parameter (default false) that returns a JSON array of statuses
15+
- /api/datasets/{id}/listCurationStates includes new columns "Status Set Time" and "Status Set By" columns listing the time the current status was applied and by whom. It also supports the boolean includeHistory parameter.
1416
- Due to updates in libraries used by Dataverse, XML serialization may have changed slightly with respect to whether self-closing tags are used for empty elements. This primiarily affects XML-based metadata exports. The XML structure of the export itself has not changed, so this is only an incompatibility if you are not using an XML parser.
1517

1618
v6.6

doc/sphinx-guides/source/api/curation-labels.rst

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
1-
Dataset Curation Label API
2-
==========================
1+
Dataset Curation Status API
2+
===========================
33

44
When the :ref:`:AllowedCurationLabels <:AllowedCurationLabels>` setting has been used to define Curation Labels, this API can be used to set these labels on draft datasets.
55
Superusers can define which set of labels are allowed for a given datasets in a collection/an individual dataset using the api described in the :doc:`/admin/dataverses-datasets` section.
66
The API here can be used by curators/those who have permission to publish the dataset to get/set/change/delete the label currently assigned to a draft dataset.
7+
If the :ref:`dataverse.ui.show-curation-status-to-all` flag is enabled, users who can see the draft dataset version can use the get API call.
78

89
This functionality is intended as a mechanism to integrate the Dataverse software with an external curation process/application: it is a way to make the state of a draft dataset,
910
as defined in the external process, visible within Dataverse. These labels have no other effect in Dataverse and are only visible to curators/those with permission to publish the dataset.
1011
Any curation label assigned to a draft dataset will be removed upon publication.
12+
13+
Dataverse tracks the Curation Label as well as when it was assigned and by whom. It also keeps track of the history of prior assignments.
1114

12-
Get a Draft Dataset's Curation Label
13-
------------------------------------
15+
Get a Draft Dataset's Curation Status
16+
-------------------------------------
1417

1518
.. code-block:: bash
1619
@@ -27,8 +30,13 @@ Get a Draft Dataset's Curation Label
2730
2831
curl -H X-Dataverse-key:$API_TOKEN "$SERVER_URL/api/datasets/:persistentId/curationStatus?persistentId=$DATASET_PID"
2932
30-
You should expect a 200 ("OK") response and the draft dataset's curation status label contained in a JSON 'data' object.
33+
You should expect a 200 ("OK") response and the draft dataset's curation status as a JSON object contained in a JSON 'data' object. The status will include a 'label','createTime', and the 'assigner'.
34+
35+
If the optional includeHistory query parameter is set to true, the responses 'data' entry will be a JSON array of curation status objects
36+
37+
curl -H X-Dataverse-key:$API_TOKEN "$SERVER_URL/api/datasets/:persistentId/curationStatus?persistentId=$DATASET_PID&includeHistory=true"
3138

39+
For draft datasets that were created prior to v6.7, it is possible that curation status objects will have no createTime or assigner.
3240

3341
Set a Draft Dataset's Curation Label
3442
------------------------------------
@@ -53,6 +61,8 @@ To add a curation label for a draft Dataset, specify the Dataset ID (DATASET_ID)
5361
5462
You should expect a 200 ("OK") response indicating that the label has been set. 403/Forbidden and 400/Bad Request responses are also possible, i.e. if you don't have permission to make this change or are trying to add a label that isn't in the allowed set or to add a label to a dataset with no draft version.
5563

64+
Note that Dataverse will add the current time as the createTime and the user as the 'assigner' of the label.
65+
5666

5767
Delete a Draft Dataset's Curation Label
5868
---------------------------------------
@@ -98,7 +108,7 @@ You should expect a 200 ("OK") response with a comma-separated list of allowed l
98108
Get a Report on the Curation Status of All Datasets
99109
---------------------------------------------------
100110

101-
To get a CSV file listing the curation label assigned to each Dataset with a draft version, along with the creation and last modification dates, and list of those with permissions to publish the version.
111+
To get a CSV file listing the curation statuses assigned to each Dataset with a draft version, along with the creation and last modification dates, and list of those with permissions to publish the version.
102112

103113
This API call is restricted to superusers.
104114

@@ -112,3 +122,14 @@ This API call is restricted to superusers.
112122
curl -H X-Dataverse-key:$API_TOKEN "$SERVER_URL/api/datasets/listCurationStates"
113123
114124
You should expect a 200 ("OK") response with a CSV formatted response.
125+
126+
The CSV response includes the following columns in order:
127+
#. Dataset Title (as a hyperlink to the dataset page)
128+
#. Creation Date of the draft dataset version
129+
#. Latest Modification Date of the draft dataset version
130+
#. Assigned curation status or '<none>' if no curation status is assigned but was previously, null if no curation state has every been set.
131+
#. Time when the curation status was applied to the draft dataset version
132+
#. The user who assigned this curation status
133+
#. (and beyond): Users (comma separated list) with the Roles (column headings) that can publish datasets and therefore see/set curation status
134+
When includeHistory is true, multiple rows may be present for each dataset, showing the full history of curation statuses.
135+

doc/sphinx-guides/source/installation/config.rst

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3269,6 +3269,21 @@ Defaults to ``true``.
32693269
Can also be set via any `supported MicroProfile Config API source`_, e.g. the environment variable
32703270
``DATAVERSE_API_SHOW_LABEL_FOR_INCOMPLETE_WHEN_PUBLISHED``. Will accept ``[tT][rR][uU][eE]|1|[oO][nN]`` as "true" expressions.
32713271

3272+
.. _dataverse.ui.show-curation-status-to-all:
3273+
3274+
dataverse.ui.show-curation-status-to-all
3275+
++++++++++++++++++++++++++++++++++++++++
3276+
3277+
By default the curation status assigned to a draft dataset versioncan only be seen by those who can publish it. When this flag is true, anyone who can see the draft dataset can see the assigned status.
3278+
These users will also get notifications/emails about changes to the status.
3279+
See :ref:`:AllowedCurationLabels <:AllowedCurationLabels>` and the :doc:`/admin/dataverses-datasets` section for more information about curation status.
3280+
3281+
Defaults to ``false``.
3282+
3283+
Can also be set via any `supported MicroProfile Config API source`_, e.g. the environment variable
3284+
``DATAVERSE_API_SHOW_CURATION_STATUS_TO_ALL``. Will accept ``[tT][rR][uU][eE]|1|[oO][nN]`` as "true" expressions.
3285+
3286+
32723287
.. _dataverse.signposting.level1-author-limit:
32733288

32743289
dataverse.signposting.level1-author-limit
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package edu.harvard.iq.dataverse;
2+
3+
import edu.harvard.iq.dataverse.authorization.users.AuthenticatedUser;
4+
import jakarta.persistence.*;
5+
6+
import java.io.Serializable;
7+
import java.util.Date;
8+
9+
@Entity
10+
@Table(name = "curationstatus", indexes = {
11+
@Index(name = "index_curationstatus_datasetversion", columnList = "datasetversion_id")
12+
})
13+
public class CurationStatus implements Serializable {
14+
15+
@Id
16+
@GeneratedValue(strategy = GenerationType.IDENTITY)
17+
private Long id;
18+
19+
@Column(nullable = true)
20+
private String label;
21+
22+
@ManyToOne
23+
@JoinColumn(name = "datasetversion_id", nullable = false)
24+
private DatasetVersion datasetVersion;
25+
26+
@ManyToOne
27+
@JoinColumn(name = "authenticateduser_id", nullable = true)
28+
private AuthenticatedUser authenticatedUser;
29+
30+
@Temporal(TemporalType.TIMESTAMP)
31+
@Column(nullable = true)
32+
private Date createTime;
33+
34+
// Constructors, getters, and setters
35+
36+
public CurationStatus() {
37+
}
38+
39+
public CurationStatus(String label, DatasetVersion datasetVersion, AuthenticatedUser authenticatedUser) {
40+
this.label = label;
41+
this.datasetVersion = datasetVersion;
42+
this.authenticatedUser = authenticatedUser;
43+
this.createTime = new Date();
44+
}
45+
46+
// Getters and setters for all fields
47+
48+
public Long getId() {
49+
return id;
50+
}
51+
52+
public void setId(Long id) {
53+
this.id = id;
54+
}
55+
56+
public String getLabel() {
57+
return label;
58+
}
59+
60+
public void setLabel(String label) {
61+
this.label = label;
62+
}
63+
64+
public DatasetVersion getDatasetVersion() {
65+
return datasetVersion;
66+
}
67+
68+
public void setDatasetVersion(DatasetVersion datasetVersion) {
69+
this.datasetVersion = datasetVersion;
70+
}
71+
72+
public AuthenticatedUser getAuthenticatedUser() {
73+
return authenticatedUser;
74+
}
75+
76+
public void setAuthenticatedUser(AuthenticatedUser authenticatedUser) {
77+
this.authenticatedUser = authenticatedUser;
78+
}
79+
80+
public Date getCreateTime() {
81+
return createTime;
82+
}
83+
84+
public void setCreateTime(Date createTime) {
85+
this.createTime = createTime;
86+
}
87+
88+
public boolean isNoStatus() {
89+
return label == null || label.trim().isEmpty();
90+
}
91+
}

src/main/java/edu/harvard/iq/dataverse/DatasetPage.java

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@
143143
import jakarta.servlet.http.HttpServletResponse;
144144

145145
import org.apache.commons.text.StringEscapeUtils;
146+
import org.apache.logging.log4j.util.Strings;
146147
import org.apache.commons.lang3.mutable.MutableBoolean;
147148
import org.apache.commons.io.IOUtils;
148149
import org.primefaces.component.selectonemenu.SelectOneMenu;
@@ -1477,6 +1478,15 @@ public boolean canViewUnpublishedDataset() {
14771478
return permissionsWrapper.canViewUnpublishedDataset( dvRequestService.getDataverseRequest(), dataset);
14781479
}
14791480

1481+
public boolean canSeeCurationStatus() {
1482+
boolean creatorsCanSeeStatus = JvmSettings.UI_SHOW_CURATION_STATUS_TO_ALL.lookupOptional(Boolean.class).orElse(false);
1483+
if (creatorsCanSeeStatus) {
1484+
return canViewUnpublishedDataset();
1485+
} else {
1486+
return canPublishDataset();
1487+
}
1488+
}
1489+
14801490
/*
14811491
* 4.2.1 optimization.
14821492
* HOWEVER, this doesn't appear to be saving us anything!
@@ -6278,28 +6288,28 @@ public String getFieldLanguage(String languages) {
62786288
return fieldService.getFieldLanguage(languages,session.getLocaleCode());
62796289
}
62806290

6281-
public void setExternalStatus(String status) {
6291+
public void setCurationStatus(String status) {
62826292
try {
62836293
dataset = commandEngine.submit(new SetCurationStatusCommand(dvRequestService.getDataverseRequest(), dataset, status));
62846294
workingVersion=dataset.getLatestVersion();
6285-
if (status == null || status.isEmpty()) {
6286-
JsfHelper.addInfoMessage(BundleUtil.getStringFromBundle("dataset.externalstatus.removed"));
6295+
if (Strings.isBlank(status)) {
6296+
JsfHelper.addInfoMessage(BundleUtil.getStringFromBundle("dataset.curationstatus.removed"));
62876297
} else {
6288-
JH.addMessage(FacesMessage.SEVERITY_INFO, BundleUtil.getStringFromBundle("dataset.externalstatus.header"),
6289-
BundleUtil.getStringFromBundle("dataset.externalstatus.info",
6290-
Arrays.asList(DatasetUtil.getLocaleExternalStatus(status))
6298+
JH.addMessage(FacesMessage.SEVERITY_INFO, BundleUtil.getStringFromBundle("dataset.curationstatus.header"),
6299+
BundleUtil.getStringFromBundle("dataset.curationstatus.info",
6300+
Arrays.asList(DatasetUtil.getLocaleCurationStatusLabelFromString(status))
62916301
));
62926302
}
62936303

62946304
} catch (CommandException ex) {
6295-
String msg = BundleUtil.getStringFromBundle("dataset.externalstatus.cantchange");
6305+
String msg = BundleUtil.getStringFromBundle("dataset.curationstatus.cantchange");
62966306
logger.warning("Unable to change external status to " + status + " for dataset id " + dataset.getId() + ". Message to user: " + msg + " Exception: " + ex);
62976307
JsfHelper.addErrorMessage(msg);
62986308
}
62996309
}
63006310

6301-
public List<String> getAllowedExternalStatuses() {
6302-
return settingsWrapper.getAllowedExternalStatuses(dataset);
6311+
public List<String> getAllowedCurationStatuses() {
6312+
return settingsWrapper.getAllowedCurationStatuses(dataset);
63036313
}
63046314

63056315
public Embargo getSelectionEmbargo() {

src/main/java/edu/harvard/iq/dataverse/DatasetVersion.java

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -216,9 +216,10 @@ public enum VersionState {
216216
@OneToMany(mappedBy = "datasetVersion", cascade={CascadeType.REMOVE, CascadeType.MERGE, CascadeType.PERSIST})
217217
private List<WorkflowComment> workflowComments;
218218

219-
@Column(nullable=true)
220-
private String externalStatusLabel;
221-
219+
@OneToMany(mappedBy = "datasetVersion", cascade = CascadeType.ALL, orphanRemoval = true)
220+
@OrderBy("createTime DESC NULLS LAST")
221+
private List<CurationStatus> curationStatuses = new ArrayList<>();
222+
222223
@Transient
223224
private DatasetVersionDifference dvd;
224225

@@ -2156,12 +2157,44 @@ public String getLocaleLastUpdateTime() {
21562157
return DateUtil.formatDate(new Timestamp(lastUpdateTime.getTime()));
21572158
}
21582159

2159-
public String getExternalStatusLabel() {
2160-
return externalStatusLabel;
2160+
// Add methods to manage curationLabels
2161+
public List<CurationStatus> getCurationStatuses() {
2162+
return curationStatuses;
2163+
}
2164+
2165+
protected void setCurationStatuses(List<CurationStatus> curationStatuses) {
2166+
this.curationStatuses = curationStatuses;
2167+
}
2168+
2169+
public CurationStatus getCurrentCurationStatus() {
2170+
return !getCurationStatuses().isEmpty() ? getCurationStatuses().get(0) : null;
2171+
}
2172+
2173+
2174+
public void addCurationStatus(CurationStatus status) {
2175+
status.setDatasetVersion(this);
2176+
curationStatuses.add(0, status); // Add the new status at the beginning of the list
21612177
}
21622178

2163-
public void setExternalStatusLabel(String externalStatusLabel) {
2164-
this.externalStatusLabel = externalStatusLabel;
2179+
public void removeCurationStatus(CurationStatus curationStatus) {
2180+
curationStatuses.remove(curationStatus);
2181+
curationStatus.setDatasetVersion(null);
2182+
}
2183+
2184+
public CurationStatus getCurationStatusAsOfDate(Date date) {
2185+
if (curationStatuses == null || curationStatuses.isEmpty()) {
2186+
return null;
2187+
}
2188+
2189+
// Find the first status whose createTime is before or equal to the given date
2190+
for (CurationStatus status : curationStatuses) {
2191+
if (status.getCreateTime().compareTo(date) <= 0) {
2192+
return status;
2193+
}
2194+
}
2195+
2196+
// If no status is found before the given date, return null
2197+
return null;
21652198
}
21662199

21672200
public String getVersionNote() {

src/main/java/edu/harvard/iq/dataverse/DataversePage.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1241,7 +1241,7 @@ public Set<Entry<String, String>> getCurationLabelSetOptions() {
12411241
setNames.put(BundleUtil.getStringFromBundle("dataverse.curationLabels.disabled"), SystemConfig.CURATIONLABELSDISABLED);
12421242

12431243
allowedSetNames.forEach(name -> {
1244-
String localizedName = DatasetUtil.getLocaleExternalStatus(name) ;
1244+
String localizedName = DatasetUtil.getLocaleCurationStatusLabelFromString(name) ;
12451245
setNames.put(localizedName,name);
12461246
});
12471247
}

src/main/java/edu/harvard/iq/dataverse/FilePage.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,15 @@ private boolean canViewUnpublishedDataset() {
345345
public boolean canPublishDataset(){
346346
return permissionsWrapper.canIssuePublishDatasetCommand(fileMetadata.getDatasetVersion().getDataset());
347347
}
348-
348+
349+
public boolean canSeeCurationStatus() {
350+
boolean creatorsCanSeeStatus = JvmSettings.UI_SHOW_CURATION_STATUS_TO_ALL.lookupOptional(Boolean.class).orElse(false);
351+
if (creatorsCanSeeStatus) {
352+
return canViewUnpublishedDataset();
353+
} else {
354+
return canPublishDataset();
355+
}
356+
}
349357

350358
public FileMetadata getFileMetadata() {
351359
return fileMetadata;

0 commit comments

Comments
 (0)