From fcbeb44cbdb04c78245061eb85dcbf7925beb47f Mon Sep 17 00:00:00 2001
From: "jindra12.underdark" <jindra12.underdark@gmail.com>
Date: Sun, 16 Jul 2023 21:55:51 +0200
Subject: [PATCH] Displays tags for shared pages

#210
---
 district/models.py                            |  40 +++++--
 .../district/district_article_page.html       |   2 +-
 shared/models.py                              | 107 ++++++++++++++----
 shared/templates/shared/article_preview.html  |   2 +-
 uniweb/models.py                              |  12 +-
 5 files changed, 123 insertions(+), 40 deletions(-)

diff --git a/district/models.py b/district/models.py
index 4663c64c..3ccbf6fc 100644
--- a/district/models.py
+++ b/district/models.py
@@ -452,6 +452,12 @@ class DistrictArticlePage(
 
         return cleaned_data
 
+    @property
+    def get_tags(self):
+        if self.shared_from is not None:
+            return self.articles_page.search_tags_by_article_id([self.pk])
+        return self.tags.all
+
     def get_context(self, request):
         context = super().get_context(request)
         context["related_articles"] = (
@@ -556,9 +562,9 @@ class DistrictArticlesPage(
         # (tags__slug)
         context = super().get_context(request)
 
-        site_article_ids = self.append_all_shared_articles(
+        site_article_ids = self.append_all_shared_articles_ids(
             DistrictArticlePage.objects.child_of(self)
-        ).values_list("id", flat=True)
+        )
 
         # Naplním "tag" a "article_page_list" parametry
         context.update(**self.get_tag_and_articles(request, site_article_ids))
@@ -574,15 +580,26 @@ class DistrictArticlesPage(
         pro danou stránku (site_article_ids). Lepší by bylo články a tag řešit
         separátně, ale pak by se musel rozpadnout ten try/except na více bloků.
         """
-        article_page_qs = self.append_all_shared_articles(
-            DistrictArticlePage.objects
-        ).filter(id__in=site_article_ids)
+
+        article_page_qs = None
+        tag = None
 
         try:
             tag = Tag.objects.filter(tag__slug=request.GET["tag"])[0].tag
-            article_page_qs = article_page_qs.filter(tags__slug=tag.slug)
+            article_page_qs = self.append_all_shared_articles(
+                DistrictArticlePage.objects.filter(
+                    id__in=site_article_ids, tags__slug=tag.slug
+                ),
+                filter=lambda shared: shared.filter(
+                    id__in=site_article_ids, tags__slug=tag.slug
+                ),
+            )
         except (KeyError, IndexError):
             tag = None
+            article_page_qs = self.append_all_shared_articles(
+                DistrictArticlePage.objects.filter(id__in=site_article_ids),
+                filter=lambda shared: shared.filter(id__in=site_article_ids),
+            )
 
         return {
             "article_page_list": Paginator(
@@ -597,11 +614,12 @@ class DistrictArticlesPage(
         Getuje Tagy pouze pro DistrictArticlePage omezeno IDčky getnutých přes
         root_page. Počítá, kolik článků je s daným tagem.
         """
-        return (
-            Tag.objects.filter(**self.get_search_tags_params(site_article_ids))
-            .order_by("slug")
-            .annotate(count=models.Count("slug"))
-            .values("name", "slug", "count")
+        return self.search_tags_by_article_id(
+            site_article_ids,
+            additional_query=lambda query: query.annotate(count=models.Count("slug")),
+            custom_query_to_values=lambda query: query.order_by("slug").values(
+                "name", "slug", "count"
+            ),
         )
 
 
diff --git a/district/templates/district/district_article_page.html b/district/templates/district/district_article_page.html
index da90b002..67b066cd 100644
--- a/district/templates/district/district_article_page.html
+++ b/district/templates/district/district_article_page.html
@@ -24,7 +24,7 @@
           </span>
       </div>
       <div class="my-4">
-        {% for tag in page.tags.all %}
+        {% for tag in page.get_tags %}
           <a href="{{ articles_tag_page_url }}?tag={{ tag.slug }}" class="btn btn--grey-125 btn--condensed">
             <div class="btn__body ">{{ tag }}</div>
           </a>
diff --git a/shared/models.py b/shared/models.py
index 144000d4..eef7456d 100644
--- a/shared/models.py
+++ b/shared/models.py
@@ -1,4 +1,5 @@
 import logging
+from enum import Enum
 from functools import reduce
 
 from django.apps import apps
@@ -6,7 +7,7 @@ from django.db import models
 from django.db.models.expressions import F, Value
 from django.utils import timezone
 from modelcluster.fields import ParentalKey, ParentalManyToManyField
-from taggit.models import ItemBase, TagBase
+from taggit.models import ItemBase, Tag, TagBase
 from wagtail.admin.panels import FieldPanel, MultiFieldPanel, PublishingPanel
 from wagtail.fields import StreamField
 from wagtail.models import Page
@@ -280,6 +281,12 @@ class SharedTaggedMainArticle(ItemBase):
     )
 
 
+class SharedArticlesPageType(Enum):
+    DISTRICT = ("district",)
+    UNIWEB = ("uniweb",)
+    MAIN = "main"
+
+
 class ArticlesMixin(models.Model):
     shared_tags = ParentalManyToManyField(
         "shared.SharedTag",
@@ -290,22 +297,32 @@ class ArticlesMixin(models.Model):
 
     content_panels = Page.content_panels + [FieldPanel("shared_tags")]
 
-    def append_all_shared_articles(
-        self, previous_query: models.QuerySet | None = None, filter=None
-    ):
-        """
-        To prevent circular deps, we get class models during runtime
-        """
+    def determine_page_type(self):
         DistrictArticlesPage = apps.get_model(app_label="district.DistrictArticlesPage")
         UniwebArticlesIndexPage = apps.get_model(
             app_label="uniweb.UniwebArticlesIndexPage"
         )
         MainArticlesPage = apps.get_model(app_label="main.MainArticlesPage")
+        if isinstance(self, DistrictArticlesPage):
+            return SharedArticlesPageType.DISTRICT
+        elif isinstance(self, UniwebArticlesIndexPage):
+            return SharedArticlesPageType.UNIWEB
+        elif isinstance(self, MainArticlesPage):
+            return SharedArticlesPageType.MAIN
+
+    def append_all_shared_articles_query(
+        self, previous_query: models.QuerySet | None = None, filter=None
+    ):
+        """
+        To prevent circular deps, we get class models during runtime
+        """
 
         DistrictArticlePage = apps.get_model(app_label="district.DistrictArticlePage")
         UniwebArticlePage = apps.get_model(app_label="uniweb.UniwebArticlePage")
         MainArticlePage = apps.get_model(app_label="main.MainArticlePage")
 
+        page_type = self.determine_page_type()
+
         """
             In order to balance union() requirements for tables with same-fields only, we are adding null fields using values().
             These values must be in correct order
@@ -424,17 +441,17 @@ class ArticlesMixin(models.Model):
         if previous_query is not None:
             prepared_query = previous_query.live().specific()
 
-            if isinstance(self, DistrictArticlesPage):
+            if page_type == SharedArticlesPageType.DISTRICT:
                 prepared_query = prepared_query.values(
                     **district_fields,
                     union_shared_from_id=empty_shared_field,
                 )
-            elif isinstance(self, UniwebArticlesIndexPage):
+            elif page_type == SharedArticlesPageType.UNIWEB:
                 prepared_query = prepared_query.values(
                     **uniweb_fields,
                     union_shared_from_id=empty_shared_field,
                 )
-            elif isinstance(self, MainArticlesPage):
+            elif page_type == SharedArticlesPageType.MAIN:
                 prepared_query = prepared_query.values(
                     **main_fields,
                     union_shared_from_id=empty_shared_field,
@@ -442,7 +459,31 @@ class ArticlesMixin(models.Model):
 
             results = results.union(prepared_query)
 
-        results = results.order_by("union_date")
+        return results.order_by("union_date")
+
+    def append_all_shared_articles_ids(
+        self, previous_query: models.QuerySet | None = None, filter=None
+    ):
+        results = self.append_all_shared_articles_query(previous_query, filter)
+        return results.values_list("id", Flat=True)
+
+    def append_all_shared_articles(
+        self, previous_query: models.QuerySet | None = None, filter=None
+    ):
+        """
+        To prevent circular deps, we get class models during runtime
+        """
+        page_type = self.determine_page_type()
+
+        DistrictArticlePage = apps.get_model(app_label="district.DistrictArticlePage")
+        UniwebArticlePage = apps.get_model(app_label="uniweb.UniwebArticlePage")
+        MainArticlePage = apps.get_model(app_label="main.MainArticlePage")
+
+        main_meta_fields = MainArticlePage._meta.fields
+        district_meta_fields = DistrictArticlePage._meta.fields
+        uniweb_meta_fields = UniwebArticlePage._meta.fields
+
+        results = self.append_all_shared_articles_query(previous_query, filter)
 
         evaluated = list(
             results
@@ -452,7 +493,7 @@ class ArticlesMixin(models.Model):
             field.column: unioned[f"union_{field.column}"]
         }
 
-        if isinstance(self, DistrictArticlesPage):
+        if page_type == SharedArticlesPageType.DISTRICT:
             return list(
                 map(
                     lambda unioned: DistrictArticlePage(
@@ -462,7 +503,7 @@ class ArticlesMixin(models.Model):
                 )
             )
 
-        if isinstance(self, UniwebArticlesIndexPage):
+        if page_type == SharedArticlesPageType.UNIWEB:
             return list(
                 map(
                     lambda unioned: UniwebArticlePage(
@@ -472,7 +513,7 @@ class ArticlesMixin(models.Model):
                 )
             )
 
-        if isinstance(self, MainArticlesPage):
+        if page_type == SharedArticlesPageType.MAIN:
             return list(
                 map(
                     lambda unioned: MainArticlePage(
@@ -492,14 +533,38 @@ class ArticlesMixin(models.Model):
         slug = request.GET.get("sdilene", "")
         return self.get_article_page_by_slug(slug).serve(request)
 
-    def get_search_tags_params(self, site_article_ids: list):
-        return {
-            "districtarticlepage__id__in": site_article_ids,
-            "uniwebarticlepage__id__in": site_article_ids,
-            "mainarticlepage__id__in": site_article_ids,
-        }
+    def search_tags_by_article_id(
+        self, site_article_ids: list, additional_query=None, custom_query_to_values=None
+    ):
+        DistrictArticleTag = apps.get_model(app_label="district.DistrictArticleTag")
+        UniwebArticleTag = apps.get_model(app_label="uniweb.UniwebArticleTag")
+        MainArticleTag = apps.get_model(app_label="main.MainArticleTag")
+
+        district_tags = DistrictArticleTag.objects.filter(
+            content_object_id__in=site_article_ids
+        )
+        uniweb_tags = UniwebArticleTag.objects.filter(
+            content_object_id__in=site_article_ids
+        )
+        main_tags = MainArticleTag.objects.filter(
+            content_object_id__in=site_article_ids
+        )
+
+        if additional_query is not None:
+            district_tags = additional_query(district_tags)
+            uniweb_tags = additional_query(uniweb_tags)
+            main_tags = additional_query(main_tags)
+
+        union = district_tags.union(uniweb_tags).union(main_tags)
+
+        if custom_query_to_values is not None:
+            union = custom_query_to_values(union)
+            return union
+        else:
+            union = union.values_list("id")
+            return Tag.objects.filter(id__in=union)
 
-    def get_search_article_params_by_tags(self, tag: str):
+    def search_tags_by_tag_name(self, tag: str):
         return {
             "uniwebarticlepage__tags__name": tag,
             "districtarticlepage__tags__name": tag,
diff --git a/shared/templates/shared/article_preview.html b/shared/templates/shared/article_preview.html
index 4add013a..1ed70820 100644
--- a/shared/templates/shared/article_preview.html
+++ b/shared/templates/shared/article_preview.html
@@ -85,7 +85,7 @@
       {{ article.perex }}
     </p>
     <div class="inline-block-nogap mt-4">
-      {% for tag in article.tags.all %}
+      {% for tag in article.get_tags %}
         <a
           href="{{ articles_tag_page_url }}?tag={{ tag.slug }}"
           class="btn article-card__category-button btn--condensed text-sm font-light btn--grey-{% if article.is_black %}700{% else %}125{% endif %} btn--hoveractive"
diff --git a/uniweb/models.py b/uniweb/models.py
index ad9e2dde..e28e03c2 100644
--- a/uniweb/models.py
+++ b/uniweb/models.py
@@ -548,17 +548,17 @@ class UniwebArticlesIndexPage(
         num = request.GET.get("page")
         tag = request.GET.get("tag")
 
+        tag_params = self.search_tags_by_tag_name(tag)
+
         articles = self.append_all_shared_articles(
-            UniwebArticlePage.objects.child_of(self)
+            UniwebArticlePage.objects.child_of(self),
+            filter=lambda articles: articles.filter(**tag_params),
         )
 
-        if tag is not None:
-            articles = articles.filter(**self.get_search_article_params_by_tags(tag))
+        articles_ids = list(map(lambda article: article.pk, articles))
 
         context["articles"] = Paginator(articles, ARTICLES_PER_PAGE).get_page(num)
-        context["tags"] = Tag.objects.filter(
-            **self.get_search_tags_params(articles.values_list("id", flat=True))
-        )
+        context["tags"] = self.search_tags_by_article_id(articles_ids)
         context["active_tag"] = tag
         return context
 
-- 
GitLab