SciPost Code Repository

Skip to content
Snippets Groups Projects
Commit 841d815d authored by Jorran de Wit's avatar Jorran de Wit
Browse files

Centralize regex: Update central regex to accept more arxiv codes

parent 8ee34144
No related branches found
No related tags found
No related merge requests found
from journals.constants import SCIPOST_JOURNAL_PHYSICS
STATUS_UNASSIGNED = 'unassigned'
STATUS_RESUBMISSION_INCOMING = 'resubmitted_incoming'
STATUS_REVISION_REQUESTED = 'revision_requested'
......@@ -216,3 +219,19 @@ EVENT_TYPES = (
(EVENT_FOR_EIC, 'Comment for Editor-in-charge'),
(EVENT_FOR_AUTHOR, 'Comment for author'),
)
# Use `.format()` https://docs.python.org/3.5/library/string.html#format-string-syntax
# In your regex multiple brackets may occur;
# Please make sure to double them in that case as per instructions in the reference!
SUBMISSIONS_NO_VN_REGEX = '(?P<arxiv_identifier_wo_vn_nr>[0-9]{4,}.[0-9]{4,})'
SUBMISSIONS_COMPLETE_REGEX = '(?P<arxiv_identifier_w_vn_nr>[0-9]{4,}.[0-9]{4,}v[0-9]{1,2})'
# `EXPLICIT_REGEX_MANUSCRIPT_CONSTRAINTS` tracks the regex rules for the manuscripts
# submitted per journal.
#
# CAUTION: *triple* check whether the `default` regex also meets any other explicit journal regex!
EXPLICIT_REGEX_MANUSCRIPT_CONSTRAINTS = {
SCIPOST_JOURNAL_PHYSICS: '(?P<arxiv_identifier_w_vn_nr>[0-9]{4,}.[0-9]{5,}v[0-9]{1,2})',
'default': SUBMISSIONS_COMPLETE_REGEX
}
from django import forms
from django.conf import settings
from django.contrib.auth.models import Group
from django.db import transaction
from django.utils import timezone
from guardian.shortcuts import assign_perm
from .constants import ASSIGNMENT_BOOL, ASSIGNMENT_REFUSAL_REASONS, STATUS_RESUBMITTED,\
REPORT_ACTION_CHOICES, REPORT_REFUSAL_CHOICES, STATUS_REVISION_REQUESTED,\
STATUS_REJECTED, STATUS_REJECTED_VISIBLE, STATUS_RESUBMISSION_INCOMING,\
STATUS_DRAFT, STATUS_UNVETTED, REPORT_ACTION_ACCEPT, REPORT_ACTION_REFUSE,\
STATUS_VETTED
from . import exceptions, helpers
from .models import Submission, RefereeInvitation, Report, EICRecommendation, EditorialAssignment,\
iThenticateReport
from scipost.constants import SCIPOST_SUBJECT_AREAS
from scipost.services import ArxivCaller
from scipost.models import Contributor
import strings
import iThenticate
class SubmissionSearchForm(forms.Form):
author = forms.CharField(max_length=100, required=False, label="Author(s)")
title = forms.CharField(max_length=100, required=False)
abstract = forms.CharField(max_length=1000, required=False)
subject_area = forms.CharField(max_length=10, required=False, widget=forms.Select(
choices=((None, 'Show all'),) + SCIPOST_SUBJECT_AREAS[0][1]))
def search_results(self):
"""Return all Submission objects according to search"""
return Submission.objects.public_newest().filter(
title__icontains=self.cleaned_data.get('title', ''),
author_list__icontains=self.cleaned_data.get('author', ''),
abstract__icontains=self.cleaned_data.get('abstract', ''),
subject_area__icontains=self.cleaned_data.get('subject_area', '')
)
###############################
# Submission and resubmission #
###############################
class SubmissionChecks:
"""
Use this class as a blueprint containing checks which should be run
in multiple forms.
"""
is_resubmission = False
last_submission = None
def __init__(self, *args, **kwargs):
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)
# `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 _submission_already_exists(self, identifier):
if Submission.objects.filter(arxiv_identifier_w_vn_nr=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):
caller = ArxivCaller(identifier)
if caller.is_valid:
self.arxiv_data = ArxivCaller(identifier).data
self.metadata = ArxivCaller(identifier).metadata
else:
error_message = 'A preprint associated to this identifier does not exist.'
raise forms.ValidationError(error_message)
def _submission_is_already_published(self, identifier):
published_id = None
if 'arxiv_doi' in self.arxiv_data:
published_id = self.arxiv_data['arxiv_doi']
elif 'arxiv_journal_ref' in self.arxiv_data:
published_id = self.arxiv_data['arxiv_journal_ref']
if published_id:
error_message = ('This paper has been published under DOI %(published_id)s'
'. Please comment on the published version.'),
raise forms.ValidationError(error_message, code='published',
params={'published_id': published_id})
def _submission_previous_version_is_valid_for_submission(self, identifier):
'''Check if previous submitted versions have the appropriate status.'''
identifiers = self.identifier_into_parts(identifier)
submission = (Submission.objects
.filter(arxiv_identifier_wo_vn_nr=identifiers['arxiv_identifier_wo_vn_nr'])
.order_by('arxiv_vn_nr').last())
# If submissions are found; check their statuses
if submission:
self.last_submission = submission
if submission.status == STATUS_REVISION_REQUESTED:
self.is_resubmission = True
elif submission.status in [STATUS_REJECTED, STATUS_REJECTED_VISIBLE]:
error_message = ('This arXiv preprint has previously undergone refereeing '
'and has been rejected. Resubmission is only possible '
'if the manuscript has been substantially reworked into '
'a new arXiv submission with distinct identifier.')
raise forms.ValidationError(error_message)
else:
error_message = ('There exists a preprint with this arXiv 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 submission_is_resubmission(self):
return self.is_resubmission
def identifier_into_parts(self, identifier):
data = {
'arxiv_identifier_w_vn_nr': identifier,
'arxiv_identifier_wo_vn_nr': identifier.rpartition('v')[0],
'arxiv_vn_nr': int(identifier.rpartition('v')[2])
}
return data
def do_pre_checks(self, identifier):
self._submission_already_exists(identifier)
self._call_arxiv(identifier)
self._submission_is_already_published(identifier)
self._submission_previous_version_is_valid_for_submission(identifier)
class SubmissionIdentifierForm(SubmissionChecks, forms.Form):
IDENTIFIER_PATTERN_NEW = r'^[0-9]{4,}.[0-9]{4,5}v[0-9]{1,2}$'
IDENTIFIER_PLACEHOLDER = 'new style (with version nr) ####.####(#)v#(#)'
identifier = forms.RegexField(regex=IDENTIFIER_PATTERN_NEW, strip=True,
# help_text=strings.arxiv_query_help_text,
error_messages={'invalid': strings.arxiv_query_invalid},
widget=forms.TextInput({'placeholder': IDENTIFIER_PLACEHOLDER}))
def clean_identifier(self):
identifier = self.cleaned_data['identifier']
self.do_pre_checks(identifier)
return identifier
def _gather_data_from_last_submission(self):
'''Return dictionary with data coming from previous submission version.'''
if self.submission_is_resubmission():
data = {
'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_journal': self.last_submission.submitted_to_journal,
'submission_type': self.last_submission.submission_type,
}
return data or {}
def request_arxiv_preprint_form_prefill_data(self):
'''Return dictionary to prefill `RequestSubmissionForm`.'''
form_data = self.arxiv_data
form_data.update(self.identifier_into_parts(self.cleaned_data['identifier']))
if self.submission_is_resubmission():
form_data.update(self._gather_data_from_last_submission())
return form_data
class RequestSubmissionForm(SubmissionChecks, forms.ModelForm):
class Meta:
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_link',
'author_comments',
'list_of_changes',
'remarks_for_editors',
'referees_suggested',
'referees_flagged'
]
widgets = {
'is_resubmission': forms.HiddenInput(),
'arxiv_identifier_w_vn_nr': forms.HiddenInput(),
'secondary_areas': forms.SelectMultiple(choices=SCIPOST_SUBJECT_AREAS)
}
def __init__(self, *args, **kwargs):
self.requested_by = kwargs.pop('requested_by', None)
super().__init__(*args, **kwargs)
if not self.submission_is_resubmission():
# These fields are only available for resubmissions
del self.fields['author_comments']
del self.fields['list_of_changes']
else:
self.fields['author_comments'].widget.attrs.update({
'placeholder': 'Your resubmission letter (will be viewable online)', })
self.fields['list_of_changes'].widget.attrs.update({
'placeholder': 'Give a point-by-point list of changes (will be viewable online)'})
# Update placeholder for the other fields
self.fields['arxiv_link'].widget.attrs.update({
'placeholder': 'ex.: arxiv.org/abs/1234.56789v1'})
self.fields['abstract'].widget.attrs.update({'cols': 100})
self.fields['remarks_for_editors'].widget.attrs.update({
'placeholder': 'Any private remarks (for the editors only)', })
self.fields['referees_suggested'].widget.attrs.update({
'placeholder': 'Optional: names of suggested referees',
'rows': 3})
self.fields['referees_flagged'].widget.attrs.update({
'placeholder': ('Optional: names of referees whose reports should'
' be treated with caution (+ short reason)'),
'rows': 3})
def clean(self, *args, **kwargs):
"""
Do all prechecks which are also done in the prefiller.
"""
cleaned_data = super().clean(*args, **kwargs)
self.do_pre_checks(cleaned_data['arxiv_identifier_w_vn_nr'])
return cleaned_data
def clean_author_list(self):
"""
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.
"""
author_list = self.cleaned_data['author_list']
if not self.requested_by.last_name.lower() in author_list.lower():
error_message = ('Your name does not match that of any of the authors. '
'You are not authorized to submit this preprint.')
raise forms.ValidationError(error_message, code='not_an_author')
return author_list
@transaction.atomic
def copy_and_save_data_from_resubmission(self, submission):
"""
Fill given Submission with data coming from last_submission in the SubmissionChecks
blueprint.
"""
if not self.last_submission:
raise Submission.DoesNotExist
# Open for comment and reporting
submission.open_for_reporting = True
submission.open_for_commenting = True
# Close last submission
self.last_submission.is_current = False
self.last_submission.open_for_reporting = False
self.last_submission.status = STATUS_RESUBMITTED
self.last_submission.save()
# Editor-in-charge
submission.editor_in_charge = self.last_submission.editor_in_charge
submission.status = STATUS_RESUBMISSION_INCOMING
# Author claim fields
submission.authors.add(*self.last_submission.authors.all())
submission.authors_claims.add(*self.last_submission.authors_claims.all())
submission.authors_false_claims.add(*self.last_submission.authors_false_claims.all())
submission.save()
return submission
@transaction.atomic
def reassign_eic_and_admins(self, submission):
# Assign permissions
assign_perm('can_take_editorial_actions', submission.editor_in_charge.user, submission)
ed_admins = Group.objects.get(name='Editorial Administrators')
assign_perm('can_take_editorial_actions', ed_admins, submission)
# Assign editor
assignment = EditorialAssignment(
submission=submission,
to=submission.editor_in_charge,
accepted=True
)
assignment.save()
submission.save()
return submission
@transaction.atomic
def save(self):
"""
Prefill instance before save.
Because of the ManyToManyField on `authors`, commit=False for this form
is disabled. Saving the form without the database call may loose `authors`
data without notice.
"""
submission = super().save(commit=False)
submission.submitted_by = self.requested_by.contributor
# Save metadata directly from ArXiv call without possible user interception
submission.metadata = self.metadata
# Update identifiers
identifiers = self.identifier_into_parts(submission.arxiv_identifier_w_vn_nr)
submission.arxiv_identifier_wo_vn_nr = identifiers['arxiv_identifier_wo_vn_nr']
submission.arxiv_vn_nr = identifiers['arxiv_vn_nr']
# Save
submission.save()
if self.submission_is_resubmission():
submission = self.copy_and_save_data_from_resubmission(submission)
submission = self.reassign_eic_and_admins(submission)
submission.authors.add(self.requested_by.contributor)
return submission
class SubmissionReportsForm(forms.ModelForm):
class Meta:
model = Submission
fields = ['pdf_refereeing_pack']
######################
# Editorial workflow #
######################
class AssignSubmissionForm(forms.Form):
def __init__(self, *args, **kwargs):
discipline = kwargs.pop('discipline')
super(AssignSubmissionForm, self).__init__(*args, **kwargs)
self.fields['editor_in_charge'] = forms.ModelChoiceField(
queryset=Contributor.objects.filter(user__groups__name='Editorial College',
user__contributor__discipline=discipline,
).order_by('user__last_name'),
required=True, label='Select an Editor-in-charge')
class ConsiderAssignmentForm(forms.Form):
accept = forms.ChoiceField(widget=forms.RadioSelect, choices=ASSIGNMENT_BOOL,
label="Are you willing to take charge of this Submission?")
refusal_reason = forms.ChoiceField(choices=ASSIGNMENT_REFUSAL_REASONS, required=False)
class RefereeSelectForm(forms.Form):
last_name = forms.CharField()
def __init__(self, *args, **kwargs):
super(RefereeSelectForm, self).__init__(*args, **kwargs)
self.fields['last_name'].widget.attrs.update(
{'size': 20, 'placeholder': 'Search in contributors database'})
class RefereeRecruitmentForm(forms.ModelForm):
class Meta:
model = RefereeInvitation
fields = ['title', 'first_name', 'last_name', 'email_address']
def __init__(self, *args, **kwargs):
super(RefereeRecruitmentForm, self).__init__(*args, **kwargs)
self.fields['first_name'].widget.attrs.update({'size': 20})
self.fields['last_name'].widget.attrs.update({'size': 20})
class ConsiderRefereeInvitationForm(forms.Form):
accept = forms.ChoiceField(widget=forms.RadioSelect, choices=ASSIGNMENT_BOOL,
label="Are you willing to referee this Submission?")
refusal_reason = forms.ChoiceField(choices=ASSIGNMENT_REFUSAL_REASONS, required=False)
class SetRefereeingDeadlineForm(forms.Form):
deadline = forms.DateField(required=False, label='', widget=forms.SelectDateWidget)
def clean_deadline(self):
if not self.cleaned_data.get('deadline'):
self.add_error('deadline', 'Please use a valid date.')
return self.cleaned_data.get('deadline')
class VotingEligibilityForm(forms.Form):
def __init__(self, *args, **kwargs):
discipline = kwargs.pop('discipline')
subject_area = kwargs.pop('subject_area')
super(VotingEligibilityForm, self).__init__(*args, **kwargs)
self.fields['eligible_Fellows'] = forms.ModelMultipleChoiceField(
queryset=Contributor.objects.filter(
user__groups__name__in=['Editorial College'],
user__contributor__discipline=discipline,
user__contributor__expertises__contains=[subject_area]
).order_by('user__last_name'),
widget=forms.CheckboxSelectMultiple({'checked': 'checked'}),
required=True, label='Eligible for voting',
)
############
# Reports:
############
class ReportPDFForm(forms.ModelForm):
class Meta:
model = Report
fields = ['pdf_report']
class ReportForm(forms.ModelForm):
class Meta:
model = Report
fields = ['qualification', 'strengths', 'weaknesses', 'report', 'requested_changes',
'validity', 'significance', 'originality', 'clarity', 'formatting', 'grammar',
'recommendation', 'remarks_for_editors', 'anonymous']
def __init__(self, *args, **kwargs):
if kwargs.get('instance'):
if kwargs['instance'].is_followup_report:
# Prefill data from latest report in the series
latest_report = kwargs['instance'].latest_report_from_series()
kwargs.update({
'initial': {
'qualification': latest_report.qualification,
'anonymous': latest_report.anonymous
}
})
super(ReportForm, self).__init__(*args, **kwargs)
self.fields['strengths'].widget.attrs.update({
'placeholder': ('Give a point-by-point '
'(numbered 1-, 2-, ...) list of the paper\'s strengths'),
'rows': 10,
'cols': 100
})
self.fields['weaknesses'].widget.attrs.update({
'placeholder': ('Give a point-by-point '
'(numbered 1-, 2-, ...) list of the paper\'s weaknesses'),
'rows': 10,
'cols': 100
})
self.fields['report'].widget.attrs.update({'placeholder': 'Your general remarks',
'rows': 10, 'cols': 100})
self.fields['requested_changes'].widget.attrs.update({
'placeholder': 'Give a numbered (1-, 2-, ...) list of specifically requested changes',
'cols': 100
})
# If the Report is not a followup: Explicitly assign more fields as being required!
if not self.instance.is_followup_report:
required_fields = [
'strengths',
'weaknesses',
'requested_changes',
'validity',
'significance',
'originality',
'clarity',
'formatting',
'grammar'
]
for field in required_fields:
self.fields[field].required = True
# Let user know the field is required!
for field in self.fields:
if self.fields[field].required:
self.fields[field].label += ' *'
def save(self, submission):
"""
Update meta data if ModelForm is submitted (non-draft).
Possibly overwrite the default status if user asks for saving as draft.
"""
report = super().save(commit=False)
report.submission = submission
report.date_submitted = timezone.now()
# Save with right status asked by user
if 'save_draft' in self.data:
report.status = STATUS_DRAFT
elif 'save_submit' in self.data:
report.status = STATUS_UNVETTED
# Update invitation and report meta data if exist
invitation = submission.referee_invitations.filter(referee=report.author).first()
if invitation:
invitation.fulfilled = True
invitation.save()
report.invited = True
# Check if report author if the report is being flagged on the submission
if submission.referees_flagged:
if report.author.user.last_name in submission.referees_flagged:
report.flagged = True
report.save()
return report
class VetReportForm(forms.Form):
action_option = forms.ChoiceField(widget=forms.RadioSelect,
choices=REPORT_ACTION_CHOICES,
required=True, label='Action')
refusal_reason = forms.ChoiceField(choices=REPORT_REFUSAL_CHOICES, required=False)
email_response_field = forms.CharField(widget=forms.Textarea(),
label='Justification (optional)', required=False)
report = forms.ModelChoiceField(queryset=Report.objects.awaiting_vetting(), required=True,
widget=forms.HiddenInput())
def __init__(self, *args, **kwargs):
super(VetReportForm, self).__init__(*args, **kwargs)
self.fields['email_response_field'].widget.attrs.update({
'placeholder': ('Optional: give a textual justification '
'(will be included in the email to the Report\'s author)'),
'rows': 5
})
def clean_refusal_reason(self):
'''Require a refusal reason if report is rejected.'''
reason = self.cleaned_data['refusal_reason']
if self.cleaned_data['action_option'] == REPORT_ACTION_REFUSE:
if not reason:
self.add_error('refusal_reason', 'A reason must be given to refuse a report.')
return reason
def process_vetting(self, current_contributor):
'''Set the right report status and update submission fields if needed.'''
report = self.cleaned_data['report']
report.vetted_by = current_contributor
if self.cleaned_data['action_option'] == REPORT_ACTION_ACCEPT:
# Accept the report as is
report.status = STATUS_VETTED
report.submission.latest_activity = timezone.now()
report.submission.save()
elif self.cleaned_data['action_option'] == REPORT_ACTION_REFUSE:
# The report is rejected
report.status = self.cleaned_data['refusal_reason']
else:
raise exceptions.InvalidReportVettingValue(self.cleaned_data['action_option'])
report.save()
return report
###################
# Communications #
###################
class EditorialCommunicationForm(forms.Form):
text = forms.CharField(widget=forms.Textarea(), label='')
def __init__(self, *args, **kwargs):
super(EditorialCommunicationForm, self).__init__(*args, **kwargs)
self.fields['text'].widget.attrs.update(
{'rows': 5, 'cols': 50, 'placeholder': 'Write your message in this box.'})
######################
# EIC Recommendation #
######################
class EICRecommendationForm(forms.ModelForm):
class Meta:
model = EICRecommendation
fields = ['recommendation',
'remarks_for_authors', 'requested_changes',
'remarks_for_editorial_college']
def __init__(self, *args, **kwargs):
super(EICRecommendationForm, self).__init__(*args, **kwargs)
self.fields['remarks_for_authors'].widget.attrs.update(
{'placeholder': 'Your general remarks for the authors',
'rows': 10, 'cols': 100})
self.fields['requested_changes'].widget.attrs.update(
{'placeholder': 'If you request revisions, give a numbered (1-, 2-, ...) list of specifically requested changes',
'cols': 100})
self.fields['remarks_for_editorial_college'].widget.attrs.update(
{'placeholder': 'If you recommend to accept or refuse, the Editorial College will vote; write any relevant remarks for the EC here.'})
###############
# Vote form #
###############
class RecommendationVoteForm(forms.Form):
vote = forms.ChoiceField(widget=forms.RadioSelect,
choices=[('agree', 'Agree'),
('disagree', 'Disagree'),
('abstain', 'Abstain')],
label='',
)
remark = forms.CharField(widget=forms.Textarea(), label='', required=False)
def __init__(self, *args, **kwargs):
super(RecommendationVoteForm, self).__init__(*args, **kwargs)
self.fields['remark'].widget.attrs.update(
{'rows': 3, 'cols': 30, 'placeholder': 'Your remarks (optional)'})
class SubmissionCycleChoiceForm(forms.ModelForm):
referees_reinvite = forms.ModelMultipleChoiceField(queryset=RefereeInvitation.objects.none(),
widget=forms.CheckboxSelectMultiple({
'checked': 'checked'}),
required=False, label='Reinvite referees')
class Meta:
model = Submission
fields = ('refereeing_cycle',)
widgets = {'refereeing_cycle': forms.RadioSelect}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['refereeing_cycle'].default = None
other_submission = self.instance.other_versions.first()
if other_submission:
self.fields['referees_reinvite'].queryset = other_submission.referee_invitations.all()
class iThenticateReportForm(forms.ModelForm):
class Meta:
model = iThenticateReport
fields = []
def __init__(self, submission, *args, **kwargs):
self.submission = submission
super().__init__(*args, **kwargs)
if kwargs.get('files', {}).get('file'):
# Add file field if file data is coming in!
self.fields['file'] = forms.FileField()
def clean(self):
cleaned_data = super().clean()
doc_id = self.instance.doc_id
if not doc_id and not self.fields.get('file'):
try:
cleaned_data['document'] = helpers.retrieve_pdf_from_arxiv(
self.submission.arxiv_identifier_w_vn_nr)
except exceptions.ArxivPDFNotFound:
self.add_error(None, ('The pdf could not be found at arXiv.'
' Please upload the pdf manually.'))
self.fields['file'] = forms.FileField()
elif not doc_id and cleaned_data.get('file'):
cleaned_data['document'] = cleaned_data['file'].read()
elif doc_id:
self.document_id = doc_id
# Login client to append login-check to form
self.client = self.get_client()
if not self.client:
self.add_error(None, "Failed to login to iThenticate.")
return None
# Document (id) is found
if cleaned_data.get('document'):
self.document = cleaned_data['document']
self.response = self.call_ithenticate()
elif hasattr(self, 'document_id'):
self.response = self.call_ithenticate()
if hasattr(self, 'response') and self.response:
return cleaned_data
# Don't return anything as someone submitted invalid data for the form at this point!
return None
def save(self, *args, **kwargs):
data = self.response
report, created = iThenticateReport.objects.get_or_create(doc_id=data['id'])
if not created:
try:
iThenticateReport.objects.filter(doc_id=data['id']).update(
uploaded_time=data['uploaded_time'],
processed_time=data['processed_time'],
percent_match=data['percent_match'],
part_id=data.get('parts', [{}])[0].get('id')
)
except KeyError:
pass
else:
report.save()
self.submission.plagiarism_report = report
self.submission.save()
return report
def call_ithenticate(self):
if hasattr(self, 'document_id'):
# Update iThenticate status
return self.update_status()
elif hasattr(self, 'document'):
# Upload iThenticate document first time
return self.upload_document()
def get_client(self):
client = iThenticate.API.Client(settings.ITHENTICATE_USERNAME,
settings.ITHENTICATE_PASSWORD)
if client.login():
return client
self.add_error(None, "Failed to login to iThenticate.")
return None
def update_status(self):
client = self.client
response = client.documents.get(self.document_id)
if response['status'] == 200:
return response.get('data')[0].get('documents')
self.add_error(None, "Updating failed. iThenticate didn't return valid data [1]")
for msg in client.messages:
self.add_error(None, msg)
return None
def upload_document(self):
from .plagiarism import iThenticate
plagiarism = iThenticate()
data = plagiarism.upload_submission(self.document, self.submission)
# Give feedback to the user
if not data:
self.add_error(None, "Updating failed. iThenticate didn't return valid data [3]")
for msg in plagiarism.get_messages():
self.add_error(None, msg)
return None
return data
......@@ -2,6 +2,7 @@ from django.conf.urls import url
from django.views.generic import TemplateView
from . import views
from .constants import SUBMISSIONS_NO_VN_REGEX, SUBMISSIONS_COMPLETE_REGEX
urlpatterns = [
# Submissions
......@@ -14,25 +15,25 @@ urlpatterns = [
url(r'^author_guidelines$',
TemplateView.as_view(template_name='submissions/author_guidelines.html'),
name='author_guidelines'),
url(r'^(?P<arxiv_identifier_wo_vn_nr>[0-9]{4,}.[0-9]{5,})/$', views.submission_detail_wo_vn_nr,
url(r'^{regex}/$'.format(regex=SUBMISSIONS_NO_VN_REGEX), views.submission_detail_wo_vn_nr,
name='submission_wo_vn_nr'),
url(r'^(?P<arxiv_identifier_w_vn_nr>[0-9]{4,}.[0-9]{5,}v[0-9]{1,2})/$',
url(r'^{regex}/$'.format(regex=SUBMISSIONS_COMPLETE_REGEX),
views.submission_detail, name='submission'),
url(r'^(?P<arxiv_identifier_w_vn_nr>[0-9]{4,}.[0-9]{5,}v[0-9]{1,2})/reports/(?P<report_nr>[0-9]+)/pdf$',
url(r'^{regex}/reports/(?P<report_nr>[0-9]+)/pdf$'.format(regex=SUBMISSIONS_COMPLETE_REGEX),
views.report_detail_pdf, name='report_detail_pdf'),
url(r'^(?P<arxiv_identifier_w_vn_nr>[0-9]{4,}.[0-9]{5,}v[0-9]{1,2})/reports/pdf$',
url(r'^{regex}/reports/pdf$'.format(regex=SUBMISSIONS_COMPLETE_REGEX),
views.submission_refereeing_package_pdf, name='refereeing_package_pdf'),
# Editorial Administration
url(r'^admin$', views.EditorialSummaryView.as_view(), name='admin'),
url(r'^admin/treated$', views.treated_submissions_list, name='treated_submissions_list'),
url(r'^admin/(?P<arxiv_identifier_w_vn_nr>[0-9]{4,}.[0-9]{5,}v[0-9]{1,2})/reports/compile$',
url(r'^admin/{regex}/reports/compile$'.format(regex=SUBMISSIONS_COMPLETE_REGEX),
views.treated_submission_pdf_compile, name='treated_submission_pdf_compile'),
url(r'^admin/(?P<arxiv_identifier_w_vn_nr>[0-9]{4,}.[0-9]{5,}v[0-9]{1,2})/plagiarism$',
url(r'^admin/{regex}/plagiarism$'.format(regex=SUBMISSIONS_COMPLETE_REGEX),
views.PlagiarismView.as_view(), name='plagiarism'),
url(r'^admin/(?P<arxiv_identifier_w_vn_nr>[0-9]{4,}.[0-9]{5,}v[0-9]{1,2})/plagiarism/report$',
url(r'^admin/{regex}/plagiarism/report$'.format(regex=SUBMISSIONS_COMPLETE_REGEX),
views.PlagiarismReportPDFView.as_view(), name='plagiarism_report'),
url(r'^admin/(?P<arxiv_identifier_w_vn_nr>[0-9]{4,}.[0-9]{5,}v[0-9]{1,2})/recommendations/(?P<rec_id>[0-9]+)$',
url(r'^admin/{regex}/recommendations/(?P<rec_id>[0-9]+)$'.format(regex=SUBMISSIONS_COMPLETE_REGEX),
views.EICRecommendationView.as_view(), name='eic_recommendation_detail'),
url(r'^admin/reports$', views.reports_accepted_list, name='reports_accepted_list'),
url(r'^admin/reports/(?P<report_id>[0-9]+)/compile$',
......@@ -46,30 +47,30 @@ urlpatterns = [
url(r'^pool$', views.pool, name='pool'),
url(r'^submissions_by_status/(?P<status>[a-zA-Z_]+)$',
views.submissions_by_status, name='submissions_by_status'),
url(r'^add_remark/(?P<arxiv_identifier_w_vn_nr>[0-9]{4,}.[0-9]{5,}v[0-9]{1,2})$',
url(r'^add_remark/{regex}$'.format(regex=SUBMISSIONS_COMPLETE_REGEX),
views.add_remark, name='add_remark'),
# Assignment of Editor-in-charge
url(r'^assign_submission/(?P<arxiv_identifier_w_vn_nr>[0-9]{4,}.[0-9]{5,}v[0-9]{1,2})$',
url(r'^assign_submission/{regex}$'.format(regex=SUBMISSIONS_COMPLETE_REGEX),
views.assign_submission, name='assign_submission'),
url(r'^assign_submission_ack/(?P<arxiv_identifier_w_vn_nr>[0-9]{4,}.[0-9]{5,}v[0-9]{1,2})$',
url(r'^assign_submission_ack/{regex}$'.format(regex=SUBMISSIONS_COMPLETE_REGEX),
views.assign_submission_ack, name='assign_submission_ack'),
url(r'^accept_or_decline_assignment_ack/(?P<assignment_id>[0-9]+)$',
views.accept_or_decline_assignment_ack, name='accept_or_decline_assignment_ack'),
url(r'^volunteer_as_EIC/(?P<arxiv_identifier_w_vn_nr>[0-9]{4,}.[0-9]{5,}v[0-9]{1,2})$',
url(r'^volunteer_as_EIC/{regex}$'.format(regex=SUBMISSIONS_COMPLETE_REGEX),
views.volunteer_as_EIC, name='volunteer_as_EIC'),
url(r'^assignment_failed/(?P<arxiv_identifier_w_vn_nr>[0-9]{4,}.[0-9]{5,}v[0-9]{1,2})$',
url(r'^assignment_failed/{regex}$'.format(regex=SUBMISSIONS_COMPLETE_REGEX),
views.assignment_failed, name='assignment_failed'),
# Editorial workflow and refereeing
url(r'^editorial_workflow$', views.editorial_workflow, name='editorial_workflow'),
url(r'^assignments$', views.assignments, name='assignments'),
url(r'^editorial_page/(?P<arxiv_identifier_w_vn_nr>[0-9]{4,}.[0-9]{5,}v[0-9]{1,2})$',
url(r'^editorial_page/{regex}$'.format(regex=SUBMISSIONS_COMPLETE_REGEX),
views.editorial_page, name='editorial_page'),
url(r'^select_referee/(?P<arxiv_identifier_w_vn_nr>[0-9]{4,}.[0-9]{5,}v[0-9]{1,2})$',
url(r'^select_referee/{regex}$'.format(regex=SUBMISSIONS_COMPLETE_REGEX),
views.select_referee, name='select_referee'),
url(r'^recruit_referee/(?P<arxiv_identifier_w_vn_nr>[0-9]{4,}.[0-9]{5,}v[0-9]{1,2})$',
url(r'^recruit_referee/{regex}$'.format(regex=SUBMISSIONS_COMPLETE_REGEX),
views.recruit_referee, name='recruit_referee'),
url(r'^send_refereeing_invitation/(?P<arxiv_identifier_w_vn_nr>[0-9]{4,}.[0-9]{5,}v[0-9]{1,2})/(?P<contributor_id>[0-9]+)$',
url(r'^send_refereeing_invitation/{regex}/(?P<contributor_id>[0-9]+)$'.format(regex=SUBMISSIONS_COMPLETE_REGEX),
views.send_refereeing_invitation, name='send_refereeing_invitation'),
url(r'^accept_or_decline_ref_invitations/$',
views.accept_or_decline_ref_invitations, name='accept_or_decline_ref_invitations'),
......@@ -77,27 +78,28 @@ urlpatterns = [
views.accept_or_decline_ref_invitation_ack, name='accept_or_decline_ref_invitation_ack'),
url(r'^decline_ref_invitation/(?P<invitation_key>.+)$',
views.decline_ref_invitation, name='decline_ref_invitation'),
url(r'^ref_invitation_reminder/(?P<arxiv_identifier_w_vn_nr>[0-9]{4,}.[0-9]{5,}v[0-9]{1,2})/(?P<invitation_id>[0-9]+)$', views.ref_invitation_reminder, name='ref_invitation_reminder'),
url(r'^cancel_ref_invitation/(?P<arxiv_identifier_w_vn_nr>[0-9]{4,}.[0-9]{5,}v[0-9]{1,2})/(?P<invitation_id>[0-9]+)$',
url(r'^ref_invitation_reminder/{regex}/(?P<invitation_id>[0-9]+)$'.format(regex=SUBMISSIONS_COMPLETE_REGEX),
views.ref_invitation_reminder, name='ref_invitation_reminder'),
url(r'^cancel_ref_invitation/{regex}/(?P<invitation_id>[0-9]+)$'.format(regex=SUBMISSIONS_COMPLETE_REGEX),
views.cancel_ref_invitation, name='cancel_ref_invitation'),
url(r'^extend_refereeing_deadline/(?P<arxiv_identifier_w_vn_nr>[0-9]{4,}.[0-9]{5,}v[0-9]{1,2})/(?P<days>[0-9]+)$',
url(r'^extend_refereeing_deadline/{regex}/(?P<days>[0-9]+)$'.format(regex=SUBMISSIONS_COMPLETE_REGEX),
views.extend_refereeing_deadline, name='extend_refereeing_deadline'),
url(r'^set_refereeing_deadline/(?P<arxiv_identifier_w_vn_nr>[0-9]{4,}.[0-9]{5,}v[0-9]{1,2})$',
url(r'^set_refereeing_deadline/{regex}$'.format(regex=SUBMISSIONS_COMPLETE_REGEX),
views.set_refereeing_deadline, name='set_refereeing_deadline'),
url(r'^close_refereeing_round/(?P<arxiv_identifier_w_vn_nr>[0-9]{4,}.[0-9]{5,}v[0-9]{1,2})$',
url(r'^close_refereeing_round/{regex}$'.format(regex=SUBMISSIONS_COMPLETE_REGEX),
views.close_refereeing_round, name='close_refereeing_round'),
url(r'^refereeing_overview$', views.refereeing_overview, name='refereeing_overview'),
url(r'^communication/(?P<arxiv_identifier_w_vn_nr>[0-9]{4,}.[0-9]{5,}v[0-9]{1,2})/(?P<comtype>[a-zA-Z]{4,})$',
url(r'^communication/{regex}/(?P<comtype>[a-zA-Z]{{4,}})$'.format(regex=SUBMISSIONS_COMPLETE_REGEX),
views.communication, name='communication'),
url(r'^communication/(?P<arxiv_identifier_w_vn_nr>[0-9]{4,}.[0-9]{5,}v[0-9]{1,2})/(?P<comtype>[a-zA-Z]{4,})/(?P<referee_id>[0-9]+)$',
url(r'^communication/{regex}/(?P<comtype>[a-zA-Z]{{4,}})/(?P<referee_id>[0-9]+)$'.format(regex=SUBMISSIONS_COMPLETE_REGEX),
views.communication, name='communication'),
url(r'^eic_recommendation/(?P<arxiv_identifier_w_vn_nr>[0-9]{4,}.[0-9]{5,}v[0-9]{1,2})$',
url(r'^eic_recommendation/{regex}$'.format(regex=SUBMISSIONS_COMPLETE_REGEX),
views.eic_recommendation, name='eic_recommendation'),
url(r'^cycle/(?P<arxiv_identifier_w_vn_nr>[0-9]{4,}.[0-9]{5,}v[0-9]{1,2})/submit$',
url(r'^cycle/{regex}/submit$'.format(regex=SUBMISSIONS_COMPLETE_REGEX),
views.cycle_form_submit, name='cycle_confirmation'),
# Reports
url(r'^(?P<arxiv_identifier_w_vn_nr>[0-9]{4,}.[0-9]{5,}v[0-9]{1,2})/reports/submit$',
url(r'^{regex}/reports/submit$'.format(regex=SUBMISSIONS_COMPLETE_REGEX),
views.submit_report, name='submit_report'),
url(r'^reports/vet$', views.vet_submitted_reports_list, name='vet_submitted_reports_list'),
url(r'^reports/(?P<report_id>[0-9]+)/vet$', views.vet_submitted_report,
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment