Skip to content
New issue

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

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

Already on GitHub? # to your account

Adds support for Issue and Journal DOIs to Crossref Integration #3128

Merged
merged 14 commits into from
Sep 14, 2022
Merged
Show file tree
Hide file tree
Changes from 6 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
3 changes: 3 additions & 0 deletions docs/source/manager/articlesissues/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ To create an issue select *Create Issue* in the top right and in the modal that

New issue form

- Issue DOI
- Issues can have a DOI, which will be registered with all of its articles in crossref. If you are using Janeway's autoregistration (recommended) or if you are not interested on registering DOIs for issues, you can leave this field blank.

Issue Articles
~~~~~~~~~~~~~~
You can manage the article associated with a given issue by selecting the *View* option, the data of the issue will be displayed along with a list of articles grouped by section.
Expand Down
17 changes: 17 additions & 0 deletions docs/source/manager/identifiers/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,23 @@ DOI Pattern

``{{ article.journal.code }}.{{ article.pk }}``

Title DOI
The DOI (not in URL format) registered for this journal. It is included on all deposits for this journal. It must be registered ahead of time(eg.: 10.001/my-journal"
It is mandatory for a journal to have a DOI registered **only** when an ISSN is not available for a journal, as Crossref requires at least one unique identifier for a journal.
However, even if your journal has an ISSN, Crossref still recommends registering a DOI for your journal. We recommend using your journal code as the DOI. For example, with the prefix
of 10.0001, you could set the journal DOI to 10.0001/journal-code.

Issue DOI Pattern
Janeway supports minting DOIs for journal issues automatically. With this setting, you can define the pattern used to generate the issue-level DOI that will be used for registration.
With the default pattern, an issue with ID 1 will have a generated DOI of XX.XXXX/journal-code.issue.1
A collection with an ID of 2 would have a generated DOI of XX.XXXX/journal-code.collection.2

Auto-register issue-level DOIs
When enabled, issues will have a doi assigned and registered as soon as the first article in the issue is scheduled for publication. If an issue DOI has not been entered manually,
Janeway will use the pattern defined in the setting above to generate one automatically.



Crosscheck Settings
-------------------
Janeway also has support for Crosscheck (also called Similarity Check), which is provided by iThenticate. You can # for an account via Crossref and this will allow you to send submitted manuscripts for originality checking.
Expand Down
2 changes: 1 addition & 1 deletion src/core/logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ def get_settings_to_edit(display_group, journal):
xref_settings = [
'use_crossref', 'crossref_test', 'crossref_username', 'crossref_password', 'crossref_email',
'crossref_name', 'crossref_prefix', 'crossref_registrant', 'doi_display_prefix', 'doi_display_suffix',
'doi_pattern', 'doi_manager_action_maximum_size',
'doi_pattern', 'doi_manager_action_maximum_size', 'title_doi', 'issue_doi_pattern', 'register_issue_dois'
]

settings = process_setting_list(xref_settings, 'Identifiers', journal)
Expand Down
6 changes: 5 additions & 1 deletion src/events/logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,11 @@ class Events:
# raised when proofing is complete
ON_PROOFING_COMPLETE = 'on_proofing_complete'

#kwargs: request, article
# kwargs: article, issue, user
# raised when an article is assigned to an issue
ON_ARTICLE_ASSIGNED_TO_ISSUE = 'on_article_assigned_to_issue'

# kwargs: request, article
# raised when an article is marked as published
ON_ARTICLE_PUBLISHED = 'on_article_published'

Expand Down
6 changes: 6 additions & 0 deletions src/events/registration.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from utils import transactional_emails, workflow_tasks
from events import logic as event_logic
from journal import logic as journal_logic
from identifiers import logic as id_logic

# wire up event notifications

Expand Down Expand Up @@ -181,3 +182,8 @@
# N.B. this is critical to the operation of the task framework. It automatically tears down tasks that have registered
# for event listeners
event_logic.Events.register_for_event('destroy_tasks', core_models.Task.destroyer)

event_logic.Events.register_for_event(
event_logic.Events.ON_ARTICLE_ASSIGNED_TO_ISSUE,
id_logic.on_article_assign_to_issue,
)
25 changes: 25 additions & 0 deletions src/identifiers/logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -625,3 +625,28 @@ def register_preprint_doi(request, crossref_enabled, identifier):
util_models.LogEntry.add_entry('Submission', "Deposited {0}. Status: {1}".format(token, status), 'Info',
target=identifier.article)
logger.info("Status of {} in {}: {}".format(token, identifier.identifier, status))


def generate_issue_doi_from_logic(issue):
doi_prefix = setting_handler.get_setting(
'Identifiers', 'crossref_prefix', issue.journal).value
doi_suffix = render_template.get_requestless_content(
{'issue': issue},
issue.journal,
'issue_doi_pattern',
group_name='Identifiers')
return '{0}/{1}'.format(doi_prefix, doi_suffix)


def auto_assign_issue_doi(issue):
auto_assign_enabled = setting_handler.get_setting(
'Identifiers', 'use_crossref', issue.journal,
default=True,
).processed_value
if auto_assign_enabled and not issue.doi:
issue.doi = generate_issue_doi_from_logic(issue)
issue.save()


def on_article_assign_to_issue(article, issue, user):
auto_assign_issue_doi(issue)
48 changes: 44 additions & 4 deletions src/identifiers/tests/test_logic.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
import datetime
from io import BytesIO, StringIO
import json
import mock
import pytz
import os


from django.test import TestCase
from django.conf import settings
from django.utils import timezone

from identifiers import logic, models
from core.models import SettingGroup
from journal import logic as journal_logic
from submission import models as submission_models
from utils.testing import helpers
from utils.setting_handler import save_setting
from utils.shared import clear_cache
from lxml import etree
from bs4 import BeautifulSoup
import requests
from io import BytesIO, StringIO
import os
import json

class TestLogic(TestCase):

@classmethod
Expand Down Expand Up @@ -353,6 +355,44 @@ def test_conference_deposit_xml_document_has_basically_correct_components(self):
# There should be two conference papers (articles)
self.assertEqual(2, len(soup.find_all('conference_paper')))

def test_issue_doi_deposited_correctly(self):
template = 'common/identifiers/crossref_doi_batch.xml'
issue = self.article_one.issue
issue.doi = issue_doi = "10.0001/issue"
issue.save()
identifier = self.article_one.get_doi_object
clear_cache()

template_context = logic.create_crossref_doi_batch_context(
self.article_one.journal,
{identifier},
)
deposit = logic.render_to_string(template, template_context)

soup = BeautifulSoup(deposit, 'lxml')
# There should be one doi_batch
issue_soup = soup.find('journal_issue')
found = False
if issue_soup:
issue_doi_soup = issue_soup.find("doi_data")
if issue_doi_soup:
self.assertEqual(issue_doi_soup.find("doi").string, issue_doi)
self.assertEqual(issue_doi_soup.find("resource").string, issue.url)
found = True
if not found:
raise AssertionError("No Issue DOI found on article deposit")

def test_issue_doi_auto_assigned(self):
issue = helpers.create_issue(self.journal_one, vol=99, number=99)
self.request.POST = {"assign_issue": issue.pk}
mock_messages = mock.patch('journal.logic.messages').start()
mock_messages.messages = mock.MagicMock()
save_setting('Identifiers', 'register_issue_dois', self.journal_one, '')
from events import registration
journal_logic.handle_assign_issue(self.request, self.article_one, [issue])
issue.refresh_from_db()
Copy link
Member

Choose a reason for hiding this comment

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

Wow that is a useful method.

self.assertTrue(issue.doi)

def test_check_crossref_settings(self):

# Missing settings
Expand Down
2 changes: 1 addition & 1 deletion src/journal/issue_forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class Meta:
fields = (
'issue_title', 'volume', 'issue', 'date', 'issue_description',
'short_description', 'cover_image', 'large_image', 'issue_type',
'code',
'code', 'doi',
)


Expand Down
13 changes: 10 additions & 3 deletions src/journal/logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,12 +204,19 @@ def handle_new_issue(request):

def handle_assign_issue(request, article, issues):
try:
issue_to_assign = journal_models.Issue.objects.get(pk=request.POST.get('assign_issue', None))

issue_to_assign = journal_models.Issue.objects.get(
pk=request.POST.get('assign_issue', None))
if issue_to_as# issues:
issue_to_assign.articles.add(article)
issue_to_assign.save()
messages.add_message(request, messages.SUCCESS, 'Article assigned to issue.')
messages.add_message(
request, messages.SUCCESS, 'Article assigned to issue.')
event_logic.Events.raise_event(
event_logic.Events.ON_ARTICLE_ASSIGNED_TO_ISSUE,
article=article,
issue=issue_to_assign,
user=request.user,
)
else:

messages.add_message(request, messages.WARNING, 'Issue not in this journals issue list.')
Expand Down
20 changes: 20 additions & 0 deletions src/journal/migrations/0053_issue_doi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.29 on 2022-09-08 14:28
from __future__ import unicode_literals

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('journal', '0052_issue_display_title'),
]

operations = [
migrations.AddField(
model_name='issue',
name='doi',
field=models.CharField(blank=True, help_text='The DOI (not URL) to be registered for the issue when registering articles part this issue. If you have enabled issue auto-registration in your settings, this field should be ignored', max_length=255, null=True, verbose_name='DOI'),
),
]
19 changes: 19 additions & 0 deletions src/journal/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,15 @@ def publisher(self, value):
def issn(self):
return setting_handler.get_setting('general', 'journal_issn', self, default=True).value

@property
@cached_property
def doi(self):
return setting_handler.get_setting('Identifiers', 'title_doi', self, default=True).value or None

@doi.setter
def doi(self, value):
setting_handler.save_setting('Identifiers', 'title_doi', self, value)

@property
@cache(120)
def print_issn(self):
Expand Down Expand Up @@ -553,6 +562,16 @@ class Issue(AbstractLastModifiedModel):
" url for this issue. e.g: 'winter-special-issue'."
),
)
doi = models.CharField(
max_length=255,
blank=True,
null=True,
verbose_name='DOI',
help_text='The DOI (not URL) to be registered for the issue when registering'
' articles part this issue. If you have enabled issue auto-'
'registration in your settings, this field should be ignored',
)


@property
def hero_image_url(self):
Expand Down
4 changes: 3 additions & 1 deletion src/templates/admin/elements/issue/issue_modal.html
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,11 @@ <h4>{% if issue %}Edit Issue{% else %}Create Issue{% endif %}</h4>
<div class="large-12 columns">
{{ form.issue_description|foundation }}
</div>
<div class="large-12 columns">
<div class="large-6 columns">
{{ form.issue_type|foundation }}
</div>
<div class="large-6 columns">
{{ form.doi|foundation }}
<div class="large-12 columns">
<button name="save_issue" type="submit" class="button success">{% if issue %}Edit{% else %}Add{% endif %} Issue</button>
</div>
Expand Down
12 changes: 12 additions & 0 deletions src/templates/common/identifiers/crossref_journal_issue.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@
{% if crossref_issue.journal.journal_issn and crossref_issue.journal.journal_issn != '0000-0000' %}
<issn media_type="electronic">{{ crossref_issue.journal.journal_issn }}</issn>
{% endif %}
{% if crossref_issue.journal.doi %}
<doi_data>
<doi>{{ crossref_issue.journal.doi }}</doi>
<resource>{{ crosref_issue.journal.url }}</resource>
</doi_data>
{% endif %}
</journal_metadata>

{% if crossref_issue.issue and crossref_issue.issue.date %}
Expand All @@ -26,6 +32,12 @@
<volume>{{ crossref_issue.issue.volume }}</volume>
</journal_volume>
<issue>{{ crossref_issue.issue.issue }}</issue>
{% if crossref_issue.issue.doi %}
<doi_data>
<doi>{{ crossref_issue.issue.doi }}</doi>
<resource>{{ crossref_issue.issue.url }}</resource>
</doi_data>
{%endif%}
</journal_issue>
{% endif %}

Expand Down
47 changes: 46 additions & 1 deletion src/utils/install/journal_defaults.json
Original file line number Diff line number Diff line change
Expand Up @@ -1477,7 +1477,7 @@
"description": "You can set your DOI pattern. The default is ``{{ article.journal.code }}.{{ article.pk }}``",
"is_translatable": false,
"name": "doi_pattern",
"pretty_name": "DOI Pattern",
"pretty_name": "Article DOI Pattern",
"type": "char"
},
"value": {
Expand Down Expand Up @@ -3224,6 +3224,51 @@
"default": "656"
}
},
{
"group": {
"name": "Identifiers"
},
"setting": {
"description": "The DOI (not URL) registered for this journal. It is included on all deposits for this journal. It must be registered ahead of time(eg.: 10.001/my-journal",
"is_translatable": false,
"name": "title_doi",
"pretty_name": "Title DOI",
"type": "char"
},
"value": {
"default": ""
}
},
{
"group": {
"name": "Identifiers"
},
"setting": {
"description": "The suffix pattern from which Issue DOIs are auto-generated. eg: 10.0001/myjournal.issue.1",
"is_translatable": false,
"name": "issue_doi_pattern",
"pretty_name": "Issue DOI Pattern",
"type": "char"
},
"value": {
"default": "{{ issue.journal.code }}.{{ issue.issue_type.code }}.{{ issue.id }}"
}
},
{
"group": {
"name": "Identifiers"
},
"setting": {
"description": "Automatically register issue DOIs on article publication, based on the issue DOI pattern",
"is_translatable": false,
"name": "register_issue_dois",
"pretty_name": "Auto-register issue-level DOIs",
"type": "boolean"
},
"value": {
"default": ""
}
},
{
"group": {
"name": "general"
Expand Down