Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

file citation via API #10246

Merged
merged 15 commits into from
Feb 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions doc/release-notes/10240-file-citation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
## Get file citation as JSON

It is now possible to retrieve via API the file citation as it appears on the file landing page. It is formatted in HTML and encoded in JSON.

This API is not for downloading various citation formats such as EndNote XML, RIS, or BibTeX. This functionality has been requested in https://github.com/IQSS/dataverse/issues/3140 and https://github.com/IQSS/dataverse/issues/9994
58 changes: 57 additions & 1 deletion doc/sphinx-guides/source/api/native-api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -845,7 +845,12 @@ Datasets

**Note** Creation of new datasets is done with a ``POST`` onto a Dataverse collection. See the Dataverse Collections section above.

**Note** In all commands below, dataset versions can be referred to as:
.. _dataset-version-specifiers:

Dataset Version Specifiers
~~~~~~~~~~~~~~~~~~~~~~~~~~

In all commands below, dataset versions can be referred to as:

* ``:draft`` the draft version, if any
* ``:latest`` either a draft (if exists) or the latest published version.
Expand Down Expand Up @@ -2712,6 +2717,8 @@ The fully expanded example above (without environment variables) looks like this
Files
-----

.. _get-json-rep-of-file:

Get JSON Representation of a File
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Expand Down Expand Up @@ -3499,6 +3506,55 @@ The fully expanded example above (without environment variables) looks like this

You can download :download:`dct.xml <../../../../src/test/resources/xml/dct.xml>` from the example above to see what the XML looks like.

Get File Citation as JSON
~~~~~~~~~~~~~~~~~~~~~~~~~

This API is for getting the file citation as it appears on the file landing page. It is formatted in HTML and encoded in JSON.

To specify the version, you can use ``:latest-published`` or ``:draft`` or ``1.0`` or any other style listed under :ref:`dataset-version-specifiers`.

When the dataset version is published, authentication is not required:

.. code-block:: bash

export SERVER_URL=https://demo.dataverse.org
export FILE_ID=42
export DATASET_VERSION=:latest-published

curl "$SERVER_URL/api/files/$FILE_ID/versions/$DATASET_VERSION/citation"

The fully expanded example above (without environment variables) looks like this:

.. code-block:: bash

curl "https://demo.dataverse.org/api/files/42/versions/:latest-published/citation"

When the dataset version is a draft or deaccessioned, authentication is required.

By default, deaccessioned dataset versions are not included in the search when applying the :latest or :latest-published identifiers. Additionally, when filtering by a specific version tag, you will get a "unauthorized" error if the version is deaccessioned and you do not enable the ``includeDeaccessioned`` option described below.

If you want to include deaccessioned dataset versions, you must set ``includeDeaccessioned`` query parameter to ``true``.

.. code-block:: bash

export API_TOKEN=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
export SERVER_URL=https://demo.dataverse.org
export FILE_ID=42
export DATASET_VERSION=:draft
export INCLUDE_DEACCESSIONED=true

curl -H "X-Dataverse-key:$API_TOKEN" "$SERVER_URL/api/files/$FILE_ID/versions/$DATASET_VERSION/citation?includeDeaccessioned=$INCLUDE_DEACCESSIONED"

The fully expanded example above (without environment variables) looks like this:

.. code-block:: bash

curl -H "X-Dataverse-key:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" "https://demo.dataverse.org/api/files/42/versions/:draft/citation?includeDeaccessioned=true"

If your file has a persistent identifier (PID, such as a DOI), you can pass it using the technique described under :ref:`get-json-rep-of-file`.

This API is not for downloading various citation formats such as EndNote XML, RIS, or BibTeX. This functionality has been requested in https://github.com/IQSS/dataverse/issues/3140 and https://github.com/IQSS/dataverse/issues/9994.
pdurbin marked this conversation as resolved.
Show resolved Hide resolved

Provenance
~~~~~~~~~~

Expand Down
31 changes: 31 additions & 0 deletions src/main/java/edu/harvard/iq/dataverse/api/AbstractApiBean.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import edu.harvard.iq.dataverse.*;
import edu.harvard.iq.dataverse.actionlogging.ActionLogServiceBean;
import static edu.harvard.iq.dataverse.api.Datasets.handleVersion;
import edu.harvard.iq.dataverse.authorization.AuthenticationServiceBean;
import edu.harvard.iq.dataverse.authorization.DataverseRole;
import edu.harvard.iq.dataverse.authorization.RoleAssignee;
Expand All @@ -15,6 +16,10 @@
import edu.harvard.iq.dataverse.engine.command.exception.CommandException;
import edu.harvard.iq.dataverse.engine.command.exception.IllegalCommandException;
import edu.harvard.iq.dataverse.engine.command.exception.PermissionException;
import edu.harvard.iq.dataverse.engine.command.impl.GetDraftDatasetVersionCommand;
import edu.harvard.iq.dataverse.engine.command.impl.GetLatestAccessibleDatasetVersionCommand;
import edu.harvard.iq.dataverse.engine.command.impl.GetLatestPublishedDatasetVersionCommand;
import edu.harvard.iq.dataverse.engine.command.impl.GetSpecificPublishedDatasetVersionCommand;
import edu.harvard.iq.dataverse.externaltools.ExternalToolServiceBean;
import edu.harvard.iq.dataverse.license.LicenseServiceBean;
import edu.harvard.iq.dataverse.locality.StorageSiteServiceBean;
Expand Down Expand Up @@ -390,6 +395,32 @@ protected Dataset findDatasetOrDie(String id) throws WrappedResponse {
}
}
}

protected DatasetVersion findDatasetVersionOrDie(final DataverseRequest req, String versionNumber, final Dataset ds, boolean includeDeaccessioned, boolean checkPermsWhenDeaccessioned) throws WrappedResponse {
DatasetVersion dsv = execCommand(handleVersion(versionNumber, new Datasets.DsVersionHandler<Command<DatasetVersion>>() {

@Override
public Command<DatasetVersion> handleLatest() {
return new GetLatestAccessibleDatasetVersionCommand(req, ds, includeDeaccessioned, checkPermsWhenDeaccessioned);
}

@Override
public Command<DatasetVersion> handleDraft() {
return new GetDraftDatasetVersionCommand(req, ds);
}

@Override
public Command<DatasetVersion> handleSpecific(long major, long minor) {
return new GetSpecificPublishedDatasetVersionCommand(req, ds, major, minor, includeDeaccessioned, checkPermsWhenDeaccessioned);
}

@Override
public Command<DatasetVersion> handleLatestPublished() {
return new GetLatestPublishedDatasetVersionCommand(req, ds, includeDeaccessioned, checkPermsWhenDeaccessioned);
}
}));
return dsv;
}

protected DataFile findDataFileOrDie(String id) throws WrappedResponse {

Expand Down
23 changes: 1 addition & 22 deletions src/main/java/edu/harvard/iq/dataverse/api/Datasets.java
Original file line number Diff line number Diff line change
Expand Up @@ -2727,28 +2727,7 @@ private DatasetVersion getDatasetVersionOrDie(final DataverseRequest req, String
* Will allow to define when the permissions should be checked when a deaccesioned dataset is requested. If the user doesn't have edit permissions will result in an error.
*/
private DatasetVersion getDatasetVersionOrDie(final DataverseRequest req, String versionNumber, final Dataset ds, UriInfo uriInfo, HttpHeaders headers, boolean includeDeaccessioned, boolean checkPermsWhenDeaccessioned) throws WrappedResponse {
DatasetVersion dsv = execCommand(handleVersion(versionNumber, new DsVersionHandler<Command<DatasetVersion>>() {

@Override
public Command<DatasetVersion> handleLatest() {
return new GetLatestAccessibleDatasetVersionCommand(req, ds, includeDeaccessioned, checkPermsWhenDeaccessioned);
}

@Override
public Command<DatasetVersion> handleDraft() {
return new GetDraftDatasetVersionCommand(req, ds);
}

@Override
public Command<DatasetVersion> handleSpecific(long major, long minor) {
return new GetSpecificPublishedDatasetVersionCommand(req, ds, major, minor, includeDeaccessioned, checkPermsWhenDeaccessioned);
}

@Override
public Command<DatasetVersion> handleLatestPublished() {
return new GetLatestPublishedDatasetVersionCommand(req, ds, includeDeaccessioned, checkPermsWhenDeaccessioned);
}
}));
DatasetVersion dsv = findDatasetVersionOrDie(req, versionNumber, ds, includeDeaccessioned, checkPermsWhenDeaccessioned);
if (dsv == null || dsv.getId() == null) {
throw new WrappedResponse(notFound("Dataset version " + versionNumber + " of dataset " + ds.getId() + " not found"));
}
Expand Down
35 changes: 35 additions & 0 deletions src/main/java/edu/harvard/iq/dataverse/api/Files.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.google.gson.Gson;
import com.google.gson.JsonObject;
import edu.harvard.iq.dataverse.DataCitation;
import edu.harvard.iq.dataverse.DataFile;
import edu.harvard.iq.dataverse.DataFileServiceBean;
import edu.harvard.iq.dataverse.DataFileTag;
Expand All @@ -27,6 +28,7 @@
import edu.harvard.iq.dataverse.datasetutility.DataFileTagException;
import edu.harvard.iq.dataverse.datasetutility.NoFilesException;
import edu.harvard.iq.dataverse.datasetutility.OptionalFileParams;
import edu.harvard.iq.dataverse.engine.command.Command;
import edu.harvard.iq.dataverse.engine.command.DataverseRequest;
import edu.harvard.iq.dataverse.engine.command.exception.CommandException;
import edu.harvard.iq.dataverse.engine.command.exception.IllegalCommandException;
Expand Down Expand Up @@ -931,4 +933,37 @@ public Response getHasBeenDeleted(@Context ContainerRequestContext crc, @PathPar
return ok(dataFileServiceBean.hasBeenDeleted(dataFile));
}, getRequestUser(crc));
}

/**
* @param fileIdOrPersistentId Database ID or PID of the data file.
* @param versionNumber The version of the dataset, such as 1.0, :draft,
* :latest-published, etc.
* @param includeDeaccessioned Defaults to false.
*/
@GET
@AuthRequired
@Path("{id}/versions/{dsVersionString}/citation")
public Response getFileCitationByVersion(@Context ContainerRequestContext crc, @PathParam("id") String fileIdOrPersistentId, @PathParam("dsVersionString") String versionNumber, @QueryParam("includeDeaccessioned") boolean includeDeaccessioned) {
try {
DataverseRequest req = createDataverseRequest(getRequestUser(crc));
final DataFile df = execCommand(new GetDataFileCommand(req, findDataFileOrDie(fileIdOrPersistentId)));
Dataset ds = df.getOwner();
DatasetVersion dsv = findDatasetVersionOrDie(req, versionNumber, ds, includeDeaccessioned, true);
if (dsv == null) {
return unauthorized(BundleUtil.getStringFromBundle("files.api.no.draftOrUnauth"));
}

Long getDatasetVersionID = dsv.getId();
FileMetadata fm = dataFileServiceBean.findFileMetadataByDatasetVersionIdAndDataFileId(getDatasetVersionID, df.getId());
if (fm == null) {
return notFound(BundleUtil.getStringFromBundle("files.api.fileNotFound"));
}
boolean direct = df.isIdentifierRegistered();
DataCitation citation = new DataCitation(fm, direct);
return ok(citation.toString(true));
} catch (WrappedResponse ex) {
return ex.getResponse();
}
}

}
2 changes: 2 additions & 0 deletions src/main/java/propertyFiles/Bundle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2633,7 +2633,9 @@ admin.api.deleteUser.success=Authenticated User {0} deleted.
#Files.java
files.api.metadata.update.duplicateFile=Filename already exists at {0}
files.api.no.draft=No draft available for this file
files.api.no.draftOrUnauth=Dataset version cannot be found or unauthorized.
files.api.only.tabular.supported=This operation is only available for tabular files.
files.api.fileNotFound=File could not be found.

#Datasets.java
datasets.api.updatePIDMetadata.failure.dataset.must.be.released=Modify Registration Metadata must be run on a published dataset.
Expand Down
31 changes: 31 additions & 0 deletions src/test/java/edu/harvard/iq/dataverse/DataCitationTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,36 @@ public void testTitleWithQuotes() throws ParseException {

}

@Test
public void testFileCitationToStringHtml() throws ParseException {
DatasetVersion dsv = createATestDatasetVersion("Dataset Title", true);
FileMetadata fileMetadata = new FileMetadata();
fileMetadata.setLabel("foo.txt");
fileMetadata.setDataFile(new DataFile());
dsv.setVersionState(DatasetVersion.VersionState.RELEASED);
fileMetadata.setDatasetVersion(dsv);
dsv.setDataset(dsv.getDataset());
DataCitation fileCitation = new DataCitation(fileMetadata, false);
assertEquals("First Last, 1955, \"Dataset Title\", <a href=\"https://doi.org/10.5072/FK2/LK0D1H\" target=\"_blank\">https://doi.org/10.5072/FK2/LK0D1H</a>, LibraScholar, V1; foo.txt [fileName]", fileCitation.toString(true));
}

@Test
public void testFileCitationToStringHtmlFilePid() throws ParseException {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great, thanks!

DatasetVersion dsv = createATestDatasetVersion("Dataset Title", true);
FileMetadata fileMetadata = new FileMetadata();
fileMetadata.setLabel("foo.txt");
DataFile dataFile = new DataFile();
dataFile.setProtocol("doi");
dataFile.setAuthority("10.42");
dataFile.setIdentifier("myFilePid");
fileMetadata.setDataFile(dataFile);
dsv.setVersionState(DatasetVersion.VersionState.RELEASED);
fileMetadata.setDatasetVersion(dsv);
dsv.setDataset(dsv.getDataset());
DataCitation fileCitation = new DataCitation(fileMetadata, true);
assertEquals("First Last, 1955, \"foo.txt\", <em>Dataset Title</em>, <a href=\"https://doi.org/10.42/myFilePid\" target=\"_blank\">https://doi.org/10.42/myFilePid</a>, LibraScholar, V1", fileCitation.toString(true));
}

private DatasetVersion createATestDatasetVersion(String withTitle, boolean withAuthor) throws ParseException {

Dataverse dataverse = new Dataverse();
Expand All @@ -400,6 +430,7 @@ private DatasetVersion createATestDatasetVersion(String withTitle, boolean withA
fields.add(createTitleField(withTitle));
}
if (withAuthor) {
// TODO: "Last, First" would make more sense.
fields.add(createAuthorField("First Last"));
}

Expand Down
Loading
Loading