Repository: liqd/a4-meinberlin

Issue:
Counting Comments on map popup and list items (2 issues - similar problem  in a+)
**URL:** https://meinberlin-dev.liqd.net/mapideas/2023-01031/ ; https://meinberlin-dev.liqd.net/projekte/testprojekt-newsletter/
**user:** any
**expected behaviour:** the counting of comments should be consistent
**behaviour:** 

1. The number of comments in the detail idea view is not the same anymore as the number in the idea overview (list & map). This is because the detail ide view now counts as well child comments while the idea overview doesn't. (see screenshot 1 vs. 2)

2. The counting in the detail view stops at 100 seperate comments. If there are child comments, it adds to counting of 100. The number is then also different to the idea overview. If I scroll down, then new comments are loaded and the counting number on top changes. This can be very confusing. (see screenshot 1, 2 & 3)

**important screensize:** any
**device & browser:** mac ff
**Comment/Question:** 

Screenshot?
**1. screenshot of idea overview (map)**
<img width="821" alt="Bildschirm­foto 2023-08-01 um 15 36 52" src="https://github.com/liqd/a4-meinberlin/assets/113608720/ac6d7dd2-9785-49ad-85d4-f380cda6401d">

**2. screenshot of idea detail view with child comments**
<img width="847" alt="Bildschirm­foto 2023-08-01 um 15 37 17" src="https://github.com/liqd/a4-meinberlin/assets/113608720/45951686-f9d2-4acb-8615-8b75182ac943">

**3. screenshot of idea detail view with child comments and scrolled down**
<img width="972" alt="Bildschirm­foto 2023-08-01 um 15 37 40" src="https://github.com/liqd/a4-meinberlin/assets/113608720/3e2c3d16-0578-4a87-8f47-285d61e04be3">




Discussion:
this should be fixed already I think, but maybe good to double check
partial fix, the multimodule project view with the module tile count is not showing correctly https://meinberlin-dev.liqd.net/projekte/testprojekt-newsletter/
map popups and list items only counting parent comments not child comments
map popup and list items count correctly for me, so it's only the multi module tiles showing the wrong count

Resolving patch:
diff --git a/meinberlin/apps/projects/templatetags/meinberlin_project_tags.py b/meinberlin/apps/projects/templatetags/meinberlin_project_tags.py
--- a/meinberlin/apps/projects/templatetags/meinberlin_project_tags.py
+++ b/meinberlin/apps/projects/templatetags/meinberlin_project_tags.py
@@ -1,4 +1,7 @@
 from django import template
+from django.db.models import Count
+from django.db.models import Q
+from django.db.models import Sum
 
 from adhocracy4.comments.models import Comment
 from adhocracy4.polls.models import Vote as Vote
@@ -38,17 +41,28 @@ def get_num_entries(module):
         + MapIdea.objects.filter(module=module).count()
         + budget_proposal.objects.filter(module=module).count()
         + kiezkasse_proposal.objects.filter(module=module).count()
-        + Comment.objects.filter(idea__module=module).count()
-        + Comment.objects.filter(mapidea__module=module).count()
-        + Comment.objects.filter(budget_proposal__module=module).count()
-        + Comment.objects.filter(kiezkasse_proposal__module=module).count()
-        + Comment.objects.filter(topic__module=module).count()
-        + Comment.objects.filter(maptopic__module=module).count()
-        + Comment.objects.filter(paragraph__chapter__module=module).count()
-        + Comment.objects.filter(chapter__module=module).count()
-        + Comment.objects.filter(poll__module=module).count()
         + Vote.objects.filter(choice__question__poll__module=module).count()
         + LiveQuestion.objects.filter(module=module).count()
         + Like.objects.filter(question__module=module).count()
     )
-    return item_count
+    comment_filter = (
+        Q(idea__module=module)
+        | Q(mapidea__module=module)
+        | Q(budget_proposal__module=module)
+        | Q(kiezkasse_proposal__module=module)
+        | Q(topic__module=module)
+        | Q(maptopic__module=module)
+        | Q(paragraph__chapter__module=module)
+        | Q(chapter__module=module)
+        | Q(poll__module=module)
+    )
+    comment_count = (
+        Comment.objects.filter(comment_filter)
+        .annotate(child_comment_count=Count("child_comments__pk", distinct=True))
+        .aggregate(comment_count=Count("pk") + Sum("child_comment_count"))[
+            "comment_count"
+        ]
+    )
+    if comment_count is None:
+        comment_count = 0
+    return item_count + comment_count


Test patch:
diff --git a/tests/helpers.py b/tests/helpers.py
--- a/tests/helpers.py
+++ b/tests/helpers.py
@@ -1,5 +1,8 @@
 import re
 
+from django.contrib.contenttypes.models import ContentType
+from django.contrib.sites.models import Site
+
 
 class pytest_regex:
     """Assert that a given string meets some expectations."""
@@ -12,3 +15,11 @@ def __eq__(self, actual):
 
     def __repr__(self):
         return self._regex.pattern
+
+
+def clear_query_cache():
+    """Clear the cache for GenericRelations.
+    Required for django_assert_num_queries to work correctly, as otherwise
+    some queries will be cached. Call directly before django_assert_num_queries."""
+    ContentType.objects.clear_cache()
+    Site.objects.clear_cache()
diff --git a/tests/projects/conftest.py b/tests/projects/conftest.py
--- a/tests/projects/conftest.py
+++ b/tests/projects/conftest.py
@@ -1,6 +1,11 @@
 from pytest_factoryboy import register
 
+from meinberlin.test.factories import budgeting
+from meinberlin.test.factories import ideas
+
 from . import factories as invites
 
 register(invites.ModeratorInviteFactory)
 register(invites.ParticipantInviteFactory)
+register(ideas.IdeaFactory)
+register(budgeting.ProposalFactory)
diff --git a/tests/projects/test_project_tags.py b/tests/projects/test_project_tags.py
--- a/tests/projects/test_project_tags.py
+++ b/tests/projects/test_project_tags.py
@@ -2,6 +2,13 @@
 import pytest
 
 from adhocracy4.test.helpers import render_template
+from adhocracy4.test.helpers import setup_phase
+from meinberlin.apps.budgeting import phases as budgeting_phases
+from meinberlin.apps.ideas import phases as idea_phases
+from meinberlin.apps.projects.templatetags.meinberlin_project_tags import (
+    get_num_entries,
+)
+from tests.helpers import clear_query_cache
 
 
 @pytest.mark.django_db
@@ -49,3 +56,74 @@ def test_is_a4_project(project, external_project, bplan):
         template, {"project": external_project}
     )
     assert "is no a4 project" == render_template(template, {"project": bplan})
+
+
+@pytest.mark.django_db
+def test_get_num_entries(
+    django_assert_num_queries,
+    module_factory,
+    phase_factory,
+    idea_factory,
+    proposal_factory,
+    comment_factory,
+):
+    phase, module, project, idea = setup_phase(
+        phase_factory, idea_factory, idea_phases.FeedbackPhase
+    )
+    phase1, module1, project1, proposal = setup_phase(
+        phase_factory, proposal_factory, budgeting_phases.CollectPhase
+    )
+    empty_module = module_factory(project=project)
+
+    # no items or comments
+    clear_query_cache()
+    with django_assert_num_queries(18):
+        count = get_num_entries(empty_module)
+        assert count == 0
+
+    # one comment, no subcomments
+    comment = comment_factory(content_object=idea)
+    clear_query_cache()
+    with django_assert_num_queries(18):
+        count = get_num_entries(module)
+        assert count == 2
+
+    # two comments, no subcomments
+    comment_1 = comment_factory(content_object=idea)
+    clear_query_cache()
+    with django_assert_num_queries(18):
+        count = get_num_entries(module)
+        assert count == 3
+
+    # two subcomments for comment
+    comment_factory(content_object=comment)
+    comment_factory(content_object=comment)
+    clear_query_cache()
+    with django_assert_num_queries(18):
+        count = get_num_entries(module)
+        assert count == 5
+
+    # two subcomments for comment_1
+    comment_factory(content_object=comment_1)
+    comment_factory(content_object=comment_1)
+    clear_query_cache()
+    with django_assert_num_queries(18):
+        count = get_num_entries(module)
+        assert count == 7
+
+    # add another comment
+    comment_factory(content_object=idea)
+    clear_query_cache()
+    with django_assert_num_queries(18):
+        count = get_num_entries(module)
+        assert count == 8
+
+    # proposal comments
+    proposal_comment = comment_factory(content_object=proposal)
+    comment_factory(content_object=proposal_comment)
+    comment_factory(content_object=proposal_comment)
+    # add subcomments
+    clear_query_cache()
+    with django_assert_num_queries(18):
+        count = get_num_entries(module1)
+        assert count == 4