SciPost Code Repository

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

Implement assignments

parent 308a3de4
No related branches found
No related tags found
No related merge requests found
......@@ -28,4 +28,5 @@ class ProductionStreamAdmin(admin.ModelAdmin):
)
admin.site.register(ProductionUser)
admin.site.register(ProductionStream, ProductionStreamAdmin)
from django import forms
from .models import ProductionEvent
from .models import ProductionUser, ProductionStream, ProductionEvent
class ProductionEventForm(forms.ModelForm):
......@@ -15,3 +15,22 @@ class ProductionEventForm(forms.ModelForm):
'comments': forms.Textarea(attrs={'rows': 4}),
'duration': forms.TextInput(attrs={'placeholder': 'HH:MM:SS'})
}
class AssignOfficerForm(forms.ModelForm):
officer = forms.ModelChoiceField(queryset=ProductionUser.objects.all())
class Meta:
model = ProductionStream
fields = ()
def clean_officer(self):
officer = self.cleaned_data['officer']
if officer in self.instance.officers.all():
self.add_error('officer', 'Officer already assigned to Stream')
return officer
def save(self, commit=True):
officer = self.cleaned_data['officer']
self.instance.officers.add(officer)
return self.instance
......@@ -3,13 +3,16 @@ from django.db import models
from .constants import PRODUCTION_STREAM_COMPLETED, PRODUCTION_STREAM_ONGOING
class ProductionStreamManager(models.Manager):
class ProductionStreamQuerySet(models.QuerySet):
def completed(self):
return self.filter(status=PRODUCTION_STREAM_COMPLETED)
def ongoing(self):
return self.filter(status=PRODUCTION_STREAM_ONGOING)
def filter_for_user(self, production_user):
return self.filter(officers=production_user)
class ProductionEventManager(models.Manager):
def get_my_events(self, current_contributor):
......
# -*- coding: utf-8 -*-
# Generated by Django 1.11.4 on 2017-09-14 18:20
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('production', '0011_productionuser'),
]
operations = [
migrations.AddField(
model_name='productionstream',
name='officers',
field=models.ManyToManyField(blank=True, to='production.ProductionUser'),
),
]
......@@ -4,7 +4,7 @@ from django.contrib.auth.models import User
from django.utils import timezone
from .constants import PRODUCTION_STREAM_STATUS, PRODUCTION_STREAM_ONGOING, PRODUCTION_EVENTS
from .managers import ProductionStreamManager, ProductionEventManager
from .managers import ProductionStreamQuerySet, ProductionEventManager
class ProductionUser(models.Model):
......@@ -12,7 +12,8 @@ class ProductionUser(models.Model):
Production Officers will have a ProductionUser object related to their account
to relate all production related actions to.
"""
user = models.OneToOneField(User, on_delete=models.PROTECT, unique=True)
user = models.OneToOneField(User, on_delete=models.PROTECT, unique=True,
related_name='production_user')
# objects = ProductionUserQuerySet.as_manager() -- Not implemented yet
......@@ -26,8 +27,10 @@ class ProductionStream(models.Model):
closed = models.DateTimeField(default=timezone.now)
status = models.CharField(max_length=32,
choices=PRODUCTION_STREAM_STATUS, default=PRODUCTION_STREAM_ONGOING)
officers = models.ManyToManyField('production.ProductionUser', blank=True,
related_name='streams')
objects = ProductionStreamManager()
objects = ProductionStreamQuerySet.as_manager()
def __str__(self):
return '{arxiv}, {title}'.format(arxiv=self.submission.arxiv_identifier_w_vn_nr,
......
......@@ -21,3 +21,8 @@
<li>No events were found.</li>
{% endfor %}
</ul>
{% if stream.total_duration %}
<hr class="sm">
<p class="pl-4 ml-3">Total duration for this stream: <strong>{{ stream.total_duration|duration }}</strong></p>
{% endif %}
{% load bootstrap %}
{% load scipost_extras %}
<div class="w-100" id="stream_{{stream.id}}">
{% include 'submissions/_submission_card_content_sparse.html' with submission=stream.submission %}
</div>
<div class="card-body">
<div class="row">
<div class="{% if form %}col-lg-7{% else %}col-12{% endif %}">
<div class="{% if prodevent_form %}col-lg-7{% else %}col-12{% endif %}">
<h3>Officers</h3>
<ul>
{% for officer in stream.officers.all %}
<li>{{ officer }}{% if perms.scipost.can_assign_production_officer %} &middot; <a href="{% url 'production:remove_officer' stream_id=stream.id officer_id=officer.id %}" class="text-danger">Remove from stream</a>{% endif %}</li>
{% empty %}
<li>No Officer assigned yet.</li>
{% endfor %}
</ul>
<h3>Events</h3>
{% include 'production/partials/production_events.html' with events=stream.productionevent_set.all %}
<br/>
{% if stream.total_duration %}
<h3>Total duration for this stream: {{ stream.total_duration|duration }}</h3>
{% endif %}
{% if perms.scipost.can_publish_accepted_submission %}
<h3><a href="{% url 'production:mark_as_completed' stream_id=stream.id %}">Mark this stream as completed</a></h3>
{% if perms.scipost.can_publish_accepted_submission or perms.scipost.can_assign_production_officer %}
<h3>Actions</h3>
<ul class="">
{% if perms.scipost.can_assign_production_officer and assignment_form %}
<li>
<a href="javascript:;" data-toggle="toggle" data-target="#add_officer_{{stream.id}}">Assign Production Officer to this stream</a>
<div id="add_officer_{{stream.id}}" style="display: none;">
<form class="my-3" action="{% url 'production:add_officer' stream_id=stream.id %}" method="post">
{% csrf_token %}
{{ assignment_form|bootstrap_inline }}
<input type="submit" class="btn btn-outline-primary" name="submit" value="Add officer">
</form>
</div>
</li>
{% endif %}
{% if perms.scipost.can_publish_accepted_submission %}
<li><a href="{% url 'production:mark_as_completed' stream_id=stream.id %}">Mark this stream as completed</a></li>
{% endif %}
</ul>
{% endif %}
</div>
{% if form %}
{% if prodevent_form %}
<div class="col-lg-5 mt-4 mt-lg-0">
<h3>Add an event to this production stream:</h3>
<form action="{% url 'production:add_event' stream_id=stream.id %}" method="post">
{% csrf_token %}
{{ form|bootstrap }}
{{ prodevent_form|bootstrap }}
<input type="submit" class="btn btn-secondary" name="submit" value="Submit">
</form>
</div>
......
......@@ -71,7 +71,7 @@
</tr>
<tr id="collapse{{ stream.id }}" class="collapse" role="tabpanel" aria-labelledby="heading{{ stream.id }}" style="background-color: #fff;">
<td colspan="5">
{% include 'production/partials/production_stream_card.html' with stream=stream form=prodevent_form %}
{% include 'production/partials/production_stream_card.html' with stream=stream prodevent_form=prodevent_form assignment_form=assignment_form %}
</td>
</tr>
{% empty %}
......
......@@ -7,6 +7,10 @@ urlpatterns = [
url(r'^completed$', production_views.completed, name='completed'),
url(r'^streams/(?P<stream_id>[0-9]+)/events/add$',
production_views.add_event, name='add_event'),
url(r'^streams/(?P<stream_id>[0-9]+)/officer/add$',
production_views.add_officer, name='add_officer'),
url(r'^streams/(?P<stream_id>[0-9]+)/officer/(?P<officer_id>[0-9]+)/remove$',
production_views.remove_officer, name='remove_officer'),
url(r'^streams/(?P<stream_id>[0-9]+)/mark_completed$',
production_views.mark_as_completed, name='mark_as_completed'),
url(r'^events/(?P<event_id>[0-9]+)/edit',
......
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,8 +10,8 @@ from django.views.generic.edit import UpdateView, DeleteView
from guardian.decorators import permission_required
from .constants import PRODUCTION_STREAM_COMPLETED
from .models import ProductionStream, ProductionEvent
from .forms import ProductionEventForm
from .models import ProductionUser, ProductionStream, ProductionEvent
from .forms import ProductionEventForm, AssignOfficerForm
from .signals import notify_stream_completed
from scipost.models import Contributor
......@@ -28,14 +27,19 @@ def production(request):
Overview page for the production process.
All papers with accepted but not yet published status are included here.
"""
streams = ProductionStream.objects.ongoing().order_by('opened')
if request.user.has_perm('scipost.can_assign_production_officer'):
streams = ProductionStream.objects.ongoing().order_by('opened')
else:
streams = ProductionStream.objects.ongoing().filter_for_user(request.user.production_user).order_by('opened')
prodevent_form = ProductionEventForm()
assignment_form = AssignOfficerForm()
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,
'assignment_form': assignment_form,
'ownevents': ownevents,
}
if request.user.has_perm('scipost.can_view_timesheets'):
......@@ -68,6 +72,33 @@ def add_event(request, stream_id):
return redirect(reverse('production:production'))
@permission_required('scipost.can_assign_production_officer', return_403=True)
def add_officer(request, stream_id):
stream = get_object_or_404(ProductionStream.objects.ongoing(), pk=stream_id)
form = AssignOfficerForm(request.POST or None, instance=stream)
if form.is_valid():
form.save()
messages.success(request, 'Officer {officer} has been assigned.'.format(
officer=form.cleaned_data.get('officer')))
else:
for key, error in form.errors.items():
messages.warning(request, error[0])
return redirect(reverse('production:production'))
@permission_required('scipost.can_assign_production_officer', return_403=True)
def remove_officer(request, stream_id, officer_id):
stream = get_object_or_404(ProductionStream.objects.ongoing(), pk=stream_id)
try:
officer = stream.officers.get(pk=officer_id)
except ProductionUser.DoesNotExist:
return redirect(reverse('production:production'))
stream.officers.remove(officer)
messages.success(request, 'Officer {officer} has been removed.'.format(officer=officer))
return redirect(reverse('production:production'))
@method_decorator(permission_required('scipost.can_view_production', raise_exception=True),
name='dispatch')
class UpdateEventView(UpdateView):
......
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