diff --git a/taiga/projects/api.py b/taiga/projects/api.py index dffa03fa..2ea2a7cf 100644 --- a/taiga/projects/api.py +++ b/taiga/projects/api.py @@ -32,7 +32,7 @@ from taiga.base.utils.slug import slugify_uniquely from taiga.projects.history.mixins import HistoryResourceMixin from taiga.projects.notifications.mixins import WatchedResourceMixin, WatchersViewSetMixin -from taiga.projects.notifications.services import set_notify_policy +from taiga.projects.notifications.services import set_notify_policy, attach_notify_level_to_project_queryset from taiga.projects.mixins.ordering import BulkUpdateOrderMixin from taiga.projects.mixins.on_destroy import MoveOnDestroyMixin @@ -45,7 +45,6 @@ from . import serializers from . import models from . import permissions from . import services -from . import utils from .votes.mixins.viewsets import LikedResourceMixin, VotersViewSetMixin @@ -66,7 +65,7 @@ class ProjectViewSet(LikedResourceMixin, HistoryResourceMixin, WatchedResourceMi qs = super().get_queryset() qs = self.attach_votes_attrs_to_queryset(qs) qs = self.attach_watchers_attrs_to_queryset(qs) - qs = utils.attach_notify_level_to_queryset(qs, self.request.user) + qs = attach_notify_level_to_project_queryset(qs, self.request.user) return qs @detail_route(methods=["POST"]) diff --git a/taiga/projects/notifications/services.py b/taiga/projects/notifications/services.py index 3951622a..4a954fe6 100644 --- a/taiga/projects/notifications/services.py +++ b/taiga/projects/notifications/services.py @@ -90,22 +90,28 @@ def get_notify_policy(project, user): return instance -def attach_notify_policy_to_project_queryset(current_user, queryset): +def attach_notify_level_to_project_queryset(queryset, user): """ Function that attach "notify_level" attribute on each queryset result for query notification level of current user for each project in the most efficient way. + + :param queryset: A Django queryset object. + :param user: A User model object. + + :return: Queryset object with the additional `as_field` field. """ + user_id = getattr(user, "id", None) or "NULL" + default_level = NotifyLevel.notwatch sql = strip_lines(""" COALESCE((SELECT notifications_notifypolicy.notify_level - FROM notifications_notifypolicy - WHERE notifications_notifypolicy.project_id = projects_project.id - AND notifications_notifypolicy.user_id = {userid}), {default_level}) + FROM notifications_notifypolicy + WHERE notifications_notifypolicy.project_id = projects_project.id + AND notifications_notifypolicy.user_id = {user_id}), + {default_level}) """) - - sql = sql.format(userid=current_user.pk, - default_level=NotifyLevel.notwatch) + sql = sql.format(user_id=user_id, default_level=default_level) return queryset.extra(select={"notify_level": sql}) diff --git a/taiga/projects/serializers.py b/taiga/projects/serializers.py index b986e6ff..2f3b0ab1 100644 --- a/taiga/projects/serializers.py +++ b/taiga/projects/serializers.py @@ -318,7 +318,7 @@ class ProjectSerializer(WatchersValidator, LikedResourceSerializerMixin, Watched i_am_owner = serializers.SerializerMethodField("get_i_am_owner") tags_colors = TagsColorsField(required=False) total_closed_milestones = serializers.SerializerMethodField("get_total_closed_milestones") - notify_level = serializers.IntegerField(read_only=True) + notify_level = serializers.SerializerMethodField("get_notify_level") class Meta: model = models.Project @@ -339,6 +339,9 @@ class ProjectSerializer(WatchersValidator, LikedResourceSerializerMixin, Watched def get_total_closed_milestones(self, obj): return obj.milestones.filter(closed=True).count() + def get_notify_level(self, obj): + return getattr(obj, "notify_level", None) + def validate_total_milestones(self, attrs, source): """ Check that total_milestones is not null, it's an optional parameter but diff --git a/taiga/projects/utils.py b/taiga/projects/utils.py deleted file mode 100644 index 356aa769..00000000 --- a/taiga/projects/utils.py +++ /dev/null @@ -1,40 +0,0 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán -# Copyright (C) 2014 Anler Hernández -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -from django.apps import apps - -def attach_notify_level_to_queryset(queryset, user, as_field="notify_level"): - """Attach notify level to each object of the queryset. - - :param queryset: A Django queryset object. - :param as_field: Attach the notify level as an attribute with this name. - - :return: Queryset object with the additional `as_field` field. - """ - - if user.is_authenticated(): - model = queryset.model - sql = ("""SELECT notifications_notifypolicy.notify_level - FROM notifications_notifypolicy - WHERE notifications_notifypolicy.user_id = {user_id} - AND notifications_notifypolicy.project_id = {tbl}.id""") - sql = sql.format(user_id=user.id, tbl=model._meta.db_table) - else: - sql = "SELECT CAST(NULL as text)" - - qs = queryset.extra(select={as_field: sql}) - return qs diff --git a/tests/integration/test_notifications.py b/tests/integration/test_notifications.py index 1a1b98b0..183a581a 100644 --- a/tests/integration/test_notifications.py +++ b/tests/integration/test_notifications.py @@ -51,12 +51,12 @@ def mail(): return mail -def test_attach_notify_policy_to_project_queryset(): +def test_attach_notify_level_to_project_queryset(): project1 = f.ProjectFactory.create() f.ProjectFactory.create() qs = project1.__class__.objects.order_by("id") - qs = services.attach_notify_policy_to_project_queryset(project1.owner, qs) + qs = services.attach_notify_level_to_project_queryset(qs, project1.owner) assert len(qs) == 2 assert qs[0].notify_level == NotifyLevel.notwatch @@ -64,7 +64,7 @@ def test_attach_notify_policy_to_project_queryset(): services.create_notify_policy(project1, project1.owner, NotifyLevel.watch) qs = project1.__class__.objects.order_by("id") - qs = services.attach_notify_policy_to_project_queryset(project1.owner, qs) + qs = services.attach_notify_level_to_project_queryset(qs, project1.owner) assert qs[0].notify_level == NotifyLevel.watch assert qs[1].notify_level == NotifyLevel.notwatch