Skip to content

Commit 4cb3d52

Browse files
Merge branch 'modifyCrossrefDeposit-340-902' into 'main'
Adicionar relação de conjunto de dados na exportação Crossref - 3.4.0 See merge request softwares-pkp/plugins_ojs/dataverse!218
2 parents 141ed73 + c382fc6 commit 4cb3d52

15 files changed

Lines changed: 605 additions & 9 deletions

DataversePlugin.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@ private function loadDispatcherClasses(): void
6161
'DatasetInformationDispatcher',
6262
'DatasetTabDispatcher',
6363
'DatasetReviewDispatcher',
64-
'DataverseEventsDispatcher'
64+
'DataverseEventsDispatcher',
65+
'CrossrefDispatcher'
6566
];
6667

6768
foreach ($dispatcherClasses as $dispatcherClass) {

api/v1/datasets/DatasetHandler.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ public function get($slimRequest, $response, $args)
120120
$dataset = $dataverseClient->getDatasetActions()->get($study->getPersistentId());
121121
} catch (DataverseException $e) {
122122
if ($e->getCode() === 404) {
123-
DAORegistry::getDAO('DataverseStudyDAO')->deleteStudy($study);
123+
Repo::dataverseStudy()->delete($study);
124124
}
125125

126126
$error = $e->getMessage();
@@ -251,7 +251,7 @@ public function addDataset($slimRequest, $response, $args)
251251
$depositInfo = $datasetService->deposit($submission, $dataset);
252252
if ($depositInfo['status'] != 'Success') {
253253
return $response->withStatus(403)->withJsonError(
254-
$depositInfo['message'].'.author',
254+
$depositInfo['message'] . '.author',
255255
$depositInfo['messageParams']
256256
);
257257
}

classes/CrossrefXmlEditor.php

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
<?php
2+
3+
namespace APP\plugins\generic\dataverse\classes;
4+
5+
use DOMDocument;
6+
use DOMElement;
7+
use Illuminate\Support\Facades\DB;
8+
use APP\plugins\generic\dataverse\classes\facades\Repo;
9+
use APP\plugins\generic\dataverse\dataverseAPI\actions\DatasetActions;
10+
use APP\plugins\generic\dataverse\classes\exception\DataverseException;
11+
12+
class CrossrefXmlEditor
13+
{
14+
private const RELATIONS_NAMESPACE = 'http://www.crossref.org/relations.xsd';
15+
16+
private DatasetActions $datasetActions;
17+
18+
public function __construct(?DatasetActions $actions = null)
19+
{
20+
$this->datasetActions = $actions ?? (new DatasetActions());
21+
}
22+
23+
public function addDatasetRelationToDepositXml(DOMDocument $depositXml): DOMDocument
24+
{
25+
$submissionNodes = $depositXml->getElementsByTagName('journal_article');
26+
if ($submissionNodes->count() == 0) {
27+
$submissionNodes = $depositXml->getElementsByTagName('posted_content');
28+
}
29+
30+
foreach ($submissionNodes as $submissionNode) {
31+
$doiDataNode = $submissionNode->getElementsByTagName('doi_data')->item(0);
32+
$doiNode = $doiDataNode->getElementsByTagName('doi')->item(0);
33+
$doi = $doiNode->nodeValue;
34+
35+
$submissionId = DB::table('submissions as s')
36+
->leftJoin('publications as p', 'p.submission_id', '=', 's.submission_id')
37+
->leftJoin('dois as d', 'd.doi_id', '=', 'p.doi_id')
38+
->where('d.doi', '=', $doi)
39+
->value('s.submission_id');
40+
41+
if (!$submissionId) {
42+
continue;
43+
}
44+
45+
$study = Repo::dataverseStudy()->getBySubmissionId($submissionId);
46+
if (!$study) {
47+
continue;
48+
}
49+
50+
try {
51+
$dataset = $this->datasetActions->get($study->getPersistentId());
52+
} catch (DataverseException $e) {
53+
$error = $e->getMessage();
54+
error_log('Dataverse API error on Crossref export: ' . $error);
55+
56+
return $depositXml;
57+
}
58+
59+
if ($dataset->isPublished()) {
60+
$this->addDatasetRelationToWorkNode($submissionNode, $study->getPersistentId());
61+
}
62+
}
63+
64+
return $depositXml;
65+
}
66+
67+
public function addDatasetRelationToWorkNode(DOMElement $workNode, string $persistentId): DOMElement
68+
{
69+
$doc = $workNode->ownerDocument;
70+
71+
$relatedItemNode = $doc->createElementNS(self::RELATIONS_NAMESPACE, 'related_item');
72+
73+
$descriptionNode = $doc->createElementNS(self::RELATIONS_NAMESPACE, 'description');
74+
$descriptionNode->appendChild($doc->createTextNode('Dataset deposited in Dataverse repository.'));
75+
76+
$doi = preg_replace('/^doi:/i', '', $persistentId);
77+
78+
$interWorkRelationNode = $doc->createElementNS(self::RELATIONS_NAMESPACE, 'inter_work_relation');
79+
$interWorkRelationNode->setAttribute('relationship-type', 'isSupplementedBy');
80+
$interWorkRelationNode->setAttribute('identifier-type', 'doi');
81+
$interWorkRelationNode->appendChild($doc->createTextNode($doi));
82+
83+
$relatedItemNode->appendChild($descriptionNode);
84+
$relatedItemNode->appendChild($interWorkRelationNode);
85+
86+
$existingProgramNodes = $workNode->getElementsByTagNameNS(self::RELATIONS_NAMESPACE, 'program');
87+
if ($existingProgramNodes->count() > 0) {
88+
$existingProgramNodes->item(0)->appendChild($relatedItemNode);
89+
} else {
90+
$programNode = $doc->createElementNS(self::RELATIONS_NAMESPACE, 'program');
91+
$programNode->appendChild($relatedItemNode);
92+
$doiDataNode = $workNode->getElementsByTagName('doi_data')->item(0);
93+
$workNode->insertBefore($programNode, $doiDataNode);
94+
}
95+
96+
return $workNode;
97+
}
98+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
namespace APP\plugins\generic\dataverse\classes\dispatchers;
4+
5+
use PKP\plugins\Hook;
6+
use APP\plugins\generic\dataverse\classes\dispatchers\DataverseDispatcher;
7+
use APP\plugins\generic\dataverse\classes\CrossrefXmlEditor;
8+
9+
class CrossrefDispatcher extends DataverseDispatcher
10+
{
11+
protected function registerHooks(): void
12+
{
13+
Hook::add('articlecrossrefxmlfilter::execute', [$this, 'addDatasetRelationToCrossrefExport']);
14+
Hook::add('preprintcrossrefxmlfilter::execute', [$this, 'addDatasetRelationToCrossrefExport']);
15+
}
16+
17+
public function addDatasetRelationToCrossrefExport(string $hookName, array $params)
18+
{
19+
$preliminaryOutput = &$params[0];
20+
21+
$crossrefXmlEditor = new CrossrefXmlEditor();
22+
$preliminaryOutput = $crossrefXmlEditor->addDatasetRelationToDepositXml($preliminaryOutput);
23+
24+
return Hook::CONTINUE;
25+
}
26+
}

dataverseAPI/actions/DataverseActions.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ abstract class DataverseActions
2222
protected const ONE_DAY_SECONDS = 24 * 60 * 60;
2323

2424
public function __construct(
25-
DataverseConfiguration $configuration = null,
26-
\GuzzleHttp\Client $client = null
25+
?DataverseConfiguration $configuration = null,
26+
?\GuzzleHttp\Client $client = null
2727
) {
2828
if (is_null($configuration)) {
2929
$this->contextId = Application::get()->getRequest()->getContext()->getId();
@@ -48,7 +48,7 @@ public function createNativeAPIURI(string ...$pathParams): string
4848

4949
public function createSWORDAPIURI(string ...$pathParams): string
5050
{
51-
return $this->serverURL . '/dvn/api/data-deposit/v1.1/swordv2/' .join('/', $pathParams);
51+
return $this->serverURL . '/dvn/api/data-deposit/v1.1/swordv2/' . join('/', $pathParams);
5252
}
5353

5454
public function getCurrentDataverseURI(): string

tests/CrossrefXmlEditorTest.php

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
<?php
2+
3+
use PKP\doi\Doi;
4+
use PKP\tests\DatabaseTestCase;
5+
use APP\core\Application;
6+
use APP\publication\Publication;
7+
use APP\submission\Submission;
8+
use APP\plugins\generic\dataverse\classes\CrossrefXmlEditor;
9+
use APP\plugins\generic\dataverse\classes\dataverseStudy\DataverseStudy;
10+
use APP\plugins\generic\dataverse\classes\entities\Dataset;
11+
use APP\plugins\generic\dataverse\classes\facades\Repo;
12+
use APP\plugins\generic\dataverse\dataverseAPI\actions\DatasetActions;
13+
use APP\plugins\generic\dataverse\classes\dispatchers\DataStatementDispatcher;
14+
use APP\plugins\generic\dataverse\DataversePlugin;
15+
16+
class CrossrefXmlEditorTest extends DatabaseTestCase
17+
{
18+
private CrossrefXmlEditor $xmlEditor;
19+
private DOMDocument $doc;
20+
private $contextId = 1;
21+
private ?int $submissionId = null;
22+
private ?int $doiId = null;
23+
private string $doi = '10.1234/PublicKnowledge.17';
24+
private ?DataverseStudy $study = null;
25+
private ?Dataset $dataset = null;
26+
private string $persistentId = 'doi:10.5072/FK2/ABCDEF';
27+
28+
public function setUp(): void
29+
{
30+
parent::setUp();
31+
$plugin = new DataversePlugin();
32+
$dispatcher = new DataStatementDispatcher($plugin);
33+
34+
$this->doc = $this->createTestXml();
35+
$this->submissionId = $this->createTestSubmission();
36+
$this->study = $this->createDataverseStudy();
37+
$this->dataset = $this->createTestDataset();
38+
$this->xmlEditor = $this->createXmlEditor();
39+
}
40+
41+
public function tearDown(): void
42+
{
43+
parent::tearDown();
44+
45+
$submission = Repo::submission()->get($this->submissionId);
46+
if ($submission) {
47+
Repo::submission()->delete($submission);
48+
}
49+
50+
$doi = Repo::doi()->get($this->doiId);
51+
if ($doi) {
52+
Repo::doi()->delete($doi);
53+
}
54+
}
55+
56+
private function createTestSubmission(): int
57+
{
58+
$context = Application::getContextDAO()->getById($this->contextId);
59+
60+
$submission = new Submission();
61+
$submission->setData('contextId', $this->contextId);
62+
$publication = new Publication();
63+
64+
$submissionId = Repo::submission()->add($submission, $publication, $context);
65+
$submission = Repo::submission()->get($submissionId);
66+
67+
$doi = Repo::doi()->newDataObject([
68+
'contextId' => $this->contextId,
69+
'doi' => $this->doi,
70+
'status' => Doi::STATUS_REGISTERED,
71+
]);
72+
$this->doiId = Repo::doi()->add($doi);
73+
74+
$publication = $submission->getCurrentPublication();
75+
$publication->setData('doiId', $this->doiId);
76+
Repo::publication()->dao->update($publication);
77+
78+
return $submissionId;
79+
}
80+
81+
private function createDataverseStudy(): DataverseStudy
82+
{
83+
$study = new DataverseStudy();
84+
$study->setSubmissionId($this->submissionId);
85+
$study->setEditUri('https://demo.dataverse.org/dvn/api/data-deposit/v1.1/swordv2/edit/study/' . $this->persistentId);
86+
$study->setEditMediaUri('https://demo.dataverse.org/dvn/api/data-deposit/v1.1/swordv2/edit-media/study/' . $this->persistentId);
87+
$study->setStatementUri('https://demo.dataverse.org/dvn/api/data-deposit/v1.1/swordv2/statement/study/' . $this->persistentId);
88+
$study->setPersistentUri('https://doi.org/10.5072/FK2/ABCDEF');
89+
$study->setPersistentId($this->persistentId);
90+
91+
$id = Repo::dataverseStudy()->add($study);
92+
$study->setId($id);
93+
94+
return $study;
95+
}
96+
97+
private function createTestXml()
98+
{
99+
$xml = new DOMDocument('1.0', 'UTF-8');
100+
$xml->appendChild($xml->createElement('work'));
101+
102+
return $xml;
103+
}
104+
105+
private function createTestDataset(): Dataset
106+
{
107+
$dataset = new Dataset();
108+
$dataset->setPersistentId($this->persistentId);
109+
$dataset->setVersionState(Dataset::VERSION_STATE_RELEASED);
110+
111+
return $dataset;
112+
}
113+
114+
private function createXmlEditor(): CrossrefXmlEditor
115+
{
116+
$mockDatasetActions = $this->createMock(DatasetActions::class);
117+
$mockDatasetActions->method('get')->willReturn($this->dataset);
118+
119+
return new CrossrefXmlEditor($mockDatasetActions);
120+
}
121+
122+
public function testAddsDatasetRelationToWorkNode(): void
123+
{
124+
$workNode = $this->doc->documentElement;
125+
126+
$result = $this->xmlEditor->addDatasetRelationToWorkNode($workNode, $this->persistentId);
127+
128+
$programNode = $result->getElementsByTagNameNS('http://www.crossref.org/relations.xsd', 'program')->item(0);
129+
$resultXml = $result->ownerDocument->saveXML($programNode);
130+
131+
$expectedXml = file_get_contents(__DIR__ . '/fixtures/crossref/expected/dataset_relation.xml');
132+
133+
$this->assertXmlStringEqualsXmlString($expectedXml, $resultXml);
134+
}
135+
136+
public function testAddsDatasetRelationToDepositXml(): void
137+
{
138+
$this->assertAddingOfRelationToXmlMatchesExpected('preprint_deposit.xml');
139+
$this->assertAddingOfRelationToXmlMatchesExpected('article_deposit.xml');
140+
}
141+
142+
public function testAddsRelationToXmlAlreadyWithRelation(): void
143+
{
144+
$this->assertAddingOfRelationToXmlMatchesExpected('preprint_deposit_with_relation.xml');
145+
}
146+
147+
private function assertAddingOfRelationToXmlMatchesExpected(string $fixture): void
148+
{
149+
$depositXml = new DOMDocument();
150+
$depositXml->load(__DIR__ . '/fixtures/crossref/' . $fixture);
151+
152+
$result = $this->xmlEditor->addDatasetRelationToDepositXml($depositXml);
153+
154+
$expectedXml = file_get_contents(__DIR__ . '/fixtures/crossref/expected/' . $fixture);
155+
156+
$this->assertXmlStringEqualsXmlString($expectedXml, $result->saveXML());
157+
}
158+
}

0 commit comments

Comments
 (0)