SciPost Code Repository

Skip to content
Snippets Groups Projects
Commit 311f7c47 authored by Jorran de Wit's avatar Jorran de Wit
Browse files

Merge branch 'development' into commentary

parents 91d233a2 6e0589ad
No related branches found
No related tags found
No related merge requests found
...@@ -20,14 +20,17 @@ def petition(request, slug): ...@@ -20,14 +20,17 @@ def petition(request, slug):
if request.user.is_authenticated: if request.user.is_authenticated:
is_signed = petition.petition_signatories.verified().filter( is_signed = petition.petition_signatories.verified().filter(
signatory=request.user.contributor).exists() signatory=request.user.contributor).exists()
affiliation = request.user.contributor.affiliations.first() or {}
institition = affiliation.institution.name if affiliation else ''
country = affiliation.institution.country if affiliation else ''
initial = { initial = {
'petition': petition, 'petition': petition,
'title': request.user.contributor.title, 'title': request.user.contributor.title,
'first_name': request.user.first_name, 'first_name': request.user.first_name,
'last_name': request.user.last_name, 'last_name': request.user.last_name,
'email': request.user.email, 'email': request.user.email,
'country_of_employment': request.user.contributor.affiliation.country_of_employment, 'country_of_employment': country,
'affiliation': request.user.contributor.affiliation.name, 'affiliation': institition,
} }
form = SignPetitionForm(request.POST or None, initial=initial, petition=petition, form = SignPetitionForm(request.POST or None, initial=initial, petition=petition,
......
...@@ -2,7 +2,8 @@ from django.contrib import admin ...@@ -2,7 +2,8 @@ from django.contrib import admin
from guardian.admin import GuardedModelAdmin from guardian.admin import GuardedModelAdmin
from .models import ProductionStream, ProductionEvent, ProductionUser, Proofs from .models import ProductionStream, ProductionEvent, ProductionUser, Proofs,\
ProductionEventAttachment
def event_count(obj): def event_count(obj):
...@@ -38,4 +39,5 @@ class ProductionProofsAdmin(admin.ModelAdmin): ...@@ -38,4 +39,5 @@ class ProductionProofsAdmin(admin.ModelAdmin):
admin.site.register(Proofs, ProductionProofsAdmin) admin.site.register(Proofs, ProductionProofsAdmin)
admin.site.register(ProductionUser) admin.site.register(ProductionUser)
admin.site.register(ProductionEvent) admin.site.register(ProductionEvent)
admin.site.register(ProductionEventAttachment)
admin.site.register(ProductionStream, ProductionStreamAdmin) admin.site.register(ProductionStream, ProductionStreamAdmin)
...@@ -3,7 +3,8 @@ import datetime ...@@ -3,7 +3,8 @@ import datetime
from django import forms from django import forms
from . import constants from . import constants
from .models import ProductionUser, ProductionStream, ProductionEvent, Proofs from .models import ProductionUser, ProductionStream, ProductionEvent, Proofs,\
ProductionEventAttachment
from .signals import notify_stream_status_change from .signals import notify_stream_status_change
today = datetime.datetime.today() today = datetime.datetime.today()
...@@ -144,6 +145,7 @@ class ProofsDecisionForm(forms.ModelForm): ...@@ -144,6 +145,7 @@ class ProofsDecisionForm(forms.ModelForm):
decision = forms.ChoiceField(choices=[(True, 'Accept Proofs for publication'), decision = forms.ChoiceField(choices=[(True, 'Accept Proofs for publication'),
(False, 'Decline Proofs for publication')]) (False, 'Decline Proofs for publication')])
feedback = forms.CharField(required=False, widget=forms.Textarea) feedback = forms.CharField(required=False, widget=forms.Textarea)
feedback_attachment = forms.FileField(required=False)
class Meta: class Meta:
model = Proofs model = Proofs
...@@ -173,9 +175,14 @@ class ProofsDecisionForm(forms.ModelForm): ...@@ -173,9 +175,14 @@ class ProofsDecisionForm(forms.ModelForm):
prodevent = ProductionEvent( prodevent = ProductionEvent(
stream=proofs.stream, stream=proofs.stream,
event='status', event='status',
comments='Received feedback from the authors: {comments}'.format( comments='<em>Received feedback from the authors:</em><br>{comments}'.format(
comments=comments), comments=comments),
noted_by=proofs.stream.supervisor noted_by=proofs.stream.supervisor
) )
prodevent.save() prodevent.save()
if self.cleaned_data.get('feedback_attachment'):
attachment = ProductionEventAttachment(
attachment=self.cleaned_data['feedback_attachment'],
production_event=prodevent)
attachment.save()
return proofs return proofs
# -*- coding: utf-8 -*-
# Generated by Django 1.11.4 on 2017-11-12 16:05
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
import production.models
import scipost.storage
class Migration(migrations.Migration):
dependencies = [
('production', '0032_auto_20171010_1008'),
]
operations = [
migrations.CreateModel(
name='ProductionEventAttachment',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('attachment', models.FileField(storage=scipost.storage.SecureFileStorage(), upload_to=production.models.production_event_upload_location)),
('production_event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='attachments', to='production.ProductionEvent')),
],
),
]
...@@ -106,6 +106,29 @@ class ProductionEvent(models.Model): ...@@ -106,6 +106,29 @@ class ProductionEvent(models.Model):
return self.stream.notification_name return self.stream.notification_name
def production_event_upload_location(instance, filename):
submission = instance.production_event.stream.submission
return 'UPLOADS/PRODSTREAMS/{year}/{arxiv}/{filename}'.format(
year=submission.submission_date.year,
arxiv=submission.arxiv_identifier_wo_vn_nr,
filename=filename)
class ProductionEventAttachment(models.Model):
"""
An ProductionEventAttachment is in general used by authors to reply to an Proofs version
with their version of the Proofs with comments.
"""
production_event = models.ForeignKey('production.ProductionEvent', on_delete=models.CASCADE,
related_name='attachments')
attachment = models.FileField(upload_to=production_event_upload_location,
storage=SecureFileStorage())
def get_absolute_url(self):
return reverse('production:production_event_attachment_pdf',
args=(self.production_event.stream.id, self.id,))
def proofs_upload_location(instance, filename): def proofs_upload_location(instance, filename):
submission = instance.stream.submission submission = instance.stream.submission
return 'UPLOADS/PROOFS/{year}/{arxiv}/{filename}'.format( return 'UPLOADS/PROOFS/{year}/{arxiv}/{filename}'.format(
......
...@@ -34,10 +34,18 @@ ...@@ -34,10 +34,18 @@
{% if event.noted_to %} {% if event.noted_to %}
{{ event.noted_by.user.first_name }} {{ event.noted_by.user.last_name }} {{ event.comments|linebreaksbr }} {{ event.noted_to.user.first_name }} {{ event.noted_to.user.last_name }}. {{ event.noted_by.user.first_name }} {{ event.noted_by.user.last_name }} {{ event.comments|linebreaksbr }} {{ event.noted_to.user.first_name }} {{ event.noted_to.user.last_name }}.
{% else %} {% else %}
{{ event.comments|linebreaksbr }} {{ event.comments|safe|linebreaksbr }}
{% endif %} {% endif %}
</p> </p>
{% endif %} {% endif %}
{% if event.attachments.exists %}
<ul>
{% for attachment in event.attachments.all %}
<li><a href="{{ attachment.get_absolute_url }}" target="_blank">Download Attachment {{ forloop.counter }}</a></li>
{% endfor %}
</ul>
{% endif %}
</li> </li>
{% empty %} {% empty %}
<li>No events were found.</li> <li>No events were found.</li>
......
...@@ -54,8 +54,8 @@ ...@@ -54,8 +54,8 @@
<ul> <ul>
{% for proofs in stream.proofs.all %} {% for proofs in stream.proofs.all %}
<li class="py-1"> <li class="py-1">
<a href="{% url 'production:proofs' stream_id=stream.id version=proofs.version %}">Version {{ proofs.version }}</a><br> <a href="{% url 'production:proofs' stream_id=stream.id version=proofs.version %}">Version {{ proofs.version }}</a> &middot; <span class="label label-secondary label-sm">{{ proofs.get_status_display }}</span><br>
Uploaded by {{ proofs.uploaded_by.user.first_name }} {{ proofs.uploaded_by.user.last_name }}<br> Uploaded by: {{ proofs.uploaded_by.user.first_name }} {{ proofs.uploaded_by.user.last_name }}<br>
Accessible for authors: {{ proofs.accessible_for_authors|yesno:'<strong>Yes</strong>,No'|safe }}<br> Accessible for authors: {{ proofs.accessible_for_authors|yesno:'<strong>Yes</strong>,No'|safe }}<br>
{% if perms.scipost.can_run_proofs_by_authors %} {% if perms.scipost.can_run_proofs_by_authors %}
...@@ -71,8 +71,6 @@ ...@@ -71,8 +71,6 @@
</ul> </ul>
{% endif %} {% endif %}
{% endif %} {% endif %}
<span class="label label-secondary label-sm">{{ proofs.get_status_display }}</span>
</li> </li>
{% empty %} {% empty %}
<li>No Proofs found.</li> <li>No Proofs found.</li>
......
...@@ -38,8 +38,7 @@ ...@@ -38,8 +38,7 @@
</li> </li>
{% elif proofs.status == 'accepted_sup' %} {% elif proofs.status == 'accepted_sup' %}
<li><a href="{% url 'production:send_proofs' proofs.stream.id proofs.version %}">Send proofs to authors</a></li> <li><a href="{% url 'production:send_proofs' proofs.stream.id proofs.version %}">Send proofs to authors</a></li>
{% endif %} {% else %}
{% if proofs.status != 'uploaded' %}
<li><a href="{% url 'production:toggle_accessibility' proofs.stream.id proofs.version %}">{{ proofs.accessible_for_authors|yesno:'Hide,Make accessible' }} for authors</a></li> <li><a href="{% url 'production:toggle_accessibility' proofs.stream.id proofs.version %}">{{ proofs.accessible_for_authors|yesno:'Hide,Make accessible' }} for authors</a></li>
{% endif %} {% endif %}
{% endif %} {% endif %}
......
...@@ -21,6 +21,8 @@ urlpatterns = [ ...@@ -21,6 +21,8 @@ urlpatterns = [
production_views.send_proofs, name='send_proofs'), production_views.send_proofs, name='send_proofs'),
url(r'^streams/(?P<stream_id>[0-9]+)/proofs/(?P<version>[0-9]+)/toggle_access$', url(r'^streams/(?P<stream_id>[0-9]+)/proofs/(?P<version>[0-9]+)/toggle_access$',
production_views.toggle_accessibility, name='toggle_accessibility'), production_views.toggle_accessibility, name='toggle_accessibility'),
url(r'^streams/(?P<stream_id>[0-9]+)/proofs/(?P<attachment_id>[0-9]+)/reply/pdf$',
production_views.production_event_attachment_pdf, name='production_event_attachment_pdf'),
url(r'^streams/(?P<stream_id>[0-9]+)/events/add$', url(r'^streams/(?P<stream_id>[0-9]+)/events/add$',
production_views.add_event, name='add_event'), production_views.add_event, name='add_event'),
url(r'^streams/(?P<stream_id>[0-9]+)/logs/add$', url(r'^streams/(?P<stream_id>[0-9]+)/logs/add$',
......
...@@ -18,7 +18,8 @@ from finances.forms import WorkLogForm ...@@ -18,7 +18,8 @@ from finances.forms import WorkLogForm
from mails.views import MailEditingSubView from mails.views import MailEditingSubView
from . import constants from . import constants
from .models import ProductionUser, ProductionStream, ProductionEvent, Proofs from .models import ProductionUser, ProductionStream, ProductionEvent, Proofs,\
ProductionEventAttachment
from .forms import ProductionEventForm, AssignOfficerForm, UserToOfficerForm,\ from .forms import ProductionEventForm, AssignOfficerForm, UserToOfficerForm,\
AssignSupervisorForm, StreamStatusForm, ProofsUploadForm, ProofsDecisionForm,\ AssignSupervisorForm, StreamStatusForm, ProofsUploadForm, ProofsDecisionForm,\
AssignInvitationsOfficerForm AssignInvitationsOfficerForm
...@@ -492,7 +493,7 @@ def proofs_pdf(request, slug): ...@@ -492,7 +493,7 @@ def proofs_pdf(request, slug):
# because now it will return 404 instead of a redirect to the login page. # because now it will return 404 instead of a redirect to the login page.
raise Http404 raise Http404
proofs = Proofs.objects.get(id=proofs_slug_to_id(slug)) proofs = get_object_or_404(Proofs, id=proofs_slug_to_id(slug))
stream = proofs.stream stream = proofs.stream
# Check if user has access! # Check if user has access!
...@@ -511,6 +512,34 @@ def proofs_pdf(request, slug): ...@@ -511,6 +512,34 @@ def proofs_pdf(request, slug):
return response return response
def production_event_attachment_pdf(request, stream_id, attachment_id):
""" Open ProductionEventAttachment pdf. """
if not request.user.is_authenticated:
# Don't use the decorator but this strategy,
# because now it will return 404 instead of a redirect to the login page.
raise Http404
stream = get_object_or_404(ProductionStream, id=stream_id)
attachment = get_object_or_404(
ProductionEventAttachment.objects.filter(production_event__stream=stream),
id=attachment_id)
# Check if user has access!
checker = ObjectPermissionChecker(request.user)
access = checker.has_perm('can_work_for_stream', stream) and request.user.has_perm('scipost.can_view_production')
if not access and request.user.contributor:
access = request.user.contributor in stream.submission.authors.all()
if not access:
raise Http404
# Passed the test! The user may see the file!
content_type, encoding = mimetypes.guess_type(attachment.attachment.path)
content_type = content_type or 'application/octet-stream'
response = HttpResponse(attachment.attachment.read(), content_type=content_type)
response["Content-Encoding"] = encoding
return response
@login_required @login_required
@transaction.atomic @transaction.atomic
def author_decision(request, slug): def author_decision(request, slug):
...@@ -526,7 +555,7 @@ def author_decision(request, slug): ...@@ -526,7 +555,7 @@ def author_decision(request, slug):
if request.user.contributor not in proofs.stream.submission.authors.all(): if request.user.contributor not in proofs.stream.submission.authors.all():
raise Http404 raise Http404
form = ProofsDecisionForm(request.POST or None, instance=proofs) form = ProofsDecisionForm(request.POST or None, request.FILES or None, instance=proofs)
if form.is_valid(): if form.is_valid():
proofs = form.save() proofs = form.save()
notify_stream_status_change(request.user, stream, False) notify_stream_status_change(request.user, stream, False)
...@@ -595,7 +624,7 @@ def decision(request, stream_id, version, decision): ...@@ -595,7 +624,7 @@ def decision(request, stream_id, version, decision):
) )
prodevent.save() prodevent.save()
messages.success(request, 'Proofs have been {decision}.'.format(decision=decision)) messages.success(request, 'Proofs have been {decision}.'.format(decision=decision))
return redirect(stream.get_absolute_url()) return redirect(reverse('production:proofs', args=(stream.id, proofs.version)))
@is_production_user() @is_production_user()
......
...@@ -105,13 +105,15 @@ ...@@ -105,13 +105,15 @@
<li> <li>
<a href="{{ proofs.get_absolute_url }}" target="_blank">Download version {{ proofs.version }}</a> &middot; uploaded: {{ proofs.created|date:"DATE_FORMAT" }} &middot; <a href="{{ proofs.get_absolute_url }}" target="_blank">Download version {{ proofs.version }}</a> &middot; uploaded: {{ proofs.created|date:"DATE_FORMAT" }} &middot;
status: <span class="label label-secondary label-sm">{{ proofs.get_status_display }}</span> status: <span class="label label-secondary label-sm">{{ proofs.get_status_display }}</span>
{% if proofs.status == 'accepted_sup' and proofs_decision_form and is_author %} {% if proofs.status == 'accepted_sup' or proofs.status == 'sent' %}
<h3 class="mb-0 mt-2">Please advise the Production Team on your findings on Proofs version {{ proof.version }}</h3> {% if proofs_decision_form and is_author %}
<form method="post" action="{% url 'production:author_decision' proofs.slug %}" class="my-2"> <h3 class="mb-0 mt-2">Please advise the Production Team on your findings on Proofs version {{ proofs.version }}</h3>
{% csrf_token %} <form method="post" enctype="multipart/form-data" action="{% url 'production:author_decision' proofs.slug %}" class="my-2">
{{ proofs_decision_form|bootstrap }} {% csrf_token %}
<input class="btn btn-primary btn-sm" type="submit" value="Submit"> {{ proofs_decision_form|bootstrap }}
</form> <input class="btn btn-primary btn-sm" type="submit" value="Submit">
</form>
{% endif %}
{% endif %} {% endif %}
</li> </li>
{% endfor %} {% endfor %}
......
...@@ -6,9 +6,9 @@ ...@@ -6,9 +6,9 @@
<div style="text-align: center;"> <div style="text-align: center;">
<img src="//scipost.org/static/scipost/images/logo_scipost_RGB_HTML_groot.png" alt="SciPost logo" width="240" style="margin-top: 20px; margin-bottom: 20px" /> <img src="//scipost.org/static/scipost/images/logo_scipost_RGB_HTML_groot.png" alt="SciPost logo" width="240" style="margin-top: 20px; margin-bottom: 20px" />
<h1>The server responded with an error.</h1> <h1>We are sorry, something went wrong.</h1>
<h2>500</h2> <h2>500</h2>
<h3>We are sorry, something went wrong. The SciPost administrators have been informed.</h3> <h3>The SciPost administrators have been informed.</h3>
<p style="margin-top: 20px;">Go back to <a href="//scipost.org">the homepage</a>.</p> <p style="margin-top: 20px;">Go back to <a href="//scipost.org">the homepage</a>.</p>
</div> </div>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment