diff --git a/journals/forms.py b/journals/forms.py index d173a344773c53a27ea61e613631c63cdf7b8edc..eb54e4deb587df7ccbc6bbb533d2eb6450988883 100644 --- a/journals/forms.py +++ b/journals/forms.py @@ -1,3 +1,5 @@ +import re + from django import forms from django.utils import timezone @@ -45,6 +47,20 @@ class CitationListBibitemsForm(forms.Form): self.fields['latex_bibitems'].widget.attrs.update( {'rows': 30, 'cols': 50, 'placeholder': 'Paste the .tex bibitems here'}) + def extract_dois(self): + entries_list = self.cleaned_data['latex_bibitems'] + entries_list = re.sub(r'(?m)^\%.*\n?', '', entries_list) + entries_list = entries_list.split('\doi{') + dois = [] + nentries = 1 + for entry in entries_list[1:]: # drop first bit before first \doi{ + dois.append( + {'key': 'ref' + str(nentries), + 'doi': entry.partition('}')[0], } + ) + nentries += 1 + return dois + class FundingInfoForm(forms.Form): funding_statement = forms.CharField(widget=forms.Textarea()) diff --git a/journals/views.py b/journals/views.py index 899edf79899b8dd5e12edf3f45d78449de89af82..c901e579b419c757e395de4d9054f3b1ef467a33 100644 --- a/journals/views.py +++ b/journals/views.py @@ -73,7 +73,8 @@ def recent(request, doi_label): """ journal = get_object_or_404(Journal, doi_label=doi_label) recent_papers = Publication.objects.published( - in_issue__in_volume__in_journal=journal).order_by('-publication_date')[:20] + in_issue__in_volume__in_journal=journal).order_by('-publication_date', + '-paper_nr')[:20] context = { 'recent_papers': recent_papers, 'journal': journal, @@ -371,15 +372,7 @@ def create_citation_list_metadata(request, doi_label): if request.method == 'POST': bibitems_form = CitationListBibitemsForm(request.POST, request.FILES) if bibitems_form.is_valid(): - publication.metadata['citation_list'] = [] - entries_list = bibitems_form.cleaned_data['latex_bibitems'].split('\doi{') - nentries = 1 - for entry in entries_list[1:]: # drop first bit before first \doi{ - publication.metadata['citation_list'].append( - {'key': 'ref' + str(nentries), - 'doi': entry.partition('}')[0], } - ) - nentries += 1 + publication.metadata['citation_list'] = bibitems_form.extract_dois() publication.save() bibitems_form = CitationListBibitemsForm() context = { diff --git a/partners/views.py b/partners/views.py index fb9f563ecbac768ba26484e15e88a10c470f0ab2..33d20a49fb83a44565825edb02b53836ec713bd3 100644 --- a/partners/views.py +++ b/partners/views.py @@ -312,7 +312,7 @@ def email_prospartner_generic(request, prospartner_id): PartnerUtils.email_prospartner_generic() messages.success(request, 'Email successfully sent') - return redirect(reverse('partners:manage')) + return redirect(reverse('partners:dashboard')) context = {'prospartner': prospartner, 'form': form} return render(request, 'partners/email_prospartner_generic.html', context) diff --git a/production/constants.py b/production/constants.py index 26590270812cf4365e7a17919a5a7bada0d06eb6..e9453a6f7d140b0902509621cd54f3d616d3a132 100644 --- a/production/constants.py +++ b/production/constants.py @@ -19,4 +19,5 @@ PRODUCTION_EVENTS = ( ('corrections_implemented', 'Corrections implemented'), ('authors_have_accepted_proofs', 'Authors have accepted proofs'), ('paper_published', 'Paper has been published'), + ('cited_notified', 'Cited people have been notified/invited to SciPost'), ) diff --git a/production/migrations/0010_auto_20170707_0600.py b/production/migrations/0010_auto_20170707_0600.py new file mode 100644 index 0000000000000000000000000000000000000000..c3b931f3b04bcbc272695348794dfcb26d474a47 --- /dev/null +++ b/production/migrations/0010_auto_20170707_0600.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.3 on 2017-07-07 04:00 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('production', '0009_auto_20170701_1356'), + ] + + operations = [ + migrations.AlterField( + model_name='productionevent', + name='event', + field=models.CharField(choices=[('assigned_to_supervisor', 'Assigned by EdAdmin to Supervisor'), ('message_edadmin_to_supervisor', 'Message from EdAdmin to Supervisor'), ('message_supervisor_to_edadmin', 'Message from Supervisor to EdAdmin'), ('officer_tasked_with_proof_production', 'Supervisor tasked officer with proofs production'), ('message_supervisor_to_officer', 'Message from Supervisor to Officer'), ('message_officer_to_supervisor', 'Message from Officer to Supervisor'), ('proofs_produced', 'Proofs have been produced'), ('proofs_checked_by_supervisor', 'Proofs have been checked by Supervisor'), ('proofs_sent_to_authors', 'Proofs sent to Authors'), ('proofs_returned_by_authors', 'Proofs returned by Authors'), ('corrections_implemented', 'Corrections implemented'), ('authors_have_accepted_proofs', 'Authors have accepted proofs'), ('paper_published', 'Paper has been published'), ('cited_notified', 'Cited people have been notified/invited to SciPost')], max_length=64), + ), + ] diff --git a/production/templates/production/_production_timesheet_card.html b/production/templates/production/_production_timesheet_card.html new file mode 100644 index 0000000000000000000000000000000000000000..fcb30532b488a1c1aac300d0b2ff74b0df9885d7 --- /dev/null +++ b/production/templates/production/_production_timesheet_card.html @@ -0,0 +1,30 @@ +{% load bootstrap %} + +<div class="card-block"> + <table class="table mb-5"> + <thead class="thead-default"> + <tr> + <th>Date</th> + <th>Stream</th> + <th>Event</th> + <th>Duration</th> + </tr> + </thead> + <tbody role="tablist"> + {% for event in events %} + {% if event.duration %} + <tr> + <td>{{ event.noted_on }}</td> + <td>{{ event.stream }}</td> + <td>{{ event.get_event_display }}</td> + <td>{{ event.duration }}</td> + </tr> + {% endif %} + {% empty %} + <tr> + <td colspan="4">No events found.</td> + </tr> + {% endfor %} + </tbody> + </table> +</div> diff --git a/production/templates/production/production.html b/production/templates/production/production.html index 40f12bcc417bf4cee25452b0d8bf2651efef68b7..c748477f0602c7fa9752847ad328fc7915bb4869 100644 --- a/production/templates/production/production.html +++ b/production/templates/production/production.html @@ -13,21 +13,147 @@ <div class="row"> <div class="col-12"> - <h1 class="highlight">Manuscripts under production</h1> + <h1 class="highlight">Production</h1> </div> </div> + + <div class="row"> <div class="col-12"> - <ul class="list-group list-group-flush"> - {% for stream in streams %} - <li class="list-group-item{% if not forloop.first %} pt-3{% endif %}"> - {% include 'production/_production_stream_card.html' with stream=stream form=prodevent_form %} - </li> - {% empty %} - <li class="list-group-item">No production streams found.</li> - {% endfor %} - </ul> + <div class="tab-nav-container"> + <div class="tab-nav-inner"> + <ul class="nav btn-group personal-page-nav" role="tablist"> + <li class="nav-item btn btn-secondary"> + <a href="#streams" class="nav-link active" data-toggle="tab">Streams</a> + </li> + <li class="nav-item btn btn-secondary"> + <a href="#mytimesheet" class="nav-link" data-toggle="tab">My Timesheet</a> + </li> + {% if perms.scipost.can_view_timesheets %} + <li class="nav-item btn btn-secondary"> + <a href="#teamtimesheets" class="nav-link" data-toggle="tab">Team Timesheets</a> + </li> + {% endif %} + </ul> + </div> + </div> + </div> +</div> + + +<div class="tab-content"> + <div class="tab-pane active" id="streams" role="tabpanel"> + <div class="row"> + <div class="col-12"> + <h2 class="highlight">Streams</h2> + </div> + </div> + + <table class="table table-hover mb-5"> + <thead class="thead-default"> + <tr> + <th>Authors</th> + <th>Title</th> + <th>Accepted</th> + <th>Latest Event</th> + <th>Date</th> + </tr> + </thead> + + <tbody id="accordion" role="tablist" aria-multiselectable="true"> + {% for stream in streams %} + <tr data-toggle="collapse" data-parent="#accordion" href="#collapse{{ stream.id }}" aria-expanded="true" aria-controls="collapse{{ stream.id }}" style="cursor: pointer;"> + <td>{{ stream.submission.author_list }}</td> + <td>{{ stream.submission.title }}</td> + <td>{{ stream.submission.acceptance_date|date:"Y-m-d" }}</td> + <td>{{ stream.productionevent_set.last.get_event_display }}</td> + <td>{{ stream.productionevent_set.last.noted_on }}</td> + </tr> + <tr id="collapse{{ stream.id }}" class="collapse" role="tabpanel" aria-labelledby="heading{{ stream.id }}" style="background-color: #fff;"> + <td colspan="5"> + {% include 'production/_production_stream_card.html' with stream=stream form=prodevent_form %} + </td> + </tr> + {% empty %} + <tr> + <td colspan="5">No production streams found.</td> + </tr> + {% endfor %} + </tbody> + </table> </div> + + + <div class="tab-pane" id="mytimesheet" role="tabpanel"> + <div class="row"> + <div class="col-12"> + <h2 class="highlight">My Timesheet</h2> + </div> + </div> + + <table class="table mb-5"> + <thead class="thead-default"> + <tr> + <th>Date</th> + <th>Stream</th> + <th>Event</th> + <th>Duration</th> + </tr> + </thead> + + <tbody role="tablist"> + {% for event in ownevents %} + <tr> + <td>{{ event.noted_on }}</td> + <td>{{ event.stream }}</td> + <td>{{ event.get_event_display }}</td> + <td>{{ event.duration }}</td> + </tr> + {% empty %} + <tr> + <td colspan="4">No events found.</td> + </tr> + {% endfor %} + </tbody> + </table> + </div> + + {% if perms.scipost.can_view_timesheets %} + <div class="tab-pane" id="teamtimesheets" role="tabpanel"> + <div class="row"> + <div class="col-12"> + <h2 class="highlight">Team Timesheets</h2> + </div> + </div> + + <table class="table table-hover mb-5"> + <thead class="thead-default"> + <tr> + <th>Name</th> + </tr> + </thead> + + <tbody id="accordion" role="tablist" aria-multiselectable="true"> + {% for member in production_team.all %} + <tr data-toggle="collapse" data-parent="#accordion" href="#collapse{{ member.id }}" aria-expanded="true" aria-controls="collapse{{ member.id }}" style="cursor: pointer;"> + <td>{{ member }}</td> + </tr> + <tr id="collapse{{ member.id }}" class="collapse" role="tabpanel" aria-labelledby="heading{{ member.id }}" style="background-color: #fff;"> + <td> + {% include 'production/_production_timesheet_card.html' with events=member.productionevent_set.all %} + </td> + </tr> + {% empty %} + <tr> + <td>No Team Member found.</td> + </tr> + {% endfor %} + </tbody> + </table> + + </div> + {% endif %} + </div> {% endblock content %} diff --git a/production/views.py b/production/views.py index 0a7536f6a3c0f4de8f0e0e13250ddfc462497e09..06c723470234e8d9acf18dddf4a589860526be55 100644 --- a/production/views.py +++ b/production/views.py @@ -1,4 +1,7 @@ +import datetime + from django.contrib import messages +from django.contrib.auth.models import Group from django.core.urlresolvers import reverse from django.shortcuts import get_object_or_404, render, redirect from django.utils import timezone @@ -11,6 +14,8 @@ from .constants import PRODUCTION_STREAM_COMPLETED from .models import ProductionStream, ProductionEvent from .forms import ProductionEventForm +from scipost.models import Contributor + ###################### # Production process # @@ -24,10 +29,17 @@ def production(request): """ streams = ProductionStream.objects.ongoing().order_by('opened') prodevent_form = ProductionEventForm() + ownevents = ProductionEvent.objects.filter( + noted_by=request.user.contributor, + duration__gte=datetime.timedelta(minutes=1)).order_by('-noted_on') context = { 'streams': streams, 'prodevent_form': prodevent_form, + 'ownevents': ownevents, } + if request.user.has_perm('scipost.can_view_timesheets'): + context['production_team'] = Contributor.objects.filter( + user__groups__name='Production Officers').order_by('user__last_name') return render(request, 'production/production.html', context) diff --git a/scipost/management/commands/add_groups_and_permissions.py b/scipost/management/commands/add_groups_and_permissions.py index 9f36daa9743bb4577b32fdf3de121e677e8ec4a4..f0c48b82fcd072ae442c9d478b61f52408025418 100644 --- a/scipost/management/commands/add_groups_and_permissions.py +++ b/scipost/management/commands/add_groups_and_permissions.py @@ -206,6 +206,12 @@ class Command(BaseCommand): name='Can view docs: scipost', content_type=content_type) + # Financial administration + can_view_timesheets, created = Permission.objects.get_or_create( + codename='can_view_timesheets', + name='Can view timesheets', + content_type=content_type) + # Mailchimp can_manage_mailchimp, created = Permission.objects.get_or_create( codename='can_manage_mailchimp', @@ -229,12 +235,19 @@ class Command(BaseCommand): can_fix_College_decision, can_view_production, can_attend_VGMs, + can_view_timesheets, can_manage_mailchimp, ]) + + FinancialAdmin.permissions.set([ + can_view_timesheets, + ]) + AdvisoryBoard.permissions.set([ can_manage_registration_invitations, can_attend_VGMs, ]) + EditorialAdmin.permissions.set([ can_view_pool, can_assign_submissions, @@ -242,10 +255,12 @@ class Command(BaseCommand): can_prepare_recommendations_for_voting, can_fix_College_decision, can_view_production, + can_view_timesheets, can_publish_accepted_submission, can_attend_VGMs, can_manage_reports, ]) + EditorialCollege.permissions.set([ can_view_pool, can_take_charge_of_submissions, @@ -253,12 +268,14 @@ class Command(BaseCommand): view_bylaws, can_attend_VGMs, ]) + VettingEditors.permissions.set([ can_vet_commentary_requests, can_vet_thesislink_requests, can_vet_authorship_claims, can_vet_comments, ]) + RegisteredContributors.permissions.set([ can_submit_manuscript, can_submit_comments, @@ -267,15 +284,19 @@ class Command(BaseCommand): can_request_thesislinks, can_referee, ]) + Developers.permissions.set([ can_view_docs_scipost, ]) + Ambassadors.permissions.set([ can_manage_registration_invitations, ]) + JuniorAmbassadors.permissions.set([ can_draft_registration_invitations, ]) + ProductionOfficers.permissions.set([ can_view_docs_scipost, can_view_production, @@ -289,12 +310,14 @@ class Command(BaseCommand): can_email_prospartner_contact, can_view_partners, ]) + PartnersOfficers.permissions.set([ can_read_partner_page, can_view_own_partner_details, can_manage_SPB, can_view_partners, ]) + PartnerAccounts.permissions.set([ can_read_partner_page, can_view_own_partner_details, diff --git a/scipost/templates/scipost/personal_page.html b/scipost/templates/scipost/personal_page.html index 9cdbf70f2364e1180760d713625d8e16bdca2e87..36c2123832831132d921ecfd98c8d130ef468cd4 100644 --- a/scipost/templates/scipost/personal_page.html +++ b/scipost/templates/scipost/personal_page.html @@ -99,40 +99,43 @@ {% endif %} </div> <div class="col-md-6"> - {% if 'SciPost Administrators' in user_groups %} - <h3>You are a SciPost Administrator.</h3> - {% endif %} - {% if 'Editorial Administrators' in user_groups %} - <h3>You are a SciPost Editorial Administrator.</h3> - {% endif %} - {% if 'Advisory Board' in user_groups %} - <h3>You are a member of the Advisory Board.</h3> - {% endif %} - {% if 'Editorial College' in user_groups %} - <h3>You are a member of the Editorial College.</h3> - {% endif %} - {% if 'Vetting Editors' in user_groups %} - <h3>You are a SciPost Vetting Editor.</h3> - {% endif %} - {% if 'Registered Contributors' in user_groups %} - <h3>You are a Registered Contributor.</h3> - {% endif %} - {% if 'Testers' in user_groups %} - <h3>You are a SciPost Tester.</h3> - {% endif %} - {% if 'Ambassadors' in user_groups %} - <h3>You are a SciPost Ambassador.</h3> - {% endif %} - {% if 'Junior Ambassadors' in user_groups %} - <h3>You are a SciPost Junior Ambassador.</h3> - {% endif %} - - <h3 class="mt-3">Update your personal data or password</h3> - - <ul> - <li><a href="{% url 'scipost:update_personal_data' %}">Update your personal data</a></li> - <li><a href="{% url 'scipost:change_password' %}">Change your password</a></li> - </ul> + {% if 'SciPost Administrators' in user_groups %} + <h3>You are a SciPost Administrator.</h3> + {% endif %} + {% if 'Editorial Administrators' in user_groups %} + <h3>You are a SciPost Editorial Administrator.</h3> + {% endif %} + {% if 'Advisory Board' in user_groups %} + <h3>You are a member of the Advisory Board.</h3> + {% endif %} + {% if 'Editorial College' in user_groups %} + <h3>You are a member of the Editorial College.</h3> + {% endif %} + {% if 'Vetting Editors' in user_groups %} + <h3>You are a SciPost Vetting Editor.</h3> + {% endif %} + {% if 'Registered Contributors' in user_groups %} + <h3>You are a Registered Contributor.</h3> + {% endif %} + {% if 'Testers' in user_groups %} + <h3>You are a SciPost Tester.</h3> + {% endif %} + {% if 'Ambassadors' in user_groups %} + <h3>You are a SciPost Ambassador.</h3> + {% endif %} + {% if 'Junior Ambassadors' in user_groups %} + <h3>You are a SciPost Junior Ambassador.</h3> + {% endif %} + {% if 'Production Officers' in user_groups %} + <h3>You are a SciPost Production Officer.</h3> + {% endif %} + + <h3 class="mt-3">Update your personal data or password</h3> + + <ul> + <li><a href="{% url 'scipost:update_personal_data' %}">Update your personal data</a></li> + <li><a href="{% url 'scipost:change_password' %}">Change your password</a></li> + </ul> </div> </div> diff --git a/submissions/constants.py b/submissions/constants.py index 2b247ffa6a78a9c83e72934b3871e42c21ec3435..140c2824fa82f42a3df3ba88475894897eb8ac1a 100644 --- a/submissions/constants.py +++ b/submissions/constants.py @@ -45,7 +45,7 @@ SUBMISSION_HTTP404_ON_EDITORIAL_PAGE = [ ] SUBMISSION_STATUS_OUT_OF_POOL = SUBMISSION_HTTP404_ON_EDITORIAL_PAGE + [ - 'resubmitted' + STATUS_RESUBMITTED ] SUBMISSION_EXCLUDE_FROM_REPORTING = SUBMISSION_HTTP404_ON_EDITORIAL_PAGE + [ @@ -69,24 +69,22 @@ SUBMISSION_STATUS_PUBLICLY_INVISIBLE = [ STATUS_UNASSIGNED, STATUS_RESUBMISSION_INCOMING, 'assignment_failed', - 'resubmitted_rejected', STATUS_RESUBMITTED_REJECTED, - 'rejected', + STATUS_REJECTED, 'withdrawn', ] # Submissions which should not appear in search lists SUBMISSION_STATUS_PUBLICLY_UNLISTED = SUBMISSION_STATUS_PUBLICLY_INVISIBLE + [ - 'resubmitted', - 'resubmitted_rejected_visible', + STATUS_RESUBMITTED, STATUS_RESUBMITTED_REJECTED_VISIBLE, - 'published' + STATUS_PUBLISHED ] # Submissions for which voting on a related recommendation is deprecated: SUBMISSION_STATUS_VOTING_DEPRECATED = [ - 'rejected', - 'published', + STATUS_REJECTED, + STATUS_PUBLISHED, 'withdrawn', ]