Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add child, sibling, and archive controls to filterable lists #6039

Merged
merged 3 commits into from
Sep 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions cfgov/scripts/_atomic_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -382,5 +382,8 @@
'categories': {
'page_type': '',
},
'filter_children': True,
'filter_siblings': False,
'filter_archive': False,
}
}
27 changes: 27 additions & 0 deletions cfgov/v1/atomic_elements/organisms.py
Original file line number Diff line number Diff line change
Expand Up @@ -792,6 +792,33 @@ class FilterableList(BaseExpandable):
' headings in filterable list results'
)

filter_children = blocks.BooleanBlock(
default=True,
required=False,
help_text=(
"If checked this list will only filter its child pages. "
"If both children and siblings are checked, only child pages will "
"be filtered."
),
)
filter_siblings = blocks.BooleanBlock(
default=False,
required=False,
help_text=(
"If checked this list will only filter its sibling pages. "
"If both children and siblings are checked, only child pages will "
"be filtered."
),
)
filter_archive = blocks.BooleanBlock(
default=False,
required=False,
help_text=(
"If checked this list will only filter archived pages."
chosak marked this conversation as resolved.
Show resolved Hide resolved
"If unchecked this list will exclude archive pages."
),
)

class Meta:
label = 'Filterable List'
icon = 'form'
Expand Down
29 changes: 29 additions & 0 deletions cfgov/v1/migrations/0231_add_filterable_list_filter_options.py

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Generated by Django 2.2.16 on 2020-09-24 19:20

from django.db import migrations

from v1.util.migrations import migrate_page_types_and_fields


def forward_mapper(page_or_revision, data):
# Manipulate the stream block data forwards
data['filter_children'] = False
return data


def forwards(apps, schema_editor):
page_types_and_fields = [
('v1', 'ActivityLogPage', 'content', 'filter_controls'),
('v1', 'NewsroomLandingPage', 'content', 'filter_controls'),
Scotchester marked this conversation as resolved.
Show resolved Hide resolved
]
migrate_page_types_and_fields(
apps,
page_types_and_fields,
forward_mapper
)

def backwards(apps, schema_editor):
# There's nothing to do going backwards
pass


class Migration(migrations.Migration):

dependencies = [
('v1', '0231_add_filterable_list_filter_options'),
]

operations = [
migrations.RunPython(forwards, backwards),
]
1 change: 0 additions & 1 deletion cfgov/v1/models/browse_filterable_page.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,5 @@ def get_form_class():
class NewsroomLandingPage(CategoryFilterableMixin, BrowseFilterablePage):
template = 'newsroom/index.html'
filterable_categories = ['Newsroom']
Scotchester marked this conversation as resolved.
Show resolved Hide resolved
filterable_children_only = False

objects = PageManager()
46 changes: 31 additions & 15 deletions cfgov/v1/models/filterable_list_mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@
class FilterableListMixin(RoutablePageMixin):
"""Wagtail Page mixin that allows for filtering of other pages."""

filterable_children_only = True
"""Determines page tree to be filtered; see filterable_pages."""

filterable_per_page_limit = 10
"""Number of results to return per page."""

Expand All @@ -30,6 +27,12 @@ def get_model_class():
def get_form_class():
return FilterableListForm

def get_filterable_list_wagtail_block(self):
return next(
(b for b in self.content if b.block_type == 'filter_controls'),
None
)

def get_filterable_queryset(self):
"""Return the queryset of pages to be filtered by this page.

Expand All @@ -38,10 +41,18 @@ def get_filterable_queryset(self):
Wagtail site (for example, if it does not live under a site root),
then it will not return any filterable results.

The class property filterable_children_only determines whether this
page filters only pages that are direct children of this page. By
default this is True; set this to False to allow this page to filter
pages that are not direct children of this page.
The filter_children attribute determines whether this page filters
pages that are direct children of this page. By default this is True;
set this to False to allow this page to filter pages that are not
direct children of this page.

The filter_siblings attribute determines whether this page filters
pages that are siblings of this page. By default this is False.

The filter_archive attribute determines whether this page filters
pages that are archived. By default this is False, and archived pages
will be excluded. If this is True, only archived pages will be
filtered.
"""
site = self.get_site()

Expand All @@ -50,8 +61,19 @@ def get_filterable_queryset(self):

queryset = self.get_model_class().objects.in_site(site).live()

if self.filterable_children_only:
filterable_list_block = self.get_filterable_list_wagtail_block()
if filterable_list_block is None:
return queryset

if filterable_list_block.value['filter_children']:
queryset = queryset.child_of(self)
elif filterable_list_block.value['filter_siblings']:
queryset = queryset.sibling_of(self)

if filterable_list_block.value['filter_archive']:
queryset = queryset.filter(is_archived=True)
else:
queryset = queryset.filter(is_archived=False)

return queryset

Expand All @@ -64,7 +86,7 @@ def get_context(self, request, *args, **kwargs):
form = self.get_form_class()(
form_data,
filterable_pages=self.get_filterable_queryset(),
wagtail_block=self.filterable_list_wagtail_block(),
wagtail_block=self.get_filterable_list_wagtail_block(),
)

context.update({
Expand All @@ -75,12 +97,6 @@ def get_context(self, request, *args, **kwargs):

return context

def filterable_list_wagtail_block(self):
return next(
(b for b in self.content if b.block_type == 'filter_controls'),
None
)

def process_form(self, request, form):
filter_data = {}
if form.is_valid():
Expand Down
1 change: 0 additions & 1 deletion cfgov/v1/models/sublanding_filterable_page.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ class SublandingFilterablePage(FilterableListMixin, CFGOVPage):
class ActivityLogPage(CategoryFilterableMixin, SublandingFilterablePage):
template = 'activity-log/index.html'
filterable_categories = ('Blog', 'Newsroom', 'Research Report')
filterable_children_only = False
filterable_per_page_limit = 100

objects = PageManager()
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def set_up_filterable_list_page(self, value):
self.page = BrowseFilterablePage(title='Browse filterable page')
self.page.content = StreamValue(self.page.content.stream_block, [value], True)
helpers.publish_page(child=self.page)
self.block = self.page.filterable_list_wagtail_block()
self.block = self.page.get_filterable_list_wagtail_block()

def test_get_filterable_topics_sort_by_frequency(self):
self.set_up_filterable_list_page(self.topics_by_frequency())
Expand Down
60 changes: 60 additions & 0 deletions cfgov/v1/tests/models/test_filterable_list_mixins.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from django.test import RequestFactory, TestCase

from wagtail.core.blocks import StreamValue
from wagtail.core.models import Site

import mock

from scripts._atomic_helpers import filter_controls
from v1.models import BlogPage
from v1.models.browse_filterable_page import BrowseFilterablePage
from v1.models.filterable_list_mixins import FilterableListMixin
Expand Down Expand Up @@ -110,3 +112,61 @@ def test_feed_route(self):
response["content-type"],
"application/rss+xml; charset=utf-8"
)


class FilterableListRelationsTestCase(TestCase):

def setUp(self):
self.filter_controls = filter_controls

self.filterable_page = BrowseFilterablePage(title="Blog", slug="test")
self.root = Site.objects.get(is_default_site=True).root_page
self.root.add_child(instance=self.filterable_page)

self.set_filterable_controls(filter_controls)

self.child_page = BlogPage(title="Child test page", live=True)
self.sibling_page = BlogPage(title="Sibling test page", live=True)
self.archived_sibling_page = BlogPage(title="Archive test page", live=True,
is_archived=True)
self.filterable_page.add_child(instance=self.child_page)
self.filterable_page.get_parent().add_child(instance=self.sibling_page)
self.filterable_page.get_parent().add_child(instance=self.archived_sibling_page)

def set_filterable_controls(self, value):
self.filterable_page.content = StreamValue(
self.filterable_page.content.stream_block,
[value],
True
)
self.filterable_page.save()

def test_get_filterable_children_pages(self):
filter_controls['value']['filter_children'] = True
filter_controls['value']['filter_siblings'] = False
filter_controls['value']['filter_archive'] = False
self.set_filterable_controls(self.filter_controls)

qs = self.filterable_page.get_filterable_queryset()
self.assertEqual(qs.count(), 1)
self.assertEqual(qs[0].pk, self.child_page.pk)

def test_get_filterable_siblings_pages(self):
filter_controls['value']['filter_children'] = False
filter_controls['value']['filter_siblings'] = True
filter_controls['value']['filter_archive'] = False
self.set_filterable_controls(self.filter_controls)

qs = self.filterable_page.get_filterable_queryset()
self.assertEqual(qs.count(), 1)
self.assertEqual(qs[0].pk, self.sibling_page.pk)

def test_get_filterable_archived_pages(self):
filter_controls['value']['filter_children'] = False
filter_controls['value']['filter_siblings'] = True
filter_controls['value']['filter_archive'] = True
self.set_filterable_controls(self.filter_controls)

qs = self.filterable_page.get_filterable_queryset()
self.assertEqual(qs.count(), 1)
self.assertEqual(qs[0].pk, self.archived_sibling_page.pk)