diff --git a/journals/models.py b/journals/models.py index c443f7f99d6392a1096dca531aa9b2d75069c4bb..5ca16ad8fe0dbe6dab6e636209d9ff728154e276 100644 --- a/journals/models.py +++ b/journals/models.py @@ -2,6 +2,7 @@ from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.models import ContentType from django.contrib.postgres.fields import JSONField from django.db import models +from django.db.models import Avg, F from django.utils import timezone from django.urls import reverse @@ -51,14 +52,32 @@ class Journal(models.Model): def get_abbreviation_citation(self): return journal_name_abbrev_citation(self.name) - def citation_rate(self): + def nr_publications(self, tier=None): + publications = Publication.objects.filter(in_issue__in_volume__in_journal=self) + if tier: + publications = publications.filter( + accepted_submission__eicrecommendations__recommendation=tier) + return publications.count() + + def avg_processing_duration(self): + duration = Publication.objects.filter( + in_issue__in_volume__in_journal=self).aggregate( + avg=Avg(F('publication_date') - F('submission_date')))['avg'] + if duration: + return duration.total_seconds() / 86400 + return 0 + + def citation_rate(self, tier=None): """ Returns the citation rate in units of nr citations per article per year. """ - pubs = Publication.objects.filter(in_issue__in_volume__in_journal=self) + publications = Publication.objects.filter(in_issue__in_volume__in_journal=self) + if tier: + publications = publications.filter( + accepted_submission__eicrecommendations__recommendation=tier) ncites = 0 - deltat = 1 # to avoid division by zero - for pub in pubs: + deltat = 1 # to avoid division by zero + for pub in publications: if pub.citedby and pub.latest_citedby_update: ncites += len(pub.citedby) deltat += (pub.latest_citedby_update.date() - pub.publication_date).days @@ -83,18 +102,36 @@ class Volume(models.Model): def doi_string(self): return '10.21468/' + self.doi_label - def citation_rate(self): + def nr_publications(self, tier=None): + publications = Publication.objects.filter(in_issue__in_volume=self) + if tier: + publications = publications.filter( + accepted_submission__eicrecommendations__recommendation=tier) + return publications.count() + + def avg_processing_duration(self): + duration = Publication.objects.filter( + in_issue__in_volume=self).aggregate( + avg=Avg(F('publication_date') - F('submission_date')))['avg'] + if duration: + return duration.total_seconds() / 86400 + return 0 + + def citation_rate(self, tier=None): """ Returns the citation rate in units of nr citations per article per year. """ - pubs = Publication.objects.filter(in_issue__in_volume=self) + publications = Publication.objects.filter(in_issue__in_volume=self) + if tier: + publications = publications.filter( + accepted_submission__eicrecommendations__recommendation=tier) ncites = 0 - deltat = 1 # to avoid division by zero - for pub in pubs: + deltat = 1 # to avoid division by zero + for pub in publications: if pub.citedby and pub.latest_citedby_update: ncites += len(pub.citedby) deltat += (pub.latest_citedby_update.date() - pub.publication_date).days - return (ncites * 365.25/deltat) + return (ncites * 365.25 / deltat) class Issue(models.Model): @@ -147,14 +184,32 @@ class Issue(models.Model): return self.start_date <= timezone.now().date() and\ self.until_date >= timezone.now().date() - def citation_rate(self): + def nr_publications(self, tier=None): + publications = Publication.objects.filter(in_issue=self) + if tier: + publications = publications.filter( + accepted_submission__eicrecommendations__recommendation=tier) + return publications.count() + + def avg_processing_duration(self): + duration = Publication.objects.filter( + in_issue=self).aggregate( + avg=Avg(F('publication_date') - F('submission_date')))['avg'] + if duration: + return duration.total_seconds() / 86400 + return 0 + + def citation_rate(self, tier=None): """ Returns the citation rate in units of nr citations per article per year. """ - pubs = Publication.objects.filter(in_issue=self) + publications = Publication.objects.filter(in_issue=self) + if tier: + publications = publications.filter( + accepted_submission__eicrecommendations__recommendation=tier) ncites = 0 - deltat = 1 # to avoid division by zero - for pub in pubs: + deltat = 1 # to avoid division by zero + for pub in publications: if pub.citedby and pub.latest_citedby_update: ncites += len(pub.citedby) deltat += (pub.latest_citedby_update.date() - pub.publication_date).days diff --git a/scipost/models.py b/scipost/models.py index 7666fdf99faf4ec9d2d9a1b0a806cd0af4037545..03121a51bf68120856deb74203694cd5482c2fd3 100644 --- a/scipost/models.py +++ b/scipost/models.py @@ -83,17 +83,19 @@ class Contributor(models.Model): return not self.unavailability_periods.today().exists() def is_EdCol_Admin(self): - return self.user.groups.filter(name='Editorial Administrators').exists() + return (self.user.groups.filter(name='Editorial Administrators').exists() + or self.user.is_superuser) def is_SP_Admin(self): return (self.user.groups.filter(name='SciPost Administrators').exists() or self.user.is_superuser) def is_MEC(self): - return self.user.groups.filter(name='Editorial College').exists() + return self.fellowships.active().exists() or self.user.is_superuser def is_VE(self): - return self.user.groups.filter(name='Vetting Editors').exists() + return (self.user.groups.filter(name='Vetting Editors').exists() + or self.user.is_superuser) def generate_key(self, feed=''): """ diff --git a/scipost/static/scipost/assets/js/dynamic_loading.js b/scipost/static/scipost/assets/js/dynamic_loading.js index 5064be98fb68d318671d523bd3694ce75d0efb3c..f7cd54c7384f91c03420ffc389d6c8b3d4d5907c 100644 --- a/scipost/static/scipost/assets/js/dynamic_loading.js +++ b/scipost/static/scipost/assets/js/dynamic_loading.js @@ -26,29 +26,4 @@ $(function(){ dynamic_load_tab( e.target ) }) $('[data-toggle="tab"][sp-autoload="true"]').tab('show'); - - // Simple simple Angular-like loading! - $('a[data-toggle="dynamic"]').on('click', function(event) { - event.preventDefault(); - var self = this, - url = $(this).attr('href'), - target = $(this).attr('data-target'); - - $(target) - .show() - .html('<div class="loading"><i class="fa fa-circle-o-notch fa-spin fa-3x fa-fw"></i></div>'); - - $.get(url + '?json=1').done(function(data) { - $(target).html(data).promise().done(function() { - init_page(); - }); - $('[data-target="active-list"]') - .find('> li') - .removeClass('active') - $(self).parents('[data-target="active-list"] > li') - .addClass('active'); - - window.history.replaceState('scipost', document.title, url); - }); - }); }); diff --git a/scipost/views.py b/scipost/views.py index dd6fd4fbb2413aa6b2b07277d62d0cb60a6ae4a3..992def46ff62caf21d8143621e142890cefefba7 100644 --- a/scipost/views.py +++ b/scipost/views.py @@ -741,7 +741,7 @@ def _personal_page_editorial_actions(request): 'Editorial Administrators', 'Editorial College', 'Vetting Editors', - 'Junior Ambassadors']).exists() + 'Junior Ambassadors']).exists() or request.user.is_superuser if not permission: raise PermissionDenied diff --git a/stats/templates/stats/statistics.html b/stats/templates/stats/statistics.html index 61fa1a21de5800c7f380a3937c80e4ad18dcd890..23e5ba55d74407e294bfab43697556b705755cd1 100644 --- a/stats/templates/stats/statistics.html +++ b/stats/templates/stats/statistics.html @@ -41,78 +41,122 @@ </ul> {% if journal %} - <h2>Results:</h2> - <table class="table"> - <tr> - <th>DOI label</th> - {% if year %} - <th>Year</th> - <th>Nr submissions<br/>(distinct)</th> - <th>Nr submissions<br/>(including resubmissions)</th> - <th>Nr assignment failed</th> - <th>Nr accepted/<br/>published</th> - <th>Nr rejected</th> - <th>Nr withdrawn</th> - {% else %} - <th>Nr publications</th> - <th>Duration average</th> - <th>Citation rate (per paper per year)</ht> - {% endif %} - </tr> - <tr> - {% if issue %} - <td>{{ issue.doi_label }}</td> - <td>{{ issue|issue_nr_publications }}</td> - <td>{{ issue|issue_avg_processing_duration|floatformat:2 }} days</td> - <td>{{ issue|issue_citation_rate|floatformat:2 }}</td> - {% elif volume %} - <td>{{ volume.doi_label }}</td> - <td>{{ volume|volume_nr_publications }}</td> - <td>{{ volume|volume_avg_processing_duration|floatformat:2 }} days</td> - <td>{{ volume|volume_citation_rate|floatformat:2 }}</td> - {% else %} - <td>{{ journal.doi_label }}</td> - {% if year %} - <td>{{ year }}</td> - <td>{{ submissions|submissions_count_distinct }}</td> - <td>{{ submissions|length }}</td> - <td>{{ submissions.assignment_failed.count }}</td> - <td>{{ submissions.accepted.count|add:submissions.published.count }}</td> - <td>{{ submissions.rejected.count }}</td> - <td>{{ submissions.withdrawn.count }}</td> - {% else %} - <td>{{ journal|journal_nr_publications }}</td> - <td>{{ journal|journal_avg_processing_duration|floatformat:2 }} days</td> - <td>{{ journal|journal_citation_rate|floatformat:2 }}</td> - {% endif %} - {% endif %} - </tr> - </table> +<h2>Results:</h2> +<table class="table"> + <tr> + <th>DOI label</th> + {% if year %} + <th>Year</th> + <th>Nr submissions<br/>(distinct)</th> + <th>Nr submissions<br/>(including resubmissions)</th> + <th>Nr assignment failed</th> + <th>Nr accepted/<br/>published</th> + <th>Nr rejected</th> + <th>Nr withdrawn</th> + {% else %} + <th>Nr publications</th> + <th>Duration average</th> + <th>Citation rate (per paper per year)</th> {% endif %} - + </tr> + <tr> + {% if issue %} + <td>{{ issue.doi_label }}</td> + <td>{% nr_publications issue %}</td> + <td>{% avg_processing_duration issue as avg %}{{ avg|floatformat:2 }} days</td> + <td>{% citation_rate issue as cr %}{{ cr|floatformat:2 %}</td> + {% elif volume %} + <td>{{ volume.doi_label }}</td> + <td>{% nr_publications volume %}</td> + <td>{% avg_processing_duration volume as avg %}{{ avg|floatformat:2 }} days</td> + <td>{% citation_rate volume as cr %}{{ cr|floatformat:2 }}</td> + {% else %} + <td>{{ journal.doi_label }}</td> {% if year %} - <h2>Refereeing stats for {{ year }}</h2> - <table class="table"> - <tr> - <th>Nr refereeing<br/>invitations</th> - <th>Nr accepted</th> - <th>Nr declined</th> - <th>Nr pending</th> - <th>Nr reports<br/>obtained</th> - <th>Nr obtained<br/>(invited)</th> - <th>Nr obtained<br/>(contributed)</th> - </tr> - <tr> - <td>{{ nr_ref_inv }}</td> - <td>{{ nr_acc }} ({% widthratio nr_acc nr_ref_inv 100 %}%)</td> - <td>{{ nr_dec }} ({% widthratio nr_dec nr_ref_inv 100 %}%)</td> - <td>{{ nr_pen }} ({% widthratio nr_pen nr_ref_inv 100 %}%)</td> - <td>{{ nr_rep_obt }}</td> - <td>{{ nr_rep_obt_inv }} ({% widthratio nr_rep_obt_inv nr_rep_obt 100 %}%)</td> - <td>{{ nr_rep_obt_con }} ({% widthratio nr_rep_obt_con nr_rep_obt 100 %}%)</td> - </tr> - </table> + <td>{{ year }}</td> + <td>{{ submissions|submissions_count_distinct }}</td> + <td>{{ submissions|length }}</td> + <td>{{ submissions.assignment_failed.count }}</td> + <td>{{ submissions.accepted.count|add:submissions.published.count }}</td> + <td>{{ submissions.rejected.count }}</td> + <td>{{ submissions.withdrawn.count }}</td> + {% else %} + <td>{% nr_publications journal %}</td> + <td>{% avg_processing_duration journal as avg %}{{ avg|floatformat:2 }} days</td> + <td>{% citation_rate journal as cr %}{{ cr|floatformat:2 }}</td> + {% endif %} {% endif %} + </tr> +</table> + +<table class="table"> + <tr> + <th>DOI label</th> + <th colspan="3">Nr per Tier</th> + <th colspan="3">Citation rate (per paper per year)</th> + </tr> + <tr> + <th></th> + <th>I</th> + <th>II</th> + <th>IIII</th> + <th>I</th> + <th>II</th> + <th>IIII</th> + </tr> + <tr> + {% if issue %} + <td>{{ issue.doi_label }}</td> + <td>{% nr_publications issue tier=1 %}</td> + <td>{% nr_publications issue tier=2 %}</td> + <td>{% nr_publications issue tier=3 %}</td> + <td>{% citation_rate issue tier=1 as cr %}{{ cr|floatformat:2 }}</td> + <td>{% citation_rate issue tier=2 as cr %}{{ cr|floatformat:2 }}</td> + <td>{% citation_rate issue tier=3 as cr %}{{ cr|floatformat:2 }}</td> + {% elif volume %} + <td>{{ volume.doi_label }}</td> + <td>{% nr_publications volume tier=1 %}</td> + <td>{% nr_publications volume tier=2 %}</td> + <td>{% nr_publications volume tier=3 %}</td> + <td>{% citation_rate volume tier=1 as cr %}{{ cr|floatformat:2 }}</td> + <td>{% citation_rate volume tier=2 as cr %}{{ cr|floatformat:2 }}</td> + <td>{% citation_rate volume tier=3 as cr %}{{ cr|floatformat:2 }}</td> + {% else %} + <td>{{ journal.doi_label }}</td> + <td>{% nr_publications journal tier=1 %}</td> + <td>{% nr_publications journal tier=2 %}</td> + <td>{% nr_publications journal tier=3 %}</td> + <td>{% citation_rate journal tier=1 as cr %}{{ cr|floatformat:2 }}</td> + <td>{% citation_rate journal tier=2 as cr %}{{ cr|floatformat:2 }}</td> + <td>{% citation_rate journal tier=3 as cr %}{{ cr|floatformat:2 }}</td> + {% endif %} + </tr> +</table> +{% endif %} + +{% if year %} +<h2>Refereeing stats for {{ year }}</h2> +<table class="table"> + <tr> + <th>Nr refereeing<br/>invitations</th> + <th>Nr accepted</th> + <th>Nr declined</th> + <th>Nr pending</th> + <th>Nr reports<br/>obtained</th> + <th>Nr obtained<br/>(invited)</th> + <th>Nr obtained<br/>(contributed)</th> + </tr> + <tr> + <td>{{ nr_ref_inv }}</td> + <td>{{ nr_acc }} ({% widthratio nr_acc nr_ref_inv 100 %}%)</td> + <td>{{ nr_dec }} ({% widthratio nr_dec nr_ref_inv 100 %}%)</td> + <td>{{ nr_pen }} ({% widthratio nr_pen nr_ref_inv 100 %}%)</td> + <td>{{ nr_rep_obt }}</td> + <td>{{ nr_rep_obt_inv }} ({% widthratio nr_rep_obt_inv nr_rep_obt 100 %}%)</td> + <td>{{ nr_rep_obt_con }} ({% widthratio nr_rep_obt_con nr_rep_obt 100 %}%)</td> + </tr> +</table> +{% endif %} {% endblock content %} diff --git a/stats/templatetags/stats_extras.py b/stats/templatetags/stats_extras.py index d6362255440acb8a9f2a27db5fd665b2bc8f2c55..335c813cc216907cb5ee9828390e200a42969854 100644 --- a/stats/templatetags/stats_extras.py +++ b/stats/templatetags/stats_extras.py @@ -1,13 +1,22 @@ from django import template -from django.db.models import Avg, F - -from journals.models import Publication -from submissions.constants import SUBMISSION_STATUS_OUT_OF_POOL -from submissions.models import Submission register = template.Library() +@register.simple_tag +def avg_processing_duration(obj, *args, **kwargs): + return getattr(obj, 'avg_processing_duration')(*args, **kwargs) + + +@register.simple_tag +def nr_publications(obj, *args, **kwargs): + return getattr(obj, 'nr_publications')(*args, **kwargs) + + +@register.simple_tag +def citation_rate(obj, *args, **kwargs): + return getattr(obj, 'citation_rate')(*args, **kwargs) + @register.filter(name='submissions_count_distinct') def submissions_count_distinct(submissions): @@ -25,54 +34,3 @@ def journal_publication_years(journal): if volume.until_date.year not in years: years.append(volume.until_date.year) return sorted(years) - - -@register.filter(name='journal_nr_publications') -def journal_nr_publications(journal): - return Publication.objects.filter(in_issue__in_volume__in_journal=journal).count() - -@register.filter(name='journal_avg_processing_duration') -def journal_avg_processing_duration(journal): - duration = Publication.objects.filter( - in_issue__in_volume__in_journal=journal).aggregate( - avg=Avg(F('publication_date') - F('submission_date')))['avg'] - if not duration: return 0 - return duration.days + duration.seconds/86400 - -@register.filter(name='journal_citation_rate') -def journal_citation_rate(journal): - return journal.citation_rate() - - -@register.filter(name='volume_nr_publications') -def volume_nr_publications(volume): - return Publication.objects.filter(in_issue__in_volume=volume).count() - -@register.filter(name='volume_avg_processing_duration') -def volume_avg_processing_duration(volume): - duration = Publication.objects.filter( - in_issue__in_volume=volume).aggregate( - avg=Avg(F('publication_date') - F('submission_date')))['avg'] - if not duration: return 0 - return duration.days + duration.seconds/86400 - -@register.filter(name='volume_citation_rate') -def volume_citation_rate(volume): - return volume.citation_rate() - - -@register.filter(name='issue_nr_publications') -def issue_nr_publications(issue): - return Publication.objects.filter(in_issue=issue).count() - -@register.filter(name='issue_avg_processing_duration') -def issue_avg_processing_duration(issue): - duration = Publication.objects.filter( - in_issue=issue).aggregate( - avg=Avg(F('publication_date') - F('submission_date')))['avg'] - if not duration: return 0 - return duration.days + duration.seconds/86400 - -@register.filter(name='issue_citation_rate') -def issue_citation_rate(issue): - return issue.citation_rate() diff --git a/stats/views.py b/stats/views.py index 11d7bd845e11df31b0c12fc591974163be366f78..dd416c843a34dfa75973cb963d437ddbd601ba5e 100644 --- a/stats/views.py +++ b/stats/views.py @@ -1,10 +1,9 @@ import datetime -from django.contrib.auth.decorators import login_required, permission_required +from django.contrib.auth.decorators import permission_required from django.shortcuts import get_object_or_404, render -from journals.constants import SCIPOST_JOURNALS_SUBMIT -from journals.models import Journal, Volume, Issue, Publication +from journals.models import Journal, Volume, Issue from submissions.models import Submission @@ -19,8 +18,9 @@ def statistics(request, journal_doi_label=None, volume_nr=None, issue_nr=None, y context['journal'] = journal if year: context['year'] = year - submissions = Submission.objects.filter(submitted_to_journal=journal_doi_label - ).originally_submitted(datetime.date(int(year), 1, 1), datetime.date(int(year), 12, 31)) + submissions = Submission.objects.filter( + submitted_to_journal=journal_doi_label).originally_submitted( + datetime.date(int(year), 1, 1), datetime.date(int(year), 12, 31)) context['submissions'] = submissions nr_ref_inv = 0 nr_acc = 0 @@ -29,8 +29,6 @@ def statistics(request, journal_doi_label=None, volume_nr=None, issue_nr=None, y nr_rep_obt = 0 nr_rep_obt_inv = 0 nr_rep_obt_con = 0 - nr_rep_ref = 0 - nr_aw_vet = 0 for submission in submissions: nr_ref_inv += submission.referee_invitations.count() nr_acc += submission.count_accepted_invitations() diff --git a/submissions/migrations/0005_auto_20180123_2312.py b/submissions/migrations/0005_auto_20180123_2312.py index 35d948ca90f1ac4165226f5ba50b886734d7a877..767d4f45f872a126ed66f8336489e6ba7a2d3485 100644 --- a/submissions/migrations/0005_auto_20180123_2312.py +++ b/submissions/migrations/0005_auto_20180123_2312.py @@ -21,8 +21,4 @@ class Migration(migrations.Migration): name='version', field=models.SmallIntegerField(default=1), ), - migrations.AlterUniqueTogether( - name='eicrecommendation', - unique_together=set([('submission', 'version')]), - ), ] diff --git a/submissions/migrations/0008_auto_20180127_2208.py b/submissions/migrations/0008_auto_20180127_2208.py new file mode 100644 index 0000000000000000000000000000000000000000..e51d43e5b0d97ee50a5083bee889bf74df46fac7 --- /dev/null +++ b/submissions/migrations/0008_auto_20180127_2208.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.4 on 2018-01-27 21:08 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('submissions', '0007_auto_20180124_1501'), + ] + + operations = [ + migrations.AlterUniqueTogether( + name='eicrecommendation', + unique_together=set([('submission', 'version')]), + ), + ] diff --git a/submissions/templates/submissions/pool/pool.html b/submissions/templates/submissions/pool/pool.html index a1112527805bd55a2242b181b9dcb4d99a96aab4..e64124a83bdf0397e76a22156c6a89bced31181f 100644 --- a/submissions/templates/submissions/pool/pool.html +++ b/submissions/templates/submissions/pool/pool.html @@ -115,15 +115,3 @@ </div><!-- End page content --> </div> {% endblock %} - -{% comment %} - <h3><em>Click on a submission to see its summary and actions</em></h3> - - {% if is_ECAdmin %} - <h2>All events in the last 24 hours</h2> - <div id="eventslist"> - {% include 'submissions/submission_event_list_general.html' with events=latest_events %} - </div> - {% endif %} - - {% endcomment %} diff --git a/submissions/views.py b/submissions/views.py index ca61186bf2d3666eb0ef21c3a68a80624206198b..2ecd5d295baffa8ed75b62ebee75399c44d8c5fa 100644 --- a/submissions/views.py +++ b/submissions/views.py @@ -392,9 +392,10 @@ def pool(request, arxiv_identifier_w_vn_nr=None): # EdColAdmin related variables if request.user.has_perm('scipost.can_oversee_refereeing'): - context['latest_submission_events'] = SubmissionEvent.objects.for_eic().last_hours() + context['latest_submission_events'] = SubmissionEvent.objects.for_eic().last_hours()\ + .filter(submission__in=context['submissions']) - # Temporary test logic: only testers see the new Pool + # Pool gets Submission details via ajax request if context['submission'] and request.is_ajax(): template = 'partials/submissions/pool/submission_details.html' else: @@ -1106,7 +1107,7 @@ def communication(request, arxiv_identifier_w_vn_nr, comtype, referee_id=None): # Editorial Administration to Editor if not request.user.has_perm('scipost.can_oversee_refereeing'): raise PermissionDenied - submissions_qs = Submission.objects.filter_for_author(request.user) + submissions_qs = Submission.objects.pool_editable(request.user) referee = request.user.contributor else: # Invalid commtype in the url!