diff --git a/SciPost_v1/settings/base.py b/SciPost_v1/settings/base.py index 28b54d4d47b36226691056e38982ab5f4569efe6..1a8895ceea48d95034c0b14f10c83a6086c8d3d1 100644 --- a/SciPost_v1/settings/base.py +++ b/SciPost_v1/settings/base.py @@ -236,6 +236,12 @@ MAILCHIMP_DATABASE_CODE = 'us6' MAILCHIMP_API_USER = 'test_API-user' MAILCHIMP_API_KEY = 'test_API-key' + +# iThenticate +ITHENTICATE_USERNAME = 'test_ithenticate_username' +ITHENTICATE_PASSWORD = 'test_ithenticate_password' + + # Own settings JOURNALS_DIR = 'journals' diff --git a/SciPost_v1/settings/local_jorran.py b/SciPost_v1/settings/local_jorran.py index 9b0833751e951b28d7e4727fbf642ed9a5ffd0b0..f1f650d012c570fc7a512b1dbc8269640a9e66f1 100644 --- a/SciPost_v1/settings/local_jorran.py +++ b/SciPost_v1/settings/local_jorran.py @@ -22,3 +22,7 @@ MAILCHIMP_API_USER = get_secret("MAILCHIMP_API_USER") MAILCHIMP_API_KEY = get_secret("MAILCHIMP_API_KEY") DATABASES['default']['PORT'] = '5433' + +# iThenticate +ITHENTICATE_USERNAME = get_secret('ITHENTICATE_USERNAME') +ITHENTICATE_PASSWORD = get_secret('ITHENTICATE_PASSWORD') diff --git a/journals/templates/journals/create_metadata_xml.html b/journals/templates/journals/create_metadata_xml.html index f3fef831804f54439c2bc9c56553e12761afa422..02603da8d4dab8ef25ff0646321d0d3a77588f03 100644 --- a/journals/templates/journals/create_metadata_xml.html +++ b/journals/templates/journals/create_metadata_xml.html @@ -41,50 +41,12 @@ <h3>Current metadata xml:</h3> <div> - <pre><code> - {{ publication.metadata_xml|linebreaks }} - </code></pre> + <pre><code>{{ publication.metadata_xml|linebreaksbr }}</code></pre> </div> - <hr class="hr6"/> - <h3>Once you're happy with this metadata, you can <a href="{{publication.get_absolute_url}}">return to the publication's page</a> or to the <a href="{% url 'journals:manage_metadata' %}">metadata management page</a></h3> </div> </div> - {% endblock content %} -======= -{% block bodysup %} - - -<section> - <div class="flex-greybox"> - <h1>Create metadata XML (for Crossref deposit)</h1> - </div> - - {% if errormessage %} - <h2 style="color: red;">{{ errormessage }}</h2> - {% endif %} - - <form action="{% url 'journals:create_metadata_xml' publication.doi_label %}" method="post"> - {% csrf_token %} - {{ create_metadata_xml_form.as_p }} - <input type="submit" value="Accept the metadata"> - </form> - - <hr class="hr6"/> - - <h3>Current metadata xml:</h3> - <p>{{ publication.metadata_xml|linebreaks }}</p> - - <hr class="hr6"/> - - <h3>Once you're happy with this metadata, you can <a href="{{publication.get_absolute_url}}">return to the publication's page</a> or to the <a href="{% url 'journals:manage_metadata' %}">general metadata management page</a> or to <a href="{% url 'journals:manage_metadata' doi_label=publication.doi_label %}">this publication's metadata management page</a></h3> - -</section> - - -{% endblock bodysup %} ->>>>>>> development diff --git a/journals/templates/journals/harvest_citedby_links.html b/journals/templates/journals/harvest_citedby_links.html index 5d10c46e5af66d536f061f553a2058d49d47f7f7..9a9b94b4a44a79229887b759cb01d706f2adc289 100644 --- a/journals/templates/journals/harvest_citedby_links.html +++ b/journals/templates/journals/harvest_citedby_links.html @@ -2,38 +2,41 @@ {% block pagetitle %}: harvest cited-by links{% endblock pagetitle %} -{% block bodysup %} +{% block content %} -<section> - <div class="flex-greybox"> - <h1>Cited-by links</h1> - </div> +<div class="row"> + <div class="col-12"> + <h1>Cited-by links</h1> - {% if errormessage %} - <h2 style="color: red;">{{ errormessage }}</h2> - {% endif %} - <h3>Response headers:</h3> - <p>{{ response_headers|linebreaks }}</p> + {% if errormessage %} + <h2 style="color: red;">{{ errormessage }}</h2> + {% endif %} - <h3>Response text:</h3> - <p>{{ response_text|linebreaks }}</p> + <h3>Response headers:</h3> + <p>{{ response_headers|linebreaks }}</p> - <h3>Response (deserialized):</h3> - <p>{{ response_deserialized }}</p> - <ul> - {% for obj in response_deserialized %} - <li>{{ obj }} - <ul> - {% for obj2 in obj %} - <li>{{ obj2 }} + <h3>Response text:</h3> + <p>{{ response_text|linebreaks }}</p> + + <h3>Response (deserialized):</h3> + <p>{{ response_deserialized }}</p> + <ul> + {% for obj in response_deserialized %} + <li>{{ obj }} <ul> - {% for obj3 in obj2 %} - <li>{{ obj3 }} + {% for obj2 in obj %} + <li>{{ obj2 }} <ul> - {% for obj4 in obj3 %} - <li>{{ obj4 }}</li> + {% for obj3 in obj2 %} + <li>{{ obj3 }} + <ul> + {% for obj4 in obj3 %} + <li>{{ obj4 }}</li> + {% endfor %} + </ul> + </li> {% endfor %} </ul> </li> @@ -41,20 +44,17 @@ </ul> </li> {% endfor %} - </ul> - </li> - {% endfor %} - </ul> - - <h3>Citations</h3> - {% for cit in citations %} - {{ cit }} - {% endfor %} - {{ nr }} + </ul> - <h3><a href="{{publication.get_absolute_url}}">return to the publication's page</a> or to the <a href="{% url 'journals:manage_metadata' %}">metadata management page</a> or to <a href="{% url 'journals:manage_metadata' doi_label=publication.doi_label %}">this publication's metadata management page</a></h3> + <h3>Citations</h3> + {% for cit in citations %} + {{ cit }} + {% endfor %} + {{ nr }} -</section> + <h3><a href="{{publication.get_absolute_url}}">return to the publication's page</a> or to the <a href="{% url 'journals:manage_metadata' %}">metadata management page</a> or to <a href="{% url 'journals:manage_metadata' doi_label=publication.doi_label %}">this publication's metadata management page</a></h3> + </div> +</div> -{% endblock bodysup %} +{% endblock content %} diff --git a/journals/templates/journals/initiate_publication.html b/journals/templates/journals/initiate_publication.html index c4946383e32beedf591ade9228a6f4cb32745828..c9fa7acc87fb2a42b4829a2ce0363c7bec8d1863 100644 --- a/journals/templates/journals/initiate_publication.html +++ b/journals/templates/journals/initiate_publication.html @@ -1,26 +1,21 @@ {% extends 'scipost/base.html' %} -{% block pagetitle %}: Initiate publication{% endblock pagetitle %} +{% load bootstrap %} -{% block bodysup %} +{% block pagetitle %}: Initiate publication{% endblock pagetitle %} +{% block content %} -<section> - <div class="flex-greybox"> - <h1>Initiate publication page</h1> - </div> - {% if errormessage %} - <h2 style="color: red;">{{ errormessage }}</h2> - {% endif %} +<h1>Initiate publication page</h1> - <form action="{% url 'journals:initiate_publication' %}" method="post" enctype="multipart/form-data"> +<form action="{% url 'journals:initiate_publication' %}" method="post" enctype="multipart/form-data"> {% csrf_token %} - {{ initiate_publication_form.as_p }} - <input type="submit" value="Submit"> - </form> + {{ initiate_publication_form|bootstrap }} + <input type="submit" class="btn btn-primary" value="Submit"> +</form> + -</section> -{% endblock bodysup %} +{% endblock %} 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/requirements.txt b/requirements.txt index fe99a6a6fd57b124ee6a3bc066c10aa7087b4092..3971b15ab259191d78dff68dd758c3a747583e93 100644 --- a/requirements.txt +++ b/requirements.txt @@ -21,6 +21,7 @@ factory-boy==2.7.0 Faker==0.7.11 fake-factory==0.7.2 feedparser==5.2.1 +ithenticate-api-python==0.3 imagesize==0.7.1 Jinja2==2.8 mailchimp3==2.0.11 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/static/scipost/assets/css/_alert.scss b/scipost/static/scipost/assets/css/_alert.scss index 395845eaaa7ee9cb89be5c18455c349ec681b35b..3556a5aaee18e4be626f6682f8ecfb66f18ce2dd 100644 --- a/scipost/static/scipost/assets/css/_alert.scss +++ b/scipost/static/scipost/assets/css/_alert.scss @@ -5,6 +5,11 @@ clear: both; float: left; + form &, + .form & { + float: none; + } + &:last-child { margin-bottom: 1rem; } diff --git a/scipost/static/scipost/assets/css/_breadcrumb.scss b/scipost/static/scipost/assets/css/_breadcrumb.scss index 1da897ebf0d61707eb8641737dd725d427597d1f..1b59ea0a01aefadab30da6a58def6b22cbbe8d2a 100644 --- a/scipost/static/scipost/assets/css/_breadcrumb.scss +++ b/scipost/static/scipost/assets/css/_breadcrumb.scss @@ -1,6 +1,6 @@ .breadcrumb { - margin-top: -0.75rem; - margin-bottom: 0.75rem; + margin-top: -1.5rem; + margin-bottom: 1.5rem; border-top: 1px solid #fff; border-bottom: 1px solid #ddd; diff --git a/scipost/static/scipost/assets/css/_general.scss b/scipost/static/scipost/assets/css/_general.scss index 0021920522e687e5f92235ec65bfe2a74dc02314..bd291703ecce886c40d13ad9a4f17b24884c2537 100644 --- a/scipost/static/scipost/assets/css/_general.scss +++ b/scipost/static/scipost/assets/css/_general.scss @@ -35,3 +35,7 @@ footer .logos a { max-width: 100px; padding: 0 0.25rem; } + +body > .container { + padding-bottom: 1.5rem; +} diff --git a/scipost/static/scipost/assets/css/_list_group.scss b/scipost/static/scipost/assets/css/_list_group.scss index 835bd38a5a4b649a88d37390a38d5e0f7d622602..116dfc7010ba3bab58767c23669fe593a5d928d1 100644 --- a/scipost/static/scipost/assets/css/_list_group.scss +++ b/scipost/static/scipost/assets/css/_list_group.scss @@ -11,43 +11,37 @@ ul.events-list { padding-left: 30px; - &:before { - content: ''; - width: 3px; - height: calc(100% - 16px); - position: absolute; - left: 20px; - background: $scipost-darkblue; - top: 16px; - z-index: 97; - } - li { padding-top: 0.5rem; padding-bottom: 0.5rem; &:before { content: ''; - width: 13px; - height: 13px; - left: -25px; + width: 12px; + height: 12px; + left: -24px; border-radius: 99px; position: absolute; top: 11px; background: #fff; - border: 3px solid $scipost-darkblue; + border: 2px solid $scipost-darkblue; z-index: 99; } - &:last-child:after { + &:after { content: ''; - width: 3px; + width: 2px; height: calc(100% - 16px); position: absolute; - left: -20px; - background: #fff; - bottom: 0; - z-index: 98; + left: -19px; + background: $scipost-darkblue;; + top: 26px; + z-index: 97; + border-radius: 99px; + } + + &:last-child:after { + content: none; } } } diff --git a/scipost/static/scipost/assets/css/_nav.scss b/scipost/static/scipost/assets/css/_nav.scss index 9f69a08b97a0434fa678082f00991d336ae44663..4ff137786c65dd73d06efbb4dea0415f80e395cb 100644 --- a/scipost/static/scipost/assets/css/_nav.scss +++ b/scipost/static/scipost/assets/css/_nav.scss @@ -64,7 +64,7 @@ } nav.submenu { - margin-top: -0.75rem; + margin-top: -1.5rem; margin-bottom: 1rem; padding: 0.75rem 1rem; background-color: $scipost-white; diff --git a/scipost/static/scipost/assets/css/_navbar.scss b/scipost/static/scipost/assets/css/_navbar.scss index 6fa3d115318e741b69b899c7bccf9c422fe71ab0..b067e77bb63204258e1f3507cc32da8905efd6fd 100644 --- a/scipost/static/scipost/assets/css/_navbar.scss +++ b/scipost/static/scipost/assets/css/_navbar.scss @@ -3,7 +3,7 @@ * */ .navbar { - margin-bottom: 0.75rem; + margin-bottom: 1.5rem; &.main-nav { border-bottom: 1px solid #ddd; diff --git a/scipost/static/scipost/assets/css/_tooltip.scss b/scipost/static/scipost/assets/css/_tooltip.scss index dd8445262fc4f7e9b5d1ce039a73a942668e3561..11b5c6cf5c0a154a99830d333f0240611eae1a54 100644 --- a/scipost/static/scipost/assets/css/_tooltip.scss +++ b/scipost/static/scipost/assets/css/_tooltip.scss @@ -8,4 +8,11 @@ .tooltip-inner { border-radius: 0.15rem; + max-width: none; + + hr { + background-color: #fff; + margin: 0.25rem 0; + box-shadow: none; + } } diff --git a/scipost/static/scipost/assets/css/_type.scss b/scipost/static/scipost/assets/css/_type.scss index 47d2ee08d44b92cdecbbf268576a6b3f2bcc1ea4..b977a46b47376d351398b5b1eb5263cee18a4de4 100644 --- a/scipost/static/scipost/assets/css/_type.scss +++ b/scipost/static/scipost/assets/css/_type.scss @@ -106,3 +106,23 @@ hr.hr12 { .blockquote > p:last-child { margin-bottom: 0; } + +.circle-clickable { + display: inline-block; + cursor: pointer; + background: #fff; + border: 2px solid #bbb; + border-radius: 100%; + color: #bbb; + width: 17px; + height: 17px; + line-height: 16px; + text-align: center; + font-size: 9px; + transition: 0.1s; + + &:hover { + border-color: #333; + color: #333; + } +} 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/scipost/templates/scipost/about.html b/scipost/templates/scipost/about.html index eb6f866f1309ef2daf8f9ace35206b52e700b331..b4a1fba125f328492168844f124c3b0400ecd0a9 100644 --- a/scipost/templates/scipost/about.html +++ b/scipost/templates/scipost/about.html @@ -76,7 +76,7 @@ <div class="col-md-3 offset-md-1"> <h3><a href="{% url 'scipost:foundation' %}">The SciPost Foundation</a></h3> <ul> - <li>Chairman: Prof. <a href="http://jscaux.org">J.-S. Caux</a></li> + <li>Chairman: Prof. <a href="http://jscaux.org" target="_blank">J.-S. Caux</a></li> <li>Secretary: Dr J. van Mameren</li> <li>Treasurer: Dr J. van Wezel</li> </ul> diff --git a/scipost/templates/scipost/already_activated.html b/scipost/templates/scipost/already_activated.html deleted file mode 100644 index e2b5be6a5a8beaaa350a9e6a11626002234061b0..0000000000000000000000000000000000000000 --- a/scipost/templates/scipost/already_activated.html +++ /dev/null @@ -1,12 +0,0 @@ -{% extends 'scipost/base.html' %} - -{% block pagetitle %}: already activated (email confirmed){% endblock pagetitle %} - -{% block bodysup %} - -<section> - <h1>Your email has already been confirmed.</h1> - <p>Please wait for vetting of your registration. We shall strive to send you an update by email within 24 hours.</p> -</section> - -{% endblock bodysup %} diff --git a/scipost/templates/scipost/base.html b/scipost/templates/scipost/base.html index c223d7c9a461da9988ee6e44efb9e435813dceff..5a0d411a2566f9f7caa2c81743e4e33865f2ea16 100644 --- a/scipost/templates/scipost/base.html +++ b/scipost/templates/scipost/base.html @@ -9,9 +9,6 @@ {% block content_footer %}{% endblock content_footer %} </div> - {% block bodysup %} - {% endblock bodysup %} - {% block secondary_footer %}{% endblock secondary_footer %} {% include 'scipost/footer.html' %} diff --git a/scipost/templates/scipost/base_for_sidebar.html b/scipost/templates/scipost/base_for_sidebar.html index 86d25e5bace4d9fcf476e55640bb601c80f07ec1..6c413c9a89a80652d3270455b33cc28e794880ea 100644 --- a/scipost/templates/scipost/base_for_sidebar.html +++ b/scipost/templates/scipost/base_for_sidebar.html @@ -11,9 +11,6 @@ {% block content_footer %}{% endblock content_footer %} </div> - {% block bodysup %} - {% endblock bodysup %} - {% block secondary_footer %}{% endblock secondary_footer %} </div> diff --git a/scipost/templates/scipost/edit_invitation_personal_message.html b/scipost/templates/scipost/edit_invitation_personal_message.html index c66d5269321b4c6deca125a61dbebdf5f08c55ad..5a63e60729afb5e3f300043644f926ab7cfbb2e7 100644 --- a/scipost/templates/scipost/edit_invitation_personal_message.html +++ b/scipost/templates/scipost/edit_invitation_personal_message.html @@ -1,23 +1,21 @@ {% extends 'scipost/base.html' %} +{% load scipost_extras %} +{% load bootstrap %} + {% block pagetitle %}: registration invitation: edit personal message{% endblock pagetitle %} -{% block bodysup %} +{% block content %} -{% load scipost_extras %} +<h2>Edit invitation's personal message: for {{ invitation.first_name }} {{ invitation.last_name }}</h2> -<section> - <div class="flex-greybox"> - <h2>Edit invitation's personal message: for {{ invitation.first_name }} {{ invitation.last_name }}</h2> - </div> - {% if errormessage %} - <h3 style="color: red;">{{ errormessage }}</h3> - {% endif %} - <form action="{% url 'scipost:edit_invitation_personal_message' invitation_id=invitation.id %}" method="post"> +{% if errormessage %} + <h3 class="text-danger">{{ errormessage }}</h3> +{% endif %} +<form action="{% url 'scipost:edit_invitation_personal_message' invitation_id=invitation.id %}" method="post"> {% csrf_token %} - {{ form }} - <input type="submit" value="Submit"> - </form> -</section> + {{ form|bootstrap }} + <input type="submit" value="Submit" class="btn btn-primary"> +</form> -{% endblock bodysup %} +{% endblock %} diff --git a/scipost/templates/scipost/error.html b/scipost/templates/scipost/error.html index 75741f8c77c47fd87d2e66a5981fe0a315563421..6a638b54518799f32308753ca02c9313e4d06431 100644 --- a/scipost/templates/scipost/error.html +++ b/scipost/templates/scipost/error.html @@ -2,11 +2,9 @@ {% block pagetitle %}: error{% endblock pagetitle %} -{% block bodysup %} +{% block content %} -<section> - <h3 style="color: red;">Error</h3> - <p>{{ errormessage }}</p> -</section> +<h3 class="text-danger">Error</h3> +<p>{{ errormessage }}</p> -{% endblock bodysup %} +{% endblock %} diff --git a/scipost/templates/scipost/navbar.html b/scipost/templates/scipost/navbar.html index bd532a8c97695037aed64adb9b47b9479655505e..1fe3d19ff1f98cd673abec4a3173515d6e054274 100644 --- a/scipost/templates/scipost/navbar.html +++ b/scipost/templates/scipost/navbar.html @@ -24,6 +24,11 @@ <li class="nav-item highlighted"> <span class="nav-link">Logged in as {{ user.username }}</span> </li> + {% if perms.scipost.can_oversee_refereeing %} + <li class="nav-item{% if '/submissions/admin' in request.path %} active{% endif %}"> + <a class="nav-link" href="{% url 'submissions:admin' %}">Editorial Administration</a> + </li> + {% endif %} {% if user.contributor %} <li class="nav-item{% if '/personal_page' in request.path %} active{% endif %}"> <a class="nav-link" href="{% url 'scipost:personal_page' %}">Personal Page</a> diff --git a/scipost/templates/scipost/registration_invitation_sent.html b/scipost/templates/scipost/registration_invitation_sent.html index b58b41fbdec6e9fbaf2ec119c9088ae05117527c..f2908e563f0affa74ab03d291cfa0f62a1d13e15 100644 --- a/scipost/templates/scipost/registration_invitation_sent.html +++ b/scipost/templates/scipost/registration_invitation_sent.html @@ -2,13 +2,9 @@ {% block pagetitle %}: registration invitation sent{% endblock pagetitle %} -{% block bodysup %} +{% block content %} -<section> - <h1>Registration Invitation sent</h1> +<h1>Registration Invitation sent</h1> +<p>Return to the <a href="{% url 'scipost:registration_invitations' %}">registration invitations page</a>.</p> - <p>Return to the <a href="{% url 'scipost:registration_invitations' %}">registration invitations page</a>.</p> - -</section> - -{% endblock bodysup %} +{% endblock content %} diff --git a/scipost/templates/scipost/reset_password_complete.html b/scipost/templates/scipost/reset_password_complete.html index dd226a8df6f40d4867d08a24b794f52fc3889a0a..5b8a2148b6d5672e67af8e0913cecd4eeec4811b 100644 --- a/scipost/templates/scipost/reset_password_complete.html +++ b/scipost/templates/scipost/reset_password_complete.html @@ -2,6 +2,6 @@ {% block pagetitle %}: Reset password complete{% endblock pagetitle %} -{% block bodysup %} +{% block content %} <p>You have successfully reset your password.</p> -{% endblock bodysup %} +{% endblock content %} diff --git a/scipost/templates/tags/bootstrap/form.html b/scipost/templates/tags/bootstrap/form.html index 97a499eb78aedd6a105adde185ac06df059b55c1..be6f109105552d0574b2525192bb9be676498a8a 100644 --- a/scipost/templates/tags/bootstrap/form.html +++ b/scipost/templates/tags/bootstrap/form.html @@ -1,10 +1,10 @@ {% if form.non_field_errors %} - <div class="alert alert-danger"> - <a class="close" data-dismiss="alert">×</a> - {% for non_field_error in form.non_field_errors %} - {{ non_field_error }} - {% endfor %} - </div> + {% for non_field_error in form.non_field_errors %} + <div class="alert alert-danger"> + <a class="close" data-dismiss="alert">×</a> + {{ non_field_error }} + </div> + {% endfor %} {% endif %} {% for field in form.hidden_fields %} diff --git a/scipost/views.py b/scipost/views.py index c211b4ef1b0e182a0d88e7d0a6a3472f10fe7667..76c12a1db9250d35dc2ed8eba6db4561b87d9a64 100644 --- a/scipost/views.py +++ b/scipost/views.py @@ -627,6 +627,13 @@ def remove_registration_invitation(request, invitation_id): @permission_required('scipost.can_manage_registration_invitations', return_403=True) def edit_invitation_personal_message(request, invitation_id): + """ + + DOES THIS THING STILL WORK? OR CAN IT BE REMOVED? + + -- JdW (August 14th, 2017) + + """ invitation = get_object_or_404(RegistrationInvitation, pk=invitation_id) errormessage = None if request.method == 'POST': diff --git a/submissions/admin.py b/submissions/admin.py index 2d88cdc52264761d1405028e361c86c7ec52982f..fc25f4403c9257f32a0c62e048c1ea10d0398310 100644 --- a/submissions/admin.py +++ b/submissions/admin.py @@ -6,7 +6,8 @@ from guardian.admin import GuardedModelAdmin from comments.models import Comment from submissions.models import Submission, EditorialAssignment, RefereeInvitation, Report,\ - EditorialCommunication, EICRecommendation, SubmissionEvent + EditorialCommunication, EICRecommendation, SubmissionEvent,\ + iThenticateReport from scipost.models import Contributor @@ -18,6 +19,17 @@ def submission_short_title(obj): return obj.submission.title[:30] +class iThenticateReportAdmin(admin.ModelAdmin): + readonly_fields = ['doc_id'] + + def has_add_permission(self, request): + """ Don't add manually. This will gives conflict with the iThenticate db. """ + return False + + +admin.site.register(iThenticateReport, iThenticateReportAdmin) + + class SubmissionAdminForm(forms.ModelForm): authors = forms.ModelMultipleChoiceField( required=False, diff --git a/submissions/behaviors.py b/submissions/behaviors.py new file mode 100644 index 0000000000000000000000000000000000000000..27ed6ecaf106e82235680f9816a5645198560c94 --- /dev/null +++ b/submissions/behaviors.py @@ -0,0 +1,6 @@ +class SubmissionRelatedObjectMixin: + def save(self, *args, **kwargs): + obj = super().save(*args, **kwargs) + if hasattr(self, 'submission'): + self.submission.touch() + return obj diff --git a/submissions/exceptions.py b/submissions/exceptions.py index a6e26c2d6c946fec39d3b402415bd0110e63378a..474d3b343b4eff03158cc7456bd2c57dbcab2d8d 100644 --- a/submissions/exceptions.py +++ b/submissions/exceptions.py @@ -12,3 +12,7 @@ class CycleUpdateDeadlineError(BaseCustomException): class InvalidReportVettingValue(BaseCustomException): pass + + +class ArxivPDFNotFound(Exception): + pass diff --git a/submissions/forms.py b/submissions/forms.py index e6b3cd433e0f6adc4f31f247a07fd4751ce104fa..9b4723b598e80d2a957c42e6bb9c9b1bcbde6717 100644 --- a/submissions/forms.py +++ b/submissions/forms.py @@ -1,4 +1,5 @@ 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 @@ -10,17 +11,18 @@ from .constants import ASSIGNMENT_BOOL, ASSIGNMENT_REFUSAL_REASONS, STATUS_RESUB STATUS_REJECTED, STATUS_REJECTED_VISIBLE, STATUS_RESUBMISSION_INCOMING,\ STATUS_DRAFT, STATUS_UNVETTED, REPORT_ACTION_ACCEPT, REPORT_ACTION_REFUSE,\ STATUS_VETTED -from .exceptions import InvalidReportVettingValue -from .models import Submission, RefereeInvitation, Report, EICRecommendation, EditorialAssignment +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 from crispy_forms.helper import FormHelper from crispy_forms.layout import Layout, Div, Field, HTML, Submit - -import strings +import iThenticate class SubmissionSearchForm(forms.Form): @@ -554,7 +556,7 @@ class VetReportForm(forms.Form): # The report is rejected report.status = self.cleaned_data['refusal_reason'] else: - raise InvalidReportVettingValue(self.cleaned_data['action_option']) + raise exceptions.InvalidReportVettingValue(self.cleaned_data['action_option']) report.save() return report @@ -643,3 +645,116 @@ class SubmissionCycleChoiceForm(forms.ModelForm): 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() + + # 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 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): + if self.instance: + report = self.instance + else: + report = iThenticateReport.objects.get_or_create(doc_id=self.response['data']['id']) + report.submission = self.submission + report.uploaded_time = data['uploaded_time'] + report.processed_time = data['processed_time'] + report.percent_match = data['percent_match'] + report.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['data'] + self.add_error(None, "Updating failed. iThenticate didn't return valid data [1]") + self.add_error(None, client.messages[0]) + return None + + def upload_document(self): + client = self.client + + # Get first folder available + # TODO: Fix this ugly piece of crap + folders = client.folders.all() + if folders['status'] == 200: + folder_id = folders['data'][0]['id'] + else: + self.add_error(None, "Uploading failed. iThenticate didn't return valid data [2]") + self.add_error(None, client.messages[0]) + + # Finally, upload the file + author = self.submission.authors.first() + response = client.documents.add( + self.document, + folder_id, + author.user.first_name, + author.user.last_name, + self.submission.title, + ) + + if response['status'] == 200: + self.submission.add_general_event(('The document has been submitted ' + 'for a plagiarism check.')) + return response['data'] + + self.add_error(None, "Updating failed. iThenticate didn't return valid data [3]") + self.add_error(None, client.messages[0]) + return None diff --git a/submissions/helpers.py b/submissions/helpers.py new file mode 100644 index 0000000000000000000000000000000000000000..1d7a8b5fdf2eaec109b9304c38f8b82287d243fe --- /dev/null +++ b/submissions/helpers.py @@ -0,0 +1,17 @@ +import requests + +from .exceptions import ArxivPDFNotFound + + +def retrieve_pdf_from_arxiv(arxiv_id): + """ + Try to download the pdf as bytes object from arXiv for a certain arXiv Identifier. + Raise ArxivPDFNotFound instead. + + :arxiv_id: Arxiv Identifier with or without (takes latest version instead) version number + """ + path_to_pdf = 'https://arxiv.org/pdf/{arxiv_id}.pdf'.format(arxiv_id=arxiv_id) + response = requests.get(path_to_pdf) + if response.status_code != 200: + raise ArxivPDFNotFound('No pdf found on arXiv.') + return response.content diff --git a/submissions/managers.py b/submissions/managers.py index 232c7ed9da7949148e0775276619a0378a089b64..3465c643a9d6c0accbaf9fafd86763e35c2474dc 100644 --- a/submissions/managers.py +++ b/submissions/managers.py @@ -1,5 +1,8 @@ +import datetime + from django.db import models from django.db.models import Q +from django.utils import timezone from .constants import SUBMISSION_STATUS_OUT_OF_POOL, SUBMISSION_STATUS_PUBLICLY_UNLISTED,\ SUBMISSION_STATUS_PUBLICLY_INVISIBLE, STATUS_UNVETTED, STATUS_VETTED,\ @@ -7,10 +10,10 @@ 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 SubmissionManager(models.Manager): +class SubmissionQuerySet(models.QuerySet): def _newest_version_only(self, queryset): """ The current Queryset should return only the latest version @@ -41,7 +44,7 @@ class SubmissionManager(models.Manager): 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 +53,30 @@ class SubmissionManager(models.Manager): 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__in=[STATUS_UNASSIGNED, STATUS_ACCEPTED])) + def public(self): """ This query contains set of public submissions, i.e. also containing @@ -82,13 +101,6 @@ class SubmissionManager(models.Manager): """ 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 +111,16 @@ class SubmissionManager(models.Manager): 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) @@ -116,6 +137,12 @@ class SubmissionEventQuerySet(models.QuerySet): """ return self.filter(event__in=[EVENT_FOR_EIC, EVENT_GENERAL]) + def last_hours(self, hours=24): + """ + Return all events of the last `hours` hours. + """ + return self.filter(created__gte=timezone.now() - datetime.timedelta(hours=hours)) + class EditorialAssignmentManager(models.Manager): def get_for_user_in_pool(self, user): @@ -167,3 +194,20 @@ class ReportQuerySet(models.QuerySet): def non_draft(self): return self.exclude(status=STATUS_DRAFT) + + def contributed(self): + return self.filter(invited=False) + + def invited(self): + return self.filter(invited=True) + + +class RefereeInvitationQuerySet(models.QuerySet): + def pending(self): + return self.filter(accepted=None) + + def accepted(self): + return self.filter(accepted=True) + + def declined(self): + return self.filter(accepted=False) 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 new file mode 100644 index 0000000000000000000000000000000000000000..6f82693b101bc7bcad1e706d73f1c381ef4f36a2 --- /dev/null +++ b/submissions/mixins.py @@ -0,0 +1,94 @@ +from django.contrib.auth.mixins import PermissionRequiredMixin +from django.views.generic.list import ListView + +from .models import Submission + + +class FriendlyPermissionMixin(PermissionRequiredMixin): + """ + This mixin controls the permissions with a fallback for anonymous users + to help them login first. If a logged in user is refused, he will get the + http 403 screen anyway. + + :permission_required: The permission code the user should comply with. + """ + permission_required = 'scipost.dummy_permission' + + def dispatch(self, request, *args, **kwargs): + if not request.user.is_authenticated: + return self.handle_no_permission() + self.raise_exception = True + return super().dispatch(request, *args, **kwargs) + + +class SubmissionFormViewMixin: + def get_form_kwargs(self): + """ + Ideally all ModelForms on Submission-related objects have a required argument `submission`. + """ + kwargs = super().get_form_kwargs() + kwargs['submission'] = self._original_submission + return kwargs + + +class SubmissionAdminViewMixin(FriendlyPermissionMixin, SubmissionFormViewMixin): + """ + This mixin will provide all basic methods and checks required for Submission + administrational actions regarding Submissions. + + :editorial_page: Submission is element of the set pool() if False, + else Submission is element of the subset: editorial_page() + """ + editorial_page = False + slug_field = 'arxiv_identifier_w_vn_nr' + 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): + """ + Return either of two sets of Submissions, with an author filter for the current user. + + This method is used in all Class-Based-Views. However, if one overwrites either one of the + - get_object() + - get_queryset() + methods, please don't forget to call super().method_name() to not remove this filter! + """ + qs = super().get_queryset() + if self.pool: + return qs.get_pool(self.request.user) + return qs.filter_editorial_page(self.request.user) + + def get_object(self): + """ + Save the original Submission instance for performance reasons to the view, + which may be used in get_context_data(). + """ + obj = super().get_object() + self.submission = obj + return obj + + def get_context_data(self, *args, **kwargs): + """ + If the main object in a DetailView is not a Submission instance, it will be lost. + Here, explicitly save the Submission instance to the context data. + """ + ctx = super().get_context_data(*args, **kwargs) + + if not ctx.get('submission') and not isinstance(self, ListView): + # Call parent get_object() to explicitly save the submission which is related + # to the view's main object. + ctx['submission'] = self._original_submission + return ctx + + @property + def _original_submission(self): + if hasattr(self, 'submission'): + return self.submission + obj = super().get_object() + if isinstance(obj, Submission): + return obj + return None diff --git a/submissions/models.py b/submissions/models.py index cd0056ce17b3f73fc836d7f01c704745a6c9e33d..6603cf00781e2445d4c02d2a28f0730a0e9ce4a2 100644 --- a/submissions/models.py +++ b/submissions/models.py @@ -8,14 +8,15 @@ from django.db.models import Q from django.urls import reverse from django.utils.functional import cached_property +from .behaviors import SubmissionRelatedObjectMixin from .constants import ASSIGNMENT_REFUSAL_REASONS, ASSIGNMENT_NULLBOOL,\ SUBMISSION_TYPE, ED_COMM_CHOICES, REFEREE_QUALIFICATION, QUALITY_SPEC,\ RANKING_CHOICES, REPORT_REC, SUBMISSION_STATUS, STATUS_UNASSIGNED,\ REPORT_STATUSES, STATUS_UNVETTED, SUBMISSION_EIC_RECOMMENDATION_REQUIRED,\ SUBMISSION_CYCLES, CYCLE_DEFAULT, CYCLE_SHORT, CYCLE_DIRECT_REC,\ EVENT_GENERAL, EVENT_TYPES, EVENT_FOR_AUTHOR, EVENT_FOR_EIC -from .managers import SubmissionManager, EditorialAssignmentManager, EICRecommendationManager,\ - ReportQuerySet, SubmissionEventQuerySet +from .managers import SubmissionQuerySet, EditorialAssignmentManager, EICRecommendationManager,\ + ReportQuerySet, SubmissionEventQuerySet, RefereeInvitationQuerySet from .utils import ShortSubmissionCycle, DirectRecommendationSubmissionCycle,\ GeneralSubmissionCycle @@ -79,6 +80,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') @@ -94,7 +101,7 @@ class Submission(models.Model): acceptance_date = models.DateField(verbose_name='acceptance date', null=True, blank=True) latest_activity = models.DateTimeField(auto_now=True) - objects = SubmissionManager() + objects = SubmissionQuerySet.as_manager() class Meta: permissions = ( @@ -123,6 +130,11 @@ class Submission(models.Model): pass return header + def touch(self): + """ Update latest activity as a service """ + self.latest_activity = timezone.now() + self.save() + def comments_set_complete(self): """ Return comments to Submission, comments on Reports of Submission and @@ -205,7 +217,7 @@ class Submission(models.Model): event.save() -class SubmissionEvent(TimeStampedModel): +class SubmissionEvent(SubmissionRelatedObjectMixin, TimeStampedModel): """ The SubmissionEvent's goal is to act as a messaging/logging model for the Submission cycle. Its main audience will be the author(s) and @@ -234,7 +246,7 @@ class SubmissionEvent(TimeStampedModel): # Editorial workflow # ###################### -class EditorialAssignment(models.Model): +class EditorialAssignment(SubmissionRelatedObjectMixin, models.Model): submission = models.ForeignKey('submissions.Submission', on_delete=models.CASCADE) to = models.ForeignKey('scipost.Contributor', on_delete=models.CASCADE) accepted = models.NullBooleanField(choices=ASSIGNMENT_NULLBOOL, default=None) @@ -254,7 +266,7 @@ class EditorialAssignment(models.Model): ', requested on ' + self.date_created.strftime('%Y-%m-%d')) -class RefereeInvitation(models.Model): +class RefereeInvitation(SubmissionRelatedObjectMixin, models.Model): submission = models.ForeignKey('submissions.Submission', on_delete=models.CASCADE, related_name='referee_invitations') referee = models.ForeignKey('scipost.Contributor', related_name='referee', blank=True, @@ -277,6 +289,8 @@ class RefereeInvitation(models.Model): fulfilled = models.BooleanField(default=False) # True if a Report has been submitted cancelled = models.BooleanField(default=False) # True if EIC has deactivated invitation + objects = RefereeInvitationQuerySet.as_manager() + def __str__(self): return (self.first_name + ' ' + self.last_name + ' to referee ' + self.submission.title[:30] + ' by ' + self.submission.author_list[:30] + @@ -301,7 +315,7 @@ class RefereeInvitation(models.Model): # Reports: ########### -class Report(models.Model): +class Report(SubmissionRelatedObjectMixin, models.Model): """ Both types of reports, invited or contributed. @@ -423,7 +437,7 @@ class Report(models.Model): # EditorialCommunication # ########################## -class EditorialCommunication(models.Model): +class EditorialCommunication(SubmissionRelatedObjectMixin, models.Model): """ Each individual communication between Editor-in-charge to and from Referees and Authors becomes an instance of this class. @@ -453,7 +467,7 @@ class EditorialCommunication(models.Model): ############################ # From the Editor-in-charge of a Submission -class EICRecommendation(models.Model): +class EICRecommendation(SubmissionRelatedObjectMixin, models.Model): submission = models.ForeignKey('submissions.Submission', on_delete=models.CASCADE, related_name='eicrecommendations') date_submitted = models.DateTimeField('date submitted', default=timezone.now) @@ -465,7 +479,7 @@ class EICRecommendation(models.Model): recommendation = models.SmallIntegerField(choices=REPORT_REC) # Editorial Fellows who have assessed this recommendation: - eligible_to_vote = models.ManyToManyField(Contributor, blank=True, + eligible_to_vote = models.ManyToManyField('scipost.Contributor', blank=True, related_name='eligible_to_vote') voted_for = models.ManyToManyField(Contributor, blank=True, related_name='voted_for') voted_against = models.ManyToManyField(Contributor, blank=True, related_name='voted_against') @@ -489,3 +503,39 @@ 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) + + class Meta: + verbose_name = 'iThenticate Report' + verbose_name_plural = 'iThenticate Reports' + + def get_absolute_url(self): + if hasattr(self, 'to_submission'): + return reverse('submissions:plagiarism', kwargs={ + 'arxiv_identifier_w_vn_nr': + self.to_submission.arxiv_identifier_w_vn_nr}) + return None + + def __str__(self): + _str = 'Report {doc_id}'.format(doc_id=self.doc_id) + if hasattr(self, 'to_submission'): + _str += ' on Submission {arxiv}'.format( + arxiv=self.to_submission.arxiv_identifier_w_vn_nr) + return _str + + def save(self, commit=True, **kwargs): + obj = super().save(commit, **kwargs) + if hasattr(self, 'to_submission') and commit: + self.to_submission.touch() + return obj + + @property + def score(self): + return self.percent_match diff --git a/submissions/templates/partials/submissions/admin/editorial_admin_summary.html b/submissions/templates/partials/submissions/admin/editorial_admin_summary.html new file mode 100644 index 0000000000000000000000000000000000000000..60f10bf1a8c09a4a22ccc488b10801ad09221c97 --- /dev/null +++ b/submissions/templates/partials/submissions/admin/editorial_admin_summary.html @@ -0,0 +1,149 @@ +{% load guardian_tags %} +{% load scipost_extras %} +{% load submissions_extras %} +{% load render_bundle from webpack_loader %} + + +<h5 class="pb-0">{{submission.get_subject_area_display}}</h5> +<h3 class="card-title"> + <a href="{{submission.get_absolute_url}}">{{submission.title}}</a> +</h3> + +<p class="card-text mb-3">by {{submission.author_list}}</p> +<h3>Info</h3> +<table class="text-muted w-100 mb-1"> + <tr> + <td style="min-width: 40%;">Version</td> + <td>{{submission.arxiv_vn_nr}} ({% if submission.is_current %}current version{% else %}deprecated version {{submission.arxiv_vn_nr}}{% endif %})</td> + </tr> + <tr> + <td>Submitted</td> + <td>{{submission.submission_date}} to {{submission.get_submitted_to_journal_display}}</td> + </tr> + + {% if submission.acceptance_date %} + <tr> + <td>Accepted</td> + <td>{{submission.acceptance_date}}</td> + </tr> + {% endif %} + + <tr> + <td>Latest activity</td> + <td>{{submission.latest_activity}}</td> + </tr> + <tr> + <td>Editor-in-charge</td> + <td> + {% if submission.editor_in_charge %} + {{ submission.editor_in_charge }} + {% elif perms.scipost.can_assign_submissions %} + <a href="{% url 'submissions:assign_submission' submission.arxiv_identifier_w_vn_nr %}">Send a new assignment request</a> + {% else %} + - + {% endif %} + </td> + </tr> + <tr> + <td>Status</td> + <td>{{ submission.get_status_display }}</td> + </tr> + <tr> + <td>Refereeing cycle</td> + <td>{{ submission.get_refereeing_cycle_display }}</td> + </tr> + + {% include 'partials/submissions/refereeing_status_as_tr.html' with submission=submission %} + + <tr> + <td>Comments</td> + <td> + {{submission.comments.vetted.count}} + <span class="circle-clickable" data-toggle="tooltip" data-placement='bottom' data-html="true" title="{{submission.comments.regular_comments.vetted.count}} comments<br>{{submission.comments.author_replies.vetted.count}} author replies<hr>{{submission.comments.awaiting_vetting.count}} awaiting vetting">?</span> + </td> + </tr> + + <tr> + <td>Reporting deadline</td> + <td> + {% if submission.reporting_deadline > now %} + in {{submission.reporting_deadline|timeuntil}} + {% else %} + {{submission.reporting_deadline|timesince}} ago + {% endif %} + </td> + </tr> + + <tr> + <td>Plagiarism score</td> + <td> + {% if submission.plagiarism_report %} + {{ submission.plagiarism_report.score }} + {% else %} + <a href="{% url 'submissions:plagiarism' submission.arxiv_identifier_w_vn_nr %}">Run plagiarism check</a> + {% endif %} + </td> + </tr> + +</table> +<a href="{% url 'submissions:editorial_page' submission.arxiv_identifier_w_vn_nr %}" class="d-inline-block mb-3">Go to Editorial Page</a> + +<h3>Actions</h3> + +<ul class="pl-4 mb-3"> + {# EIC Assignments #} + {% if perms.scipost.can_assign_submissions %} + {% if not submission.editor_in_charge %} + <li>EIC Assignment requests:</li> + <ul> + {% for assignment in submission.editorialassignment_set.all %} + {% include 'submissions/_assignment_info.html' with assignment=assignment %} + {% empty %} + <li>None found. <a href="{% url 'submissions:assign_submission' submission.arxiv_identifier_w_vn_nr %}">Send a first assignment request</a></li> + {% endfor %} + </ul> + <li><a href="{% url 'submissions:assign_submission' submission.arxiv_identifier_w_vn_nr %}">Send a new assignment request</a></li> + <li><a href="{% url 'submissions:assignment_failed' submission.arxiv_identifier_w_vn_nr %}">Close pre-screening: failure to find EIC</a></li> + {% endif %} + {% endif %} + + {# Plagiarism #} + <li><a href="{% url 'submissions:plagiarism' submission.arxiv_identifier_w_vn_nr %}">Run plagiarism check</a></li> + + {# Compile pdfs #} + {% if submission.reports.accepted.exists %} + <li><a href="{% url 'submissions:reports_accepted_list' %}?submission={{submission.arxiv_identifier_w_vn_nr}}">Compile accepted reports</a></li> + {% endif %} + + {# Communication #} + {% if submission.editor_in_charge %} + <li><a href="{% url 'submissions:communication' arxiv_identifier_w_vn_nr=submission.arxiv_identifier_w_vn_nr comtype='StoE' %}">Send a communication to the Editor-in-charge</a></li> + {% endif %} + + {# EIC Recommendations #} + {% if submission.eicrecommendations.exists %} + <li>See Editorial Recommendations:</li> + <ul> + {% for rec in submission.eicrecommendations.all %} + <li><a href="{% url 'submissions:eic_recommendation_detail' arxiv_identifier_w_vn_nr=submission.arxiv_identifier_w_vn_nr rec_id=rec.id %}">{{rec.get_recommendation_display}}</a></li> + {% endfor %} + </ul> + {% endif %} + + {# Accepted submission actions #} + {% if submission.status == 'accepted' %} + <li><a href="{% url 'submissions:treated_submission_pdf_compile' submission.arxiv_identifier_w_vn_nr %}">Update the Refereeing Package pdf</a></li> + <li>After proofs have been accepted, you can <a href="{% url 'journals:initiate_publication' %}">initiate the publication process</a> (leads to the validation page)</li> + {% endif %} +</ul> + + +<h3>Events</h3> +<a href="javascript:;" data-toggle="toggle" data-target="#eventslist">Show/hide events</a> +<div id="eventslist"> + {% include 'submissions/submission_event_list.html' with events=submission.events.for_eic %} +</div> + +{% block footer_script %} + {% render_bundle 'tooltip' 'js' %} +{% endblock %} diff --git a/submissions/templates/partials/submissions/admin/submission_tooltip.html b/submissions/templates/partials/submissions/admin/submission_tooltip.html new file mode 100644 index 0000000000000000000000000000000000000000..21149b9984934c660ebcb527c134125dad71bfa2 --- /dev/null +++ b/submissions/templates/partials/submissions/admin/submission_tooltip.html @@ -0,0 +1,4 @@ +<span class="circle-clickable no-break" data-toggle="tooltip" data-html="true" data-placement='right' + title=" + {{submission.arxiv_identifier_w_vn_nr}}<hr>Status: {{submission.get_status_display}}<br>Latest activity: {{submission.latest_activity}} +">?</span> diff --git a/submissions/templates/partials/submissions/refereeing_status_as_tr.html b/submissions/templates/partials/submissions/refereeing_status_as_tr.html new file mode 100644 index 0000000000000000000000000000000000000000..6a9692aa6033791ca716766ee6288dafc76907cf --- /dev/null +++ b/submissions/templates/partials/submissions/refereeing_status_as_tr.html @@ -0,0 +1,10 @@ +{% if submission.refereeing_cycle != 'direct_rec' %} + <tr> + <td>Referees invited</td> + <td>{{submission.referee_invitations.count}} <span class="circle-clickable" data-placement='bottom' data-toggle="tooltip" data-html="true" title="{{submission.referee_invitations.accepted.count}} accepted<br>{{submission.referee_invitations.declined.count}} declined<br>{{submission.referee_invitations.pending.count}} response pending">?</span> + <tr> + <tr> + <td>Reports obtained</td> + <td>{{submission.reports.accepted.count}} <span class="circle-clickable" data-placement='bottom' data-toggle="tooltip" data-html="true" title="{{submission.reports.accepted.invited.count}} invited<br>{{submission.reports.accepted.contributed.count}} contributed<hr>{{submission.reports.awaiting_vetting.count}} awaiting vetting<br>{{submission.reports.rejected.count}} rejected">?</span> + <tr> +{% endif %} diff --git a/submissions/templates/submissions/_recommendation_author_content.html b/submissions/templates/submissions/_recommendation_author_content.html index cfbda0714ebfd86bfb0e8955a91bc7f6e288b411..d7a2bee96eb5cf833da8980c3b38ae62da5ef409 100644 --- a/submissions/templates/submissions/_recommendation_author_content.html +++ b/submissions/templates/submissions/_recommendation_author_content.html @@ -6,10 +6,10 @@ {% endblock %} <h3 class="pb-0">Remarks for authors</h3> - <p class="pl-md-3">{{recommendation.remarks_for_authors}}</p> + <p class="pl-md-3">{{recommendation.remarks_for_authors|default:'-'}}</p> <h3 class="pb-0">Requested changes</h3> - <p class="pl-md-3">{{recommendation.requested_changes}}</p> + <p class="pl-md-3">{{recommendation.requested_changes|default:'-'}}</p> {% block recommendation_before_recommendation %}{% endblock %} diff --git a/submissions/templates/submissions/_recommendation_fellow_content.html b/submissions/templates/submissions/_recommendation_fellow_content.html index 79e16508879e5d4d9670eff4b62453487cfa4f28..e2e3926a7571e729e8b1f8683e479bd170862049 100644 --- a/submissions/templates/submissions/_recommendation_fellow_content.html +++ b/submissions/templates/submissions/_recommendation_fellow_content.html @@ -6,5 +6,5 @@ {% block recommendation_before_recommendation %} <h3 class="pb-0">Remarks for Editorial College</h3> - <p class="pl-md-3">{{recommendation.remarks_for_editorial_college}}</p> + <p class="pl-md-3">{{recommendation.remarks_for_editorial_college|default:'-'}}</p> {% endblock %} diff --git a/submissions/templates/submissions/_submission_refereeing_status.html b/submissions/templates/submissions/_submission_refereeing_status.html index 5d1ea7b1d2c3baea8d9b6efab91ba7bc895382c8..225405c15291193e2e4c3bbdabb2a24d6ad3f67e 100644 --- a/submissions/templates/submissions/_submission_refereeing_status.html +++ b/submissions/templates/submissions/_submission_refereeing_status.html @@ -1,6 +1,7 @@ {% if submission.refereeing_cycle != 'direct_rec' %} - <div class="card-block"> - <p class="card-text">Nr referees invited: {{submission.referee_invitations.count}} <span>[{{submission.count_accepted_invitations}} acccepted / {{submission.count_declined_invitations}} declined / {{submission.count_pending_invitations}} response pending]</span></p> - <p class="card-text">Nr reports obtained: {{submission.count_obtained_reports}} [{{submission.count_invited_reports}} invited / {{submission.count_contrib_reports}} contributed], nr refused: {{submission.reports.rejected.count}}, nr awaiting vetting: {{submission.reports.awaiting_vetting.count}}</p> - </div> +<p> + Nr referees invited: {{submission.referee_invitations.count}} <span>[{{submission.count_accepted_invitations}} acccepted / {{submission.count_declined_invitations}} declined / {{submission.count_pending_invitations}} response pending]</span> + <br> + Nr reports obtained: {{submission.count_obtained_reports}} [{{submission.count_invited_reports}} invited / {{submission.count_contrib_reports}} contributed], nr refused: {{submission.reports.rejected.count}}, nr awaiting vetting: {{submission.reports.awaiting_vetting.count}} +</p> {% endif %} diff --git a/submissions/templates/submissions/accept_or_decline_assignment_ack.html b/submissions/templates/submissions/accept_or_decline_assignment_ack.html index ea1c7ede2f7d3e56caddc5393cc3c1c97ec6f757..67a7551cca06e5c12e852c533d727220874f89e1 100644 --- a/submissions/templates/submissions/accept_or_decline_assignment_ack.html +++ b/submissions/templates/submissions/accept_or_decline_assignment_ack.html @@ -2,20 +2,19 @@ {% block pagetitle %}: accept or decline assignment (ack){% endblock pagetitle %} -{% block bodysup %} +{% block content %} + -<section> {% if errormessage %} - <p>{{ errormessage }}</p> - <p>Return to the <a href="{% url 'submissions:pool' %}">Submissions Pool</a>.</p> + <p>{{ errormessage }}</p> + <p>Return to the <a href="{% url 'submissions:pool' %}">Submissions Pool</a>.</p> {% elif assignment.accepted %} - <h1>Thank you for becoming Editor-in-charge of this submission.</h1> - <p>Please go to the <a href="{% url 'submissions:editorial_page' arxiv_identifier_w_vn_nr=assignment.submission.arxiv_identifier_w_vn_nr %}">Submission's editorial page</a> and select referees now.</p> + <h1>Thank you for becoming Editor-in-charge of this submission.</h1> + <p>Please go to the <a href="{% url 'submissions:editorial_page' arxiv_identifier_w_vn_nr=assignment.submission.arxiv_identifier_w_vn_nr %}">Submission's editorial page</a> and select referees now.</p> {% else %} - <h1>Thank you for considering.</h1> - <p>Return to the <a href="{% url 'submissions:pool' %}">Submissions Pool</a>.</p> + <h1>Thank you for considering.</h1> + <p>Return to the <a href="{% url 'submissions:pool' %}">Submissions Pool</a>.</p> {% endif %} -</section> -{% endblock bodysup %} +{% endblock content %} diff --git a/submissions/templates/submissions/accept_or_decline_ref_invitation_ack.html b/submissions/templates/submissions/accept_or_decline_ref_invitation_ack.html index aafb2c0c240ea9b1ea250722777448fdb5f97841..3e47b51a043e1f5786e576362a63958d6c054733 100644 --- a/submissions/templates/submissions/accept_or_decline_ref_invitation_ack.html +++ b/submissions/templates/submissions/accept_or_decline_ref_invitation_ack.html @@ -2,16 +2,14 @@ {% block pagetitle %}: accept or decline refereeing invitation (ack){% endblock pagetitle %} -{% block bodysup %} +{% block content %} -<section> - {% if invitation.accepted == True %} - <h2>Thank you for agreeing to referee this Submission.</h2> - <p>When you are ready, please go to the <a href="{% url 'submissions:submission' arxiv_identifier_w_vn_nr=invitation.submission.arxiv_identifier_w_vn_nr %}">Submission's page</a> to submit your Report.</p> - {% else %} - <h1>You have declined to contribute a Report.</h1> - <p>Nonetheless, we thank you very much for considering this refereeing invitation.</p> - {% endif %} -</section> +{% if invitation.accepted == True %} + <h2>Thank you for agreeing to referee this Submission.</h2> + <p>When you are ready, please go to the <a href="{% url 'submissions:submission' arxiv_identifier_w_vn_nr=invitation.submission.arxiv_identifier_w_vn_nr %}">Submission's page</a> to submit your Report.</p> +{% else %} + <h1>You have declined to contribute a Report.</h1> + <p>Nonetheless, we thank you very much for considering this refereeing invitation.</p> +{% endif %} -{% endblock bodysup %} +{% endblock content %} diff --git a/submissions/templates/submissions/admin/editorial_admin.html b/submissions/templates/submissions/admin/editorial_admin.html new file mode 100644 index 0000000000000000000000000000000000000000..04cb76e2d2a3c39f3059bea66ed65c9ee94028b7 --- /dev/null +++ b/submissions/templates/submissions/admin/editorial_admin.html @@ -0,0 +1,99 @@ +{% 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 %} +<div class="row"> + <div class="col-md-7"> + <h1>Editorial Administration Page</h1> + + <p> + {% if submission %} + <a href="{% url 'submissions:admin' %}">All events in the last 24 hours</a><br> + {% endif %} + <a href="{% url 'submissions:pool' %}">Go to the pool</a> + </p> + + <h3>Submissions currently in pre-screening</h3> + <ul class="unstyled-list"> + {% for sub in submission_list.prescreening %} + <li> + {% include 'partials/submissions/admin/submission_tooltip.html' with submission=sub %} + {% if sub == submission %} + <strong> + <a href="?submission={{sub.arxiv_identifier_w_vn_nr}}">{{sub.title}}</a> + <br>latest activity: {{sub.latest_activity|timesince}} ago + </strong> + {% else %} + <a href="?submission={{sub.arxiv_identifier_w_vn_nr}}">{{sub.title}}</a> + <br>latest activity: {{sub.latest_activity|timesince}} ago + {% endif %} + </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 sub in submission_list.actively_refereeing %} + <li> + {% include 'partials/submissions/admin/submission_tooltip.html' with submission=sub %} + {% if sub == submission %} + <strong> + <a href="?submission={{sub.arxiv_identifier_w_vn_nr}}">{{sub.title}}</a> + <br>latest activity: {{sub.latest_activity|timesince}} ago + </strong> + {% else %} + <a href="?submission={{sub.arxiv_identifier_w_vn_nr}}">{{sub.title}}</a> + <br>latest activity: {{sub.latest_activity|timesince}} ago + {% endif %} + </li> + {% empty %} + <li>No Submissions are currently in refereeing round</li> + {% endfor %} + </ul> + + <h3>Submissions accepted</h3> + <ul class="unstyled-list"> + {% for sub in submission_list.accepted %} + <li> + {% include 'partials/submissions/admin/submission_tooltip.html' with submission=sub %} + {% if sub == submission %} + <strong> + <a href="?submission={{sub.arxiv_identifier_w_vn_nr}}">{{sub.title}}</a> + <br>latest activity: {{sub.latest_activity|timesince}} ago + </strong> + {% else %} + <a href="?submission={{sub.arxiv_identifier_w_vn_nr}}">{{sub.title}}</a> + <br>latest activity: {{sub.latest_activity|timesince}} ago + {% endif %} + </li> + {% empty %} + <li>All accepted Submissions are published</li> + {% endfor %} + </ul> + </div> + + <div class="col-md-5"> + {% if submission %} + <div class="card card-outline-secondary mt-2"> + <div class="card-block"> + {% include 'partials/submissions/admin/editorial_admin_summary.html' with submission=submission %} + </div> + </div> + {% else %} + <h3><em>Click on a submission to see its summary and actions</em></h3> + <h2>All events in the last 24 hours</h2> + <div id="eventslist" class=""> + {% include 'submissions/submission_event_list_general.html' with events=latest_events %} + </div> + {% endif %} + </div> +</div> +{% endblock content %} diff --git a/submissions/templates/submissions/admin/eic_recommendation_detail.html b/submissions/templates/submissions/admin/eic_recommendation_detail.html new file mode 100644 index 0000000000000000000000000000000000000000..eff698ae3d22c936240b21610a1ecdd0338acdbd --- /dev/null +++ b/submissions/templates/submissions/admin/eic_recommendation_detail.html @@ -0,0 +1,94 @@ +{% extends 'submissions/_pool_base.html' %} + +{% block pagetitle %}: editorial recommendation for submission{% endblock pagetitle %} + +{% load scipost_extras %} +{% load bootstrap %} + +{% block breadcrumb_items %} + {{block.super}} + <a href="{% url 'submissions:admin' %}?submission={{submission.arxiv_identifier_w_vn_nr}}" class="breadcrumb-item">Editorial Administration</a> + <span class="breadcrumb-item">Editorial Recommendation</span> +{% endblock %} + +{% block content %} + +<div class="row"> + <div class="col-12"> + <div class="card card-grey"> + <div class="card-block"> + <h1 class="card-title">Editorial Recommendation for Submission</h1> + <p class="card-text"> + Go back to the <a href="{% url 'submissions:editorial_page' arxiv_identifier_w_vn_nr=submission.arxiv_identifier_w_vn_nr %}"> + Editorial Page</a> to take editorial actions + </p> + </div> + </div> + </div> +</div> + +<h2>Submission</h2> +{% include 'submissions/_submission_summary.html' with submission=submission %} + +<div class="card card-outline-secondary"> + {% include 'submissions/_recommendation_fellow_content.html' with recommendation=object %} + <div class="card-block"> + {% if object.remark_set.all %} + <h3 class="card-title">Remarks by Fellows:</h3> + <ul> + {% for remark in object.remark_set.all|sort_by:'date' %} + {{ remark.as_li }} + {% endfor %} + </ul> + {% endif %} + + <h3 class="card-title">Fellows eligible to vote:</h3> + <ul> + <li> + {% for eligible in object.eligible_to_vote.all|sort_by:'user__last_name' %} + {{ eligible.user.last_name }}, + {% endfor %} + </li> + </ul> + + <h3 class="card-title">Voting results up to now:</h3> + <ul> + <li> + Agreed: ({{ object.voted_for.all.count }}) + {% for agreed in object.voted_for.all|sort_by:'user__last_name' %} + {{ agreed.user.last_name }}, + {% endfor %} + </li> + <li> + Disagreed: ({{ object.voted_against.all.count }}) + {% for disagreed in object.voted_against.all|sort_by:'user__last_name' %} + {{ disagreed.user.last_name }}, + {% endfor %} + </li> + <li> + Abstained: ({{ object.voted_abstain.all.count }}) + {% for abstained in object.voted_abstain.all|sort_by:'user__last_name' %} + {{ abstained.user.last_name }}, + {% endfor %} + </li> + </ul> + + {% if object.remark_set %} + <h3 class="card-title">Remarks:</h3> + <ul> + {% for rem in object.remark_set.all %} + <li>{{ rem }}</li> + {% empty %} + <li><em>No remarks</em></li> + {% endfor %} + </ul> + {% endif %} + + <h3>Actions:</h3> + <ul> + <li>To fix the College decision and follow the Editorial Recommendation as is: <a href="{% url 'submissions:fix_College_decision' rec_id=object.id %}">click here</a></li> + <li>To request a modification of the Recommendation to request for revision: click here</li> + </ul> + </div> +</div> +{% endblock content %} diff --git a/submissions/templates/submissions/admin/plagiarism_report.html b/submissions/templates/submissions/admin/plagiarism_report.html new file mode 100644 index 0000000000000000000000000000000000000000..99769fe704065a68501f6326b178923b8af84f62 --- /dev/null +++ b/submissions/templates/submissions/admin/plagiarism_report.html @@ -0,0 +1,50 @@ +{% extends 'scipost/_personal_page_base.html' %} + +{% load bootstrap %} + +{% block pagetitle %}: plagiarism report ({{ submission.arxiv_identifier_w_vn_nr }}){% endblock pagetitle %} + +{% block breadcrumb_items %} + {{block.super}} + <a href="{% url 'submissions:admin' %}?submission={{submission.arxiv_identifier_w_vn_nr}}" 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> + <h2>{{submission.title}}</h2> + <h3 class="mb-4">by {{submission.author_list}}</h3> + {% if submission.plagiarism_report %} + <table> + <tr> + <td style="min-width: 150px;">iThenticate document</td> + <td>{{submission.plagiarism_report.doc_id}}</td> + </tr> + <tr> + <td>Percent match</td> + <td>{{submission.plagiarism_report.percent_match}}</td> + </tr> + <tr> + <td>Processed</td> + <td>{{submission.plagiarism_report.processed_time}}</td> + </tr> + <tr> + <td>Uploaded</td> + <td>{{submission.plagiarism_report.uploaded_time}}</td> + </tr> + <tr> + <td>Latest update</td> + <td>{{submission.plagiarism_report.latest_activity}}</td> + </tr> + </table> + {% else %} + No Plagiarism Report found. + {% endif %} + + <form method="post" class="mt-3" enctype="multipart/form-data"> + {% csrf_token %} + {{form|bootstrap}} + <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/assignment_failed.html b/submissions/templates/submissions/assignment_failed.html index 0264daab97cf6f2add39a43c57c39e105060bf9f..cd2931bea482477f5fd6ce8777d9a2cb50db2a52 100644 --- a/submissions/templates/submissions/assignment_failed.html +++ b/submissions/templates/submissions/assignment_failed.html @@ -1,18 +1,18 @@ {% extends 'scipost/base.html' %} +{% load bootstrap %} + {% block pagetitle %}: assignment failed (ack){% endblock pagetitle %} -{% block bodysup %} +{% block content %} -<section> - <h1>Assignment has failed for Submission</h1> - <p>{{ submission.title }} by {{ submission.author_list }}.</p> - <p>Please add comments on the Submission in this box.</p> - <form action="{% url 'submissions:assignment_failed' arxiv_identifier_w_vn_nr=submission.arxiv_identifier_w_vn_nr %}" method="POST"> +<h1>Assignment has failed for Submission</h1> +<p>{{ submission.title }} by {{ submission.author_list }}.</p> +<p>Please add comments on the Submission in this box.</p> +<form action="{% url 'submissions:assignment_failed' arxiv_identifier_w_vn_nr=submission.arxiv_identifier_w_vn_nr %}" method="POST"> {% csrf_token %} - {{ form }} - <input type="submit" name="Submit" /> - </form> -</section> + {{ form|bootstrap }} + <input type="submit" name="Submit" class="btn btn-primary" /> +</form> -{% endblock bodysup %} +{% endblock content %} diff --git a/submissions/templates/submissions/reports_accepted_list.html b/submissions/templates/submissions/reports_accepted_list.html index b4a2ff77d264bdbcf7531ec7d39e47e39e15573b..76838dc98cffb20000c05e77ace0336d21d7f464 100644 --- a/submissions/templates/submissions/reports_accepted_list.html +++ b/submissions/templates/submissions/reports_accepted_list.html @@ -11,44 +11,40 @@ {% block content %} -<div class="row"> - <div class="col-12"> - <h1 class="highlight">Accepted Reports</h1> - </div> -</div> +<h1 class="highlight">Accepted Reports{% if request.GET.submission %}<small> for submission {{request.GET.submission}}</small>{% endif %}</h1> -<div class="row"> - <div class="col-12"> - <table class="table"> - <thead> - <tr> - <th>Report nr. of Submission</th> - <th>Submission</th> - <th>Report author</th> - <th>Has PDF</th> - <th>Action</th> - </tr> - </thead> - <tbody> - {% for report in reports %} - <tr{% if not report.pdf_report %} class="table-danger"{% endif %}> - <td>{{report.report_nr}}</td> - <td><a href="{{report.get_absolute_url}}">{{report.submission.arxiv_identifier_w_vn_nr}}</a></td> - <td>{% if report.anonymous %}<em>Anonymous</em>{% else %}{{report.author}}{% endif %}</td> - <td> - {{report.pdf_report|yesno:"Yes,No"}} - {% if report.pdf_report %} - · <a href="{% url 'submissions:report_detail_pdf' report.submission.arxiv_identifier_w_vn_nr report.report_nr %}" target="_blank">Download</a> - {% endif %} - </td> - <td> - <a href="{% url 'submissions:report_pdf_compile' report.id %}">Compile/upload report</a> - </td> - </tr> - {% endfor %} - </tbody> - </table> - </div> -</div> +<table class="table"> + <thead> + <tr> + <th>Report nr. of Submission</th> + <th>Submission</th> + <th>Report author</th> + <th>Has PDF</th> + <th>Action</th> + </tr> + </thead> + <tbody> + {% for report in reports %} + <tr{% if not report.pdf_report %} class="table-danger"{% endif %}> + <td>{{report.report_nr}}</td> + <td><a href="{{report.get_absolute_url}}">{{report.submission.arxiv_identifier_w_vn_nr}}</a></td> + <td>{% if report.anonymous %}<em>Anonymous</em>{% else %}{{report.author}}{% endif %}</td> + <td> + {{report.pdf_report|yesno:"Yes,No"}} + {% if report.pdf_report %} + · <a href="{% url 'submissions:report_detail_pdf' report.submission.arxiv_identifier_w_vn_nr report.report_nr %}" target="_blank">Download</a> + {% endif %} + </td> + <td> + <a href="{% url 'submissions:report_pdf_compile' report.id %}">Compile/upload report</a> + </td> + </tr> + {% empty %} + <tr> + <td colspan="5">No Reports found</td> + </tr> + {% endfor %} + </tbody> +</table> {% endblock %} diff --git a/submissions/templates/submissions/submit_manuscript.html b/submissions/templates/submissions/submit_manuscript.html deleted file mode 100644 index 55a6f49aa898a9022d570434037288017dc4ad6d..0000000000000000000000000000000000000000 --- a/submissions/templates/submissions/submit_manuscript.html +++ /dev/null @@ -1,105 +0,0 @@ -{% extends 'scipost/base.html' %} - -{% block pagetitle %}: submit manuscript{% endblock pagetitle %} - -{% block bodysup %} - - -<script> -$(document).ready(function(){ - $("#id_submission_type").closest('tr').hide() - - $('select#id_submitted_to_journal').on('change', function (){ - var selection = $(this).val(); - switch(selection){ - case "SciPost Physics": - $("#id_submission_type").closest('tr').show() - break; - default: - $("#id_submission_type").closest('tr').hide() - } -}); - - var isresub = $("#id_is_resubmission").val(); - switch(isresub){ - case "True": - $("#id_author_comments").closest('tr').show() - $("#id_list_of_changes").closest('tr').show() - break; - default: - $("#id_author_comments").closest('tr').hide() - $("#id_list_of_changes").closest('tr').hide() - } - -}); -</script> - -<section> - <div class="flex-greybox"> - <h1>Submit a manuscript to SciPost</h1> - </div> - - <p>Before submitting, make sure you agree with the - <a href="{% url 'journals:journals_terms_and_conditions' %}"> - SciPost Journals Terms and Conditions</a>.</p> - <p>You should also make sure you understand the - <a href="{% url 'submissions:sub_and_ref_procedure' %}#pwr"> - refereeing procedure</a> and its open aspect.</p> - <p>In particular, make sure you are familiar with the - <a href="{% url 'journals:journals_terms_and_conditions' %}#license_and_copyright_agreement"> - license and copyright agreement</a> - and the <a href="{% url 'journals:journals_terms_and_conditions' %}#author_obligations"> - author obligations</a>.</p> - <p>Please prepare your manuscript according to the - <a href="{% url 'submissions:author_guidelines' %}">author guidelines</a>.</p> - - - {% if perms.scipost.can_submit_manuscript %} - - <div class="flex-greybox"> - <h3><em>Please provide the arXiv identifier for your Submission:</em></h3> - <p> - <em>(give the identifier without prefix but with version number, as per the placeholder)</em> - </p> - <form action="{% url 'submissions:prefill_using_identifier' %}" method="post"> - {% csrf_token %} - {{ identifierform }} - <input type="submit" value="Query arXiv"/> - </form> - </div> - <br/> - <!-- {% if errormessage %} - <h3 style="color: red;">Error: {{ errormessage }}</h3> - {% endif %} --> - - {% if resubmessage %} - <h3 style="color: green;">{{ resubmessage }}</h3> - {% endif %} - - {% if form.arxiv_link.value %} - <form id="full_submission_form" action="{% url 'submissions:submit_manuscript' %}" method="post"> - {% csrf_token %} - <table> - <ul> - {{ form.as_table }} - </ul> - </table> - <p>By clicking on Submit, you state that you have read and agree with - the <a href="{% url 'journals:journals_terms_and_conditions' %}"> - SciPost Journals Terms and Conditions</a>, the - <a href="{% url 'journals:journals_terms_and_conditions' %}#license_and_copyright_agreement"> - license and copyright agreement</a> - and the <a href="{% url 'journals:journals_terms_and_conditions' %}#author_obligations"> - author obligations</a>.</p> - <input type="submit" value="Submit"/> - </form> - {% endif %} - - {% else %} - <h3>You are currently not allowed to submit a manuscript.</h3> - {% endif %} - -</section> - - -{% endblock bodysup %} diff --git a/submissions/urls.py b/submissions/urls.py index 8aaf200ee76128fc26c635f54bed0c6b6e93402e..d95e69f18c47bb24b4bb29e408ba65459307b50b 100644 --- a/submissions/urls.py +++ b/submissions/urls.py @@ -24,13 +24,20 @@ urlpatterns = [ 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$', 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$', + 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})/recommendations/(?P<rec_id>[0-9]+)$', + views.EICRecommendationView.as_view(), name='eic_recommendation_detail'), url(r'^admin/events/latest$', views.latest_events, name='latest_events'), url(r'^admin/reports$', views.reports_accepted_list, name='reports_accepted_list'), url(r'^admin/reports/(?P<report_id>[0-9]+)/compile$', views.report_pdf_compile, name='report_pdf_compile'), + url(r'^admin/reports/(?P<report_id>[0-9]+)/compile$', + views.report_pdf_compile, name='report_pdf_compile'), url(r'^submit_manuscript$', views.RequestSubmission.as_view(), name='submit_manuscript'), url(r'^submit_manuscript/prefill$', views.prefill_using_arxiv_identifier, diff --git a/submissions/views.py b/submissions/views.py index 3e6d66d230c8fafb88b9fd0d3870a5482341214f..3294cb0447a351baadec170da5bc914883c759d5 100644 --- a/submissions/views.py +++ b/submissions/views.py @@ -11,7 +11,8 @@ from django.shortcuts import get_object_or_404, render, redirect from django.template import Template, Context from django.utils import timezone from django.utils.decorators import method_decorator -from django.views.generic.edit import CreateView +from django.views.generic.detail import DetailView +from django.views.generic.edit import CreateView, UpdateView from django.views.generic.list import ListView from guardian.decorators import permission_required_or_403 @@ -22,12 +23,14 @@ from .constants import SUBMISSION_STATUS_VOTING_DEPRECATED, STATUS_VETTED, STATU STATUS_DRAFT from .models import Submission, EICRecommendation, EditorialAssignment,\ RefereeInvitation, Report, EditorialCommunication, SubmissionEvent +from .mixins import SubmissionAdminViewMixin from .forms import SubmissionIdentifierForm, RequestSubmissionForm, SubmissionSearchForm,\ RecommendationVoteForm, ConsiderAssignmentForm, AssignSubmissionForm,\ SetRefereeingDeadlineForm, RefereeSelectForm, RefereeRecruitmentForm,\ ConsiderRefereeInvitationForm, EditorialCommunicationForm,\ EICRecommendationForm, ReportForm, VetReportForm, VotingEligibilityForm,\ - SubmissionCycleChoiceForm, ReportPDFForm, SubmissionReportsForm + SubmissionCycleChoiceForm, ReportPDFForm, SubmissionReportsForm,\ + iThenticateReportForm from .utils import SubmissionUtils from scipost.forms import ModifyPersonalMessageForm, RemarkForm @@ -86,6 +89,7 @@ class RequestSubmission(CreateView): messages.warning(self.request, *error_messages) return super().form_invalid(form) + @login_required @permission_required('scipost.can_submit_manuscript', raise_exception=True) def prefill_using_arxiv_identifier(request): @@ -253,6 +257,9 @@ def reports_accepted_list(request): """ reports = (Report.objects.accepted() .order_by('pdf_report', 'submission').prefetch_related('submission')) + + if request.GET.get('submission'): + reports = reports.filter(submission__arxiv_identifier_w_vn_nr=request.GET.get('submission')) context = { 'reports': reports } @@ -1389,6 +1396,8 @@ def fix_College_decision(request, rec_id): """ Terminates the voting on a Recommendation. Called by an Editorial Administrator. + + TO FIX: If multiple recommendations are submitted; decisions may be overruled unexpectedly. """ recommendation = get_object_or_404((EICRecommendation.objects .get_for_user_in_pool(request.user)), pk=rec_id) @@ -1429,3 +1438,51 @@ def fix_College_decision(request, rec_id): SubmissionUtils.send_author_College_decision_email() messages.success(request, 'The Editorial College\'s decision has been fixed.') return redirect(reverse('submissions:pool')) + + +class EICRecommendationView(SubmissionAdminViewMixin, DetailView): + permission_required = 'scipost.can_fix_College_decision' + template_name = 'submissions/admin/eic_recommendation_detail.html' + editorial_page = True + + def get_context_data(self, *args, **kwargs): + """ Get the EICRecommendation as a submission-related instance. """ + ctx = super().get_context_data(*args, **kwargs) + ctx['object'] = get_object_or_404(ctx['submission'].eicrecommendations.all(), + id=self.kwargs['rec_id']) + return ctx + + +class EditorialSummaryView(SubmissionAdminViewMixin, ListView): + """ + Show all submission currently active in a refereeing process. + In addition show all EIC events of the last 24 hours. + """ + permission_required = 'scipost.can_oversee_refereeing' + template_name = 'submissions/admin/editorial_admin.html' + + def get_context_data(self, *args, **kwargs): + context = super().get_context_data(*args, **kwargs) + + # Pick submission from `submission_list` to include proper filters such as author filters. + try: + arxiv_id = self.request.GET.get('submission') + assert arxiv_id + context['submission'] = (context['submission_list'] + .get(arxiv_identifier_w_vn_nr=arxiv_id)) + except (AssertionError, Submission.DoesNotExist): + context['submission'] = None + context['latest_events'] = SubmissionEvent.objects.for_eic().last_hours() + return context + + +class PlagiarismView(SubmissionAdminViewMixin, UpdateView): + permission_required = 'scipost.can_do_plagiarism_checks' + template_name = 'submissions/admin/plagiarism_report.html' + editorial_page = True + success_url = reverse_lazy('submissions:plagiarism') + form_class = iThenticateReportForm + + def get_object(self): + submission = super().get_object() + return submission.plagiarism_report diff --git a/theses/templates/theses/vet_thesislink_requests.html b/theses/templates/theses/vet_thesislink_requests.html deleted file mode 100644 index e0a7cef6628352e762a740e08dada75f0607f269..0000000000000000000000000000000000000000 --- a/theses/templates/theses/vet_thesislink_requests.html +++ /dev/null @@ -1,32 +0,0 @@ -{% extends 'scipost/base.html' %} - -{% block pagetitle %}: vet Thesis Link requests{% endblock pagetitle %} - -{% block bodysup %} - -<section> - {% if not thesislink_to_vet %} - <h1>There are no Thesis Link requests for you to vet.</h1> - - {% else %} - - <h1>SciPost Thesis Link request to vet:</h1> - - <br> - <hr> - <div class="row"> - <div class="col-8"> - {% include "./_thesislink_information.html" with thesislink=thesislink_to_vet %} - </div> - <div class="col-4"> - <form action= {% url 'theses:vet_thesislink_request' thesislink_to_vet.id %} method="post"> - {% csrf_token %} - {{ form.as_ul }} - <input type="submit" value="Submit" /> - </div> - </div> - - {% endif %} -</section> - -{% endblock bodysup %}