Skip to content

Commit 82585f9

Browse files
authored
Merge pull request #10246 from IQSS/10240-file-citation
file citation via API
2 parents 4d529ce + ab0abaf commit 82585f9

9 files changed

Lines changed: 290 additions & 23 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
## Get file citation as JSON
2+
3+
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.
4+
5+
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

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

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -845,7 +845,12 @@ Datasets
845845

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

848-
**Note** In all commands below, dataset versions can be referred to as:
848+
.. _dataset-version-specifiers:
849+
850+
Dataset Version Specifiers
851+
~~~~~~~~~~~~~~~~~~~~~~~~~~
852+
853+
In all commands below, dataset versions can be referred to as:
849854

850855
* ``:draft`` the draft version, if any
851856
* ``:latest`` either a draft (if exists) or the latest published version.
@@ -2712,6 +2717,8 @@ The fully expanded example above (without environment variables) looks like this
27122717
Files
27132718
-----
27142719
2720+
.. _get-json-rep-of-file:
2721+
27152722
Get JSON Representation of a File
27162723
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
27172724
@@ -3499,6 +3506,55 @@ The fully expanded example above (without environment variables) looks like this
34993506
35003507
You can download :download:`dct.xml <../../../../src/test/resources/xml/dct.xml>` from the example above to see what the XML looks like.
35013508
3509+
Get File Citation as JSON
3510+
~~~~~~~~~~~~~~~~~~~~~~~~~
3511+
3512+
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.
3513+
3514+
To specify the version, you can use ``:latest-published`` or ``:draft`` or ``1.0`` or any other style listed under :ref:`dataset-version-specifiers`.
3515+
3516+
When the dataset version is published, authentication is not required:
3517+
3518+
.. code-block:: bash
3519+
3520+
export SERVER_URL=https://demo.dataverse.org
3521+
export FILE_ID=42
3522+
export DATASET_VERSION=:latest-published
3523+
3524+
curl "$SERVER_URL/api/files/$FILE_ID/versions/$DATASET_VERSION/citation"
3525+
3526+
The fully expanded example above (without environment variables) looks like this:
3527+
3528+
.. code-block:: bash
3529+
3530+
curl "https://demo.dataverse.org/api/files/42/versions/:latest-published/citation"
3531+
3532+
When the dataset version is a draft or deaccessioned, authentication is required.
3533+
3534+
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.
3535+
3536+
If you want to include deaccessioned dataset versions, you must set ``includeDeaccessioned`` query parameter to ``true``.
3537+
3538+
.. code-block:: bash
3539+
3540+
export API_TOKEN=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
3541+
export SERVER_URL=https://demo.dataverse.org
3542+
export FILE_ID=42
3543+
export DATASET_VERSION=:draft
3544+
export INCLUDE_DEACCESSIONED=true
3545+
3546+
curl -H "X-Dataverse-key:$API_TOKEN" "$SERVER_URL/api/files/$FILE_ID/versions/$DATASET_VERSION/citation?includeDeaccessioned=$INCLUDE_DEACCESSIONED"
3547+
3548+
The fully expanded example above (without environment variables) looks like this:
3549+
3550+
.. code-block:: bash
3551+
3552+
curl -H "X-Dataverse-key:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" "https://demo.dataverse.org/api/files/42/versions/:draft/citation?includeDeaccessioned=true"
3553+
3554+
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`.
3555+
3556+
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.
3557+
35023558
Provenance
35033559
~~~~~~~~~~
35043560

‎src/main/java/edu/harvard/iq/dataverse/api/AbstractApiBean.java‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import edu.harvard.iq.dataverse.*;
44
import edu.harvard.iq.dataverse.actionlogging.ActionLogServiceBean;
5+
import static edu.harvard.iq.dataverse.api.Datasets.handleVersion;
56
import edu.harvard.iq.dataverse.authorization.AuthenticationServiceBean;
67
import edu.harvard.iq.dataverse.authorization.DataverseRole;
78
import edu.harvard.iq.dataverse.authorization.RoleAssignee;
@@ -15,6 +16,10 @@
1516
import edu.harvard.iq.dataverse.engine.command.exception.CommandException;
1617
import edu.harvard.iq.dataverse.engine.command.exception.IllegalCommandException;
1718
import edu.harvard.iq.dataverse.engine.command.exception.PermissionException;
19+
import edu.harvard.iq.dataverse.engine.command.impl.GetDraftDatasetVersionCommand;
20+
import edu.harvard.iq.dataverse.engine.command.impl.GetLatestAccessibleDatasetVersionCommand;
21+
import edu.harvard.iq.dataverse.engine.command.impl.GetLatestPublishedDatasetVersionCommand;
22+
import edu.harvard.iq.dataverse.engine.command.impl.GetSpecificPublishedDatasetVersionCommand;
1823
import edu.harvard.iq.dataverse.externaltools.ExternalToolServiceBean;
1924
import edu.harvard.iq.dataverse.license.LicenseServiceBean;
2025
import edu.harvard.iq.dataverse.locality.StorageSiteServiceBean;
@@ -390,6 +395,32 @@ protected Dataset findDatasetOrDie(String id) throws WrappedResponse {
390395
}
391396
}
392397
}
398+
399+
protected DatasetVersion findDatasetVersionOrDie(final DataverseRequest req, String versionNumber, final Dataset ds, boolean includeDeaccessioned, boolean checkPermsWhenDeaccessioned) throws WrappedResponse {
400+
DatasetVersion dsv = execCommand(handleVersion(versionNumber, new Datasets.DsVersionHandler<Command<DatasetVersion>>() {
401+
402+
@Override
403+
public Command<DatasetVersion> handleLatest() {
404+
return new GetLatestAccessibleDatasetVersionCommand(req, ds, includeDeaccessioned, checkPermsWhenDeaccessioned);
405+
}
406+
407+
@Override
408+
public Command<DatasetVersion> handleDraft() {
409+
return new GetDraftDatasetVersionCommand(req, ds);
410+
}
411+
412+
@Override
413+
public Command<DatasetVersion> handleSpecific(long major, long minor) {
414+
return new GetSpecificPublishedDatasetVersionCommand(req, ds, major, minor, includeDeaccessioned, checkPermsWhenDeaccessioned);
415+
}
416+
417+
@Override
418+
public Command<DatasetVersion> handleLatestPublished() {
419+
return new GetLatestPublishedDatasetVersionCommand(req, ds, includeDeaccessioned, checkPermsWhenDeaccessioned);
420+
}
421+
}));
422+
return dsv;
423+
}
393424

394425
protected DataFile findDataFileOrDie(String id) throws WrappedResponse {
395426

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

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2727,28 +2727,7 @@ private DatasetVersion getDatasetVersionOrDie(final DataverseRequest req, String
27272727
* 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.
27282728
*/
27292729
private DatasetVersion getDatasetVersionOrDie(final DataverseRequest req, String versionNumber, final Dataset ds, UriInfo uriInfo, HttpHeaders headers, boolean includeDeaccessioned, boolean checkPermsWhenDeaccessioned) throws WrappedResponse {
2730-
DatasetVersion dsv = execCommand(handleVersion(versionNumber, new DsVersionHandler<Command<DatasetVersion>>() {
2731-
2732-
@Override
2733-
public Command<DatasetVersion> handleLatest() {
2734-
return new GetLatestAccessibleDatasetVersionCommand(req, ds, includeDeaccessioned, checkPermsWhenDeaccessioned);
2735-
}
2736-
2737-
@Override
2738-
public Command<DatasetVersion> handleDraft() {
2739-
return new GetDraftDatasetVersionCommand(req, ds);
2740-
}
2741-
2742-
@Override
2743-
public Command<DatasetVersion> handleSpecific(long major, long minor) {
2744-
return new GetSpecificPublishedDatasetVersionCommand(req, ds, major, minor, includeDeaccessioned, checkPermsWhenDeaccessioned);
2745-
}
2746-
2747-
@Override
2748-
public Command<DatasetVersion> handleLatestPublished() {
2749-
return new GetLatestPublishedDatasetVersionCommand(req, ds, includeDeaccessioned, checkPermsWhenDeaccessioned);
2750-
}
2751-
}));
2730+
DatasetVersion dsv = findDatasetVersionOrDie(req, versionNumber, ds, includeDeaccessioned, checkPermsWhenDeaccessioned);
27522731
if (dsv == null || dsv.getId() == null) {
27532732
throw new WrappedResponse(notFound("Dataset version " + versionNumber + " of dataset " + ds.getId() + " not found"));
27542733
}

‎src/main/java/edu/harvard/iq/dataverse/api/Files.java‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.google.gson.Gson;
44
import com.google.gson.JsonObject;
5+
import edu.harvard.iq.dataverse.DataCitation;
56
import edu.harvard.iq.dataverse.DataFile;
67
import edu.harvard.iq.dataverse.DataFileServiceBean;
78
import edu.harvard.iq.dataverse.DataFileTag;
@@ -27,6 +28,7 @@
2728
import edu.harvard.iq.dataverse.datasetutility.DataFileTagException;
2829
import edu.harvard.iq.dataverse.datasetutility.NoFilesException;
2930
import edu.harvard.iq.dataverse.datasetutility.OptionalFileParams;
31+
import edu.harvard.iq.dataverse.engine.command.Command;
3032
import edu.harvard.iq.dataverse.engine.command.DataverseRequest;
3133
import edu.harvard.iq.dataverse.engine.command.exception.CommandException;
3234
import edu.harvard.iq.dataverse.engine.command.exception.IllegalCommandException;
@@ -931,4 +933,37 @@ public Response getHasBeenDeleted(@Context ContainerRequestContext crc, @PathPar
931933
return ok(dataFileServiceBean.hasBeenDeleted(dataFile));
932934
}, getRequestUser(crc));
933935
}
936+
937+
/**
938+
* @param fileIdOrPersistentId Database ID or PID of the data file.
939+
* @param versionNumber The version of the dataset, such as 1.0, :draft,
940+
* :latest-published, etc.
941+
* @param includeDeaccessioned Defaults to false.
942+
*/
943+
@GET
944+
@AuthRequired
945+
@Path("{id}/versions/{dsVersionString}/citation")
946+
public Response getFileCitationByVersion(@Context ContainerRequestContext crc, @PathParam("id") String fileIdOrPersistentId, @PathParam("dsVersionString") String versionNumber, @QueryParam("includeDeaccessioned") boolean includeDeaccessioned) {
947+
try {
948+
DataverseRequest req = createDataverseRequest(getRequestUser(crc));
949+
final DataFile df = execCommand(new GetDataFileCommand(req, findDataFileOrDie(fileIdOrPersistentId)));
950+
Dataset ds = df.getOwner();
951+
DatasetVersion dsv = findDatasetVersionOrDie(req, versionNumber, ds, includeDeaccessioned, true);
952+
if (dsv == null) {
953+
return unauthorized(BundleUtil.getStringFromBundle("files.api.no.draftOrUnauth"));
954+
}
955+
956+
Long getDatasetVersionID = dsv.getId();
957+
FileMetadata fm = dataFileServiceBean.findFileMetadataByDatasetVersionIdAndDataFileId(getDatasetVersionID, df.getId());
958+
if (fm == null) {
959+
return notFound(BundleUtil.getStringFromBundle("files.api.fileNotFound"));
960+
}
961+
boolean direct = df.isIdentifierRegistered();
962+
DataCitation citation = new DataCitation(fm, direct);
963+
return ok(citation.toString(true));
964+
} catch (WrappedResponse ex) {
965+
return ex.getResponse();
966+
}
967+
}
968+
934969
}

‎src/main/java/propertyFiles/Bundle.properties‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2633,7 +2633,9 @@ admin.api.deleteUser.success=Authenticated User {0} deleted.
26332633
#Files.java
26342634
files.api.metadata.update.duplicateFile=Filename already exists at {0}
26352635
files.api.no.draft=No draft available for this file
2636+
files.api.no.draftOrUnauth=Dataset version cannot be found or unauthorized.
26362637
files.api.only.tabular.supported=This operation is only available for tabular files.
2638+
files.api.fileNotFound=File could not be found.
26372639

26382640
#Datasets.java
26392641
datasets.api.updatePIDMetadata.failure.dataset.must.be.released=Modify Registration Metadata must be run on a published dataset.

‎src/test/java/edu/harvard/iq/dataverse/DataCitationTest.java‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,36 @@ public void testTitleWithQuotes() throws ParseException {
378378

379379
}
380380

381+
@Test
382+
public void testFileCitationToStringHtml() throws ParseException {
383+
DatasetVersion dsv = createATestDatasetVersion("Dataset Title", true);
384+
FileMetadata fileMetadata = new FileMetadata();
385+
fileMetadata.setLabel("foo.txt");
386+
fileMetadata.setDataFile(new DataFile());
387+
dsv.setVersionState(DatasetVersion.VersionState.RELEASED);
388+
fileMetadata.setDatasetVersion(dsv);
389+
dsv.setDataset(dsv.getDataset());
390+
DataCitation fileCitation = new DataCitation(fileMetadata, false);
391+
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));
392+
}
393+
394+
@Test
395+
public void testFileCitationToStringHtmlFilePid() throws ParseException {
396+
DatasetVersion dsv = createATestDatasetVersion("Dataset Title", true);
397+
FileMetadata fileMetadata = new FileMetadata();
398+
fileMetadata.setLabel("foo.txt");
399+
DataFile dataFile = new DataFile();
400+
dataFile.setProtocol("doi");
401+
dataFile.setAuthority("10.42");
402+
dataFile.setIdentifier("myFilePid");
403+
fileMetadata.setDataFile(dataFile);
404+
dsv.setVersionState(DatasetVersion.VersionState.RELEASED);
405+
fileMetadata.setDatasetVersion(dsv);
406+
dsv.setDataset(dsv.getDataset());
407+
DataCitation fileCitation = new DataCitation(fileMetadata, true);
408+
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));
409+
}
410+
381411
private DatasetVersion createATestDatasetVersion(String withTitle, boolean withAuthor) throws ParseException {
382412

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

0 commit comments

Comments
 (0)