diff --git a/submissions/forms.py b/submissions/forms.py index 210a0adcb339cca19e8a4aa0c37eeab21a3c6d89..e299e58cd601c661b44c9bcd3adb8148729ed554 100644 --- a/submissions/forms.py +++ b/submissions/forms.py @@ -1,4 +1,5 @@ from django import forms +from django.core.validators import RegexValidator #from django.contrib.auth.models import User, Group from .models import * @@ -20,10 +21,22 @@ class SubmissionSearchForm(forms.Form): ############################### class SubmissionIdentifierForm(forms.Form): - identifier = forms.CharField(widget=forms.TextInput( - {'label': 'arXiv identifier', - 'placeholder': 'new style (with version nr) ####.####(#)v#(#)', - 'cols': 20})) + identifier = forms.CharField( + widget=forms.TextInput( + {'label': 'arXiv identifier', + 'placeholder': 'new style (with version nr) ####.####(#)v#(#)', + 'cols': 20} + ), + validators=[ + RegexValidator( + regex="^[0-9]{4,}.[0-9]{4,5}v[0-9]{1,2}$", + message='The identifier you entered is improperly formatted ' + '(did you forget the version number?)', + code='invalid_identifier' + ), + ]) + + class SubmissionForm(forms.ModelForm): class Meta: diff --git a/submissions/models.py b/submissions/models.py index ae7648b5b84926795a557f723aa99621ec117cf0..68a3d1e867b2720f97fd109dee8cd1d8a18d681a 100644 --- a/submissions/models.py +++ b/submissions/models.py @@ -57,18 +57,6 @@ SUBMISSION_STATUS_PUBLICLY_UNLISTED = [ 'withdrawn', ] -# SUBMISSION_ACTION_REQUIRED = ( -# ('assign_EIC', 'Editor-in-charge to be assigned'), -# # ('Fellow_accepts_or_refuse_assignment', 'Fellow must accept or refuse assignment'), -# ('EIC_runs_refereeing_round', 'Editor-in-charge to run refereeing round (inviting referees)'), -# ('EIC_closes_refereeing_round', 'Editor-in-charge to close refereeing round'), -# ('EIC_invites_author_response', 'Editor-in-charge invites authors to complete their replies'), -# ('EIC_formulates_editorial_recommendation', -# 'Editor-in-charge to formulate editorial recommendation'), -# ('EC_ratification', 'Editorial College ratifies editorial recommendation'), -# ('Decision_to_authors', 'Editor-in-charge forwards decision to authors'), -# ) - SUBMISSION_TYPE = ( ('Letter', 'Letter (broad-interest breakthrough results)'), ('Article', 'Article (in-depth reports on specialized research)'), @@ -78,6 +66,7 @@ submission_type_dict = dict(SUBMISSION_TYPE) class Submission(models.Model): + # Main submission fields is_current = models.BooleanField(default=True) is_resubmission = models.BooleanField(default=False) submitted_by = models.ForeignKey(Contributor, on_delete=models.CASCADE) @@ -89,13 +78,12 @@ class Submission(models.Model): blank=True, null=True, default=None) discipline = models.CharField(max_length=20, choices=SCIPOST_DISCIPLINES, default='physics') domain = models.CharField(max_length=3, choices=SCIPOST_JOURNALS_DOMAINS) -# specialization = models.CharField(max_length=1, choices=SCIPOST_JOURNALS_SPECIALIZATIONS) subject_area = models.CharField(max_length=10, choices=SCIPOST_SUBJECT_AREAS, verbose_name='Primary subject area', default='Phys:QP') secondary_areas = ChoiceArrayField( models.CharField(max_length=10, choices=SCIPOST_SUBJECT_AREAS), blank=True, null=True) - status = models.CharField(max_length=30, choices=SUBMISSION_STATUS) # set by Editors + status = models.CharField(max_length=30, choices=SUBMISSION_STATUS) # set by Editors author_comments = models.TextField(blank=True, null=True) list_of_changes = models.TextField(blank=True, null=True) remarks_for_editors = models.TextField(blank=True, null=True) @@ -106,17 +94,22 @@ class Submission(models.Model): open_for_commenting = models.BooleanField(default=False) title = models.CharField(max_length=300) author_list = models.CharField(max_length=1000, verbose_name="author list") + # Authors which have been mapped to contributors: - authors = models.ManyToManyField (Contributor, blank=True, related_name='authors_sub') - authors_claims = models.ManyToManyField (Contributor, blank=True, - related_name='authors_sub_claims') - authors_false_claims = models.ManyToManyField (Contributor, blank=True, - related_name='authors_sub_false_claims') + authors = models.ManyToManyField(Contributor, blank=True, related_name='authors_sub') + authors_claims = models.ManyToManyField(Contributor, blank=True, + related_name='authors_sub_claims') + authors_false_claims = models.ManyToManyField(Contributor, blank=True, + related_name='authors_sub_false_claims') abstract = models.TextField() + + # Arxiv identifiers with/without version number arxiv_identifier_w_vn_nr = models.CharField(max_length=15, default='0000.00000v0') arxiv_identifier_wo_vn_nr = models.CharField(max_length=10, default='0000.00000') arxiv_vn_nr = models.PositiveSmallIntegerField(default=1) arxiv_link = models.URLField(verbose_name='arXiv link (including version nr)') + + # Metadata metadata = JSONField(default={}, blank=True, null=True) submission_date = models.DateField(verbose_name='submission date') latest_activity = models.DateTimeField(default=timezone.now) @@ -126,7 +119,7 @@ class Submission(models.Model): ('can_take_editorial_actions', 'Can take editorial actions'), ) - def __str__ (self): + def __str__(self): header = (self.arxiv_identifier_w_vn_nr + ', ' + self.title[:30] + ' by ' + self.author_list[:30]) if self.is_current: diff --git a/submissions/services.py b/submissions/services.py new file mode 100644 index 0000000000000000000000000000000000000000..bd16fb2ae70960507034ca3e46f8b6780a85fe87 --- /dev/null +++ b/submissions/services.py @@ -0,0 +1,86 @@ +# Module for making external api calls as needed in the submissions cycle +import feedparser + +from .models import * + + +class ArxivCaller(): + def lookup_article(identifier): + # Pre-checks + if same_version_exists(identifier) + return False, "This preprint version has already been submitted to SciPost." + + # Split the given identifier in an article identifier and version number + identifier_without_vn_nr = identifier.rpartition('v')[0] + arxiv_vn_nr = int(identifier.rpartition('v')[2]) + + resubmission = False + if previous_submission_undergoing_refereeing(identifier): + errormessage = '<p>There exists a preprint with this arXiv identifier ' + 'but an earlier version number, which is still undergoing ' + 'peer refereeing.</p>' + '<p>A resubmission can only be performed after request ' + 'from the Editor-in-charge. Please wait until the ' + 'closing of the previous refereeing round and ' + 'formulation of the Editorial Recommendation ' + 'before proceeding with a resubmission.</p>' + return False, errormessage + + # Arxiv query + queryurl = ('http://export.arxiv.org/api/query?id_list=%s' + % identifier) + arxiv_response = feedparser.parse(queryurl) + + # Check if response has at least one entry + if not 'entries' in arxiv_response + errormessage = 'Bad response from Arxiv.' + return False, errormessage + + # Check if preprint exists + if not preprint_exists(arxiv_response) + errormessage = 'A preprint associated to this identifier does not exist.' + return False, errormessage + + # Check via journal ref if already published + arxiv_journal_ref = published_journal_ref + if arxiv_journal_ref + errormessage = 'This paper has been published as ' + arxiv_journal_ref + + '. You cannot submit it to SciPost anymore.' + return False, resubmission + + # Check via DOI if already published + arxiv_doi = published_journal_ref + if arxiv_doi + errormessage = 'This paper has been published under DOI ' + arxiv_doi + + '. You cannot submit it to SciPost anymore.' + return False, errormessage + + return arxiv_response, "" + + + def same_version_exists(identifier): + return Submission.objects.filter(arxiv_identifier_w_vn_nr=identifier).exists() + + def previous_submission_undergoing_refereeing(identifier): + previous_submissions = Submission.objects.filter( + arxiv_identifier_wo_vn_nr=identifier).order_by('-arxiv_vn_nr') + + if previous_submissions: + return not previous_submissions[0].status == 'revision_requested' + else: + return False + + def preprint_exists(arxiv_response): + return 'title' in arxiv_response['entries'][0] + + def published_journal_ref(arxiv_response): + if 'arxiv_journal_ref' in arxiv_response['entries'][0] + return arxiv_response['entries'][0]['arxiv_journal_ref'] + else: + return False + + def published_DOI(arxiv_response): + if 'arxiv_doi' in arxiv_response['entries'][0] + return arxiv_response['entries'][0]['arxiv_doi'] + else: + return False diff --git a/submissions/tests.py b/submissions/tests/test_models.py similarity index 55% rename from submissions/tests.py rename to submissions/tests/test_models.py index 7ce503c2dd97ba78597f6ff6e4393132753573f6..2e9cb5f6ba351402af656aec1be5d9ac257bc5c0 100644 --- a/submissions/tests.py +++ b/submissions/tests/test_models.py @@ -1,3 +1 @@ from django.test import TestCase - -# Create your tests here. diff --git a/submissions/tests/test_views.py b/submissions/tests/test_views.py new file mode 100644 index 0000000000000000000000000000000000000000..f35d237bde6def6e0755612febe441d1d10d6d63 --- /dev/null +++ b/submissions/tests/test_views.py @@ -0,0 +1,27 @@ +from django.test import TestCase +from django.test import Client + +from submissions.views import * + + + +class PrefillUsingIdentifierTest(TestCase): + fixtures = ['permissions', 'groups', 'contributors'] + + def test_retrieving_existing_arxiv_paper(self): + client = Client() + client.login(username="Test", password="testpw") + + response = client.post('/submissions/prefill_using_identifier', + {'identifier': '1512.00030v1'}) + + self.assertEqual(response.status_code, 200) + + def test_still_200_ok_if_identifier_is_wrong(self): + client = Client() + client.login(username="Test", password="testpw") + + response = client.post('/submissions/prefill_using_identifier', + {'identifier': '1512.00030'}) + + self.assertEqual(response.status_code, 200) diff --git a/submissions/views.py b/submissions/views.py index d3e7718ce4628b68cd323ac4758a668fecf86c91..f7a4ac054a33887fecb3f5c76662125ea437275f 100644 --- a/submissions/views.py +++ b/submissions/views.py @@ -44,25 +44,15 @@ def prefill_using_identifier(request): if request.method == "POST": identifierform = SubmissionIdentifierForm(request.POST) if identifierform.is_valid(): - # we allow 1 or 2 digits for version - identifierpattern = re.compile("^[0-9]{4,}.[0-9]{4,5}v[0-9]{1,2}$") - errormessage = '' - if not identifierpattern.match(identifierform.cleaned_data['identifier']): - errormessage = ('The identifier you entered is improperly formatted ' - '(did you forget the version number?)') - elif (Submission.objects - #.filter(arxiv_link__contains=identifierform.cleaned_data['identifier']) - .filter(arxiv_identifier_w_vn_nr=identifierform.cleaned_data['identifier']) - .exists()): - errormessage = 'This preprint version has already been submitted to SciPost.' - if errormessage != '': + # Perform Arxiv query and check if results are OK for submission + metadata, errormessage = lookup_article(identifierform.cleaned_data['identifier']) + + if not metadata: form = SubmissionForm() return render(request, 'submissions/submit_manuscript.html', {'identifierform': identifierform, 'form': form, 'errormessage': errormessage}) - # Otherwise we query arXiv for the information: - identifier_without_vn_nr = identifierform.cleaned_data['identifier'].rpartition('v')[0] - arxiv_vn_nr = int(identifierform.cleaned_data['identifier'].rpartition('v')[2]) + is_resubmission = False resubmessage = '' previous_submissions = Submission.objects.filter( @@ -95,6 +85,7 @@ def prefill_using_identifier(request): errormessage = 'A preprint associated to this identifier does not exist.' except: pass + # If paper has been published, should comment on published version try: arxiv_journal_ref = arxivquery['entries'][0]['arxiv_journal_ref'] @@ -113,22 +104,7 @@ def prefill_using_identifier(request): context = {'identifierform': identifierform, 'form': form, 'errormessage': errormessage} return render(request, 'submissions/submit_manuscript.html', context) - # otherwise prefill the form: - # metadata = arxivquery - # title = arxivquery['entries'][0]['title'] - # authorlist = arxivquery['entries'][0]['authors'][0]['name'] - # for author in arxivquery['entries'][0]['authors'][1:]: - # authorlist += ', ' + author['name'] - # arxiv_link = arxivquery['entries'][0]['id'] - # abstract = arxivquery['entries'][0]['summary'] - # form = SubmissionForm( - # initial={'is_resubmission': is_resubmission, - # 'metadata': metadata, - # 'title': title, 'author_list': authorlist, - # 'arxiv_identifier_w_vn_nr': identifierform.cleaned_data['identifier'], - # 'arxiv_identifier_wo_vn_nr': identifier_without_vn_nr, - # 'arxiv_vn_nr': arxiv_vn_nr, - # 'arxiv_link': arxiv_link, 'abstract': abstract}) + metadata = arxivquery title = arxivquery['entries'][0]['title'] authorlist = arxivquery['entries'][0]['authors'][0]['name'] @@ -148,7 +124,6 @@ def prefill_using_identifier(request): initialdata['submission_type'] = previous_submissions[0].submission_type initialdata['discipline'] = previous_submissions[0].discipline initialdata['domain'] = previous_submissions[0].domain -# initialdata['specialization'] = previous_submissions[0].specialization initialdata['subject_area'] = previous_submissions[0].subject_area initialdata['secondary_areas'] = previous_submissions[0].secondary_areas initialdata['referees_suggested'] = previous_submissions[0].referees_suggested @@ -165,7 +140,9 @@ def prefill_using_identifier(request): 'errormessage': errormessage,} return render(request, 'submissions/submit_manuscript.html', context) else: - pass + form = SubmissionForm() + return render(request, 'submissions/submit_manuscript.html', + {'identifierform': identifierform, 'form': form}) return redirect(reverse('submissions:submit_manuscript')) @@ -185,7 +162,7 @@ def submit_manuscript(request): return render(request, 'submissions/submit_manuscript.html', {'identifierform': identifierform, 'form': form, 'errormessage': errormessage}) - submission = Submission ( + submission = Submission( is_current = True, is_resubmission = form.cleaned_data['is_resubmission'], submitted_by = submitted_by,