Add basic proposal acceptance admin view
This commit is contained in:
parent
7248b40a60
commit
b22cdef2b8
5 changed files with 291 additions and 3 deletions
|
|
@ -1,16 +1,21 @@
|
||||||
from django.contrib import admin, auth
|
from django.contrib import admin, auth
|
||||||
from django.contrib.auth.models import Group
|
from django.contrib.auth.models import Group
|
||||||
from django.urls import reverse_lazy
|
from django.shortcuts import get_object_or_404, redirect, render
|
||||||
|
from django.urls import path, reverse, reverse_lazy
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from adminsortable2.admin import SortableAdminMixin
|
from adminsortable2.admin import SortableAdminMixin
|
||||||
|
|
||||||
from froide.follow.admin import FollowerAdmin
|
from froide.follow.admin import FollowerAdmin
|
||||||
from froide.helper.admin_utils import make_choose_object_action
|
from froide.helper.admin_utils import make_choose_object_action, make_emptyfilter
|
||||||
from froide.helper.widgets import TagAutocompleteWidget
|
from froide.helper.widgets import TagAutocompleteWidget
|
||||||
from froide.organization.models import Organization
|
from froide.organization.models import Organization
|
||||||
|
|
||||||
from .forms import GovernmentPlanForm, GovernmentPlanUpdateForm
|
from .forms import (
|
||||||
|
GovernmentPlanForm,
|
||||||
|
GovernmentPlanUpdateAcceptProposalForm,
|
||||||
|
GovernmentPlanUpdateForm,
|
||||||
|
)
|
||||||
from .models import (
|
from .models import (
|
||||||
Government,
|
Government,
|
||||||
GovernmentPlan,
|
GovernmentPlan,
|
||||||
|
|
@ -113,6 +118,17 @@ class GovernmentPlanAdmin(admin.ModelAdmin):
|
||||||
actions.update(admin_actions)
|
actions.update(admin_actions)
|
||||||
return actions
|
return actions
|
||||||
|
|
||||||
|
def get_urls(self):
|
||||||
|
urls = super().get_urls()
|
||||||
|
my_urls = [
|
||||||
|
path(
|
||||||
|
"<int:pk>/accept-proposal/",
|
||||||
|
self.admin_site.admin_view(self.accept_proposal),
|
||||||
|
name="froide_govplan-plan_accept_proposal",
|
||||||
|
),
|
||||||
|
]
|
||||||
|
return my_urls + urls
|
||||||
|
|
||||||
def get_list_display(self, request):
|
def get_list_display(self, request):
|
||||||
list_display = [
|
list_display = [
|
||||||
"title",
|
"title",
|
||||||
|
|
@ -135,6 +151,9 @@ class GovernmentPlanAdmin(admin.ModelAdmin):
|
||||||
if not has_limited_access(request.user):
|
if not has_limited_access(request.user):
|
||||||
list_filter.extend(
|
list_filter.extend(
|
||||||
[
|
[
|
||||||
|
make_emptyfilter(
|
||||||
|
"proposals", _("Has change proposals"), empty_value=None
|
||||||
|
),
|
||||||
"organization",
|
"organization",
|
||||||
"group",
|
"group",
|
||||||
"government",
|
"government",
|
||||||
|
|
@ -173,6 +192,59 @@ class GovernmentPlanAdmin(admin.ModelAdmin):
|
||||||
|
|
||||||
make_public.short_description = _("Make public")
|
make_public.short_description = _("Make public")
|
||||||
|
|
||||||
|
def accept_proposal(self, request, pk):
|
||||||
|
obj = get_object_or_404(self.get_queryset(request), pk=pk)
|
||||||
|
plan_url = reverse(
|
||||||
|
"admin:froide_govplan_governmentplan_change",
|
||||||
|
args=(obj.pk,),
|
||||||
|
current_app=self.admin_site.name,
|
||||||
|
)
|
||||||
|
if not obj.proposals:
|
||||||
|
return redirect(plan_url)
|
||||||
|
if request.method == "POST":
|
||||||
|
proposals = obj.proposals or {}
|
||||||
|
proposal_id = request.POST.get("proposal_id")
|
||||||
|
data = proposals[proposal_id]["data"]
|
||||||
|
form = GovernmentPlanUpdateAcceptProposalForm(data=data, plan=obj)
|
||||||
|
if form.is_valid():
|
||||||
|
update = form.save(
|
||||||
|
delete_unconfirmed=request.POST.get("delete", "0") == "1",
|
||||||
|
delete_reason=request.POST.get("delete_reason", ""),
|
||||||
|
proposal_id=proposal_id,
|
||||||
|
delete_proposals=request.POST.getlist("proposal_delete"),
|
||||||
|
)
|
||||||
|
if update is None:
|
||||||
|
self.message_user(request, _("The proposal has been deleted."))
|
||||||
|
|
||||||
|
return redirect(plan_url)
|
||||||
|
|
||||||
|
self.message_user(
|
||||||
|
request,
|
||||||
|
_("An unpublished update has been created."),
|
||||||
|
)
|
||||||
|
update_url = reverse(
|
||||||
|
"admin:froide_govplan_governmentplanupdate_change",
|
||||||
|
args=(update.pk,),
|
||||||
|
current_app=self.admin_site.name,
|
||||||
|
)
|
||||||
|
return redirect(update_url)
|
||||||
|
else:
|
||||||
|
form = GovernmentPlanUpdateAcceptProposalForm(plan=obj)
|
||||||
|
|
||||||
|
opts = self.model._meta
|
||||||
|
context = {
|
||||||
|
"form": form,
|
||||||
|
"proposals": form.get_proposals(),
|
||||||
|
"object": obj,
|
||||||
|
"app_label": opts.app_label,
|
||||||
|
"opts": opts,
|
||||||
|
}
|
||||||
|
return render(
|
||||||
|
request,
|
||||||
|
"froide_govplan/admin/accept_proposal.html",
|
||||||
|
context,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class GovernmentPlanUpdateAdmin(admin.ModelAdmin):
|
class GovernmentPlanUpdateAdmin(admin.ModelAdmin):
|
||||||
form = GovernmentPlanUpdateForm
|
form = GovernmentPlanUpdateForm
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
@ -132,3 +133,95 @@ class GovernmentPlanUpdateProposalForm(forms.ModelForm):
|
||||||
}
|
}
|
||||||
plan.save(update_fields=["proposals"])
|
plan.save(update_fields=["proposals"])
|
||||||
return plan
|
return plan
|
||||||
|
|
||||||
|
|
||||||
|
class GovernmentPlanUpdateAcceptProposalForm(GovernmentPlanUpdateProposalForm):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.plan = kwargs.pop("plan")
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def get_proposals(self):
|
||||||
|
data = dict(self.plan.proposals)
|
||||||
|
user_ids = self.plan.proposals.keys()
|
||||||
|
user_map = {
|
||||||
|
str(u.id): u for u in get_user_model().objects.filter(id__in=user_ids)
|
||||||
|
}
|
||||||
|
status_dict = dict(PlanStatus.choices)
|
||||||
|
rating_dict = dict(PlanRating.choices)
|
||||||
|
for user_id, v in data.items():
|
||||||
|
v["user"] = user_map[user_id]
|
||||||
|
data[user_id]["data"]["rating_label"] = rating_dict.get(
|
||||||
|
data[user_id]["data"]["rating"]
|
||||||
|
)
|
||||||
|
data[user_id]["data"]["status_label"] = status_dict.get(
|
||||||
|
data[user_id]["data"]["status"]
|
||||||
|
)
|
||||||
|
return data
|
||||||
|
|
||||||
|
def save(
|
||||||
|
self,
|
||||||
|
proposal_id=None,
|
||||||
|
delete_proposals=None,
|
||||||
|
delete_unconfirmed=False,
|
||||||
|
delete_reason="",
|
||||||
|
):
|
||||||
|
import ipdb
|
||||||
|
|
||||||
|
ipdb.set_trace()
|
||||||
|
update = super(forms.ModelForm, self).save(commit=False)
|
||||||
|
update.plan = self.plan
|
||||||
|
|
||||||
|
if delete_proposals is None:
|
||||||
|
delete_proposals = []
|
||||||
|
if proposal_id:
|
||||||
|
proposals = self.get_proposals()
|
||||||
|
proposal_user = proposals[proposal_id]["user"]
|
||||||
|
update.user = proposal_user
|
||||||
|
# if proposal_user != user:
|
||||||
|
# proposal_user.send_mail(
|
||||||
|
# _("Changes to public body “{}” have been applied").format(pb.name),
|
||||||
|
# _(
|
||||||
|
# "Hello,\n\nYou can find the changed public body here:"
|
||||||
|
# "\n\n{url}\n\nAll the Best,\n{site_name}"
|
||||||
|
# ).format(
|
||||||
|
# url=pb.get_absolute_domain_url(), site_name=settings.SITE_NAME
|
||||||
|
# ),
|
||||||
|
# priority=False,
|
||||||
|
# )
|
||||||
|
delete_proposals.append(proposal_id)
|
||||||
|
for pid in delete_proposals:
|
||||||
|
if pid in self.plan.proposals:
|
||||||
|
del self.plan.proposals[pid]
|
||||||
|
if not self.plan.proposals:
|
||||||
|
self.plan.proposals = None
|
||||||
|
self.plan.save(update_fields=["proposals"])
|
||||||
|
|
||||||
|
# if delete_unconfirmed:
|
||||||
|
# self.delete_proposal(pb, user, delete_reason)
|
||||||
|
# return None
|
||||||
|
|
||||||
|
update.save()
|
||||||
|
# PublicBody.change_proposal_accepted.send(sender=pb, user=user)
|
||||||
|
return update
|
||||||
|
|
||||||
|
# def delete_proposal(self, pb, user, delete_reason=""):
|
||||||
|
# LogEntry.objects.log_action(
|
||||||
|
# user_id=user.id,
|
||||||
|
# content_type_id=ContentType.objects.get_for_model(pb).pk,
|
||||||
|
# object_id=pb.pk,
|
||||||
|
# object_repr=str(pb),
|
||||||
|
# action_flag=DELETION,
|
||||||
|
# )
|
||||||
|
|
||||||
|
# creator = pb.created_by
|
||||||
|
# if creator:
|
||||||
|
# creator.send_mail(
|
||||||
|
# _("Your public body proposal “%s” was rejected") % pb.name,
|
||||||
|
# _(
|
||||||
|
# "Hello,\n\nA moderator has rejected your proposal for a new "
|
||||||
|
# "public body.\n\n{delete_reason}\n\nAll the Best,\n{site_name}"
|
||||||
|
# ).format(delete_reason=delete_reason, site_name=settings.SITE_NAME),
|
||||||
|
# priority=False,
|
||||||
|
# )
|
||||||
|
# PublicBody.proposal_rejected.send(sender=pb, user=user)
|
||||||
|
# pb.delete()
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
{% extends "admin/change_form.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block object-tools-items %}
|
||||||
|
{% if original.pk and original.proposals %}
|
||||||
|
<li>
|
||||||
|
<a href="{% url 'admin:froide_govplan-plan_accept_proposal' pk=original.pk %}">
|
||||||
|
{% trans "See update proposals" %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{{ block.super }}
|
||||||
|
{% endblock %}
|
||||||
77
froide_govplan/templates/froide_govplan/admin/_proposal.html
Normal file
77
froide_govplan/templates/froide_govplan/admin/_proposal.html
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
{% load i18n %}
|
||||||
|
{% load form_helper %}
|
||||||
|
{% load permission_helper %}
|
||||||
|
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{% trans "Field" %}</th>
|
||||||
|
{% for key, proposal in proposals.items %}
|
||||||
|
<th>
|
||||||
|
<span title="{{ proposal.timestamp }}">
|
||||||
|
{% if request.user.is_staff and request.user|has_perm:"account.view_user" %}
|
||||||
|
{{ proposal.user.get_full_name }} ({{ proposal.user.email }})
|
||||||
|
{% else %}
|
||||||
|
{{ proposal.timestamp | date:"SHORT_DATETIME" }}
|
||||||
|
{% endif %}
|
||||||
|
</span>
|
||||||
|
</th>
|
||||||
|
{% endfor %}
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% if proposals %}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
{% trans "Proposal" %}
|
||||||
|
</td>
|
||||||
|
{% for key, proposal in proposals.items %}
|
||||||
|
<td>
|
||||||
|
<label>
|
||||||
|
<input type="radio" name="proposal_id" class="proposal" value="{{ key }}" required>
|
||||||
|
{% trans "Turn into update draft" %}
|
||||||
|
</label>
|
||||||
|
</td>
|
||||||
|
{% endfor %}
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
{% for field in form %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ field.label }}</td>
|
||||||
|
{% for key, proposal in proposals.items %}
|
||||||
|
{% with val=proposal.data|get_item_by_key:field.name %}
|
||||||
|
<td>
|
||||||
|
{% with label_name=field.name|add:"_label" %}
|
||||||
|
{% if proposal.data|get_item_by_key:label_name %}
|
||||||
|
{{ proposal.data|get_item_by_key:label_name }}
|
||||||
|
{% else %}
|
||||||
|
{{ val|urlize|linebreaksbr }}
|
||||||
|
{% endif %}
|
||||||
|
{% endwith %}
|
||||||
|
</td>
|
||||||
|
{% endwith %}
|
||||||
|
{% endfor %}
|
||||||
|
</tr>
|
||||||
|
{{ form.field }}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
{% if proposals %}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
{% trans "Delete proposal" %}
|
||||||
|
</td>
|
||||||
|
{% for key, proposal in proposals.items %}
|
||||||
|
<td>
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" name="proposal_delete" value="{{ proposal.user.id }}">
|
||||||
|
{% trans "Delete" %}
|
||||||
|
</label>
|
||||||
|
</td>
|
||||||
|
{% endfor %}
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
</tbody>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
{% extends "admin/change_form.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block title %}{{ object.title }} - {{ block.super }}{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block content %}<div id="content-main">
|
||||||
|
<h3>{{ object.title }}</h3>
|
||||||
|
|
||||||
|
<form action="" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<fieldset>
|
||||||
|
{% include "froide_govplan/admin/_proposal.html" %}
|
||||||
|
</fieldset>
|
||||||
|
<div class="submit-row">
|
||||||
|
<input type="submit" value="{% trans 'Create update draft' %}" style="float:left"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card border-danger mt-3">
|
||||||
|
<div class="card-body">
|
||||||
|
<h3>{% trans "Delete proposal" %}</h3>
|
||||||
|
<p><label for="delete_reason">
|
||||||
|
{% trans "Please give a reason for not accepting this proposal. It will be send to the user who made the proposal." %}
|
||||||
|
</label>
|
||||||
|
<p>
|
||||||
|
<p><textarea id="delete_reason" class="form-control" name="delete_reason" placeholder="{% trans 'We could not accept this update because...' %}"></textarea></p>
|
||||||
|
<p><button class="mt-2 btn btn-danger float-right" type="submit" name="delete" value="1">
|
||||||
|
{% trans "Delete proposed update" %}
|
||||||
|
</button></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue