Add make request button to plan page

This commit is contained in:
Stefan Wehrmeyer 2022-03-15 14:35:16 +01:00
parent 3051ee51f5
commit 0bfceca2e7
5 changed files with 186 additions and 110 deletions

View file

@ -4,7 +4,7 @@ import json
from django.core.management.base import BaseCommand
from ...models import Government
from ...utils import PlanImporter
from ...plan_importer import PlanImporter
class Command(BaseCommand):

View file

@ -1,5 +1,6 @@
import functools
import re
from datetime import timedelta
from urllib.parse import urlparse
from django.conf import settings
@ -19,6 +20,8 @@ from froide.follow.models import Follower
from froide.organization.models import Organization
from froide.publicbody.models import Category, Jurisdiction, PublicBody
from .utils import PLAN_TAG_PREFIX, TAG_NAME, make_request_url
try:
from cms.models.fields import PlaceholderField
from cms.models.pluginmodel import CMSPlugin
@ -232,6 +235,35 @@ class GovernmentPlan(models.Model):
def get_status_css(self):
return STATUS_CSS.get(self.status, "")
def make_request_url(self):
if not self.responsible_publicbody:
return []
return make_request_url(self, self.responsible_publicbody)
def has_recent_foirequest(self):
frs = self.get_related_foirequests()
ago = timezone.now() - timedelta(days=90)
return any(fr.first_message > ago for fr in frs)
def get_recent_foirequest(self):
return self.get_related_foirequests()[0]
def get_related_foirequests(self):
if not self.responsible_publicbody:
return []
if hasattr(self, "_related_foirequests"):
return self._related_foirequests
self._related_foirequests = (
FoiRequest.objects.filter(
visibility=FoiRequest.VISIBILITY.VISIBLE_TO_PUBLIC,
public_body=self.responsible_publicbody,
)
.filter(tags__name=TAG_NAME)
.filter(tags__name="{}{}".format(PLAN_TAG_PREFIX, self.slug))
.order_by("-first_message")
)
return self._related_foirequests
class GovernmentPlanUpdate(models.Model):
plan = models.ForeignKey(

View file

@ -0,0 +1,115 @@
import datetime
import re
from django.template.defaultfilters import slugify
from froide.publicbody.models import Category, PublicBody
from .models import GovernmentPlan, GovernmentPlanSection
class PlanImporter(object):
def __init__(self, government, col_mapping=None):
if col_mapping is None:
col_mapping = {}
self.col_mapping = col_mapping
self.government = government
self.post_save_list = []
def import_rows(self, reader):
for row in reader:
self.import_row(row)
def import_row(self, row):
print("importing", row)
title = row[self.col_mapping["title"]]
if not title:
return
plan = GovernmentPlan.objects.filter(
government=self.government, title=title
).first()
if not plan:
plan = GovernmentPlan(government=self.government)
self.post_save_list = []
for col, row_col in self.col_mapping.items():
method_name = "handle_{}".format(col)
if hasattr(self, method_name):
getattr(self, method_name)(plan, row[row_col])
else:
setattr(plan, col, row[row_col])
plan.save()
for func in self.post_save_list:
func(plan)
def handle_title(self, plan, title):
plan.title = title
plan.slug = slugify(title)
def handle_categories(self, plan, category_name):
categories = [
x.strip() for x in re.split(r", | & | und ", category_name) if x.strip()
]
self.make_section(category_name, "-".join(categories), categories)
if categories:
self.post_save_list.append(lambda p: p.categories.set(*categories))
def make_section(self, section_name, section_slug, categories):
slug = slugify(section_slug)
section, _created = GovernmentPlanSection.objects.get_or_create(
slug=slug,
defaults={
"government": self.government,
"title": section_name,
},
)
section.categories.set([self.get_category(c) for c in categories])
def get_category(self, cat_name):
return Category.objects.get(name=cat_name)
def handle_reference(self, plan, reference):
plan.reference = ", ".join(re.split(r"\s*[,/]\s*", reference))
def handle_responsible_publicbody(self, plan, pb):
if not pb.strip():
return
pb = PublicBody.objects.get(
jurisdiction=self.government.jurisdiction,
other_names__iregex=r"(\W|^){}(\W|$)".format(pb),
)
plan.responsible_publicbody = pb
def handle_due_date(self, plan, date_descr):
if not date_descr.strip():
return
def parse_date(date_descr):
match = re.search(r"(\d{4})", date_descr)
if not match:
return
year = int(match.group(1))
if "Mitte" in date_descr:
return datetime.date(year, 7, 1)
if "Ende" in date_descr:
return datetime.date(year, 12, 1)
if "Anfang" in date_descr:
return datetime.date(year, 3, 1)
if "Innerhalb" in date_descr:
return datetime.date(year, 12, 31)
if "Juni" in date_descr:
return datetime.date(year, 6, 1)
return datetime.date(year, 1, 1)
plan.due_date = parse_date(date_descr)
def handle_status(self, plan, status):
status = status.strip()
if not status or status == "noch nicht umgesetzt":
status = "not_started"
if status == "umgesetzt":
status = "implemented"
if status == "begonnen":
status = "started"
plan.status = status

View file

@ -82,6 +82,21 @@
{% endif %}
</div>
<div class="col col-12 col-md-5 col-lg-3 mt-5 mt-md-0">
{% if object.responsible_publicbody %}
<div class="mb-3">
{% if not object.has_recent_foirequest %}
<a href="{{ object.make_request_url }}" target="_blank" class="btn btn-primary">
Anfrage zum Vorhaben stellen
</a>
{% else %}
{% with foirequest=object.get_recent_foirequest %}
{% include "foirequest/snippets/request_item.html" with object=foirequest %}
{% endwith %}
{% endif %}
</div>
{% endif %}
<dl>
{% if object.rating %}
<dt>Bewertung</dt>

View file

@ -1,115 +1,29 @@
import datetime
import re
from urllib.parse import quote, urlencode
from django.template.defaultfilters import slugify
from django.conf import settings
from django.urls import reverse
from froide.publicbody.models import Category, PublicBody
from .models import GovernmentPlan, GovernmentPlanSection
TAG_NAME = "Koalitionstracker"
PLAN_TAG_PREFIX = "Vorhaben-"
class PlanImporter(object):
def __init__(self, government, col_mapping=None):
if col_mapping is None:
col_mapping = {}
self.col_mapping = col_mapping
self.government = government
self.post_save_list = []
def import_rows(self, reader):
for row in reader:
self.import_row(row)
def import_row(self, row):
print("importing", row)
title = row[self.col_mapping["title"]]
if not title:
return
plan = GovernmentPlan.objects.filter(
government=self.government, title=title
).first()
if not plan:
plan = GovernmentPlan(government=self.government)
self.post_save_list = []
for col, row_col in self.col_mapping.items():
method_name = "handle_{}".format(col)
if hasattr(self, method_name):
getattr(self, method_name)(plan, row[row_col])
else:
setattr(plan, col, row[row_col])
plan.save()
for func in self.post_save_list:
func(plan)
def handle_title(self, plan, title):
plan.title = title
plan.slug = slugify(title)
def handle_categories(self, plan, category_name):
categories = [
x.strip() for x in re.split(r", | & | und ", category_name) if x.strip()
]
self.make_section(category_name, "-".join(categories), categories)
if categories:
self.post_save_list.append(lambda p: p.categories.set(*categories))
def make_section(self, section_name, section_slug, categories):
slug = slugify(section_slug)
section, _created = GovernmentPlanSection.objects.get_or_create(
slug=slug,
defaults={
"government": self.government,
"title": section_name,
},
def make_request_url(plan, publicbody):
pb_slug = publicbody.slug
url = reverse("foirequest-make_request", kwargs={"publicbody_slug": pb_slug})
subject = "Stand des Regierungsvorhabens „{}".format(plan.title)
if len(subject) > 250:
subject = subject[:250] + "..."
body = "Dokumente, die den Stand des Regierungsvorhabens „{}“ (siehe Koalitionsvertrag), dokumentieren.".format(
plan.title
)
section.categories.set([self.get_category(c) for c in categories])
query = {
"subject": subject.encode("utf-8"),
"body": body,
"tags": "{},{}{}".format(TAG_NAME, PLAN_TAG_PREFIX, plan.slug),
}
def get_category(self, cat_name):
return Category.objects.get(name=cat_name)
hide_features = ["hide_public", "hide_similar", "hide_draft"]
def handle_reference(self, plan, reference):
plan.reference = ", ".join(re.split(r"\s*[,/]\s*", reference))
def handle_responsible_publicbody(self, plan, pb):
if not pb.strip():
return
pb = PublicBody.objects.get(
jurisdiction=self.government.jurisdiction,
other_names__iregex=r"(\W|^){}(\W|$)".format(pb),
)
plan.responsible_publicbody = pb
def handle_due_date(self, plan, date_descr):
if not date_descr.strip():
return
def parse_date(date_descr):
match = re.search(r"(\d{4})", date_descr)
if not match:
return
year = int(match.group(1))
if "Mitte" in date_descr:
return datetime.date(year, 7, 1)
if "Ende" in date_descr:
return datetime.date(year, 12, 1)
if "Anfang" in date_descr:
return datetime.date(year, 3, 1)
if "Innerhalb" in date_descr:
return datetime.date(year, 12, 31)
if "Juni" in date_descr:
return datetime.date(year, 6, 1)
return datetime.date(year, 1, 1)
plan.due_date = parse_date(date_descr)
def handle_status(self, plan, status):
status = status.strip()
if not status or status == "noch nicht umgesetzt":
status = "not_started"
if status == "umgesetzt":
status = "implemented"
if status == "begonnen":
status = "started"
plan.status = status
query.update({f: b"1" for f in hide_features})
query = urlencode(query, quote_via=quote)
return "%s%s?%s" % (settings.SITE_URL, url, query)