Skip to content

Commit

Permalink
feat(forum): harmoniser la liste des topics (#665)
Browse files Browse the repository at this point in the history
## Description

🎸 soutenir filtration sur `ForumView`
🎸 supprimer `ForumTopicListView`, l'accès pour HTMX fournis par
`ForumView`
🎸 `FilteredTopicsListViewMixin` pour éviter la répétition de logique
entre les views du Forum et l'espace d'échanges

## Type de changement

🎢 Nouvelle fonctionnalité (changement non cassant qui ajoute une
fonctionnalité).
🥁 Changement de rupture (modification ou caractéristique qui empêcherait
une fonctionnalité existante de fonctionner comme prévu) nécéssitant une
mise à jour de la documentation

### Captures d'écran (optionnel)

<img width="1353" alt="Screenshot 2024-06-10 at 17 23 21"
src="https://github.com/gip-inclusion/itou-communaute-django/assets/10801930/25230de2-13e6-4ae9-968d-e19b672ffbef">

<img width="1353" alt="Screenshot 2024-06-10 at 17 23 40"
src="https://github.com/gip-inclusion/itou-communaute-django/assets/10801930/5b5d11de-7e61-4ddc-bef7-b3e76417a606">

<img width="1353" alt="Screenshot 2024-06-10 at 17 24 05"
src="https://github.com/gip-inclusion/itou-communaute-django/assets/10801930/9d8edc93-5b46-478b-aa6d-df3ce18aebe6">
  • Loading branch information
calummackervoy authored Jun 12, 2024
1 parent 7318b5d commit 6f8bf3c
Show file tree
Hide file tree
Showing 10 changed files with 192 additions and 194 deletions.
96 changes: 91 additions & 5 deletions lacommunaute/forum/tests/tests_views.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
import pytest # noqa

from django.contrib.contenttypes.models import ContentType
from django.template.defaultfilters import truncatechars_html
from django.test import RequestFactory, TestCase
from django.urls import reverse

from faker import Faker
from machina.core.loading import get_class
from taggit.models import Tag

from lacommunaute.forum.enums import Kind as ForumKind
from lacommunaute.forum.factories import CategoryForumFactory, ForumFactory
from lacommunaute.forum.views import ForumView
from lacommunaute.forum_conversation.enums import Filters
from lacommunaute.forum_conversation.factories import CertifiedPostFactory, PostFactory, TopicFactory
from lacommunaute.forum_conversation.forms import PostForm
from lacommunaute.forum_conversation.models import Topic
from lacommunaute.users.factories import UserFactory
from lacommunaute.utils.testing import parse_response_to_soup

Expand Down Expand Up @@ -64,6 +70,44 @@ def setUpTestData(cls):

cls.url = reverse("forum_extension:forum", kwargs={"pk": cls.forum.pk, "slug": cls.forum.slug})

def test_context(self):
response = self.client.get(self.url)

loadmoretopic_url = reverse("forum_extension:forum", kwargs={"pk": self.forum.pk, "slug": self.forum.slug})

self.assertIsInstance(response.context_data["form"], PostForm)
self.assertEqual(response.context_data["filters"], Filters.choices)
self.assertEqual(response.context_data["loadmoretopic_url"], loadmoretopic_url)
self.assertEqual(response.context_data["forum"], self.forum)
self.assertEqual(response.context_data["active_filter_name"], Filters.ALL.label)
self.assertEqual(response.context_data["active_tags"], "")

for filter, label in Filters.choices:
with self.subTest(filter=filter, label=label):
response = self.client.get(self.url + f"?filter={filter}")
self.assertEqual(
response.context_data["loadmoretopic_url"],
loadmoretopic_url + f"?filter={filter}",
)
self.assertEqual(response.context_data["active_filter_name"], label)

response = self.client.get(self.url + "?filter=FAKE")
self.assertEqual(response.context_data["active_filter_name"], Filters.ALL.label)

tag = Tag.objects.create(name="tag_1", slug="tag_1")
response = self.client.get(self.url + f"?tags=nonexistant,{tag.name}")
self.assertIn(tag.slug, response.context_data["active_tags"])
self.assertNotIn("nonexistant", response.context_data["active_tags"])
self.assertIn(tag.name, response.context_data["active_tags_label"])
self.assertNotIn("nonexistant", response.context_data["active_tags_label"])

def test_template_name(self):
response = self.client.get(self.url)
self.assertTemplateUsed(response, "forum/forum_detail.html")

response = self.client.get(self.url, **{"HTTP_HX_REQUEST": "true"})
self.assertTemplateUsed(response, "forum_conversation/topic_list.html")

def test_show_comments(self):
topic_url = reverse(
"forum_conversation_extension:showmore_posts",
Expand Down Expand Up @@ -211,8 +255,8 @@ def test_certified_post_display(self):

def test_loadmoretopic_url(self):
loadmoretopic_url = reverse(
"forum_conversation_extension:topic_list",
kwargs={"forum_pk": self.forum.pk, "forum_slug": self.forum.slug},
"forum_extension:forum",
kwargs={"pk": self.forum.pk, "slug": self.forum.slug},
)

TopicFactory.create_batch(9, with_post=True, forum=self.forum)
Expand Down Expand Up @@ -415,12 +459,54 @@ def test_filtered_queryset_on_tag(self):
tag = faker.word()
topic = TopicFactory(forum=self.forum, with_tags=[tag], with_post=True)

response = self.client.get(
reverse("forum_extension:forum", kwargs={"pk": self.forum.pk, "slug": self.forum.slug}), {"tags": tag}
)
with self.assertNumQueries(19):
response = self.client.get(
reverse("forum_extension:forum", kwargs={"pk": self.forum.pk, "slug": self.forum.slug}), {"tags": tag}
)
self.assertContains(response, topic.subject)
self.assertNotContains(response, self.topic.subject)

def test_queryset_for_unanswered_topics(self):
PostFactory(topic=self.topic)
response = self.client.get(self.url + f"?filter={Filters.NEW.value}")
self.assertEqual(response.status_code, 200)
self.assertEqual(response.context_data["paginator"].count, 0)
self.assertEqual(response.context_data["active_filter_name"], Filters.NEW.label)

new_topic = TopicFactory(with_post=True, forum=self.forum)

response = self.client.get(self.url + f"?filter={Filters.NEW.value}")
self.assertEqual(response.context_data["paginator"].count, 1)
self.assertContains(response, new_topic.subject, status_code=200)
self.assertEqual(response.context_data["active_filter_name"], Filters.NEW.label)

for topic in Topic.objects.exclude(id=new_topic.id):
with self.subTest(topic):
self.assertNotContains(response, topic.subject)

def test_queryset_for_certified_topics(self):
response = self.client.get(self.url + f"?filter={Filters.CERTIFIED.value}")
self.assertEqual(response.status_code, 200)
self.assertEqual(response.context_data["paginator"].count, 0)
self.assertEqual(response.context_data["active_filter_name"], Filters.CERTIFIED.label)

certified_topic = TopicFactory(with_post=True, with_certified_post=True, forum=self.forum)
TopicFactory(
with_post=True,
with_certified_post=True,
forum=ForumFactory(kind=ForumKind.PRIVATE_FORUM, with_public_perms=True),
)

response = self.client.get(self.url + f"?filter={Filters.CERTIFIED.value}")
self.assertEqual(response.context_data["paginator"].count, 1)
self.assertContains(response, certified_topic.subject, status_code=200)
self.assertContains(response, certified_topic.certified_post.post.content.raw[:100])
self.assertEqual(response.context_data["active_filter_name"], Filters.CERTIFIED.label)

for topic in Topic.objects.exclude(id=certified_topic.id):
with self.subTest(topic):
self.assertNotContains(response, topic.subject)

def test_banner_display_on_subcategory_forum(self):
category_forum = CategoryForumFactory(with_child=True, with_public_perms=True)
forum = category_forum.get_children().first()
Expand Down
31 changes: 17 additions & 14 deletions lacommunaute/forum/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from lacommunaute.forum.forms import ForumForm
from lacommunaute.forum.models import Forum
from lacommunaute.forum_conversation.forms import PostForm
from lacommunaute.forum_conversation.view_mixins import FilteredTopicsListViewMixin
from lacommunaute.forum_upvote.models import UpVote
from lacommunaute.utils.perms import add_public_perms_on_forum

Expand All @@ -22,22 +23,16 @@
PermissionRequiredMixin = get_class("forum_permission.viewmixins", "PermissionRequiredMixin")


class ForumView(BaseForumView):
class ForumView(BaseForumView, FilteredTopicsListViewMixin):
paginate_by = settings.FORUM_TOPICS_NUMBER_PER_PAGE

def get_tags(self):
if not hasattr(self, "tags"):
self.tags = self.request.GET.get("tags", "").lower()
return self.tags
def get_template_names(self):
if self.request.META.get("HTTP_HX_REQUEST"):
return ["forum_conversation/topic_list.html"]
return ["forum/forum_detail.html"]

def get_queryset(self):
forum = self.get_forum()
qs = forum.topics.optimized_for_topics_list(self.request.user.id)

if self.get_tags():
qs = qs.filter(tags__slug__in=self.get_tags().split(","))

return qs
return self.filter_queryset(self.get_forum().topics.optimized_for_topics_list(self.request.user.id))

def get_context_data(self, **kwargs):
forum = self.get_forum()
Expand All @@ -53,11 +48,19 @@ def get_context_data(self, **kwargs):
context["forum"] = forum
context["FORUM_NUMBER_POSTS_PER_TOPIC"] = settings.FORUM_NUMBER_POSTS_PER_TOPIC
context["next_url"] = reverse("forum_extension:forum", kwargs={"pk": forum.pk, "slug": self.forum.slug})
context["loadmoretopic_url"] = reverse(
"forum_conversation_extension:topic_list", kwargs={"forum_pk": forum.pk, "forum_slug": self.forum.slug}
context["loadmoretopic_url"] = self.get_load_more_url(
reverse("forum_extension:forum", kwargs={"pk": forum.pk, "slug": self.forum.slug})
)
context["loadmoretopic_suffix"] = "topicsinforum"
context["form"] = PostForm(forum=forum, user=self.request.user)

context["filter_dropdown_endpoint"] = (
None
if self.request.GET.get("page")
else reverse("forum_extension:forum", kwargs={"pk": forum.pk, "slug": self.forum.slug})
)
context = context | self.get_topic_filter_context()

if forum.parent and forum.is_in_documentation_area:
context["forums"] = forum.get_siblings(include_self=True)
return context
Expand Down
12 changes: 6 additions & 6 deletions lacommunaute/forum_conversation/tests/tests_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -703,7 +703,7 @@ def test_queryset_filtered_on_tag(client, db, tag):
tagged_topic = TopicFactory(with_post=True, forum=forum, with_tags=[tag])

response = client.get(reverse("forum_conversation_extension:topics"), data={"tags": tag})
assert response.context_data["total"] == 1
assert response.context_data["paginator"].count == 1
assertContains(response, tagged_topic.subject)
assertNotContains(response, other_topic.subject)

Expand Down Expand Up @@ -844,7 +844,7 @@ def test_queryset(self):
response = self.client.get(self.url)

self.assertEqual(response.status_code, 200)
self.assertEqual(response.context_data["total"], 1)
self.assertEqual(response.context_data["paginator"].count, 1)

for topic in Topic.objects.exclude(id=self.topic.id):
with self.subTest(topic):
Expand All @@ -859,7 +859,7 @@ def test_queryset_for_unanswered_topic(self):
TopicFactory(with_post=True, forum=ForumFactory(kind=ForumKind.NEWS, with_public_perms=True))

response = self.client.get(self.url + "?filter=NEW")
self.assertEqual(response.context_data["total"], 1)
self.assertEqual(response.context_data["paginator"].count, 1)
self.assertContains(response, self.topic.subject, status_code=200)

for topic in Topic.objects.exclude(id=self.topic.id):
Expand All @@ -877,7 +877,7 @@ def test_queryset_for_certified_topic(self):
)

response = self.client.get(self.url + "?filter=CERTIFIED")
self.assertEqual(response.context_data["total"], 1)
self.assertEqual(response.context_data["paginator"].count, 1)
self.assertContains(response, certified_topic.subject, status_code=200)
self.assertContains(response, certified_topic.certified_post.post.content.raw[:100])

Expand Down Expand Up @@ -921,11 +921,11 @@ def test_showmoretopics_url_with_params(self):
def test_filter_dropdown_visibility(self):
response = self.client.get(self.url)
self.assertContains(response, '<div class="dropdown-menu dropdown-menu-end" id="filterTopicsDropdown">')
self.assertEqual(response.context_data["display_filter_dropdown"], True)
self.assertEqual(response.context_data["filter_dropdown_endpoint"], self.url)

response = self.client.get(self.url + "?page=1")
self.assertNotContains(response, '<div class="dropdown-menu dropdown-menu-end" id="filterTopicsDropdown">')
self.assertEqual(response.context_data["display_filter_dropdown"], False)
self.assertEqual(response.context_data["filter_dropdown_endpoint"], None)

def test_filter_dropdown_with_tags(self):
tag = Tag.objects.create(name=faker.words(nb=3))
Expand Down
69 changes: 0 additions & 69 deletions lacommunaute/forum_conversation/tests/tests_views_htmx.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from faker import Faker
from machina.core.db.models import get_model
from machina.core.loading import get_class
from taggit.models import Tag

from lacommunaute.forum_conversation.factories import CertifiedPostFactory, PostFactory, TopicFactory
from lacommunaute.forum_conversation.forms import PostForm
Expand All @@ -26,74 +25,6 @@
PermissionHandler = get_class("forum_permission.handler", "PermissionHandler")


class ForumTopicListViewTest(TestCase):
@classmethod
def setUpTestData(cls):
cls.topic = TopicFactory(with_post=True)
cls.user = cls.topic.poster
cls.url = reverse(
"forum_conversation_extension:topic_list",
kwargs={
"forum_pk": cls.topic.forum.pk,
"forum_slug": cls.topic.forum.slug,
},
)

def test_get_forum_not_found(self):
response = self.client.get(
reverse(
"forum_conversation_extension:topic_list",
kwargs={
"forum_pk": 999,
"forum_slug": self.topic.forum.slug,
},
)
)
self.assertEqual(response.status_code, 404)

def test_get(self):
other_topic = TopicFactory(with_post=True)
assign_perm("can_read_forum", self.user, self.topic.forum)
self.client.force_login(self.user)

response = self.client.get(self.url)

self.assertContains(response, self.topic.subject, status_code=200)
self.assertNotContains(response, other_topic.subject)
self.assertEqual(response.context["forum"], self.topic.forum)

def test_get_without_permission(self):
response = self.client.get(self.url)
self.assertEqual(response.status_code, 302)

def test_loadmoretopic_url(self):
TopicFactory.create_batch(20, forum=self.topic.forum, with_post=True)
assign_perm("can_read_forum", self.user, self.topic.forum)
self.client.force_login(self.user)

response = self.client.get(self.url)

self.assertContains(
response,
reverse(
"forum_conversation_extension:topic_list",
kwargs={"forum_pk": self.topic.forum.pk, "forum_slug": self.topic.forum.slug},
),
status_code=200,
)
self.assertEqual(response.context_data["loadmoretopic_suffix"], "topicsinforum")

def test_numqueries_vs_tags(self):
tags = Tag.objects.bulk_create([Tag(name=f"tag{i}", slug=f"tag{i}") for i in range(5)])
for topic in TopicFactory.create_batch(20, forum=self.topic.forum, with_post=True):
topic.tags.add(", ".join(tag.name for tag in tags))
assign_perm("can_read_forum", self.user, self.topic.forum)
self.client.force_login(self.user)

with self.assertNumQueries(13):
self.client.get(self.url)


class TopicContentViewTest(TestCase):
@classmethod
def setUpTestData(cls):
Expand Down
2 changes: 0 additions & 2 deletions lacommunaute/forum_conversation/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from lacommunaute.forum_conversation.views import NewsFeedTopicListView, TopicListView
from lacommunaute.forum_conversation.views_htmx import (
CertifiedPostView,
ForumTopicListView,
PostFeedCreateView,
PostListView,
TopicCertifiedPostView,
Expand All @@ -19,7 +18,6 @@
path("topic/<str:slug>-<int:pk>/showmore/certified", TopicCertifiedPostView.as_view(), name="showmore_certified"),
path("topic/<str:slug>-<int:pk>/comment", PostFeedCreateView.as_view(), name="post_create"),
path("topic/<str:slug>-<int:pk>/certify", CertifiedPostView.as_view(), name="certify"),
path("topic/", ForumTopicListView.as_view(), name="topic_list"),
]


Expand Down
Loading

0 comments on commit 6f8bf3c

Please sign in to comment.