Add plan follower model and configuration

This commit is contained in:
Stefan Wehrmeyer 2022-03-11 23:40:37 +01:00
parent 829315c1dc
commit 7792674ed7
4 changed files with 150 additions and 0 deletions

View file

@ -5,3 +5,10 @@ from django.utils.translation import gettext_lazy as _
class FroideGovPlanConfig(AppConfig): class FroideGovPlanConfig(AppConfig):
name = "froide_govplan" name = "froide_govplan"
verbose_name = _("GovPlan App") verbose_name = _("GovPlan App")
def ready(self):
from froide.follow.configuration import follow_registry
from .configuration import GovernmentPlanFollowConfiguration
follow_registry.register(GovernmentPlanFollowConfiguration())

View file

@ -0,0 +1,80 @@
from datetime import datetime
from typing import Iterator
from django.utils.translation import gettext_lazy as _
from froide.follow.configuration import FollowConfiguration
from froide.helper.notifications import Notification, TemplatedEvent
from .admin import get_allowed_plans
from .models import GovernmentPlanFollower, GovernmentPlanUpdate
class GovernmentPlanFollowConfiguration(FollowConfiguration):
model = GovernmentPlanFollower
title: str = _("Government plans")
slug: str = "govplan"
follow_message: str = _("You are now following this plan.")
unfollow_message: str = _("You are not following this plan anymore.")
confirm_email_message: str = _(
"Check your emails and click the confirmation link in order to follow this government plan."
)
action_labels = {
"follow": _("Follow plan"),
"follow_q": _("Follow plan?"),
"unfollow": _("Unfollow plan"),
"following": _("Following plan"),
"follow_description": _(
"You will get notifications via email when something new happens with this plan. You can unsubscribe anytime."
),
}
def get_content_object_queryset(self, request):
return get_allowed_plans(request)
def can_follow(self, content_object, user, request=None):
if request:
get_allowed_plans(request)
return super().can_follow(content_object, user)
def get_batch_updates(
self, start: datetime, end: datetime
) -> Iterator[Notification]:
yield from get_plan_updates(start, end)
def get_confirm_follow_message(self, content_object):
return _(
"please confirm that you want to follow the plan “{title}” by clicking this link:"
).format(title=content_object.title)
def email_changed(self, user):
# Move all confirmed email subscriptions of new email
# to user except own requests
self.model.objects.filter(email=user.email, confirmed=True).update(
email="", user=user
)
def get_plan_updates(start: datetime, end: datetime):
plan_updates = GovernmentPlanUpdate.objects.filter(
public=True, timestamp__gte=start, timestamp__lt=end
).select_related("plan")
for plan_update in plan_updates:
yield Notification(
section=_("Government Plans"),
event_type="planupdate",
object=plan_update.plan,
object_label=plan_update.plan.title,
timestamp=plan_update.timestamp,
event=make_plan_event(plan_update.plan),
user_id=None,
)
def make_plan_event(plan):
return TemplatedEvent(
_("An update was posted for the government plan “{title}”."),
title=plan.title,
)

View file

@ -0,0 +1,49 @@
# Generated by Django 3.2.12 on 2022-03-11 22:30
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('froide_govplan', '0003_auto_20220228_1051'),
]
operations = [
migrations.AlterField(
model_name='governmentplanscmsplugin',
name='template',
field=models.CharField(blank=True, choices=[('froide_govplan/plugins/default.html', 'Normal'), ('froide_govplan/plugins/progress.html', 'Progress'), ('froide_govplan/plugins/card_cols.html', 'Card columns')], help_text='template used to display the plugin', max_length=250, verbose_name='template'),
),
migrations.CreateModel(
name='GovernmentPlanFollower',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('email', models.CharField(blank=True, max_length=255)),
('confirmed', models.BooleanField(default=False)),
('timestamp', models.DateTimeField(default=django.utils.timezone.now, verbose_name='Timestamp of Following')),
('context', models.JSONField(blank=True, null=True)),
('content_object', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='followers', to='froide_govplan.governmentplan', verbose_name='Government plan')),
('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='User')),
],
options={
'verbose_name': 'Government plan follower',
'verbose_name_plural': 'Government plan followers',
'ordering': ('-timestamp',),
'get_latest_by': 'timestamp',
'abstract': False,
},
),
migrations.AddConstraint(
model_name='governmentplanfollower',
constraint=models.UniqueConstraint(condition=models.Q(('user__isnull', False)), fields=('content_object', 'user'), name='unique_user_follower_froide_govplan_governmentplanfollower'),
),
migrations.AddConstraint(
model_name='governmentplanfollower',
constraint=models.UniqueConstraint(condition=models.Q(('user__isnull', True)), fields=('content_object', 'email'), name='unique_email_follower_froide_govplan_governmentplanfollower'),
),
]

View file

@ -10,6 +10,7 @@ from taggit.managers import TaggableManager
from taggit.models import TaggedItemBase from taggit.models import TaggedItemBase
from froide.foirequest.models import FoiRequest from froide.foirequest.models import FoiRequest
from froide.follow.models import Follower
from froide.organization.models import Organization from froide.organization.models import Organization
from froide.publicbody.models import Category, Jurisdiction, PublicBody from froide.publicbody.models import Category, Jurisdiction, PublicBody
@ -237,6 +238,19 @@ class GovernmentPlanUpdate(models.Model):
return "{} - {} ({})".format(self.title, self.timestamp, self.plan) return "{} - {} ({})".format(self.title, self.timestamp, self.plan)
class GovernmentPlanFollower(Follower):
content_object = models.ForeignKey(
GovernmentPlan,
on_delete=models.CASCADE,
related_name="followers",
verbose_name=_("Government plan"),
)
class Meta(Follower.Meta):
verbose_name = _("Government plan follower")
verbose_name_plural = _("Government plan followers")
if CMSPlugin: if CMSPlugin:
PLUGIN_TEMPLATES = [ PLUGIN_TEMPLATES = [