Skip to content

Commit 66269e2

Browse files
authored
Merge pull request #11247 from IQSS/11188-version-difference-summary
11188 API to get list of dataset versions with summary of differences
2 parents 567f0ac + d28ae2b commit 66269e2

File tree

8 files changed

+456
-44
lines changed

8 files changed

+456
-44
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
### Retreiving a list of Dataset Versions
2+
3+
An API has been added to get dataset versions including a summary of diiferences between consecutive versions where available. See [the docs](https://guides.dataverse.org/en/6.6/api/native-api.html#get-versions-of-a-dataset-with-summary-of-changes ), and #10888.
4+

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2002,6 +2002,27 @@ The fully expanded example above (without environment variables) looks like this
20022002
20032003
curl "https://demo.dataverse.org/api/datasets/24/versions/:latest-published/compare/:draft"
20042004
2005+
Get Versions of a Dataset with Summary of Changes
2006+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2007+
2008+
Returns a list of versions for a given dataset including a summary of differences between consecutive versions where available. Draft versions will only
2009+
be available to users who have permission to view unpublished drafts. The api token is optional.
2010+
2011+
.. code-block:: bash
2012+
2013+
export API_TOKEN=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
2014+
export SERVER_URL=https://demo.dataverse.org
2015+
export PERSISTENT_IDENTIFIER=doi:10.5072/FK2/BCCP9Z
2016+
2017+
curl -H "X-Dataverse-key: $API_TOKEN" -X PUT "$SERVER_URL/api/datasets/:persistentId/versions/compareSummary?persistentId=$PERSISTENT_IDENTIFIER"
2018+
2019+
The fully expanded example above (without environment variables) looks like this:
2020+
2021+
.. code-block:: bash
2022+
2023+
curl -H "X-Dataverse-key: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" -X PUT "https://demo.dataverse.org/api/datasets/:persistentId/versions/compareSummary?persistentId=doi:10.5072/FK2/BCCP9Z"
2024+
2025+
20052026
Update Metadata For a Dataset
20062027
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
20072028

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

Lines changed: 163 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ public final class DatasetVersionDifference {
4646
private List<FileMetadata> changedVariableMetadata = new ArrayList<>();
4747
private List<FileMetadata[]> replacedFiles = new ArrayList<>();
4848
private List<String[]> changedTermsAccess = new ArrayList<>();
49-
private List<Object[]> summaryDataForNote = new ArrayList<>();
50-
private List<Object[]> blockDataForNote = new ArrayList<>();
49+
private List<SummaryNote> summaryDataForNote = new ArrayList<>();
50+
private List<SummaryNote> blockDataForNote = new ArrayList<>();
5151

5252
private List<DifferenceSummaryGroup> differenceSummaryGroups = new ArrayList<>();
5353

@@ -300,32 +300,33 @@ private void addToSummary(DatasetField dsfo, DatasetField dsfn) {
300300

301301
private void updateBlockSummary(DatasetField dsf, int added, int deleted, int changed) {
302302
boolean addedToAll = false;
303-
for (Object[] blockList : blockDataForNote) {
304-
DatasetField dsft = (DatasetField) blockList[0];
303+
for (SummaryNote blockList : blockDataForNote) {
304+
305+
DatasetField dsft = blockList.dsfo;
305306
if (dsft.getDatasetFieldType().getMetadataBlock().equals(dsf.getDatasetFieldType().getMetadataBlock())) {
306-
blockList[1] = (Integer) blockList[1] + added;
307-
blockList[2] = (Integer) blockList[2] + deleted;
308-
blockList[3] = (Integer) blockList[3] + changed;
307+
blockList.added = blockList.added + added;
308+
blockList.deleted = blockList.deleted + deleted;
309+
blockList.changed = blockList.changed + changed;
309310
addedToAll = true;
310311
}
311312
}
312313
if (!addedToAll) {
313-
Object[] newArray = new Object[4];
314-
newArray[0] = dsf;
315-
newArray[1] = added;
316-
newArray[2] = deleted;
317-
newArray[3] = changed;
318-
blockDataForNote.add(newArray);
314+
SummaryNote newNote = new SummaryNote();
315+
newNote.dsfo = dsf;
316+
newNote.added = added;
317+
newNote.deleted = deleted;
318+
newNote.changed = changed;
319+
blockDataForNote.add(newNote);
319320
}
320321
}
321322

322-
private void addToNoteSummary(DatasetField dsfo, int added, int deleted, int changed) {
323-
Object[] noteArray = new Object[4];
324-
noteArray[0] = dsfo;
325-
noteArray[1] = added;
326-
noteArray[2] = deleted;
327-
noteArray[3] = changed;
328-
summaryDataForNote.add(noteArray);
323+
private void addToNoteSummary(DatasetField dsfo, Integer added, Integer deleted, Integer changed) {
324+
SummaryNote summaryNote = new SummaryNote();
325+
summaryNote.dsfo = dsfo;
326+
summaryNote.added = added;
327+
summaryNote.deleted = deleted;
328+
summaryNote.changed = changed;
329+
summaryDataForNote.add(summaryNote);
329330
}
330331

331332
static boolean compareVarGroup(FileMetadata fmdo, FileMetadata fmdn) {
@@ -363,14 +364,19 @@ public static Map<String,List<String>> compareFileMetadatas(FileMetadata fmdo, F
363364
List.of(StringUtil.nullToEmpty(fmdo.getDescription()), StringUtil.nullToEmpty(fmdn.getDescription())));
364365
}
365366

366-
if (!StringUtils.equals(fmdo.getCategoriesByName().toString(), fmdn.getCategoriesByName().toString())) {
367+
if (!StringUtils.equals(StringUtil.nullToEmpty(fmdo.getCategoriesByName().toString()), StringUtil.nullToEmpty(fmdn.getCategoriesByName().toString()))) {
367368
fileMetadataChanged.put("Categories",
368369
List.of(fmdo.getCategoriesByName().toString(), fmdn.getCategoriesByName().toString()));
369370
}
370371

371-
if (!StringUtils.equals(fmdo.getLabel(), fmdn.getLabel())) {
372+
if (!StringUtils.equals(StringUtil.nullToEmpty(fmdo.getLabel()), StringUtil.nullToEmpty(fmdn.getLabel()))) {
372373
fileMetadataChanged.put("Label",
373-
List.of(fmdo.getLabel(), fmdn.getLabel()));
374+
List.of(StringUtil.nullToEmpty(fmdo.getLabel()), StringUtil.nullToEmpty(fmdn.getLabel())));
375+
}
376+
377+
if (!StringUtils.equals(StringUtil.nullToEmpty(fmdo.getDirectoryLabel()), StringUtil.nullToEmpty(fmdn.getDirectoryLabel()))) {
378+
fileMetadataChanged.put("File Path",
379+
List.of(StringUtil.nullToEmpty(fmdo.getDirectoryLabel()), StringUtil.nullToEmpty(fmdn.getDirectoryLabel())));
374380
}
375381

376382
if (!StringUtils.equals(StringUtil.nullToEmpty(fmdo.getProvFreeForm()), StringUtil.nullToEmpty(fmdn.getProvFreeForm()))) {
@@ -464,6 +470,8 @@ private void compareValues(DatasetField originalField, DatasetField newField, bo
464470
}
465471
}
466472
}
473+
474+
467475

468476
public String getFileNote() {
469477
String retString = "";
@@ -568,19 +576,19 @@ public void setChangedFileMetadata(List<FileMetadata> changedFileMetadata) {
568576
this.changedFileMetadata = changedFileMetadata;
569577
}
570578

571-
public List<Object[]> getSummaryDataForNote() {
579+
public List<SummaryNote> getSummaryDataForNote() {
572580
return summaryDataForNote;
573581
}
574582

575-
public List<Object[]> getBlockDataForNote() {
583+
public List<SummaryNote> getBlockDataForNote() {
576584
return blockDataForNote;
577585
}
578586

579-
public void setSummaryDataForNote(List<Object[]> summaryDataForNote) {
587+
public void setSummaryDataForNote(List<SummaryNote> summaryDataForNote) {
580588
this.summaryDataForNote = summaryDataForNote;
581589
}
582590

583-
public void setBlockDataForNote(List<Object[]> blockDataForNote) {
591+
public void setBlockDataForNote(List<SummaryNote> blockDataForNote) {
584592
this.blockDataForNote = blockDataForNote;
585593
}
586594

@@ -1205,6 +1213,47 @@ public List<DifferenceSummaryItem> getDifferenceSummaryItems() {
12051213
public void setDifferenceSummaryItems(List<DifferenceSummaryItem> differenceSummaryItems) {
12061214
this.differenceSummaryItems = differenceSummaryItems;
12071215
}
1216+
}
1217+
1218+
public class SummaryNote {
1219+
DatasetField dsfo;
1220+
Integer added;
1221+
Integer deleted;
1222+
Integer changed;
1223+
1224+
public void setDatasetField(DatasetField dsfIn){
1225+
dsfo = dsfIn;
1226+
}
1227+
1228+
public DatasetField getDatasetField (){
1229+
return dsfo;
1230+
}
1231+
1232+
public void setAdded(Integer addin){
1233+
added = addin;
1234+
}
1235+
1236+
public Integer getAdded(){
1237+
return added;
1238+
}
1239+
1240+
public void setDeleted(Integer delin){
1241+
deleted = delin;
1242+
}
1243+
1244+
public Integer getDeleted(){
1245+
return deleted;
1246+
}
1247+
1248+
public void setChanged(Integer changedin){
1249+
changed = changedin;
1250+
}
1251+
1252+
public Integer getChanged(){
1253+
return changed;
1254+
}
1255+
1256+
12081257
}
12091258

12101259
public class DifferenceSummaryItem {
@@ -1641,6 +1690,93 @@ List<FileMetadata> getChangedVariableMetadata() {
16411690
List<FileMetadata[]> getReplacedFiles() {
16421691
return replacedFiles;
16431692
}
1693+
1694+
public JsonObjectBuilder getSummaryDifferenceAsJson(){
1695+
1696+
JsonObjectBuilder jobVersion = new NullSafeJsonBuilder();
1697+
1698+
JsonObjectBuilder jobDsfOnCreate = new NullSafeJsonBuilder();
1699+
1700+
1701+
for (SummaryNote sn : this.summaryDataForNote) {
1702+
jobDsfOnCreate.add( sn.getDatasetField().getDatasetFieldType().getDisplayName(), getSummaryNoteAsJson(sn));
1703+
}
1704+
1705+
if (!this.summaryDataForNote.isEmpty()){
1706+
1707+
jobVersion.add("Citation Metadata", jobDsfOnCreate);
1708+
1709+
}
1710+
1711+
for (SummaryNote sn : this.getBlockDataForNote()){
1712+
String mdbDisplayName = sn.getDatasetField().getDatasetFieldType().getMetadataBlock().getDisplayName();
1713+
if (mdbDisplayName.equals("Citation Metadata")){
1714+
mdbDisplayName = "Additional Citation Metadata";
1715+
}
1716+
jobVersion.add( mdbDisplayName, getSummaryNoteAsJson(sn));
1717+
}
1718+
1719+
jobVersion.add("files", getFileSummaryAsJson());
1720+
1721+
if (!this.changedTermsAccess.isEmpty()) {
1722+
jobVersion.add("termsAccessChanged", true);
1723+
} else{
1724+
jobVersion.add("termsAccessChanged", false);
1725+
}
1726+
1727+
return jobVersion;
1728+
}
1729+
1730+
private JsonObjectBuilder getSummaryNoteAsJson(SummaryNote sn){
1731+
JsonObjectBuilder job = new NullSafeJsonBuilder();
1732+
//job.add("datasetFieldType", sn.getDatasetField().getDatasetFieldType().getDisplayName());
1733+
job.add("added", sn.added);
1734+
job.add("deleted", sn.deleted);
1735+
job.add("changed", sn.changed);
1736+
return job;
1737+
}
1738+
1739+
private JsonObjectBuilder getFileSummaryAsJson(){
1740+
JsonObjectBuilder job = new NullSafeJsonBuilder();
1741+
1742+
if (!addedFiles.isEmpty()) {
1743+
job.add("added", addedFiles.size());
1744+
1745+
} else{
1746+
job.add("added", 0);
1747+
}
1748+
1749+
if (!removedFiles.isEmpty()) {
1750+
job.add("removed", removedFiles.size());
1751+
1752+
} else{
1753+
job.add("removed", 0);
1754+
}
1755+
1756+
if (!replacedFiles.isEmpty()) {
1757+
job.add("replaced", replacedFiles.size());
1758+
1759+
} else{
1760+
job.add("replaced", 0);
1761+
}
1762+
1763+
if (!changedFileMetadata.isEmpty()) {
1764+
job.add("changedFileMetaData", changedFileMetadata.size());
1765+
1766+
} else{
1767+
job.add("changedFileMetaData", 0);
1768+
}
1769+
1770+
if (!changedVariableMetadata.isEmpty()) {
1771+
job.add("changedVariableMetadata", changedVariableMetadata.size());
1772+
1773+
} else{
1774+
job.add("changedVariableMetadata", 0);
1775+
}
1776+
1777+
return job;
1778+
}
1779+
16441780
public JsonObjectBuilder compareVersionsAsJson() {
16451781
JsonObjectBuilder job = new NullSafeJsonBuilder();
16461782
JsonObjectBuilder jobVersion = new NullSafeJsonBuilder();

src/main/java/edu/harvard/iq/dataverse/api/Datasets.java

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.amazonaws.services.s3.model.PartETag;
44
import edu.harvard.iq.dataverse.*;
55
import edu.harvard.iq.dataverse.DatasetLock.Reason;
6+
import edu.harvard.iq.dataverse.DatasetVersion.VersionState;
67
import edu.harvard.iq.dataverse.actionlogging.ActionLogRecord;
78
import edu.harvard.iq.dataverse.api.auth.AuthRequired;
89
import edu.harvard.iq.dataverse.api.dto.RoleAssignmentDTO;
@@ -3047,6 +3048,62 @@ public Response getCompareVersions(@Context ContainerRequestContext crc, @PathPa
30473048
return wr.getResponse();
30483049
}
30493050
}
3051+
3052+
@GET
3053+
@AuthRequired
3054+
@Path("{id}/versions/compareSummary")
3055+
public Response getCompareVersionsSummary(@Context ContainerRequestContext crc, @PathParam("id") String id,
3056+
@Context UriInfo uriInfo, @Context HttpHeaders headers) {
3057+
try {
3058+
Dataset dataset = findDatasetOrDie(id);
3059+
User user = getRequestUser(crc);
3060+
JsonArrayBuilder differenceSummaries = Json.createArrayBuilder();
3061+
3062+
for (DatasetVersion dv : dataset.getVersions()) {
3063+
//only get summaries of draft is user may view unpublished
3064+
3065+
if (dv.isPublished() || permissionService.hasPermissionsFor(user, dv.getDataset(),
3066+
EnumSet.of(Permission.ViewUnpublishedDataset))) {
3067+
3068+
JsonObjectBuilder versionBuilder = new NullSafeJsonBuilder();
3069+
versionBuilder.add("id", dv.getId());
3070+
versionBuilder.add("versionNumber", dv.getFriendlyVersionNumber());
3071+
DatasetVersionDifference dvdiff = dv.getDefaultVersionDifference();
3072+
if (dvdiff == null) {
3073+
if (dv.isReleased()) {
3074+
if (dv.getPriorVersionState() == null) {
3075+
versionBuilder.add("summary", "firstPublished");
3076+
}
3077+
if (dv.getPriorVersionState() != null && dv.getPriorVersionState().equals(VersionState.DEACCESSIONED)) {
3078+
versionBuilder.add("summary", "previousVersionDeaccessioned");
3079+
}
3080+
}
3081+
if (dv.isDraft()) {
3082+
if (dv.getPriorVersionState() == null) {
3083+
versionBuilder.add("summary", "firstDraft");
3084+
}
3085+
if (dv.getPriorVersionState() != null && dv.getPriorVersionState().equals(VersionState.DEACCESSIONED)) {
3086+
versionBuilder.add("summary", "previousVersionDeaccessioned");
3087+
}
3088+
}
3089+
if (dv.isDeaccessioned()) {
3090+
versionBuilder.add("summary", "versionDeaccessioned");
3091+
}
3092+
3093+
} else {
3094+
versionBuilder.add("summary", dvdiff.getSummaryDifferenceAsJson());
3095+
}
3096+
3097+
versionBuilder.add("contributors", datasetversionService.getContributorsNames(dv));
3098+
versionBuilder.add("publishedOn", !dv.isDraft() ? dv.getPublicationDateAsString() : "");
3099+
differenceSummaries.add(versionBuilder);
3100+
}
3101+
}
3102+
return ok(differenceSummaries);
3103+
} catch (WrappedResponse wr) {
3104+
return wr.getResponse();
3105+
}
3106+
}
30503107

30513108
private static Set<String> getDatasetFilenames(Dataset dataset) {
30523109
Set<String> files = new HashSet<>();

0 commit comments

Comments
 (0)