diff --git a/news/migrations/0003_auto_20170812_0043.py b/news/migrations/0003_auto_20170812_0043.py new file mode 100644 index 0000000000000000000000000000000000000000..4b862c311c6ed9382219ace7139539387ec44ebf --- /dev/null +++ b/news/migrations/0003_auto_20170812_0043.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.3 on 2017-08-11 22:43 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('news', '0002_newsitem_on_homepage'), + ] + + operations = [ + migrations.AlterModelOptions( + name='newsitem', + options={'ordering': ['-date']}, + ), + ] diff --git a/scipost/management/commands/add_groups_and_permissions.py b/scipost/management/commands/add_groups_and_permissions.py index 9de95fd8b9fbd9a03677829ec90a66b3c329c83e..f002fb9b3c6bbd84a3503e1ed173ddac744036c5 100644 --- a/scipost/management/commands/add_groups_and_permissions.py +++ b/scipost/management/commands/add_groups_and_permissions.py @@ -163,6 +163,10 @@ class Command(BaseCommand): codename='can_read_all_eic_events', name='Can read all Editor-in-charge events', content_type=content_type) + can_do_plagiarism_checks, created = Permission.objects.get_or_create( + codename='can_do_plagiarism_checks', + name='Can do plagiarism checks on submissions', + content_type=content_type) # Submission handling can_view_pool, created = Permission.objects.get_or_create( @@ -268,6 +272,7 @@ class Command(BaseCommand): EditorialAdmin.permissions.set([ can_view_pool, can_assign_submissions, + can_do_plagiarism_checks can_oversee_refereeing, can_prepare_recommendations_for_voting, can_fix_College_decision, diff --git a/scipost/models.py b/scipost/models.py index 0ff51058b6f3eeee82f21da2eb3649443b170765..3017d58c3b9ccee75323d3344caef966aa9bd590 100644 --- a/scipost/models.py +++ b/scipost/models.py @@ -345,7 +345,6 @@ class AffiliationObject(models.Model): subunit = models.CharField(max_length=128) - ###################### # Static info models # ###################### diff --git a/scipost/templates/scipost/_personal_page_base.html b/scipost/templates/scipost/_personal_page_base.html index d49b0b375cca3ea9c00f41f6f7052379a5403476..2a62116b4a69f353a480e49b88570a6a77052e34 100644 --- a/scipost/templates/scipost/_personal_page_base.html +++ b/scipost/templates/scipost/_personal_page_base.html @@ -9,3 +9,5 @@ </div> </nav> {% endblock %} + +{% block container_class %}{{block.super}} pb-5{% endblock container_class %} diff --git a/submissions/managers.py b/submissions/managers.py index d6e0d2d81fee6daa16b4abd18bf19960ff279745..155f8b2437962c50960fafc0750a17fb75fda84c 100644 --- a/submissions/managers.py +++ b/submissions/managers.py @@ -7,7 +7,7 @@ from .constants import SUBMISSION_STATUS_OUT_OF_POOL, SUBMISSION_STATUS_PUBLICLY SUBMISSION_HTTP404_ON_EDITORIAL_PAGE, STATUS_DRAFT, STATUS_PUBLISHED,\ SUBMISSION_EXCLUDE_FROM_REPORTING, STATUS_REJECTED_VISIBLE,\ STATUS_ACCEPTED, STATUS_RESUBMITTED, STATUS_RESUBMITTED_REJECTED_VISIBLE,\ - EVENT_FOR_EIC, EVENT_GENERAL, EVENT_FOR_AUTHOR + EVENT_FOR_EIC, EVENT_GENERAL, EVENT_FOR_AUTHOR, STATUS_UNASSIGNED class SubmissionQuerySet(models.QuerySet): @@ -41,7 +41,7 @@ class SubmissionQuerySet(models.QuerySet): def get_pool(self, user): """ - This filter will return submission currently in an active submission cycle. + Return subset of active and newest 'alive' submissions. """ return (self.user_filter(user) .exclude(is_current=False) @@ -50,14 +50,30 @@ class SubmissionQuerySet(models.QuerySet): def filter_editorial_page(self, user): """ - This filter returns a subgroup of the `get_pool` filter, to allow opening and editing - certain submissions that are officially out of the submission cycle i.e. due - to resubmission, but should still have the possibility to be opened by the EIC. + Return Submissions currently 'alive' (being refereed, not published). + + It is meant to allow opening and editing certain submissions that are officially + out of the submission cycle i.e. due to resubmission, but should still have the + possibility to be opened by the EIC. """ return (self.user_filter(user) .exclude(status__in=SUBMISSION_HTTP404_ON_EDITORIAL_PAGE) .order_by('-submission_date')) + def prescreening(self): + """ + Return submissions just coming in and going through pre-screening. + """ + return self.filter(status=STATUS_UNASSIGNED) + + def actively_refereeing(self): + """ + Return submission currently in some point of the refereeing round. + """ + return (self.exclude(is_current=False) + .exclude(status__in=SUBMISSION_STATUS_OUT_OF_POOL) + .exclude(status=STATUS_UNASSIGNED)) + def public(self): """ This query contains set of public submissions, i.e. also containing @@ -82,13 +98,6 @@ class SubmissionQuerySet(models.QuerySet): """ return self._newest_version_only(self.public()) - def open_for_reporting(self): - """ - This query should filter submissions that do not have the right status to receive - a new report. - """ - return self.exclude(status__in=SUBMISSION_EXCLUDE_FROM_REPORTING) - def treated(self): """ This query returns all Submissions that are expected to be 'done'. @@ -99,7 +108,16 @@ class SubmissionQuerySet(models.QuerySet): def accepted(self): return self.filter(status=STATUS_ACCEPTED) + def open_for_reporting(self): + """ + Return Submissions that have appriopriate status for reporting. + The `open_for_reporting` property is not filtered as some invited visitors + still need to have access. + """ + return self.exclude(status__in=SUBMISSION_EXCLUDE_FROM_REPORTING) + def open_for_commenting(self): + """ Return Submission that allow for commenting. """ return self.filter(open_for_commenting=True) diff --git a/submissions/migrations/0063_auto_20170812_0043.py b/submissions/migrations/0063_auto_20170812_0043.py new file mode 100644 index 0000000000000000000000000000000000000000..682e01411afa60859b995bb8565f80f893484ea0 --- /dev/null +++ b/submissions/migrations/0063_auto_20170812_0043.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.3 on 2017-08-11 22:43 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone +import scipost.db.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('submissions', '0062_auto_20170727_1032'), + ] + + operations = [ + migrations.CreateModel( + name='iThenticateReport', + fields=[ + ('created', models.DateTimeField(default=django.utils.timezone.now)), + ('latest_activity', scipost.db.fields.AutoDateTimeField(blank=True, default=django.utils.timezone.now, editable=False)), + ('uploaded_time', models.DateTimeField(blank=True, null=True)), + ('processed_time', models.DateTimeField(blank=True, null=True)), + ('doc_id', models.IntegerField(primary_key=True, serialize=False)), + ('percent_match', models.IntegerField(blank=True, null=True)), + ], + options={ + 'abstract': False, + }, + ), + migrations.AddField( + model_name='submission', + name='plagiarism_report', + field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='to_submission', to='submissions.iThenticateReport'), + ), + ] diff --git a/submissions/mixins.py b/submissions/mixins.py index 78981c04a8c2a2e7841264b61b23e0177d29e5f2..133c88447706921daa9ef668d07473ae08d3985f 100644 --- a/submissions/mixins.py +++ b/submissions/mixins.py @@ -25,8 +25,6 @@ class SubmissionAdminViewMixin(FriendlyPermissionMixin): This mixin will provide all basic methods and checks required for Submission administrational actions regarding Submissions. - It assumes being mixed with either one of the `django.views.generic.detail` views. - :editorial_page: Submission is element of the set pool() if False, else Submission is element of the subset editorial_page() """ @@ -35,10 +33,12 @@ class SubmissionAdminViewMixin(FriendlyPermissionMixin): slug_url_kwarg = 'arxiv_identifier_w_vn_nr' queryset = Submission.objects.all() + @property + def pool(self): + return not self.editorial_page + def get_queryset(self): qs = super().get_queryset() - if self.editorial_page: - qs = qs.filter_editorial_page(self.request.user) - else: - qs = qs.get_pool(self.request.user) - return qs + if self.pool: + return qs.get_pool(self.request.user) + return qs.filter_editorial_page(self.request.user) diff --git a/submissions/models.py b/submissions/models.py index 779d1be3f6a4d18e8c07583b714cafb4055fcff0..9f973237aa49f16dff0cd270684a4ff0f20dae02 100644 --- a/submissions/models.py +++ b/submissions/models.py @@ -79,6 +79,12 @@ class Submission(models.Model): # Comments can be added to a Submission comments = GenericRelation('comments.Comment', related_query_name='submissions') + # iThenticate Reports + plagiarism_report = models.OneToOneField('submissions.iThenticateReport', + on_delete=models.SET_NULL, + null=True, blank=True, + related_name='to_submission') + # 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') @@ -489,3 +495,11 @@ class EICRecommendation(models.Model): @property def nr_abstained(self): return self.voted_abstain.count() + + +class iThenticateReport(TimeStampedModel): + # is_pending = models.BooleanField(default=True) + uploaded_time = models.DateTimeField(null=True, blank=True) + processed_time = models.DateTimeField(null=True, blank=True) + doc_id = models.IntegerField(primary_key=True) + percent_match = models.IntegerField(null=True, blank=True) diff --git a/submissions/templates/submissions/editorial_admin_plagiarism.html b/submissions/templates/submissions/editorial_admin_plagiarism.html new file mode 100644 index 0000000000000000000000000000000000000000..1732117652b9048ddd95e97f330dd34a18f5dff2 --- /dev/null +++ b/submissions/templates/submissions/editorial_admin_plagiarism.html @@ -0,0 +1,24 @@ +{% extends 'scipost/_personal_page_base.html' %} + +{% block pagetitle %}: plagiarism report ({{ submission.arxiv_identifier_w_vn_nr }}){% endblock pagetitle %} + +{% block breadcrumb_items %} + {{block.super}} + <a href="{% url 'submissions:admin_summary' %}" class="breadcrumb-item">Editorial Administration</a> + <span class="breadcrumb-item">Plagiarism Report ({{ submission.arxiv_identifier_w_vn_nr }})</span> +{% endblock %} + +{% block content %} + <h1>Plagiarism Report for <a href="{{submission.get_absolute_url}}">{{submission.arxiv_identifier_w_vn_nr}}</a></h1> + {% if submission.plagiarism_report %} + {{submission.plagiarism_report}} + {% else %} + No Plagiarism Report found. + {% endif %} + + <form method="post" class="mt-3"> + {% csrf_token %} + <input type="submit" class="btn btn-primary" value="{% if submission.plagiarism_report %}Update report status{% else %}Submit submission for plagiarism check{% endif %}"> + </form> + +{% endblock content %} diff --git a/submissions/templates/submissions/editorial_admin_summary.html b/submissions/templates/submissions/editorial_admin_summary.html new file mode 100644 index 0000000000000000000000000000000000000000..78747d09acb8dcba59c5a32643737b2fc6b4f508 --- /dev/null +++ b/submissions/templates/submissions/editorial_admin_summary.html @@ -0,0 +1,39 @@ +{% extends 'scipost/_personal_page_base.html' %} + +{% block pagetitle %}: Editorial Administration{% endblock pagetitle %} + +{% block breadcrumb_items %} + {{block.super}} + <span class="breadcrumb-item">Editorial Administration</span> +{% endblock %} + +{% block content %} + <h1>Summary for Editorial Administrators</h1> + + <h3>Submissions currently in pre-screening</h3> + <ul class="unstyled-list"> + {% for submission in submission_list.prescreening %} + <li>{{submission}}</li> + {% empty %} + <li>No Submissions are currently in pre-screening</li> + {% endfor %} + </ul> + + <h3>Submissions currently in refereeing round</h3> + <ul class="unstyled-list"> + {% for submission in submission_list.actively_refereeing %} + <li>{{submission}}<br>{{submission.get_status_display}}</li> + {% empty %} + <li>No Submissions are currently in refereeing round</li> + {% endfor %} + </ul> + + <h3>Submissions accepted</h3> + <ul class="unstyled-list"> + {% for submission in submission_list.accepted %} + <li>{{submission}}</li> + {% empty %} + <li>All accepted Submissions are published</li> + {% endfor %} + </ul> +{% endblock content %} diff --git a/submissions/urls.py b/submissions/urls.py index 84b9ef25a586f146df2f7950e5a94fd04cc60ca8..cfe6906e4d7f9064c8f23883f2550fdaf268eee0 100644 --- a/submissions/urls.py +++ b/submissions/urls.py @@ -24,6 +24,7 @@ urlpatterns = [ views.submission_refereeing_package_pdf, name='refereeing_package_pdf'), # Editorial Administration + url(r'^admin$', views.EditorialSummaryView.as_view(), name='admin_summary'), 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$', views.treated_submission_pdf_compile, name='treated_submission_pdf_compile'), diff --git a/submissions/views.py b/submissions/views.py index ce45302e69516811d1f769ab6dac2b8b3ecd803e..de0ffd7e000bf37b627fb6936c910fce0800cc2b 100644 --- a/submissions/views.py +++ b/submissions/views.py @@ -1,6 +1,7 @@ import datetime import feedparser +from django.conf import settings from django.contrib import messages from django.contrib.auth.decorators import login_required, permission_required from django.contrib.auth.models import Group @@ -1434,5 +1435,23 @@ def fix_College_decision(request, rec_id): return redirect(reverse('submissions:pool')) +class EditorialSummaryView(SubmissionAdminViewMixin, ListView): + permission_required = 'scipost.can_oversee_refereeing' + template_name = 'submissions/editorial_admin_summary.html' + + class PlagiarismView(SubmissionAdminViewMixin, DetailView): - template_name = '500.html' + permission_required = 'scipost.can_do_plagiarism_checks' + template_name = 'submissions/editorial_admin_plagiarism.html' + + def post(self, request, *args, **kwargs): + client = iThenticate.API.Client(settings.ITHENTICATE_USERNAME, + settings.ITHENTICATE_PASSWORD) + submission = self.get_object() + if submission.plagiarism_report: + # Plagiarism Report needs an update + client.documents.get() + else: + # Plagiarism Report needs to be uploaded still + client.folders.all() + raise NotImplementedError