Initial commit

This commit is contained in:
Stefan Wehrmeyer 2022-02-15 21:09:09 +01:00
commit 19170142c3
20 changed files with 894 additions and 0 deletions

243
froide_govplan/models.py Normal file
View file

@ -0,0 +1,243 @@
from django.conf import settings
from django.contrib.auth.models import Group
from django.db import models
from django.urls import reverse
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from filer.fields.image import FilerImageField
from taggit.managers import TaggableManager
from taggit.models import TaggedItemBase
from froide.foirequest.models import FoiRequest
from froide.organization.models import Organization
from froide.publicbody.models import Category, Jurisdiction, PublicBody
try:
from cms.models.pluginmodel import CMSPlugin
except ImportError:
CMSPlugin = None
class PlanStatus(models.TextChoices):
NOT_STARTED = ("not_started", _("not started"))
STARTED = ("started", _("started"))
PARTIALLY_IMPLEMENTED = ("partially_implemented", _("partially implemented"))
IMPLEMENTED = ("implemented", _("implemented"))
DEFERRED = ("deferred", _("deferred"))
class PlanRating(models.IntegerChoices):
TERRIBLE = 1, _("terrible")
BAD = 2, _("bad")
OK = 3, _("OK")
GOOD = 4, _("good")
EXCELLENT = 5, _("excellent")
class Government(models.Model):
name = models.CharField(max_length=255)
slug = models.SlugField(max_length=255, unique=True)
public = models.BooleanField(default=False)
jurisdiction = models.ForeignKey(Jurisdiction, null=True, on_delete=models.SET_NULL)
description = models.TextField(blank=True)
start_date = models.DateField(null=True, blank=True)
end_date = models.DateField(null=True, blank=True)
planning_document = models.URLField(blank=True)
class Meta:
verbose_name = _("Government")
verbose_name_plural = _("Governments")
def __str__(self):
return self.name
class CategorizedGovernmentPlan(TaggedItemBase):
tag = models.ForeignKey(
Category, on_delete=models.CASCADE, related_name="categorized_governmentplan"
)
content_object = models.ForeignKey("GovernmentPlan", on_delete=models.CASCADE)
class Meta:
verbose_name = _("Categorized Government Plan")
verbose_name_plural = _("Categorized Government Plans")
class GovernmentPlan(models.Model):
government = models.ForeignKey(Government, on_delete=models.CASCADE)
title = models.CharField(max_length=255)
slug = models.SlugField(max_length=255, unique=True)
image = FilerImageField(
null=True,
blank=True,
default=None,
verbose_name=_("image"),
on_delete=models.SET_NULL,
)
description = models.TextField(blank=True)
public = models.BooleanField(default=False)
status = models.CharField(
max_length=25, choices=PlanStatus.choices, default="needs_approval"
)
rating = models.IntegerField(choices=PlanRating.choices, null=True, blank=True)
reference = models.CharField(max_length=255, blank=True)
categories = TaggableManager(
through=CategorizedGovernmentPlan, verbose_name=_("categories"), blank=True
)
responsible_publicbody = models.ForeignKey(
PublicBody, null=True, blank=True, on_delete=models.SET_NULL
)
organization = models.ForeignKey(
Organization, null=True, blank=True, on_delete=models.SET_NULL
)
group = models.ForeignKey(Group, null=True, blank=True, on_delete=models.SET_NULL)
class Meta:
ordering = ("reference", "title")
verbose_name = _("Government plan")
verbose_name_plural = _("Government plans")
def __str__(self):
return 'GovernmentPlan "%s" (#%s)' % (self.title, self.pk)
def get_absolute_url(self):
return reverse("govplan:plan", kwargs={
"gov": self.government.slug,
"plan": self.slug
})
def get_absolute_domain_url(self):
return settings.SITE_URL + self.get_absolute_url()
def get_reference_link(self):
if self.reference.startswith("https://"):
return self.reference
return "{}#{}".format(
self.government.planning_document,
self.reference
)
class GovernmentPlanUpdate(models.Model):
plan = models.ForeignKey(
GovernmentPlan, on_delete=models.CASCADE, related_name="updates"
)
user = models.ForeignKey(
settings.AUTH_USER_MODEL, null=True, blank=True, on_delete=models.SET_NULL
)
organization = models.ForeignKey(
Organization, null=True, blank=True, on_delete=models.SET_NULL
)
timestamp = models.DateTimeField(default=timezone.now)
title = models.CharField(max_length=1024, blank=True)
content = models.TextField(blank=True)
url = models.URLField(blank=True)
status = models.CharField(
max_length=25, choices=PlanStatus.choices, default="", blank=True
)
rating = models.IntegerField(choices=PlanRating.choices, null=True, blank=True)
public = models.BooleanField(default=False)
foirequest = models.ForeignKey(
FoiRequest, null=True, blank=True, on_delete=models.SET_NULL
)
class Meta:
ordering = ("-timestamp",)
get_latest_by = "timestamp"
verbose_name = _("Plan update")
verbose_name_plural = _("Plan updates")
def __str__(self):
return "Plan Update (%s)" % (self.pk,)
if CMSPlugin:
PLUGIN_TEMPLATES = [
("froide_govplan/plugins/default.html", _("Normal")),
]
class GovernmentPlansCMSPlugin(CMSPlugin):
"""
CMS Plugin for displaying latest articles
"""
government = models.ForeignKey(Government, null=True, blank=True, on_delete=models.SET_NULL)
categories = models.ManyToManyField(
Category, verbose_name=_("categories"), blank=True
)
count = models.PositiveIntegerField(
_("number of plans"), default=1, help_text=_("0 means all the plans")
)
offset = models.PositiveIntegerField(
_("offset"),
default=0,
help_text=_("number of plans to skip from top of list"),
)
template = models.CharField(
_("template"),
blank=True,
max_length=250,
choices=PLUGIN_TEMPLATES,
help_text=_("template used to display the plugin"),
)
@property
def render_template(self):
"""
Override render_template to use
the template_to_render attribute
"""
return self.template_to_render
def copy_relations(self, old_instance):
"""
Duplicate ManyToMany relations on plugin copy
"""
self.categories.set(old_instance.categories.all())
def __str__(self):
if self.count == 0:
return str(_("All matching plans"))
return _("%s matching plans") % self.count
def get_plans(self, request, published_only=True):
if (
published_only
or not request
or not getattr(request, "toolbar", False)
or not request.toolbar.edit_mode_active
):
plans = GovernmentPlan.objects.filter(public=True)
else:
plans = GovernmentPlan.objects.all()
filters = {}
if self.government:
filters["government"] = self.government
cat_list = self.categories.all().values_list("id", flat=True)
if cat_list:
filters["categories__in"] = cat_list
plans = plans.filter(**filters).distinct()
plans = plans.prefetch_related(
"categories", "government", "organization"
)
if self.count == 0:
return plans[self.offset:]
return plans[self.offset : self.offset + self.count]