From 6381168bd5a39f5c7da1b80f66237f3da2d930e8 Mon Sep 17 00:00:00 2001 From: Jorran de Wit <jorrandewit@outlook.com> Date: Fri, 30 Nov 2018 16:00:06 +0100 Subject: [PATCH] Renovation --- preprints/helpers.py | 8 +- submissions/admin.py | 1 + submissions/constants.py | 1 + submissions/forms.py | 515 ++++++++++++++---- submissions/models.py | 4 +- .../submissions/submission_form.html | 16 +- .../submissions/submission_prefill_form.html | 3 +- .../submission_resubmission_candidates.html | 2 +- submissions/views.py | 39 +- 9 files changed, 451 insertions(+), 138 deletions(-) diff --git a/preprints/helpers.py b/preprints/helpers.py index 918fd3839..596cf3a1c 100644 --- a/preprints/helpers.py +++ b/preprints/helpers.py @@ -11,7 +11,11 @@ from .models import Preprint def generate_new_scipost_identifier(old_preprint=None): - """Return an identifier for a new SciPost preprint series without version number.""" + """ + Return an identifier for a new SciPost preprint series without version number. + + TODO: This method will explode as soon as it will be used similtaneously by two or more people. + """ now = timezone.now() if isinstance(old_preprint, Submission): @@ -34,7 +38,7 @@ def generate_new_scipost_identifier(old_preprint=None): else: existing_identifier = str(existing_identifier + 1) - identifier = '{year}{month}_{identifier}'.format( + identifier = '{year}{month}_{identifier}v1'.format( year=now.year, month=str(now.month).rjust(2, '0'), identifier=existing_identifier.rjust(5, '0')) return identifier, int(existing_identifier) diff --git a/submissions/admin.py b/submissions/admin.py index 7e02b3362..7b7a19e8e 100644 --- a/submissions/admin.py +++ b/submissions/admin.py @@ -67,6 +67,7 @@ class SubmissionAdmin(GuardedModelAdmin): 'fields': ( 'is_current', 'is_resubmission', + 'is_resubmission_of', 'list_of_changes'), }), ('Submission details', { diff --git a/submissions/constants.py b/submissions/constants.py index d32c9d654..a10841d53 100644 --- a/submissions/constants.py +++ b/submissions/constants.py @@ -46,6 +46,7 @@ NO_REQUIRED_ACTION_STATUSES = [ ] SUBMISSION_TYPE = ( + # ('', None), ('Letter', 'Letter (broad-interest breakthrough results)'), ('Article', 'Article (in-depth reports on specialized research)'), ('Review', 'Review (candid snapshot of current research in a given area)'), diff --git a/submissions/forms.py b/submissions/forms.py index 75ce8df0b..85cd7f1fe 100644 --- a/submissions/forms.py +++ b/submissions/forms.py @@ -98,43 +98,190 @@ class SubmissionPoolFilterForm(forms.Form): # Submission and resubmission # ############################### -class SubmissionChecks: - """Mixin with checks run at least the Submission creation form.""" +class SubmissionService: + """ + Object to run checks for prefiller and submit manuscript forms. + """ - use_arxiv_preprint = True arxiv_data = {} - is_resubmission = False - last_submission = None - def __init__(self, *args, **kwargs): - self.requested_by = kwargs.pop('requested_by', None) - super().__init__(*args, **kwargs) - # Prefill `is_resubmission` property if data is coming from initial data - if kwargs.get('initial', None): - if kwargs['initial'].get('is_resubmission', None): - self.is_resubmission = kwargs['initial']['is_resubmission'] in ('True', True) - elif kwargs['initial'].get('resubmission', None): - # Resubmission is explicitly chosen by user. - self.is_resubmission = True + def __init__(self, requested_by, preprint_server, identifier=None, resubmission_of_id=None): + self.requested_by = requested_by + self.preprint_server = preprint_server + self.identifier = identifier + self.resubmission_of_id = resubmission_of_id + + if self.preprint_server == 'arxiv': + # Do the call here to prevent multiple calls to the arXiv API in one request. + self._call_arxiv() + + @property + def latest_submission(self): + """ + Return latest version of preprint series or None. + """ + if hasattr(self, '_latest_submission'): + return self._latest_submission + + if self.identifier: + # Check if is resubmission when identifier data is submitted. + identifier = self.identifier.rpartition('v')[0] + self._latest_submission = Submission.objects.filter( + preprint__identifier_wo_vn_nr=identifier).order_by( + '-preprint__vn_nr').first() + elif self.resubmission_of_id: + # Resubmission (submission id) is selected by user. + try: + self._latest_submission = Submission.objects.filter( + id=int(self.resubmission_of_id)).order_by('-preprint__vn_nr').first() + except ValueError: + self._latest_submission = None + return self._latest_submission - # `is_resubmission` property if data is coming from (POST) request - if kwargs.get('data', None): - if kwargs['data'].get('is_resubmission', None): - self.is_resubmission = kwargs['data']['is_resubmission'] in ('True', True) + def run_checks(self): + """ + Do several pre-checks (using the arXiv API if needed). + + This is needed for both the prefill and submission forms. + """ + self.arxiv_data = {} + self._submission_already_exists() + self._submission_previous_version_is_valid_for_submission() + + if self.preprint_server == 'arxiv': + self._submission_is_already_published() - def _submission_already_exists(self, identifier): + def get_latest_submission_data(self): + """ + Return initial form data originating from earlier Submission. + """ + if self.is_resubmission(): + return { + 'title': self.latest_submission.title, + 'abstract': self.latest_submission.abstract, + 'author_list': self.latest_submission.author_list, + 'discipline': self.latest_submission.discipline, + 'domain': self.latest_submission.domain, + 'referees_flagged': self.latest_submission.referees_flagged, + 'referees_suggested': self.latest_submission.referees_suggested, + 'secondary_areas': self.latest_submission.secondary_areas, + 'subject_area': self.latest_submission.subject_area, + 'submitted_to': self.latest_submission.submitted_to, + 'submission_type': self.latest_submission.submission_type, + } + return {} + + def is_resubmission(self): + """ + Check if Submission is a SciPost or arXiv resubmission. + """ + return self.latest_submission is not None + + def identifier_matches_regex(self, journal_code): + """ + Check if identifier is valid for the Journal submitting to. + """ + if self.preprint_server != 'arxiv': + # Only check arXiv identifiers + return + + if journal_code in EXPLICIT_REGEX_MANUSCRIPT_CONSTRAINTS.keys(): + regex = EXPLICIT_REGEX_MANUSCRIPT_CONSTRAINTS[journal_code] + else: + regex = EXPLICIT_REGEX_MANUSCRIPT_CONSTRAINTS['default'] + + pattern = re.compile(regex) + if not pattern.match(self.identifier): + # No match object returned, identifier is invalid + error_message = ('The journal you want to submit to does not allow for this' + ' identifier. Please contact SciPost if you have' + ' any further questions.') + raise forms.ValidationError(error_message, code='submitted_to') + + def process_resubmission_procedure(self, submission): + """ + Update all fields for new and old Submission and EditorialAssignments to comply with + the resubmission procedures. + + -- submission: the new version of the Submission series. + """ + if not self.latest_submission: + raise Submission.DoesNotExist + + # Close last submission + Submission.objects.filter(id=self.latest_submission.id).update( + is_current=False, open_for_reporting=False, status=STATUS_RESUBMITTED) + + # Copy Topics + submission.topics.add(*self.latest_submission.topics.all()) + + # Open for comment and reporting and copy EIC info + Submission.objects.filter(id=submission.id).update( + open_for_reporting=True, + open_for_commenting=True, + is_resubmission=True, + visible_pool=True, + refereeing_cycle=CYCLE_UNDETERMINED, + editor_in_charge=self.latest_submission.editor_in_charge, + status=STATUS_EIC_ASSIGNED) + + # Add author(s) (claim) fields + submission.authors.add(*self.latest_submission.authors.all()) + submission.authors_claims.add(*self.latest_submission.authors_claims.all()) + submission.authors_false_claims.add(*self.latest_submission.authors_false_claims.all()) + + # Create new EditorialAssigment for the current Editor-in-Charge + EditorialAssignment.objects.create( + submission=submission, + to=self.latest_submission.editor_in_charge, + status=STATUS_ACCEPTED) + + def _submission_already_exists(self): """ Check if preprint has already been submitted before. """ - if Submission.objects.filter(preprint__identifier_w_vn_nr=identifier).exists(): + if Submission.objects.filter(preprint__identifier_w_vn_nr=self.identifier).exists(): error_message = 'This preprint version has already been submitted to SciPost.' raise forms.ValidationError(error_message, code='duplicate') - def _call_arxiv(self, identifier): + def _submission_previous_version_is_valid_for_submission(self): + """ + Check if previous submitted versions have the appropriate status. + """ + + if self.latest_submission: + if self.latest_submission.status == STATUS_REJECTED: + # Explicitly give rejected status warning. + error_message = ('This preprint has previously undergone refereeing ' + 'and has been rejected. Resubmission is only possible ' + 'if the manuscript has been substantially reworked into ' + 'a new submission with distinct identifier.') + raise forms.ValidationError(error_message) + elif self.latest_submission.open_for_resubmission: + # Check if verified author list contains current user. + self.is_resubmission = True + if self.requested_by.contributor not in self.latest_submission.authors.all(): + error_message = ('There exists a preprint with this identifier ' + 'but an earlier version number. Resubmission is only possible' + ' if you are a registered author of this manuscript.') + raise forms.ValidationError(error_message) + else: + # Submission has not an appropriate status for resubmission. + error_message = ('There exists a preprint with this identifier ' + 'but an earlier version number, which is still undergoing ' + 'peer refereeing. ' + '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.') + raise forms.ValidationError(error_message) + + def _call_arxiv(self): """ Retrieve all data from the ArXiv database for `identifier`. """ - caller = ArxivCaller(identifier) + caller = ArxivCaller(self.identifier) if caller.is_valid: self.arxiv_data = caller.data self.metadata = caller.metadata @@ -142,7 +289,7 @@ class SubmissionChecks: error_message = 'A preprint associated to this identifier does not exist.' raise forms.ValidationError(error_message) - def _submission_is_already_published(self, identifier): + def _submission_is_already_published(self): """ Check if preprint number is already registered with a DOI in the *ArXiv* database. """ @@ -158,117 +305,257 @@ class SubmissionChecks: raise forms.ValidationError(error_message, code='published', params={'published_id': published_id}) - def _submission_previous_version_is_valid_for_submission(self, identifier): + +class SubmissionForm(forms.ModelForm): + """ + Form to submit a new (re)Submission. + """ + + identifier_w_vn_nr = forms.CharField(widget=forms.HiddenInput()) + preprint_file = forms.FileField() + + class Meta: + model = Submission + fields = [ + 'is_resubmission_of', + 'discipline', + 'submitted_to', + 'proceedings', + 'submission_type', + 'domain', + 'subject_area', + 'secondary_areas', + 'title', + 'author_list', + 'abstract', + 'author_comments', + 'list_of_changes', + 'remarks_for_editors', + 'referees_suggested', + 'referees_flagged', + 'arxiv_link', + ] + widgets = { + 'is_resubmission_of': forms.HiddenInput(), + 'secondary_areas': forms.SelectMultiple(choices=SCIPOST_SUBJECT_AREAS), + 'arxiv_link': forms.TextInput( + attrs={'placeholder': 'ex.: arxiv.org/abs/1234.56789v1'}), + 'remarks_for_editors': forms.Textarea( + attrs={'placeholder': 'Any private remarks (for the editors only)', 'rows': 5}), + 'referees_suggested': forms.Textarea( + attrs={'placeholder': 'Optional: names of suggested referees', 'rows': 5}), + 'referees_flagged': forms.Textarea( + attrs={ + 'placeholder': 'Optional: names of referees whose reports should be treated with caution (+ short reason)', + 'rows': 5 + }), + 'author_comments': forms.Textarea( + attrs={'placeholder': 'Your resubmission letter (will be viewable online)'}), + 'list_of_changes': forms.Textarea( + attrs={'placeholder': 'Give a point-by-point list of changes (will be viewable online)'}), + } + + def __init__(self, *args, **kwargs): + self.requested_by = kwargs.pop('requested_by') + self.preprint_server = kwargs.pop('preprint_server', 'arxiv') + self.resubmission_preprint = kwargs['initial'].get('resubmission', False) + + self.service = SubmissionService( + self.requested_by, self.preprint_server, + identifier=kwargs['initial'].get('identifier_w_vn_nr', None), + resubmission_of_id=self.resubmission_preprint) + if self.preprint_server == 'scipost': + kwargs['initial'] = self.service.get_latest_submission_data() + + super().__init__(*args, **kwargs) + # self.service.run_checks() + + if not self.preprint_server == 'arxiv': + # No arXiv-specific data required. + del self.fields['identifier_w_vn_nr'] + del self.fields['arxiv_link'] + elif not self.preprint_server == 'scipost': + # No need for a file upload if user is not using the SciPost preprint server. + del self.fields['preprint_file'] + + # Find all submission allowed to be resubmitted by current user. + self.fields['is_resubmission_of'].queryset = Submission.objects.candidate_for_resubmission( + self.requested_by) + + # Fill resubmission-dependent fields + if self.is_resubmission(): + self.fields['is_resubmission_of'].initial = self.service.latest_submission + else: + # These fields are only available for resubmissions. + del self.fields['author_comments'] + del self.fields['list_of_changes'] + + if not self.fields['is_resubmission_of'].initial: + # No intial nor submitted data found. + del self.fields['is_resubmission_of'] + + # Select Journal instances. + self.fields['submitted_to'].queryset = Journal.objects.active() + self.fields['submitted_to'].label = 'Journal: submit to' + + # Proceedings submission fields + qs = self.fields['proceedings'].queryset.open_for_submission() + self.fields['proceedings'].queryset = qs + self.fields['proceedings'].empty_label = None + if not qs.exists(): + # No proceedings issue to submit to, so adapt the form fields + self.fields['submitted_to'].queryset = self.fields['submitted_to'].queryset.exclude( + doi_label=SCIPOST_JOURNAL_PHYSICS_PROC) + del self.fields['proceedings'] + + def is_resubmission(self): + return self.service.is_resubmission() + + def clean(self, *args, **kwargs): """ - Check if previous submitted versions have the appropriate status. + Do all general checks for Submission. """ - identifiers = self.identifier_into_parts(identifier) - self.last_submission = Submission.objects.filter( - preprint__identifier_wo_vn_nr=identifiers['identifier_wo_vn_nr']).order_by( - 'preprint__vn_nr').last() - - # If submissions are found; check their statuses - if self.last_submission: - if self.last_submission.open_for_resubmission: - self.is_resubmission = True - if self.requested_by.contributor not in self.last_submission.authors.all(): - error_message = ('There exists a preprint with this identifier ' - 'but an earlier version number. Resubmission is only possible' - ' if you are a registered author of this manuscript.') - raise forms.ValidationError(error_message) - elif self.last_submission.status == STATUS_REJECTED: - error_message = ('This preprint has previously undergone refereeing ' - 'and has been rejected. Resubmission is only possible ' - 'if the manuscript has been substantially reworked into ' - 'a new submission with distinct identifier.') - raise forms.ValidationError(error_message) - else: - error_message = ('There exists a preprint with this identifier ' - 'but an earlier version number, which is still undergoing ' - 'peer refereeing. ' - '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.') - raise forms.ValidationError(error_message) + cleaned_data = super().clean(*args, **kwargs) + + # SciPost preprints are auto-generated here. + if 'identifier_w_vn_nr' not in cleaned_data: + self.service.identifier, self.scipost_identifier = generate_new_scipost_identifier( + cleaned_data.get('resubmission', None)) + # Also copy to the form data + self.cleaned_data['identifier_w_vn_nr'] = self.service.identifier + + # Run checks again to clean any possible human intervention and run checks again + # with possibly newly generated identifier. + self.service.run_checks() + self.service.identifier_matches_regex(cleaned_data['submitted_to'].doi_label) + + if self.cleaned_data['submitted_to'].doi_label != SCIPOST_JOURNAL_PHYSICS_PROC: + try: + del self.cleaned_data['proceedings'] + except KeyError: + # No proceedings returned to data + return cleaned_data + return cleaned_data - def identifier_matches_regex(self, identifier, journal_code): + def clean_author_list(self): """ - Check if identifier is valid for the Journal submitting to. + Check if author list matches the Contributor submitting. """ - if journal_code in EXPLICIT_REGEX_MANUSCRIPT_CONSTRAINTS.keys(): - regex = EXPLICIT_REGEX_MANUSCRIPT_CONSTRAINTS[journal_code] - else: - regex = EXPLICIT_REGEX_MANUSCRIPT_CONSTRAINTS['default'] + if not self.requested_by.last_name.lower() in self.cleaned_data['author_list'].lower(): + error_message = ('Your name does not match that of any of the authors. ' + 'You are not authorized to submit this preprint.') + self.add_error('author_list', error_message) + return self.cleaned_data['author_list'] - pattern = re.compile(regex) - if not pattern.match(identifier): - # No match object returned, identifier is invalid - error_message = ('The journal you want to submit to does not allow for this' - ' identifier. Please contact SciPost if you have' - ' any further questions.') - raise forms.ValidationError(error_message, code='submitted_to') + def clean_submission_type(self): + """ + Validate Submission type for the SciPost Physics journal. + """ + submission_type = self.cleaned_data['submission_type'] + journal_doi_label = self.cleaned_data['submitted_to'].doi_label + if journal_doi_label == SCIPOST_JOURNAL_PHYSICS and not submission_type: + self.add_error('submission_type', 'Please specify the submission type.') + return submission_type - def submission_is_resubmission(self): - """Check if the Submission is a resubmission.""" - return self.is_resubmission + def set_pool(self, submission): + """ + Set the default set of (guest) Fellows for this Submission. + """ + qs = Fellowship.objects.active() + fellows = qs.regular().filter( + contributor__discipline=submission.discipline).return_active_for_submission(submission) + submission.fellows.set(fellows) - def identifier_into_parts(self, identifier): - """Split the preprint identifier into parts.""" - data = { - 'identifier_w_vn_nr': identifier, - 'identifier_wo_vn_nr': identifier.rpartition('v')[0], - 'vn_nr': int(identifier.rpartition('v')[2]) - } - return data + if submission.proceedings: + # Add Guest Fellowships if the Submission is a Proceedings manuscript + guest_fellows = qs.guests().filter( + proceedings=submission.proceedings).return_active_for_submission(submission) + submission.fellows.add(*guest_fellows) - def do_pre_checks(self, identifier): - """Group call of different checks.""" - self._submission_already_exists(identifier) - if self.use_arxiv_preprint: - self._call_arxiv(identifier) - self._submission_is_already_published(identifier) - self._submission_previous_version_is_valid_for_submission(identifier) + @transaction.atomic + def save(self): + """ + Create the new Submission and Preprint instances. + """ + submission = super().save(commit=False) + submission.submitted_by = self.requested_by.contributor + + # Save identifiers + identifiers = self.cleaned_data['identifier_w_vn_nr'].rpartition('v') + preprint, __ = Preprint.objects.get_or_create( + identifier_w_vn_nr=self.cleaned_data['identifier_w_vn_nr'], + identifier_wo_vn_nr=identifiers[0], + vn_nr=identifiers[2], + url=self.cleaned_data.get('arxiv_link', ''), + scipost_preprint_identifier=self.scipost_identifier, + _file=self.cleaned_data.get('preprint_file', None), ) + + # Save metadata directly from ArXiv call without possible user interception + submission.metadata = self.service.metadata + submission.preprint = preprint + + submission.save() + if self.is_resubmission(): + self.process_resubmission_procedure(submission) + + # Gather first known author and Fellows. + submission.authors.add(self.requested_by.contributor) + self.set_pool(submission) + + # Return latest version of the Submission. It could be outdated by now. + submission.refresh_from_db() + return submission -class SubmissionIdentifierForm(SubmissionChecks, forms.Form): - """Prefill SubmissionForm using this form that takes an arXiv ID only.""" +class SubmissionIdentifierForm(forms.Form): + """ + Prefill SubmissionForm using this form that takes an arXiv ID only. + """ IDENTIFIER_PLACEHOLDER = 'new style (with version nr) ####.####(#)v#(#)' identifier_w_vn_nr = forms.RegexField( + label='arXiv identifier with version number', regex=IDENTIFIER_PATTERN_NEW, strip=True, error_messages={'invalid': strings.arxiv_query_invalid}, widget=forms.TextInput({'placeholder': IDENTIFIER_PLACEHOLDER})) + def __init__(self, *args, **kwargs): + self.requested_by = kwargs.pop('requested_by') + return super().__init__(*args, **kwargs) + def clean_identifier_w_vn_nr(self): - """Do basic prechecks based on the arXiv ID only.""" + """ + Do basic prechecks based on the arXiv ID only. + """ identifier = self.cleaned_data['identifier_w_vn_nr'] - self.do_pre_checks(identifier) + self.service = SubmissionService(self.requested_by, 'arxiv', identifier=identifier) + self.service.run_checks() return identifier - def request_arxiv_preprint_form_prefill_data(self): - """Return dictionary to prefill `RequestSubmissionForm`.""" - form_data = self.arxiv_data + def get_initial_submission_data(self): + """ + Return dictionary to prefill `SubmissionForm`. + """ + form_data = self.service.arxiv_data form_data['identifier_w_vn_nr'] = self.cleaned_data['identifier_w_vn_nr'] - if self.submission_is_resubmission(): + if self.service.is_resubmission(): form_data.update({ - 'is_resubmission': True, - 'discipline': self.last_submission.discipline, - 'domain': self.last_submission.domain, - 'referees_flagged': self.last_submission.referees_flagged, - 'referees_suggested': self.last_submission.referees_suggested, - 'secondary_areas': self.last_submission.secondary_areas, - 'subject_area': self.last_submission.subject_area, - 'submitted_to': self.last_submission.submitted_to, - 'submission_type': self.last_submission.submission_type, + 'discipline': self.service.latest_submission.discipline, + 'domain': self.service.latest_submission.domain, + 'referees_flagged': self.service.latest_submission.referees_flagged, + 'referees_suggested': self.service.latest_submission.referees_suggested, + 'secondary_areas': self.service.latest_submission.secondary_areas, + 'subject_area': self.service.latest_submission.subject_area, + 'submitted_to': self.service.latest_submission.submitted_to, + 'submission_type': self.service.latest_submission.submission_type, }) return form_data -class RequestSubmissionForm(SubmissionChecks, forms.ModelForm): - """Form to submit a new Submission.""" +# class RequestSubmissionForm(SubmissionChecks, forms.ModelForm): +class RequestSubmissionForm(forms.ModelForm): + """DEPRECATD: Form to submit a new Submission.""" scipost_identifier = None @@ -316,12 +603,15 @@ class RequestSubmissionForm(SubmissionChecks, forms.ModelForm): super().__init__(*args, **kwargs) # Update resubmission-dependent fields - resubmission_preprint = kwargs.get('initial', {}).get('resubmission', None) + self.resubmission_preprint = kwargs.get('initial', {}).get('resubmission', None) self.fields['resubmission'].queryset = Submission.objects.candidate_for_resubmission( self.requested_by) # This is auto-filled by the resubmit_manuscript view. - if resubmission_preprint: - self.fields['resubmission'].initial = self.fields['resubmission'].queryset.get( - preprint__identifier_w_vn_nr=resubmission_preprint) + if self.resubmission_preprint and self.resubmission_preprint not in ('false', False): + try: + self.fields['resubmission'].initial = self.fields['resubmission'].queryset.get( + preprint__identifier_w_vn_nr=self.resubmission_preprint) + except Submission.DoesNotExist: + self.form_complete = False if not self.submission_is_resubmission(): # These fields are only available for resubmissions @@ -360,6 +650,11 @@ class RequestSubmissionForm(SubmissionChecks, forms.ModelForm): def clean(self, *args, **kwargs): """Do all prechecks which are also done in the prefiller.""" cleaned_data = super().clean(*args, **kwargs) + + if not self.form_complete: + self.add_error(None, 'The preprint identifier selected for resubmission is not valid.') + return cleaned_data + # SciPost preprints are auto-generated here. if 'identifier_w_vn_nr' not in cleaned_data: cleaned_data['identifier_w_vn_nr'], self.scipost_identifier = generate_new_scipost_identifier(cleaned_data.get('resubmission', None)) diff --git a/submissions/models.py b/submissions/models.py index 58acca549..19e17e177 100644 --- a/submissions/models.py +++ b/submissions/models.py @@ -75,6 +75,8 @@ class Submission(models.Model): is_current = models.BooleanField(default=True) visible_public = models.BooleanField("Is publicly visible", default=False) visible_pool = models.BooleanField("Is visible in the Pool", default=False) + is_resubmission_of = models.ForeignKey( + 'self', blank=True, null=True, related_name='successor') is_resubmission = models.BooleanField(default=False) refereeing_cycle = models.CharField( max_length=30, choices=SUBMISSION_CYCLES, default=CYCLE_DEFAULT, blank=True) @@ -84,7 +86,7 @@ class Submission(models.Model): subject_area = models.CharField(max_length=10, choices=SCIPOST_SUBJECT_AREAS, verbose_name='Primary subject area', default='Phys:QP') - submission_type = models.CharField(max_length=10, choices=SUBMISSION_TYPE) + submission_type = models.CharField(max_length=10, choices=SUBMISSION_TYPE, blank=True) submitted_by = models.ForeignKey('scipost.Contributor', on_delete=models.CASCADE, related_name='submitted_submissions') voting_fellows = models.ManyToManyField('colleges.Fellowship', blank=True, diff --git a/submissions/templates/submissions/submission_form.html b/submissions/templates/submissions/submission_form.html index e397677c9..433ff7ed5 100644 --- a/submissions/templates/submissions/submission_form.html +++ b/submissions/templates/submissions/submission_form.html @@ -48,16 +48,24 @@ <p> Please prepare your manuscript according to the <a href="{% url 'submissions:author_guidelines' %}">author guidelines</a>. </p> - {% if form.fields.resubmission %} + + + {% if form.is_resubmission %} <hr> <p class="mb-1">You are submitting a new version of your manuscript:</p> - <h3><a href="{{ form.fields.resubmission.initial.get_absolute_url }}" target="_blank">{{ form.fields.resubmission.initial.title }}</a></h3> + <h3><a href="{{ form.fields.is_resubmission_of.initial.get_absolute_url }}" target="_blank">{{ form.fields.is_resubmission_of.initial.title }}</a></h3> <p> - by {{ form.fields.resubmission.initial.author_list }} + by {{ form.fields.is_resubmission_of.initial.author_list }} <br> - Preprint number: {{ form.fields.resubmission.initial.preprint.identifier_w_vn_nr }} + Preprint number: {{ form.fields.is_resubmission_of.initial.preprint.identifier_w_vn_nr }} </p> {% endif %} + + {% comment %} + {% if not form.form_complete %} + <h3 class="text-danger">Please check the preprint identifier used for resubmission. Current identifier {{ form.resubmission_preprint }} is not valid.</h3> + {% endif %} + {% endcomment %} </div> </div> diff --git a/submissions/templates/submissions/submission_prefill_form.html b/submissions/templates/submissions/submission_prefill_form.html index 0b9cdb911..9a4e96e80 100644 --- a/submissions/templates/submissions/submission_prefill_form.html +++ b/submissions/templates/submissions/submission_prefill_form.html @@ -35,7 +35,8 @@ <div class='card-body'> <h3>Please provide the arXiv identifier for your Submission</h3> <p><em>(give the identifier without prefix but with version number, as per the placeholder)</em></p> - <form action="{% url 'submissions:submit_manuscript_arxiv' %}" method="get"> + <form action="{% url 'submissions:prefill_using_identifier' %}" method="post"> + {% csrf_token %} {{ form|bootstrap }} <input type="submit" class="btn btn-outline-secondary" value="Query arXiv"/> <br> diff --git a/submissions/templates/submissions/submission_resubmission_candidates.html b/submissions/templates/submissions/submission_resubmission_candidates.html index 446835d47..8cc116580 100644 --- a/submissions/templates/submissions/submission_resubmission_candidates.html +++ b/submissions/templates/submissions/submission_resubmission_candidates.html @@ -21,7 +21,7 @@ <br> Preprint number: {{ submission.preprint.identifier_w_vn_nr }} <br> - <button type="submit" name="submission" value="{{ submission.preprint.identifier_w_vn_nr }}"class="btn btn-primary py-1 mt-1">Resubmit this Submission</button> + <button type="submit" name="submission" value="{{ submission.preprint.id }}"class="btn btn-primary py-1 mt-1">Resubmit this Submission</button> </li> {% endfor %} </ul> diff --git a/submissions/views.py b/submissions/views.py index f48425f2c..95070361e 100644 --- a/submissions/views.py +++ b/submissions/views.py @@ -30,7 +30,7 @@ from .models import ( Submission, EICRecommendation, EditorialAssignment, RefereeInvitation, Report, SubmissionEvent) from .mixins import SubmissionAdminViewMixin from .forms import ( - SubmissionIdentifierForm, RequestSubmissionForm, SubmissionSearchForm, RecommendationVoteForm, + SubmissionIdentifierForm, SubmissionForm, RequestSubmissionForm, SubmissionSearchForm, RecommendationVoteForm, ConsiderAssignmentForm, InviteEditorialAssignmentForm, EditorialAssignmentForm, VetReportForm, SetRefereeingDeadlineForm, RefereeSearchForm, #RefereeSelectForm, iThenticateReportForm, VotingEligibilityForm, @@ -77,16 +77,20 @@ def resubmit_manuscript(request): if request.POST and request.POST.get('submission'): if request.POST['submission'] == 'new': return redirect(reverse('submissions:submit_manuscript_scipost') + '?resubmission=false') - try: - last_submission = Submission.objects.candidate_for_resubmission(request.user).get( - preprint__identifier_w_vn_nr=request.POST['submission']) - extra_param = '?resubmission={}'.format(request.POST['submission']) + last_submission = Submission.objects.candidate_for_resubmission(request.user).filter( + id=request.POST['submission']).first() + + if last_submission: if last_submission.preprint.scipost_preprint_identifier: # Determine right preprint-view. + extra_param = '?resubmission={}'.format(request.POST['submission']) return redirect(reverse('submissions:submit_manuscript_scipost') + extra_param) - return redirect(reverse('submissions:submit_manuscript') + extra_param) - except Submission.DoesNotExist: + else: + extra_param = '?identifier_w_vn_nr={}'.format( + last_submission.preprint.identifier_w_vn_nr) + return redirect(reverse('submissions:submit_manuscript') + extra_param) + else: # POST request invalid. Try again with GET request. return redirect('submissions:resubmit_manuscript') context = { @@ -100,7 +104,7 @@ class RequestSubmissionView(LoginRequiredMixin, PermissionRequiredMixin, CreateV permission_required = 'scipost.can_submit_manuscript' success_url = reverse_lazy('scipost:personal_page') - form_class = RequestSubmissionForm + form_class = SubmissionForm #RequestSubmissionForm template_name = 'submissions/submission_form.html' def get_context_data(self, *args, **kwargs): @@ -154,15 +158,17 @@ class RequestSubmissionUsingArXivView(RequestSubmissionView): form = SubmissionIdentifierForm(request.GET or None, requested_by=self.request.user) if form.is_valid(): # Gather data from ArXiv API if prefill form is valid - self.initial_data = form.request_arxiv_preprint_form_prefill_data() + self.initial_data = form.get_initial_submission_data() return super().get(request) else: + raise return redirect('submissions:prefill_using_identifier') def get_form_kwargs(self): """Form requires extra kwargs.""" kwargs = super().get_form_kwargs() - kwargs['use_arxiv_preprint'] = True + # kwargs['use_arxiv_preprint'] = True + kwargs['preprint_server'] = 'arxiv' return kwargs @@ -179,7 +185,8 @@ class RequestSubmissionUsingSciPostView(RequestSubmissionView): def get_form_kwargs(self): """Form requires extra kwargs.""" kwargs = super().get_form_kwargs() - kwargs['use_arxiv_preprint'] = False + # kwargs['use_arxiv_preprint'] = False + kwargs['preprint_server'] = 'scipost' return kwargs @@ -189,13 +196,10 @@ def prefill_using_arxiv_identifier(request): """Form view asking for the arXiv ID related to the new Submission to submit.""" query_form = SubmissionIdentifierForm(request.POST or None, initial=request.GET or None, requested_by=request.user) - if query_form.is_valid(): - prefill_data = query_form.request_arxiv_preprint_form_prefill_data() - form = RequestSubmissionForm( - initial=prefill_data, requested_by=request.user, use_arxiv_preprint=True) + if query_form.is_valid(): # Submit message to user - if query_form.submission_is_resubmission(): + if query_form.service.is_resubmission(): resubmessage = ('There already exists a preprint with this arXiv identifier ' 'but a different version number. \nYour Submission will be ' 'handled as a resubmission.') @@ -203,9 +207,6 @@ def prefill_using_arxiv_identifier(request): else: messages.success(request, strings.acknowledge_arxiv_query, fail_silently=True) - context = { - 'form': form, - } response = redirect('submissions:submit_manuscript_arxiv') response['location'] += '?identifier_w_vn_nr={}'.format( query_form.cleaned_data['identifier_w_vn_nr']) -- GitLab