diff --git a/scipost/templates/scipost/personal_page.html b/scipost/templates/scipost/personal_page.html index 6776d069fc8af926cd39d4b044e7a42123fd7834..ae21f523980dc7ef829eae44ee1a17b7aac1599b 100644 --- a/scipost/templates/scipost/personal_page.html +++ b/scipost/templates/scipost/personal_page.html @@ -322,6 +322,7 @@ <h3>Refereeing overview</h3> <ul> <li>View (and act on) outstanding refereeing invitations in the <a href="{% url 'submissions:refereeing_overview' %}">refereeing overview</a></li> + <li><a href="{% url 'submissions:statistics' %}">View statistics</a> for submissions, refereeing, publishing</li> </ul> <h3>Voting</h3> <ul> diff --git a/submissions/templates/submissions/statistics.html b/submissions/templates/submissions/statistics.html new file mode 100644 index 0000000000000000000000000000000000000000..3417157acbafed47ae729d3017d3d002bb73831e --- /dev/null +++ b/submissions/templates/submissions/statistics.html @@ -0,0 +1,94 @@ +{% extends 'submissions/_pool_base.html' %} + +{% block pagetitle %}: statistics for submisisons{% endblock pagetitle %} + +{% load scipost_extras %} +{% load submissions_extras %} + +{% load bootstrap %} + +{% block breadcrumb_items %} + {{block.super}} + <span class="breadcrumb-item">Statistics</span> +{% endblock %} + +{% block content %} + +<div class="row"> + <div class="col-12"> + <div class="card card-grey"> + <div class="card-body"> + <h1>Statistics</h1> + </div> + </div> + </div> +</div> + +<div class="row"> + <div class="col-12"> + <h2>Aggregate statistics</h2> + <p>For each Journal, clicking on a year gives aggregate statistics for submissions (so including rejections). Clicking on a Journal/Volume/Issue gives aggregates for publications in this object.</p> + <ul> + {% for journal in journals %} + <li><a href="{% url 'submissions:statistics' journal_doi_label=journal.doi_label %}">{{ journal }}</a></li> + <ul> + {% for year in journal|journal_publication_years %} + <li><a href="{% url 'submissions:statistics' journal_doi_label=journal.doi_label year=year %}">{{ year }}</a></li> + {% endfor %} + </ul> + <ul> + {% for volume in journal.volume_set.all %} + <li><a href="{% url 'submissions:statistics' journal_doi_label=journal.doi_label volume_nr=volume.number %}">{{ volume }}</a></li> + <ul> + {% for issue in volume.issue_set.all %} + <li><a href="{% url 'submissions:statistics' journal_doi_label=journal.doi_label volume_nr=volume.number issue_nr=issue.number %}">{{ issue }}</a></li> + {% endfor %} + </ul> + {% endfor %} + </ul> + {% endfor %} + </ul> + </div> +</div> + +{% if journal %} +<div class="row"> + <div class="col-12"> + <h2>Results:</h2> + <table class="w-50"> + <tr> + <th>DOI label</th> + {% if year %} + <th>Year</th> + <th>Nr submissions</th> + <th>Nr distinct submissions</th> + {% endif %} + <th>Nr publications</th> + <th>Duration average</th> + </tr> + <tr> + {% if issue %} + <td>{{ issue.doi_label }}</td> + <td>{{ issue|issue_nr_publications }}</td> + <td>{{ issue|issue_avg_processing_duration|floatformat:2 }}</td> + {% elif volume %} + <td>{{ volume.doi_label }}</td> + <td>{{ volume|volume_nr_publications }}</td> + <td>{{ volume|volume_avg_processing_duration|floatformat:2 }}</td> + {% else %} + <td>{{ journal.doi_label }}</td> + {% if year %} + <td>{{ year }}</td> + <td>{{ submissions|length }}</td> + <td>{{ submissions|submissions_count_distinct }}</td> + {% endif %} + <td>{{ journal|journal_nr_publications }}</td> + <td>{{ journal|journal_avg_processing_duration|floatformat:2 }}</td> + {% endif %} + </tr> + </table> + </div> +</div> +{% endif %} + +{% endblock content %} diff --git a/submissions/templatetags/submissions_extras.py b/submissions/templatetags/submissions_extras.py index 83a792a917a31309c6bb14fe9976a15be9e4337b..7782dcd81080bfde08034e43a2ac861b9d78365f 100644 --- a/submissions/templatetags/submissions_extras.py +++ b/submissions/templatetags/submissions_extras.py @@ -1,8 +1,10 @@ import datetime from django import template +from django.db.models import Avg, F from django.utils import timezone +from journals.models import Publication from submissions.constants import SUBMISSION_STATUS_OUT_OF_POOL from submissions.models import Submission @@ -24,3 +26,54 @@ def is_viewable_by_authors(recommendation): return recommendation.submission.status in ['revision_requested', 'resubmitted', 'accepted', 'rejected', 'published', 'withdrawn'] + +@register.filter(name='submissions_count_distinct') +def submissions_count_distinct(submissions): + identifiers_wo_vn_nr = [] + for submission in submissions: + identifiers_wo_vn_nr.append(submission.arxiv_identifier_wo_vn_nr) + return len(identifiers_wo_vn_nr) + + +@register.filter(name='journal_publication_years') +def journal_publication_years(journal): + years = [] + for volume in journal.volume_set.all(): + years.append(volume.until_date.year) + return 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='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='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 diff --git a/submissions/urls.py b/submissions/urls.py index c1f46c9ab3839e1758c764de519a0ae91a24eecf..20de6bfc112ed883f622939ee418f4252e7baa05 100644 --- a/submissions/urls.py +++ b/submissions/urls.py @@ -89,6 +89,14 @@ urlpatterns = [ url(r'^close_refereeing_round/{regex}$'.format(regex=SUBMISSIONS_COMPLETE_REGEX), views.close_refereeing_round, name='close_refereeing_round'), url(r'^refereeing_overview$', views.refereeing_overview, name='refereeing_overview'), + url(r'^statistics/(?P<journal_doi_label>[a-zA-Z]+)/(?P<volume_nr>[0-9]+)/(?P<issue_nr>[0-9]+)$', + views.statistics, name='statistics'), + url(r'^statistics/(?P<journal_doi_label>[a-zA-Z]+)/(?P<volume_nr>[0-9]+)$', + views.statistics, name='statistics'), + url(r'^statistics/(?P<journal_doi_label>[a-zA-Z]+)$', views.statistics, name='statistics'), + url(r'^statistics/(?P<journal_doi_label>[a-zA-Z]+)/year/(?P<year>[0-9]{4,})$', + views.statistics, name='statistics'), + url(r'^statistics$', views.statistics, name='statistics'), url(r'^communication/{regex}/(?P<comtype>[a-zA-Z]{{4,}})$'.format(regex=SUBMISSIONS_COMPLETE_REGEX), views.communication, name='communication'), url(r'^communication/{regex}/(?P<comtype>[a-zA-Z]{{4,}})/(?P<referee_id>[0-9]+)$'.format(regex=SUBMISSIONS_COMPLETE_REGEX), diff --git a/submissions/views.py b/submissions/views.py index 7f53451343204d046ff2f976e5e9fb5a96faee4f..81851f43f04cf04a0793ce768f7c2a719d191377 100644 --- a/submissions/views.py +++ b/submissions/views.py @@ -35,6 +35,7 @@ from .forms import SubmissionIdentifierForm, RequestSubmissionForm, SubmissionSe from .utils import SubmissionUtils from mails.views import MailEditingSubView +from journals.models import Journal, Volume, Issue, Publication from scipost.forms import ModifyPersonalMessageForm, RemarkForm from scipost.models import Contributor, Remark, RegistrationInvitation from scipost.utils import Utils @@ -1002,6 +1003,30 @@ def refereeing_overview(request): return render(request, 'submissions/refereeing_overview.html', context) +@permission_required('scipost.can_oversee_refereeing', raise_exception=True) +def statistics(request, journal_doi_label=None, volume_nr=None, issue_nr=None, year=None): + journals = Journal.objects.all() + context = { + 'journals': journals, + } + if journal_doi_label: + journal = get_object_or_404(Journal, doi_label=journal_doi_label) + context['journal'] = journal + if year: + context['year'] = year + submissions = Submission.objects.filter( + submitted_to_journal=journal, + submission_date__year=year, + ) + context['submissions'] = submissions + if volume_nr: + volume = get_object_or_404(Volume, number=volume_nr) + context['volume'] = volume + if issue_nr: + issue = get_object_or_404(Issue, number=issue_nr) + context['issue'] = issue + return render(request, 'submissions/statistics.html', context) + @login_required def communication(request, arxiv_identifier_w_vn_nr, comtype, referee_id=None): """