diff --git a/submissions/forms.py b/submissions/forms.py index 73f7f6fe50718aec0e89944e9908b6edfbb26385..064196bd5764aaeefd18b791b0ae72ea1400d62f 100644 --- a/submissions/forms.py +++ b/submissions/forms.py @@ -2,7 +2,7 @@ from django import forms from django.core.validators import RegexValidator from .constants import ASSIGNMENT_BOOL, ASSIGNMENT_REFUSAL_REASONS,\ - REPORT_ACTION_CHOICES, REPORT_REFUSAL_CHOICES, SUBMISSION_CYCLES + REPORT_ACTION_CHOICES, REPORT_REFUSAL_CHOICES from .models import Submission, RefereeInvitation, Report, EICRecommendation from scipost.constants import SCIPOST_SUBJECT_AREAS @@ -77,6 +77,30 @@ class SubmissionForm(forms.ModelForm): 'placeholder': 'Optional: names of referees whose reports should be treated with caution (+ short reason)', 'rows': 3}) + def check_user_may_submit(self, current_user): + """ + Important check! + + The submitting user must be an author of the submission. + Also possibly may be extended to check permissions and give ultimate submission + power to certain user groups. + """ + return current_user.last_name in self.cleaned_data['author_list'] + + def update_submission_data(self): + """ + Some fields should not be accessible in the HTML form by the user and should be + inserted by for example an extra call to Arxiv into the Submission instance, right + *after* the form is submitted. + + Example fields: + - is_resubmission + - arxiv_link + - arxiv_identifier_w_vn_nr + - metadata (!) + """ + raise NotImplementedError + ###################### # Editorial workflow # diff --git a/submissions/migrations/0043_auto_20170512_0836.py b/submissions/migrations/0043_auto_20170512_0836.py new file mode 100644 index 0000000000000000000000000000000000000000..badd52ad60321186d16f52a6cb4ba9922e5cbb20 --- /dev/null +++ b/submissions/migrations/0043_auto_20170512_0836.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.3 on 2017-05-12 06:36 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('submissions', '0042_auto_20170511_2321'), + ] + + operations = [ + migrations.AlterField( + model_name='submission', + name='latest_activity', + field=models.DateTimeField(auto_now=True), + ), + ] diff --git a/submissions/models.py b/submissions/models.py index 787754332f0ecdd4c01c8b9685b499e84333b448..c2364962e70eeff53d55c6d22691d6e965131bb1 100644 --- a/submissions/models.py +++ b/submissions/models.py @@ -79,7 +79,7 @@ class Submission(ArxivCallable, models.Model): # Metadata metadata = JSONField(default={}, blank=True, null=True) submission_date = models.DateField(verbose_name='submission date', default=datetime.date.today) - latest_activity = models.DateTimeField(default=timezone.now) + latest_activity = models.DateTimeField(auto_now=True) objects = SubmissionManager() diff --git a/submissions/templates/submissions/new_submission.html b/submissions/templates/submissions/new_submission.html index ba80d4cb1826f76836ba35354c8836d075c9e9dd..e0dbc27ad7279b3d3b2ead3ff73a8489c311fc5e 100644 --- a/submissions/templates/submissions/new_submission.html +++ b/submissions/templates/submissions/new_submission.html @@ -55,7 +55,7 @@ $(document).ready(function(){ <div class="col-12"> {% if perms.scipost.can_submit_manuscript %} - {% if form.arxiv_link.value %} + {% if form.arxiv_link %} <form id="full_submission_form" action="{% url 'submissions:submit_manuscript' %}" method="post"> {% csrf_token %} {{form|bootstrap}} diff --git a/submissions/test_views.py b/submissions/test_views.py index 8b797db80ccb9ddac353a6174482359509d428c3..ee3a64993597aa1ef1480541aaa7c5680f856f2c 100644 --- a/submissions/test_views.py +++ b/submissions/test_views.py @@ -1,59 +1,199 @@ +import json + +from django.core.urlresolvers import reverse from django.test import TestCase from django.test import Client -from submissions.views import * -import django.core.urlresolvers +from common.helpers.test import add_groups_and_permissions +from scipost.factories import ContributorFactory +# from scipost.models import Contributor + +from .constants import STATUS_UNASSIGNED from .factories import EICassignedSubmissionFactory +from .forms import SubmissionForm, SubmissionIdentifierForm +from .models import Submission + +# This is content of a real arxiv submission. As long as it isn't published it should +# be possible to run tests using this submission. +TEST_SUBMISSION = { + 'is_resubmission': False, + 'title': ('General solution of 2D and 3D superconducting quasiclassical' + ' systems:\n coalescing vortices and nanodisk geometries'), + 'author_list': 'Morten Amundsen, Jacob Linder', + 'arxiv_identifier_w_vn_nr': '1512.00030v1', + 'arxiv_identifier_wo_vn_nr': '1512.00030', + 'arxiv_vn_nr': 1, + 'arxiv_link': 'http://arxiv.org/abs/1512.00030v1', + 'abstract': ('In quasiclassical Keldysh theory, the Green function matrix $\\check{g}$' + ' is\nused to compute a variety of physical quantities in mesoscopic syst' + 'ems.\nHowever, solving the set of non-linear differential equations that' + ' provide\n$\\check{g}$ becomes a challenging task when going to higher s' + 'patial dimensions\nthan one. Such an extension is crucial in order to de' + 'scribe physical phenomena\nlike charge/spin Hall effects and topological' + ' excitations like vortices and\nskyrmions, none of which can be captured' + ' in one-dimensional models. We here\npresent a numerical finite element ' + 'method which solves the 2D and 3D\nquasiclassical Usadel equation, witho' + 'ut any linearisation, relevant for the\ndiffusive regime. We show the ap' + 'plication of this on two model systems with\nnon-trivial geometries: (i)' + ' a bottlenecked Josephson junction with external\nflux and (ii) a nanodi' + 'sk ferromagnet deposited on top of a superconductor. We\ndemonstrate tha' + 't it is possible to control externally not only the geometrical\narray i' + 'n which superconducting vortices arrange themselves, but also to cause\n' + 'coalescence and thus tune the number of vortices. The finite element met' + 'hod\npresented herein could pave the way for gaining insight in physical' + ' phenomena\nwhich so far have remained largely unexplored due to the com' + 'plexity of solving\nthe full quasiclassical equations in higher dimensio' + 'ns.') +} + + +class BaseContributorTestCase(TestCase): + def setUp(self): + add_groups_and_permissions() + ContributorFactory.create_batch(5) + ContributorFactory.create( + user__last_name='Linder', # To pass the author check in create submissions view + user__username='Test', + user__password='testpw' + ) -class PrefillUsingIdentifierTest(TestCase): - fixtures = ['permissions', 'groups', 'contributors'] +class PrefillUsingIdentifierTest(BaseContributorTestCase): + def setUp(self): + super().setUp() + self.client = Client() + self.url = reverse('submissions:prefill_using_identifier') + self.assertTrue(self.client.login(username="Test", password="testpw")) - def test_retrieving_existing_arxiv_paper(self): + def test_basic_responses(self): + # Test anonymous client is rejected client = Client() - client.login(username="Test", password="testpw") - - response = client.post(reverse('submissions:prefill_using_identifier'), - {'identifier': '1512.00030v1'}) + response = client.get(self.url) + self.assertEqual(response.status_code, 403) + response = client.post(self.url, + {'identifier': TEST_SUBMISSION['arxiv_identifier_w_vn_nr']}) + self.assertEqual(response.status_code, 403) + + # Registered Contributor should get 200 + response = self.client.get(self.url) + self.assertEqual(response.status_code, 200) + def test_retrieving_existing_arxiv_paper(self): + '''Test view with a valid post request.''' + response = self.client.post(self.url, + {'identifier': + TEST_SUBMISSION['arxiv_identifier_w_vn_nr']}) self.assertEqual(response.status_code, 200) + self.assertIsInstance(response.context['form'], SubmissionForm) + self.assertIsInstance(response.context['identifierform'], SubmissionIdentifierForm) + self.assertTrue(response.context['identifierform'].is_valid()) + + # Explicitly compare fields instead of assertDictEqual as metadata field may be outdated + self.assertEqual(TEST_SUBMISSION['is_resubmission'], + response.context['form'].initial['is_resubmission']) + self.assertEqual(TEST_SUBMISSION['title'], response.context['form'].initial['title']) + self.assertEqual(TEST_SUBMISSION['author_list'], + response.context['form'].initial['author_list']) + self.assertEqual(TEST_SUBMISSION['arxiv_identifier_w_vn_nr'], + response.context['form'].initial['arxiv_identifier_w_vn_nr']) + self.assertEqual(TEST_SUBMISSION['arxiv_identifier_wo_vn_nr'], + response.context['form'].initial['arxiv_identifier_wo_vn_nr']) + self.assertEqual(TEST_SUBMISSION['arxiv_vn_nr'], + response.context['form'].initial['arxiv_vn_nr']) + self.assertEqual(TEST_SUBMISSION['arxiv_link'], + response.context['form'].initial['arxiv_link']) + self.assertEqual(TEST_SUBMISSION['abstract'], + response.context['form'].initial['abstract']) def test_still_200_ok_if_identifier_is_wrong(self): - client = Client() - client.login(username="Test", password="testpw") - - response = client.post(reverse('submissions:prefill_using_identifier'), - {'identifier': '1512.00030'}) - + response = self.client.post(self.url, {'identifier': '1512.00030'}) self.assertEqual(response.status_code, 200) -class SubmitManuscriptTest(TestCase): - fixtures = ['permissions', 'groups', 'contributors'] - +class SubmitManuscriptTest(BaseContributorTestCase): def test_submit_correct_manuscript(self): + '''Check is view POST request works as expected.''' client = Client() - client.login(username="Test", password="testpw") + # Unauthorized request shouldn't be possible response = client.post(reverse('submissions:prefill_using_identifier'), - {'identifier': '1512.00030v1'}) - - params = response.context['form'].initial - - extras = {'discipline': 'physics', - 'submitted_to_journal': 'SciPost Physics', - 'submission_type': 'Article', - 'domain': 'T'} - response = client.post(reverse('submissions:submit_manuscript'), - {**params, **extras}) + {'identifier': TEST_SUBMISSION['arxiv_identifier_w_vn_nr']}) + self.assertEquals(response.status_code, 403) + # Registered Contributor should get 200; assuming prefiller is working properly + self.assertTrue(client.login(username="Test", password="testpw")) + response = client.post(reverse('submissions:prefill_using_identifier'), + {'identifier': TEST_SUBMISSION['arxiv_identifier_w_vn_nr']}) self.assertEqual(response.status_code, 200) + # Fill form parameters + params = response.context['form'].initial + params.update({ + 'discipline': 'physics', + 'subject_area': 'Phys:MP', + 'submitted_to_journal': 'SciPostPhys', + 'submission_type': 'Article', + 'domain': 'T' + }) + params['metadata'] = json.dumps(params['metadata'], separators=(',', ':')) + + # Submit new Submission form + response = client.post(reverse('submissions:submit_manuscript'), params) + self.assertEqual(response.status_code, 302) + + # Do a quick check on the Submission submitted. + submission = Submission.objects.filter(status=STATUS_UNASSIGNED).last() + self.assertIsNotNone(submission) + self.assertEqual(TEST_SUBMISSION['is_resubmission'], submission.is_resubmission) + self.assertEqual(TEST_SUBMISSION['title'], submission.title) + self.assertEqual(TEST_SUBMISSION['author_list'], submission.author_list) + self.assertEqual(TEST_SUBMISSION['arxiv_identifier_w_vn_nr'], + submission.arxiv_identifier_w_vn_nr) + self.assertEqual(TEST_SUBMISSION['arxiv_identifier_wo_vn_nr'], + submission.arxiv_identifier_wo_vn_nr) + self.assertEqual(TEST_SUBMISSION['arxiv_vn_nr'], submission.arxiv_vn_nr) + self.assertEqual(TEST_SUBMISSION['arxiv_link'], submission.arxiv_link) + self.assertEqual(TEST_SUBMISSION['abstract'], submission.abstract) + + def test_non_author_tries_submission(self): + '''See what happens if a non-author of an Arxiv submission submits to SciPost.''' + client = Client() -class SubmissionDetailTest(TestCase): - fixtures = ['permissions', 'groups'] + # Contributor Linder tries to submit the Quench Action. + # Eventually this call should already give an error. Waiting for + # Arxiv caller which is under construction [Jorran de Wit, 12 May 2017] + self.assertTrue(client.login(username="Test", password="testpw")) + response = client.post(reverse('submissions:prefill_using_identifier'), + {'identifier': '1603.04689v1'}) + self.assertEqual(response.status_code, 200) + # Fill form parameters + params = response.context['form'].initial + params.update({ + 'discipline': 'physics', + 'subject_area': 'Phys:MP', + 'submitted_to_journal': 'SciPostPhys', + 'submission_type': 'Article', + 'domain': 'T' + }) + params['metadata'] = json.dumps(params['metadata'], separators=(',', ':')) + + # Submit new Submission form + response = client.post(reverse('submissions:submit_manuscript'), params) + self.assertEqual(response.status_code, 302) + + # No real check is done here to see if submission submit is aborted. + # To be implemented after Arxiv caller. + # Temporary fix: + last_submission = Submission.objects.last() + if last_submission: + self.assertNotEqual(last_submission.title, 'The Quench Action') + self.assertNotEqual(last_submission.arxiv_identifier_w_vn_nr, '1603.04689v1') + + +class SubmissionDetailTest(BaseContributorTestCase): def setUp(self): + super().setUp() self.client = Client() self.submission = EICassignedSubmissionFactory() self.target = reverse( diff --git a/submissions/views.py b/submissions/views.py index ce94d8f6cecd0b88133cd1c32a4914b707cbaeff..712898a5142a610a6ce2b03f320059eda141675c 100644 --- a/submissions/views.py +++ b/submissions/views.py @@ -109,28 +109,7 @@ class PrefillUsingIdentifierView(PermissionRequiredMixin, FormView): class SubmissionCreateView(PermissionRequiredMixin, CreateView): model = Submission - fields = [ - 'is_resubmission', - 'discipline', - 'submitted_to_journal', - 'submission_type', - 'domain', - 'subject_area', - 'secondary_areas', - 'title', - 'author_list', - 'abstract', - 'arxiv_identifier_w_vn_nr', - 'arxiv_identifier_wo_vn_nr', - 'arxiv_vn_nr', - 'arxiv_link', - 'metadata', - 'author_comments', - 'list_of_changes', - 'remarks_for_editors', - 'referees_suggested', - 'referees_flagged' - ] + form_class = SubmissionForm template_name = 'submissions/new_submission.html' permission_required = 'scipost.can_submit_manuscript' @@ -148,6 +127,15 @@ class SubmissionCreateView(PermissionRequiredMixin, CreateView): submitted_by = Contributor.objects.get(user=self.request.user) form.instance.submitted_by = submitted_by + # Temporary until moved to new Arxiv Caller + # Check submitting user for authorship ! + # With the new Arxiv caller, this message should already be given in the prefil form! + if not form.check_user_may_submit(self.request.user): + msg = ('Your name does not match that of any of the authors. ' + 'You are not authorized to submit this preprint.') + messages.error(self.request, msg) + return redirect('submissions:prefill_using_identifier') + # Save all the information contained in the form submission = form.save()