start review and reject changesets

This commit is contained in:
Laura Klünder 2017-07-05 19:40:35 +02:00
parent 093b55923e
commit 2aa163e6ed
6 changed files with 128 additions and 4 deletions

View file

@ -2,12 +2,12 @@ import json
from collections import OrderedDict from collections import OrderedDict
from django.conf import settings from django.conf import settings
from django.forms import CharField, ModelForm, ValidationError from django.forms import BooleanField, CharField, ModelForm, ValidationError
from django.forms.widgets import HiddenInput from django.forms.widgets import HiddenInput
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from shapely.geometry.geo import mapping from shapely.geometry.geo import mapping
from c3nav.editor.models import ChangeSet from c3nav.editor.models import ChangeSet, ChangeSetUpdate
class MapitemFormMixin(ModelForm): class MapitemFormMixin(ModelForm):
@ -112,3 +112,11 @@ class ChangeSetForm(ModelForm):
class Meta: class Meta:
model = ChangeSet model = ChangeSet
fields = ('title', 'description') fields = ('title', 'description')
class RejectForm(ModelForm):
final = BooleanField(label=_('Final rejection'), required=False)
class Meta:
model = ChangeSetUpdate
fields = ('comment', )

View file

@ -297,6 +297,20 @@ class ChangeSet(models.Model):
def can_unpropose(self, request): def can_unpropose(self, request):
return self.author_id == request.user.pk and self.state in ('proposed', 'reproposed') return self.author_id == request.user.pk and self.state in ('proposed', 'reproposed')
def can_review(self, request):
# todo implement permissions
return self.is_author(request)
def can_start_review(self, request):
return self.can_review(request) and self.state in ('proposed', 'reproposed')
def can_end_review(self, request):
return self.can_review(request) and self.state == 'review' and self.assigned_to == request.user
def can_unreject(self, request):
return (self.can_review(request) and self.state in ('rejected', 'finallyrejected') and
self.assigned_to == request.user)
""" """
Update methods Update methods
""" """
@ -316,6 +330,43 @@ class ChangeSet(models.Model):
self.last_state_update = update self.last_state_update = update
self.save() self.save()
def start_review(self, user):
assign_to = user
if self.assigned_to == user:
assign_to = None
else:
self.assigned_to = user
if self.state != 'review':
update = self.updates.create(user=user, state='review', assigned_to=assign_to)
self.state = 'review'
self.last_state_update = update
elif assign_to is None:
return
else:
update = self.updates.create(user=user, assigned_to=assign_to)
self.last_update = update
self.save()
def reject(self, user, comment: str, final: bool):
state = 'finallyrejected' if final else 'rejected'
update = self.updates.create(user=user, state=state, comment=comment)
self.state = state
self.last_state_change = update
self.last_update = update
self.save()
def unreject(self, user):
update = self.updates.create(user=user, state='review')
self.state = 'review'
self.last_state_change = update
self.last_update = update
self.save()
def apply(self, user):
pass
def activate(self, request): def activate(self, request):
request.session['changeset'] = self.pk request.session['changeset'] = self.pk

View file

@ -127,9 +127,19 @@
{% if can_edit %} {% if can_edit %}
<a href="{% url 'editor.changesets.edit' pk=changeset.pk %}" class="btn btn-default">{% trans 'Edit' %}</a> <a href="{% url 'editor.changesets.edit' pk=changeset.pk %}" class="btn btn-default">{% trans 'Edit' %}</a>
{% endif %} {% endif %}
{% if can_edit and not changeset.proposed and active %} {% if can_edit and not changeset.proposed %}
<button type="submit" class="btn btn-primary" name="propose" value="1">{% trans 'Propose' %}</button> <button type="submit" class="btn btn-primary" name="propose" value="1">{% trans 'Propose' %}</button>
{% endif %} {% endif %}
{% if can_start_review %}
<button type="submit" class="btn btn-primary" name="review" value="1">{% trans 'Review' %}</button>
{% endif %}
{% if can_end_review %}
<button type="submit" class="btn btn-danger" name="reject" value="1">{% trans 'Reject' %}</button>
<button type="submit" class="btn btn-success" name="apply" value="1">{% trans 'Apply' %}</button>
{% endif %}
{% if can_unreject %}
<button type="submit" class="btn btn-default" name="reject" value="1">{% trans 'Unreject' %}</button>
{% endif %}
{% endif %} {% endif %}
{% if not active and not changeset.closed %} {% if not active and not changeset.closed %}
<button type="submit" class="btn btn-info" name="activate" value="1">{% trans 'Activate' %}</button> <button type="submit" class="btn btn-info" name="activate" value="1">{% trans 'Activate' %}</button>

View file

@ -0,0 +1,20 @@
{% load bootstrap3 %}
{% load i18n %}
{% include 'editor/fragment_modal_close.html' %}
<h3>{% trans 'Reject changes' %}</h3>
<form action="{{ request.path }}" method="post">
{% csrf_token %}
<p>{% trans 'Please explain why you reject these changes.' %}</p>
{% bootstrap_form form %}
<input type="hidden" name="reject" value="1">
{% buttons %}
<button class="invisiblesubmit" type="submit"></button>
<a class="btn btn-default" href="{{ request.path }}">
{% trans 'Cancel' %}
</a>
<button type="submit" name="reject_confirm" value="1" class="btn btn-danger pull-right">
{% trans 'Reject' %}
</button>
{% endbuttons %}
</form>

View file

@ -9,7 +9,7 @@ from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse from django.urls import reverse
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from c3nav.editor.forms import ChangeSetForm from c3nav.editor.forms import ChangeSetForm, RejectForm
from c3nav.editor.models import ChangeSet from c3nav.editor.models import ChangeSet
from c3nav.editor.utils import is_created_pk from c3nav.editor.utils import is_created_pk
from c3nav.editor.views.base import sidebar_view from c3nav.editor.views.base import sidebar_view
@ -83,6 +83,38 @@ def changeset_detail(request, pk):
return redirect(reverse('editor.changesets.detail', kwargs={'pk': changeset.pk})) return redirect(reverse('editor.changesets.detail', kwargs={'pk': changeset.pk}))
elif request.POST.get('review') == '1':
with changeset.lock_to_edit() as changeset:
if changeset.can_start_review(request):
changeset.start_review(request.user)
messages.success(request, _('You are not reviewing these changes.'))
else:
messages.error(request, _('You cannot review these changes.'))
return redirect(reverse('editor.changesets.detail', kwargs={'pk': changeset.pk}))
elif request.POST.get('reject') == '1':
with changeset.lock_to_edit() as changeset:
if not changeset.can_end_review(request):
messages.error(request, _('You cannot reject these changes.'))
return redirect(reverse('editor.changesets.detail', kwargs={'pk': changeset.pk}))
if request.POST.get('reject_confirm') == '1':
form = RejectForm(data=request.POST)
if form.is_valid():
changeset.reject(request.user, form.cleaned_data['comment'], form.cleaned_data['final'])
messages.success(request, _('You rejected these changes.'))
return redirect(reverse('editor.changesets.detail', kwargs={'pk': changeset.pk}))
else:
form = RejectForm()
return render(request, 'editor/changeset_reject.html', {
'changeset': changeset,
'form': form,
})
return redirect(reverse('editor.changesets.detail', kwargs={'pk': changeset.pk}))
elif request.POST.get('delete') == '1': elif request.POST.get('delete') == '1':
if not changeset.can_delete(request): if not changeset.can_delete(request):
raise PermissionDenied raise PermissionDenied
@ -263,6 +295,9 @@ def changeset_detail(request, pk):
'can_edit': can_edit, 'can_edit': can_edit,
'can_delete': can_delete, 'can_delete': can_delete,
'can_unpropose': changeset.can_unpropose(request), 'can_unpropose': changeset.can_unpropose(request),
'can_start_review': changeset.can_start_review(request),
'can_end_review': changeset.can_end_review(request),
'can_unreject': changeset.can_unreject(request),
'active': active, 'active': active,
'changed_objects': changed_objects_data, 'changed_objects': changed_objects_data,
} }