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):
name = "froide_govplan"
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 froide.foirequest.models import FoiRequest
from froide.follow.models import Follower
from froide.organization.models import Organization
from froide.publicbody.models import Category, Jurisdiction, PublicBody
@ -237,6 +238,19 @@ class GovernmentPlanUpdate(models.Model):
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:
PLUGIN_TEMPLATES = [