* Libraries profile detail page

* Define detail cards for RCC and RH

* Add Library base detail page

* Add abstract Library detail page

* Remove `rcc` and `research` prefixes from models

* Fix image height and width attributes

* Abstract detail page factory

* Lint

* Better error messages

* Fix mistyped factory model

* Add docstrings

* Rename JS file

* Rename libraries filter_form

* Base libraries LibraryPage

* Change years to year on filter form

* Add an abstract BaseLibraryPage

* Add libraries level `constants` module

* Improve API

* Fix tests

* Lint

* Fix type annotation imports

* Remove duplicated block
This commit is contained in:
Jhonatan Lopes 2023-06-21 08:25:11 -03:00 коммит произвёл GitHub
Родитель 3a06b2b756
Коммит 529c1a3092
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
17 изменённых файлов: 455 добавлений и 576 удалений

Просмотреть файл

@ -52,8 +52,8 @@ const sources = {
source: `buyers-guide/editorial-content-index.js`,
bundle: true,
},
"research-hub-library": {
source: `foundation/pages/research-hub-library.js`,
"libraries-library-page": {
source: `foundation/pages/libraries-library-page.js`,
bundle: true,
},
polyfills: {

Просмотреть файл

@ -0,0 +1,111 @@
{% extends "pages/libraries/base.html" %}
{% load i18n static wagtailcore_tags wagtailimages_tags %}
{% block library_content %}
<div>
{% block breadcrumbs %}{% endblock breadcrumbs %}
<h1>{{ page.title }}</h1>
</div>
<div class="large:tw-w-160 large:tw-h-64 tw-mt-12 tw-mb-8 large:tw-mb-0 large:-tw-ml-12 large:tw-p-12 large:tw-bg-gray-05">
{# SEARCH BAR #}
{% comment %}
The page url is necessary in the form to that the filter anchor link is not carried forward.
Say on mobile the user used the "Filter" anchor link to get to the filter section,
that adds the `#filter` part to the URL. Submitting the form in that stage without the
explicit URL would carry the anchor link forward. That would mean the view is scrolled to the
filter section again upon page reload. This seems undesireable.
{% endcomment %}
<form action="{% pageurl page %}" method="get" accept-charset="utf-8" id="search-form">
{% block search_bar %}{% endblock search_bar %}
</form>
</div>
<div class="tw-flex tw-flex-col large:tw-flex-row-reverse large:tw-gap-16">
{# FILTER, SORT AND RESULTS #}
{# For side-by-side layout, we need to pull the results up to that the upper end lines up with the search bar. #}
<div class="tw-min-w-0 tw-grow large:-tw-mt-64 tw-pb-24">
{# SORT AND RESULTS #}
<div class="tw-flex tw-flex-col large:tw-flex-row-reverse large:tw-justify-between tw-gap-12">
<div class="tw-flex tw-flex-row tw-gap-6">
{# FILTER BUTTON #}
<div class="large:tw-hidden tw-basis-1/2">
{% include "fragments/libraries/filter_button.html" with button=False %}
{% include "fragments/libraries/filter_button.html" with button=True %}
</div>
<div class="tw-flex tw-flex-row tw-items-baseline tw-basis-1/2 large:tw-basis-full">
{# SORT SELECT #}
<select id="sort-select"
name="sort"
class="tw-form-control tw-border-gray-40"
form="search-form">
{% for choice in page.SORT_CHOICES.values %}
<option value="{{ choice.value }}" {% if choice == sort %}selected{% endif %}>
{{ choice.label }}
</option>
{% endfor %}
</select>
<noscript>
{# The sort button is only needed for the no JS case. With JS, the form can be submitted on change of the select #}
<button type="submit" class="tw-btn-primary tw-text-base" form="search-form">
{% translate "Sort" context "Button" %}
</button>
</noscript>
</div>
</div>
<div class="tw-h4-heading tw-mb-0">
{# RESULTS COUNT #}
{% if search_query %}
{% blocktranslate count counter=detail_pages_count trimmed %}
<strong>{{ detail_pages_count }}</strong> result for <q>{{ search_query }}</q>
{% plural %}
<strong>{{ detail_pages_count }}</strong> results for <q>{{ search_query }}</q>
{% endblocktranslate %}
{% else %}
{% blocktranslate count counter=detail_pages_count trimmed %}
<strong>{{ detail_pages_count }}</strong> result
{% plural %}
<strong>{{ detail_pages_count }}</strong> results
{% endblocktranslate %}
{% endif %}
</div>
</div>
<ul class="tw-list-none tw-mt-16 large:tw-mt-12 tw-mb-12 tw-px-0 tw-border-t tw-border-b tw-border-gray-20 tw-divide-y tw-divide-gray-05">
{# RESULTS LIST #}
{% block detail_pages %}
{% endblock detail_pages %}
</ul>
<div>
{# PAGINATION #}
{% include "fragments/pagination.html" with page=detail_pages %}
</div>
</div>
<div id="filter"
class=" tw-bg-gray-05 large:tw-block large:tw-mr-0 large:-tw-ml-12 large:tw-overflow-y-clip tw-pt-8 large:tw-pt-0 tw-px-8 small:tw-px-12 medium:tw-px-16 large:tw-px-12 tw-shrink-0 large:tw-w-160 ">
{# FILTER SECTION #}
<div class="tw-flex tw-justify-end">
<button id="filter-section-hide-button"
class="tw-hidden large:tw-hidden tw-h-24 tw-w-24 -tw-mt-4 -tw-mr-4 -tw-mb-8 tw-text-3xl tw-font-normal tw-text-blue-80 hover:tw-text-blue-20 tw-bg-transparent"
aria-label="{% translate "Close" %}"
tabIndex="0">
<span aria-hidden="true">×</span>
</button>
</div>
<h2 class="large:tw-hidden tw-h1-heading">{% translate "Filter" %}</h2>
<form action="{% pageurl page %}"
method="get"
accept-charset="utf-8"
id="filter-form">
{% include "fragments/libraries/filter_form.html" with form=form %}
<div class=" tw-bg-gray-05 tw-bottom-0 -tw-mx-8 small:-tw-mx-12 medium:-tw-mx-16 large:-tw-mx-12 tw-pb-16 large:tw-pb-12 tw-px-8 small:tw-px-12 medium:tw-px-16 large:tw-px-12 tw-sticky ">
<div class="tw-pt-12 large:tw-pt-8 tw-border-t tw-border-t-gray-20">
<button type="submit" class="tw-w-full tw-btn-primary" form="filter-form">
{% translate "Apply filters" context "Button" %}
</button>
</div>
</div>
</form>
</div>
</div>
{% endblock library_content %}
{% block extra_scripts %}
{{ block.super }}
<script type="text/javascript" src="{% static "_js/libraries-library-page.compiled.js" %}" async defer></script>
{% endblock extra_scripts %}

Просмотреть файл

@ -1,117 +1,18 @@
{% extends "pages/libraries/base.html" %}
{% load i18n static wagtailcore_tags wagtailimages_tags breadcrumbs %}
{% block library_content %}
<div>
{% rcc_breadcrumbs %}
<h1>{{ page.title }}</h1>
</div>
<div class="large:tw-w-160 large:tw-h-64 tw-mt-12 tw-mb-8 large:tw-mb-0 large:-tw-ml-12 large:tw-p-12 large:tw-bg-gray-05">
{# SEARCH BAR #}
{% comment %}
The page url is necessary in the form to that the filter anchor link is not carried forward.
Say on mobile the user used the "Filter" anchor link to get to the filter section,
that adds the `#filter` part to the URL. Submitting the form in that stage without the
explicit URL would carry the anchor link forward. That would mean the view is scrolled to the
filter section again upon page reload. This seems undesireable.
{% endcomment %}
<form action="{% pageurl page %}"
method="get"
accept-charset="utf-8"
id="search-form">
{% include "fragments/libraries/rcc/search_bar.html" %}
</form>
</div>
<div class="tw-flex tw-flex-col large:tw-flex-row-reverse large:tw-gap-16">
{# FILTER, SORT AND RESULTS #}
{# For side-by-side layout, we need to pull the results up to that the upper end lines up with the search bar. #}
<div class="tw-min-w-0 tw-grow large:-tw-mt-64 tw-pb-24">
{# SORT AND RESULTS #}
<div class="tw-flex tw-flex-col large:tw-flex-row-reverse large:tw-justify-between tw-gap-12">
<div class="tw-flex tw-flex-row tw-gap-6">
{# FILTER BUTTON #}
<div class="large:tw-hidden tw-basis-1/2">
{% include "fragments/libraries/filter_button.html" with button=False %}
{% include "fragments/libraries/filter_button.html" with button=True %}
</div>
<div class="tw-flex tw-flex-row tw-items-baseline tw-basis-1/2 large:tw-basis-full">
{# SORT SELECT #}
<select id="sort-select"
name="sort"
class="tw-form-control tw-border-gray-40"
form="search-form">
{% for choice in page.SORT_CHOICES.values %}
<option value="{{ choice.value }}" {% if choice == sort %}selected{% endif %}>
{{ choice.label }}
</option>
{% endfor %}
</select>
<noscript>
{# The sort button is only needed for the no JS case. With JS, the form can be submitted on change of the select #}
<button type="submit" class="tw-btn-primary tw-text-base" form="search-form">
{% translate "Sort" context "Button" %}
</button>
</noscript>
</div>
</div>
<div class="tw-h4-heading tw-mb-0">
{# RESULTS COUNT #}
{% if search_query %}
{% blocktranslate count counter=rcc_detail_pages_count trimmed %}
<strong>{{ rcc_detail_pages_count }}</strong> result for <q>{{ search_query }}</q>
{% plural %}
<strong>{{ rcc_detail_pages_count }}</strong> results for <q>{{ search_query }}</q>
{% endblocktranslate %}
{% else %}
{% blocktranslate count counter=rcc_detail_pages_count trimmed %}
<strong>{{ rcc_detail_pages_count }}</strong> result
{% plural %}
<strong>{{ rcc_detail_pages_count }}</strong> results
{% endblocktranslate %}
{% endif %}
</div>
</div>
<ul class="tw-list-none tw-mt-16 large:tw-mt-12 tw-mb-12 tw-px-0 tw-border-t tw-border-b tw-border-gray-20 tw-divide-y tw-divide-gray-05">
{# RESULTS LIST #}
{% for rcc_detail_page in rcc_detail_pages %}
<li class="tw-m-0 tw-pt-12 tw-pb-12">
{% include "fragments/libraries/rcc/detail_card.html" with rcc_detail_page=rcc_detail_page hide_authors=False hide_image_on_mobile=True hide_related_content_types_on_mobile=True %}
</li>
{% endfor %}
</ul>
<div>
{# PAGINATION #}
{% include "fragments/pagination.html" with page=rcc_detail_pages %}
</div>
</div>
<div id="filter"
class=" tw-bg-gray-05 large:tw-block large:tw-mr-0 large:-tw-ml-12 large:tw-overflow-y-clip tw-pt-8 large:tw-pt-0 tw-px-8 small:tw-px-12 medium:tw-px-16 large:tw-px-12 tw-shrink-0 large:tw-w-160 ">
{# FILTER SECTION #}
<div class="tw-flex tw-justify-end">
<button id="filter-section-hide-button"
class="tw-hidden large:tw-hidden tw-h-24 tw-w-24 -tw-mt-4 -tw-mr-4 -tw-mb-8 tw-text-3xl tw-font-normal tw-text-blue-80 hover:tw-text-blue-20 tw-bg-transparent"
aria-label="{% translate "Close" %}"
tabIndex="0">
<span aria-hidden="true">×</span>
</button>
</div>
<h2 class="large:tw-hidden tw-h1-heading">{% translate "Filter" %}</h2>
<form action="{% pageurl page %}"
method="get"
accept-charset="utf-8"
id="filter-form">
{% include "fragments/research_library_form.html" with form=form %}
<div class=" tw-bg-gray-05 tw-bottom-0 -tw-mx-8 small:-tw-mx-12 medium:-tw-mx-16 large:-tw-mx-12 tw-pb-16 large:tw-pb-12 tw-px-8 small:tw-px-12 medium:tw-px-16 large:tw-px-12 tw-sticky ">
<div class="tw-pt-12 large:tw-pt-8 tw-border-t tw-border-t-gray-20">
<button type="submit" class="tw-w-full tw-btn-primary" form="filter-form">
{% translate "Apply filters" context "Button" %}
</button>
</div>
</div>
</form>
</div>
</div>
{% endblock library_content %}
{% block extra_scripts %}
{{ block.super }}
<script src="{% static "_js/research-hub-library.compiled.js" %}" async defer></script>
{% endblock extra_scripts %}
{% extends "pages/libraries/library_page.html" %}
{% load static breadcrumbs %}
{% block breadcrumbs %}
{% rcc_breadcrumbs %}
{% endblock breadcrumbs %}
{% block search_bar %}
{% include "fragments/libraries/rcc/search_bar.html" %}
{% endblock search_bar %}
{% block detail_pages %}
{% for detail_page in detail_pages %}
<li class="tw-m-0 tw-pt-12 tw-pb-12">
{% include "fragments/libraries/rcc/detail_card.html" with rcc_detail_page=detail_page hide_authors=False hide_image_on_mobile=True hide_related_content_types_on_mobile=True %}
</li>
{% endfor %}
{% endblock detail_pages %}

Просмотреть файл

@ -1,117 +1,18 @@
{% extends "pages/libraries/base.html" %}
{% load i18n static wagtailcore_tags wagtailimages_tags breadcrumbs %}
{% block library_content %}
<div>
{% research_breadcrumbs %}
<h1>{{ page.title }}</h1>
</div>
<div class="large:tw-w-160 large:tw-h-64 tw-mt-12 tw-mb-8 large:tw-mb-0 large:-tw-ml-12 large:tw-p-12 large:tw-bg-gray-05">
{# SEARCH BAR #}
{% comment %}
The page url is necessary in the form to that the filter anchor link is not carried forward.
Say on mobile the user used the "Filter" anchor link to get to the filter section,
that adds the `#filter` part to the URL. Submitting the form in that stage without the
explicit URL would carry the anchor link forward. That would mean the view is scrolled to the
filter section again upon page reload. This seems undesireable.
{% endcomment %}
<form action="{% pageurl page %}"
method="get"
accept-charset="utf-8"
id="search-form">
{% include "fragments/libraries/research_hub/search_bar.html" %}
</form>
</div>
<div class="tw-flex tw-flex-col large:tw-flex-row-reverse large:tw-gap-16">
{# FILTER, SORT AND RESULTS #}
{# For side-by-side layout, we need to pull the results up to that the upper end lines up with the search bar. #}
<div class="tw-min-w-0 tw-grow large:-tw-mt-64 tw-pb-24">
{# SORT AND RESULTS #}
<div class="tw-flex tw-flex-col large:tw-flex-row-reverse large:tw-justify-between tw-gap-12">
<div class="tw-flex tw-flex-row tw-gap-6">
{# FILTER BUTTON #}
<div class="large:tw-hidden tw-basis-1/2">
{% include "fragments/libraries/filter_button.html" with button=False %}
{% include "fragments/libraries/filter_button.html" with button=True %}
</div>
<div class="tw-flex tw-flex-row tw-items-baseline tw-basis-1/2 large:tw-basis-full">
{# SORT SELECT #}
<select id='sort-select'
name="sort"
class="tw-form-control tw-border-gray-40"
form="search-form">
{% for choice in page.SORT_CHOICES.values %}
<option value="{{ choice.value }}" {% if choice == sort %}selected{% endif %}>
{{ choice.label }}
</option>
{% endfor %}
</select>
<noscript>
{# The sort button is only needed for the no JS case. With JS, the form can be submitted on change of the select #}
<button type="submit" class="tw-btn-primary tw-text-base" form="search-form">
{% translate 'Sort' context 'Button' %}
</button>
</noscript>
</div>
</div>
<div class="tw-h4-heading tw-mb-0">
{# RESULTS COUNT #}
{% if search_query %}
{% blocktranslate count counter=research_detail_pages_count trimmed %}
<strong>{{ research_detail_pages_count }}</strong> result for <q>{{ search_query }}</q>
{% plural %}
<strong>{{ research_detail_pages_count }}</strong> results for <q>{{ search_query }}</q>
{% endblocktranslate %}
{% else %}
{% blocktranslate count counter=research_detail_pages_count trimmed %}
<strong>{{ research_detail_pages_count }}</strong> result
{% plural %}
<strong>{{ research_detail_pages_count }}</strong> results
{% endblocktranslate %}
{% endif %}
</div>
</div>
<ul class="tw-list-none tw-mt-16 large:tw-mt-12 tw-mb-12 tw-px-0 tw-border-t tw-border-b tw-border-gray-20 tw-divide-y tw-divide-gray-05">
{# RESULTS LIST #}
{% for research_detail_page in research_detail_pages %}
<li class="tw-m-0 tw-pt-12 tw-pb-12">
{% include "fragments/libraries/research_hub/detail_card.html" with research_detail_page=research_detail_page hide_image_on_mobile=True hide_related_topics_on_mobile=True %}
</li>
{% endfor %}
</ul>
<div>
{# PAGINATION #}
{% include "fragments/pagination.html" with page=research_detail_pages %}
</div>
</div>
<div id="filter"
class=" tw-bg-gray-05 large:tw-block large:tw-mr-0 large:-tw-ml-12 large:tw-overflow-y-clip tw-pt-8 large:tw-pt-0 tw-px-8 small:tw-px-12 medium:tw-px-16 large:tw-px-12 tw-shrink-0 large:tw-w-160 ">
{# FILTER SECTION #}
<div class="tw-flex tw-justify-end">
<button id="filter-section-hide-button"
class="tw-hidden large:tw-hidden tw-h-24 tw-w-24 -tw-mt-4 -tw-mr-4 -tw-mb-8 tw-text-3xl tw-font-normal tw-text-blue-80 hover:tw-text-blue-20 tw-bg-transparent"
aria-label="{% translate "Close" %}"
tabIndex="0">
<span aria-hidden="true" class="">&times;</span>
</button>
</div>
<h2 class="large:tw-hidden tw-h1-heading">{% translate 'Filter' %}</h2>
<form action="{% pageurl page %}"
method="get"
accept-charset="utf-8"
id="filter-form">
{% include "fragments/research_library_form.html" with form=form %}
<div class=" tw-bg-gray-05 tw-bottom-0 -tw-mx-8 small:-tw-mx-12 medium:-tw-mx-16 large:-tw-mx-12 tw-pb-16 large:tw-pb-12 tw-px-8 small:tw-px-12 medium:tw-px-16 large:tw-px-12 tw-sticky ">
<div class="tw-pt-12 large:tw-pt-8 tw-border-t tw-border-t-gray-20">
<button type="submit" class="tw-w-full tw-btn-primary" form="filter-form">
{% translate 'Apply filters' context 'Button' %}
</button>
</div>
</div>
</form>
</div>
</div>
{% endblock library_content %}
{% block extra_scripts %}
{{ block.super }}
<script src="{% static "_js/research-hub-library.compiled.js" %}" async defer></script>
{% endblock extra_scripts %}
{% extends "pages/libraries/library_page.html" %}
{% load static breadcrumbs %}
{% block breadcrumbs %}
{% research_breadcrumbs %}
{% endblock breadcrumbs %}
{% block search_bar %}
{% include "fragments/libraries/research_hub/search_bar.html" %}
{% endblock search_bar %}
{% block detail_pages %}
{% for detail_page in detail_pages %}
<li class="tw-m-0 tw-pt-12 tw-pb-12">
{% include "fragments/libraries/rcc/detail_card.html" with rcc_detail_page=detail_page hide_authors=False hide_image_on_mobile=True hide_related_content_types_on_mobile=True %}
</li>
{% endfor %}
{% endblock detail_pages %}

Просмотреть файл

@ -0,0 +1,142 @@
import typing
from functools import cached_property
from typing import Optional
from django.core import paginator
from django.db import models
from wagtail import images as wagtail_images
from wagtail.admin import panels
from wagtail.images import edit_handlers as image_panels
from wagtail_localize.fields import SynchronizedField, TranslatableField
from networkapi.wagtailpages.pagemodels.base import BasePage
from networkapi.wagtailpages.pagemodels.libraries import constants
if typing.TYPE_CHECKING:
from django import forms, http
from django import template as django_template
from django.db.models.query import QuerySet
class BaseLibraryPage(BasePage):
"""An abstract template for a library page.
To define a concrete library page, subclass it and implement the following attributes:
- `parent_page_types`: Return the parent page types for this page.
- `subpage_types`: Return the subpage types for this page.
- `template`: Return the template to use for this page.
In addition, the following methods have to be implemented:
- `get_form`: Return the form class used to filter detail pages.
- `get_filtered_detail_pages`: Return the article detail pages that match the given filters in the form.
Concrete implementation examples can be found in the RCC and Research_Hub apps.
"""
max_count = 1
template = "pages/libraries/library_page.html"
SORT_CHOICES = constants.SORT_CHOICES
banner_image = models.ForeignKey(
wagtail_images.get_image_model_string(),
null=True,
blank=True,
on_delete=models.SET_NULL,
)
results_count = models.PositiveSmallIntegerField(
default=10,
help_text="Maximum number of results to be displayed per page.",
)
content_panels = BasePage.content_panels + [
image_panels.FieldPanel("banner_image"),
]
settings_panels = BasePage.settings_panels + [panels.FieldPanel("results_count")]
translatable_fields = [
# Content tab fields
TranslatableField("title"),
# Promote tab fields
SynchronizedField("slug"),
TranslatableField("seo_title"),
SynchronizedField("show_in_menus"),
TranslatableField("search_description"),
SynchronizedField("search_image"),
]
class Meta:
abstract = True
@property
def filter_form(self):
"""Form class used to filter detail pages for this page."""
raise NotImplementedError("Please implement this property in your subclass.")
@cached_property
def detail_pages(self):
"""Return the article detail pages that are children of this page."""
raise NotImplementedError("Please implement this property in your subclass.")
@staticmethod
def filter_detail_pages(pages: "QuerySet", filter_form: "forms.Form") -> "QuerySet":
"""Return the article detail pages that match the given filters in the `filter_form`."""
raise NotImplementedError("Please implement this method in your subclass.")
def get_sorted_filtered_detail_pages(
self,
*,
filter_form: Optional["forms.Form"] = None,
sort: constants.SortOption = constants.SORT_NEWEST_FIRST,
search_query: Optional[str] = None,
) -> "QuerySet":
"""Get sorted article detail pages filtered by the form options and search parameters."""
detail_pages = self.detail_pages
if filter_form:
detail_pages = self.filter_detail_pages(detail_pages, filter_form)
detail_pages = detail_pages.order_by(sort.order_by_value)
if search_query:
detail_pages = detail_pages.search(
search_query,
order_by_relevance=False, # To preserve original ordering
)
return detail_pages
def get_context(self, request: "http.HttpRequest") -> "django_template.Context":
search_query: str = request.GET.get("search", "")
sort_value: str = request.GET.get("sort", "")
sort: constants.SortOption = constants.SORT_CHOICES.get(sort_value, constants.SORT_NEWEST_FIRST)
Form = self.filter_form
filter_form = Form(request.GET, label_suffix="")
sorted_and_searched_and_filtered_detail_pages = self.get_sorted_filtered_detail_pages(
filter_form=filter_form, search_query=search_query, sort=sort
)
detail_pages_paginator = paginator.Paginator(
object_list=sorted_and_searched_and_filtered_detail_pages,
per_page=self.results_count,
allow_empty_first_page=True,
)
page: Optional[str] = request.GET.get("page")
detail_pages_page = detail_pages_paginator.get_page(page)
context: "django_template.Context" = super().get_context(request)
context["search_query"] = search_query
context["sort"] = sort
context["form"] = filter_form
context["detail_pages_count"] = detail_pages_paginator.count
context["detail_pages"] = detail_pages_page
return context
def get_banner(self):
return self.banner_image

Просмотреть файл

@ -9,11 +9,8 @@ from wagtail_localize.fields import SynchronizedField, TranslatableField
from networkapi.wagtailpages import utils
from networkapi.wagtailpages.pagemodels import profiles
from networkapi.wagtailpages.pagemodels.base import BasePage
from networkapi.wagtailpages.pagemodels.libraries.rcc import (
constants,
detail_page,
library_page,
)
from networkapi.wagtailpages.pagemodels.libraries import constants as base_constants
from networkapi.wagtailpages.pagemodels.libraries.rcc import detail_page, library_page
class RCCAuthorsIndexPage(
@ -94,7 +91,7 @@ class RCCAuthorsIndexPage(
def get_latest_author_rcc_entries(self, author_profile):
author_articles = self.get_author_rcc_entries(author_profile)
author_articles = author_articles.order_by("-original_publication_date")
latest_articles = author_articles[: constants.LATEST_ARTICLES_COUNT]
latest_articles = author_articles[: base_constants.LATEST_ARTICLES_COUNT]
return latest_articles
def get_author_rcc_entries_count(self, author_profile):

Просмотреть файл

@ -1,134 +1,59 @@
import typing
from typing import Optional
from functools import cached_property
from django.core import paginator
from django.db import models
from wagtail import images as wagtail_images
from wagtail import models as wagtail_models
from wagtail.admin import panels
from wagtail.images import edit_handlers as image_panels
from wagtail_localize.fields import SynchronizedField, TranslatableField
from networkapi.wagtailpages import utils
from networkapi.wagtailpages.pagemodels import profiles as profile_models
from networkapi.wagtailpages.pagemodels.base import BasePage
from networkapi.wagtailpages.pagemodels.libraries.rcc import (
constants,
detail_page,
taxonomies,
from networkapi.wagtailpages.pagemodels.libraries import (
library_page as base_library_page,
)
from networkapi.wagtailpages.pagemodels.libraries.rcc import detail_page, taxonomies
from networkapi.wagtailpages.pagemodels.libraries.rcc.forms import (
RCCLibraryPageFilterForm,
)
if typing.TYPE_CHECKING:
from django import http
from django import template as django_template
from django import forms
from django.db.models.query import QuerySet
class RCCLibraryPage(BasePage):
max_count = 1
class RCCLibraryPage(base_library_page.BaseLibraryPage):
parent_page_types = ["RCCLandingPage"]
subpage_types = ["RCCDetailPage"]
template = "pages/libraries/rcc/library_page.html"
SORT_CHOICES = constants.SORT_CHOICES
@property
def filter_form(self) -> "forms.Form":
"""Form class used to filter detail pages for this page."""
return RCCLibraryPageFilterForm
banner_image = models.ForeignKey(
wagtail_images.get_image_model_string(),
null=True,
blank=True,
on_delete=models.SET_NULL,
)
results_count = models.PositiveSmallIntegerField(
default=10,
help_text="Maximum number of results to be displayed per page.",
)
content_panels = BasePage.content_panels + [
image_panels.FieldPanel("banner_image"),
]
settings_panels = BasePage.settings_panels + [panels.FieldPanel("results_count")]
translatable_fields = [
# Content tab fields
TranslatableField("title"),
# Promote tab fields
SynchronizedField("slug"),
TranslatableField("seo_title"),
SynchronizedField("show_in_menus"),
TranslatableField("search_description"),
SynchronizedField("search_image"),
]
def get_context(self, request: "http.HttpRequest") -> "django_template.Context":
search_query: str = request.GET.get("search", "")
sort_value: str = request.GET.get("sort", "")
sort: constants.SortOption = constants.SORT_CHOICES.get(sort_value, constants.SORT_NEWEST_FIRST)
filter_form = RCCLibraryPageFilterForm(request.GET, label_suffix="")
@cached_property
def detail_pages(self) -> "QuerySet[detail_page.RCCDetailPage]":
"""Return the article detail pages that are children of this page."""
return detail_page.RCCDetailPage.objects.live().public().filter(locale=wagtail_models.Locale.get_active())
@staticmethod
def filter_detail_pages(
pages: "QuerySet[detail_page.RCCDetailPage]", filter_form: "forms.Form"
) -> "QuerySet[detail_page.RCCDetailPage]":
"""Return the article detail pages that match the given filters in the form."""
if filter_form.is_valid():
filtered_author_ids: list[int] = filter_form.cleaned_data["authors"]
filtered_content_type_ids: list[int] = filter_form.cleaned_data["content_types"]
filtered_curricular_area_ids: list[int] = filter_form.cleaned_data["curricular_areas"]
filtered_topic_ids: list[int] = filter_form.cleaned_data["topics"]
author_profile_ids: list[int] = filter_form.cleaned_data["authors"]
content_type_ids: list[int] = filter_form.cleaned_data["content_types"]
curricular_area_ids: list[int] = filter_form.cleaned_data["curricular_areas"]
topic_ids: list[int] = filter_form.cleaned_data["topics"]
else:
# If the form is not valid, we will not filter by any of the values.
# This will result in all articles being displayed.
filtered_author_ids = []
filtered_content_type_ids = []
filtered_curricular_area_ids = []
filtered_topic_ids = []
author_profile_ids = []
content_type_ids = []
curricular_area_ids = []
topic_ids = []
searched_and_filtered_rcc_detail_pages = self._get_rcc_detail_pages(
search=search_query,
sort=sort,
author_profile_ids=filtered_author_ids,
content_type_ids=filtered_content_type_ids,
curricular_area_ids=filtered_curricular_area_ids,
topic_ids=filtered_topic_ids,
)
rcc_detail_pages_paginator = paginator.Paginator(
object_list=searched_and_filtered_rcc_detail_pages,
per_page=self.results_count,
allow_empty_first_page=True,
)
page: Optional[str] = request.GET.get("page")
rcc_detail_pages_page = rcc_detail_pages_paginator.get_page(page)
context: "django_template.Context" = super().get_context(request)
context["search_query"] = search_query
context["sort"] = sort
context["form"] = filter_form
context["rcc_detail_pages_count"] = rcc_detail_pages_paginator.count
context["rcc_detail_pages"] = rcc_detail_pages_page
return context
def _get_rcc_detail_pages(
self,
*,
search: str = "",
sort: constants.SortOption = constants.SORT_NEWEST_FIRST,
author_profile_ids: Optional[list[int]] = None,
content_type_ids: Optional[list[int]] = None,
curricular_area_ids: Optional[list[int]] = None,
topic_ids: Optional[list[int]] = None,
):
author_profile_ids = author_profile_ids or []
content_type_ids = content_type_ids or []
curricular_area_ids = curricular_area_ids or []
topic_ids = topic_ids or []
rcc_detail_pages = detail_page.RCCDetailPage.objects.live().public()
rcc_detail_pages = rcc_detail_pages.filter(locale=wagtail_models.Locale.get_active())
rcc_detail_pages = pages
author_profiles = utils.get_rcc_authors(profile_models.Profile.objects.all())
author_profiles = author_profiles.filter(id__in=author_profile_ids)
@ -157,15 +82,4 @@ class RCCLibraryPage(BasePage):
for topic in topics:
rcc_detail_pages = rcc_detail_pages.filter(related_topics__topic__translation_key=topic.translation_key)
rcc_detail_pages = rcc_detail_pages.order_by(sort.order_by_value)
if search:
rcc_detail_pages = rcc_detail_pages.search(
search,
order_by_relevance=False, # To preserve original ordering
)
return rcc_detail_pages
def get_banner(self):
return self.banner_image

Просмотреть файл

@ -9,8 +9,8 @@ from wagtail_localize.fields import SynchronizedField, TranslatableField
from networkapi.wagtailpages import utils
from networkapi.wagtailpages.pagemodels import profiles
from networkapi.wagtailpages.pagemodels.base import BasePage
from networkapi.wagtailpages.pagemodels.libraries import constants as base_constants
from networkapi.wagtailpages.pagemodels.libraries.research_hub import (
constants,
detail_page,
library_page,
)
@ -95,7 +95,7 @@ class ResearchAuthorsIndexPage(
def get_latest_author_research(self, author_profile):
author_research = self.get_author_research(author_profile)
author_research = author_research.order_by("-original_publication_date")
latest_research = author_research[: constants.LATEST_RESEARCH_COUNT_LIMIT]
latest_research = author_research[: base_constants.LATEST_ARTICLES_COUNT]
return latest_research
def get_author_research_count(self, author_profile):

Просмотреть файл

@ -1,36 +0,0 @@
import collections
from django.utils.translation import gettext_lazy as _
# We don't want to expose the actual database column value that we use for sorting.
# Therefore, we need a separate value that is used in the form and url.
SortOption = collections.namedtuple("SortOption", ["label", "value", "order_by_value"])
SORT_NEWEST_FIRST = SortOption(
label=_("Newest first"),
value="newest-first",
order_by_value="-original_publication_date",
)
SORT_OLDEST_FIRST = SortOption(
label=_("Oldest first"),
value="oldest-first",
order_by_value="original_publication_date",
)
SORT_ALPHABETICAL = SortOption(
label=_("Alphabetical (A-Z)"),
value="alphabetical",
order_by_value="title",
)
SORT_ALPHABETICAL_REVERSED = SortOption(
label=_("Alphabetical (Z-A)"),
value="alphabetical-reversed",
order_by_value="-title",
)
SORT_CHOICES = {
SORT_NEWEST_FIRST.value: SORT_NEWEST_FIRST,
SORT_OLDEST_FIRST.value: SORT_OLDEST_FIRST,
SORT_ALPHABETICAL.value: SORT_ALPHABETICAL,
SORT_ALPHABETICAL_REVERSED.value: SORT_ALPHABETICAL_REVERSED,
}
LATEST_RESEARCH_COUNT_LIMIT = 3

Просмотреть файл

@ -48,7 +48,7 @@ class ResearchLibraryPageFilterForm(forms.Form):
choices=_get_topic_options,
label=pgettext_lazy("Filter form field label", "Topics"),
)
years = forms.ChoiceField(
year = forms.ChoiceField(
required=False,
choices=_get_year_options,
widget=forms.RadioSelect(attrs={"class": "rh-radio"}),

Просмотреть файл

@ -1,19 +1,15 @@
import typing
from functools import cached_property
from typing import Optional
from django.core import paginator
from django.db import models
from wagtail import images as wagtail_images
from wagtail import models as wagtail_models
from wagtail.admin import panels
from wagtail.images import edit_handlers as image_panels
from wagtail_localize.fields import SynchronizedField, TranslatableField
from networkapi.wagtailpages import utils
from networkapi.wagtailpages.pagemodels import profiles as profile_models
from networkapi.wagtailpages.pagemodels.base import BasePage
from networkapi.wagtailpages.pagemodels.libraries import (
library_page as base_library_page,
)
from networkapi.wagtailpages.pagemodels.libraries.research_hub import (
constants,
detail_page,
taxonomies,
)
@ -22,110 +18,46 @@ from networkapi.wagtailpages.pagemodels.libraries.research_hub.forms import (
)
if typing.TYPE_CHECKING:
from django import http
from django import template as django_template
from django import forms
from django.db.models.query import QuerySet
class ResearchLibraryPage(BasePage):
max_count = 1
class ResearchLibraryPage(base_library_page.BaseLibraryPage):
parent_page_types = ["ResearchLandingPage"]
subpage_types = ["ResearchDetailPage"]
template = "pages/libraries/research_hub/library_page.html"
SORT_CHOICES = constants.SORT_CHOICES
@property
def filter_form(self) -> "forms.Form":
"""Form class used to filter detail pages for this page."""
return ResearchLibraryPageFilterForm
banner_image = models.ForeignKey(
wagtail_images.get_image_model_string(),
null=True,
blank=True,
on_delete=models.SET_NULL,
)
@cached_property
def detail_pages(self) -> "QuerySet[detail_page.ResearchDetailPage]":
"""Return the article detail pages that are children of this page."""
return detail_page.ResearchDetailPage.objects.live().public().filter(locale=wagtail_models.Locale.get_active())
results_count = models.PositiveSmallIntegerField(
default=10,
help_text="Maximum number of results to be displayed per page.",
)
content_panels = BasePage.content_panels + [
image_panels.FieldPanel("banner_image"),
]
settings_panels = BasePage.settings_panels + [panels.FieldPanel("results_count")]
translatable_fields = [
# Content tab fields
TranslatableField("title"),
# Promote tab fields
SynchronizedField("slug"),
TranslatableField("seo_title"),
SynchronizedField("show_in_menus"),
TranslatableField("search_description"),
SynchronizedField("search_image"),
]
def get_context(self, request: "http.HttpRequest") -> "django_template.Context":
search_query: str = request.GET.get("search", "")
sort_value: str = request.GET.get("sort", "")
sort: constants.SortOption = constants.SORT_CHOICES.get(sort_value, constants.SORT_NEWEST_FIRST)
filter_form = ResearchLibraryPageFilterForm(request.GET, label_suffix="")
@staticmethod
def filter_detail_pages(
pages: "QuerySet[detail_page.ResearchDetailPage]", filter_form: "forms.Form"
) -> "QuerySet[detail_page.ResearchDetailPage]":
"""Return the article detail pages that match the given filters in the form."""
if filter_form.is_valid():
filtered_author_ids: list[int] = filter_form.cleaned_data["authors"]
filtered_topic_ids: list[int] = filter_form.cleaned_data["topics"]
filtered_region_ids: list[int] = filter_form.cleaned_data["regions"]
filtered_year: Optional[int] = filter_form.cleaned_data["years"]
author_profile_ids: list[int] = filter_form.cleaned_data["authors"]
topic_ids: list[int] = filter_form.cleaned_data["topics"]
region_ids: list[int] = filter_form.cleaned_data["regions"]
year: Optional[int] = filter_form.cleaned_data["year"]
else:
# If the form is not valid, we will not filter by any of the values.
# This will result in all research being displayed.
filtered_author_ids = []
filtered_topic_ids = []
filtered_region_ids = []
filtered_year = None
author_profile_ids = []
topic_ids = []
region_ids = []
year = None
searched_and_filtered_research_detail_pages = self._get_research_detail_pages(
search=search_query,
sort=sort,
author_profile_ids=filtered_author_ids,
topic_ids=filtered_topic_ids,
region_ids=filtered_region_ids,
year=filtered_year,
)
research_detail_pages_paginator = paginator.Paginator(
object_list=searched_and_filtered_research_detail_pages,
per_page=self.results_count,
allow_empty_first_page=True,
)
page: Optional[str] = request.GET.get("page")
research_detail_pages_page = research_detail_pages_paginator.get_page(page)
context: "django_template.Context" = super().get_context(request)
context["search_query"] = search_query
context["sort"] = sort
context["form"] = filter_form
context["research_detail_pages_count"] = research_detail_pages_paginator.count
context["research_detail_pages"] = research_detail_pages_page
return context
def _get_research_detail_pages(
self,
*,
search: str = "",
sort: constants.SortOption = constants.SORT_NEWEST_FIRST,
author_profile_ids: Optional[list[int]] = None,
topic_ids: Optional[list[int]] = None,
region_ids: Optional[list[int]] = None,
year: Optional[int] = None,
):
author_profile_ids = author_profile_ids or []
topic_ids = topic_ids or []
region_ids = region_ids or []
research_detail_pages = detail_page.ResearchDetailPage.objects.live().public()
research_detail_pages = research_detail_pages.filter(locale=wagtail_models.Locale.get_active())
research_detail_pages = pages
author_profiles = utils.get_research_authors(profile_models.Profile.objects.all())
author_profiles = author_profiles.filter(id__in=author_profile_ids)
@ -153,15 +85,4 @@ class ResearchLibraryPage(BasePage):
if year:
research_detail_pages = research_detail_pages.filter(original_publication_date__year=year)
research_detail_pages = research_detail_pages.order_by(sort.order_by_value)
if search:
research_detail_pages = research_detail_pages.search(
search,
order_by_relevance=False, # To preserve original ordering
)
return research_detail_pages
def get_banner(self):
return self.banner_image

Просмотреть файл

@ -11,7 +11,10 @@ from networkapi.wagtailpages.factory.libraries.rcc import relations as relations
from networkapi.wagtailpages.factory.libraries.rcc import (
taxonomies as taxonomies_factory,
)
from networkapi.wagtailpages.pagemodels.libraries.rcc import constants
from networkapi.wagtailpages.pagemodels.libraries import constants
from networkapi.wagtailpages.pagemodels.libraries.rcc.forms import (
RCCLibraryPageFilterForm,
)
from networkapi.wagtailpages.tests.libraries.rcc import base as rcc_test_base
from networkapi.wagtailpages.tests.libraries.rcc import utils as rcc_test_utils
@ -21,7 +24,7 @@ class TestRCCLibraryPage(rcc_test_base.RCCTestCase):
with open(os.devnull, "w") as f:
management.call_command("update_index", verbosity=0, stdout=f)
def test_get_rcc_detail_pages(self):
def testget_sorted_filtered_detail_pages(self):
detail_page_1 = detail_page_factory.RCCDetailPageFactory(
parent=self.library_page,
)
@ -29,13 +32,13 @@ class TestRCCLibraryPage(rcc_test_base.RCCTestCase):
parent=self.library_page,
)
rcc_detail_pages = self.library_page._get_rcc_detail_pages()
rcc_detail_pages = self.library_page.get_sorted_filtered_detail_pages()
self.assertEqual(len(rcc_detail_pages), 2)
self.assertIn(detail_page_1, rcc_detail_pages)
self.assertIn(detail_page_2, rcc_detail_pages)
def test_get_rcc_detail_pages_with_translation_aliases(self):
def testget_sorted_filtered_detail_pages_with_translation_aliases(self):
detail_page_1 = detail_page_factory.RCCDetailPageFactory(
parent=self.library_page,
)
@ -46,7 +49,7 @@ class TestRCCLibraryPage(rcc_test_base.RCCTestCase):
fr_detail_page_1 = detail_page_1.get_translation(self.fr_locale)
fr_detail_page_2 = detail_page_2.get_translation(self.fr_locale)
rcc_detail_pages = self.library_page._get_rcc_detail_pages()
rcc_detail_pages = self.library_page.get_sorted_filtered_detail_pages()
self.assertEqual(len(rcc_detail_pages), 2)
self.assertIn(detail_page_1, rcc_detail_pages)
@ -63,7 +66,7 @@ class TestRCCLibraryPage(rcc_test_base.RCCTestCase):
)
self.make_page_private(private_detail_page)
rcc_detail_pages = self.library_page._get_rcc_detail_pages()
rcc_detail_pages = self.library_page.get_sorted_filtered_detail_pages()
self.assertEqual(len(rcc_detail_pages), 1)
self.assertIn(public_detail_page, rcc_detail_pages)
self.assertNotIn(private_detail_page, rcc_detail_pages)
@ -78,7 +81,7 @@ class TestRCCLibraryPage(rcc_test_base.RCCTestCase):
original_publication_date=rcc_test_utils.days_ago(1),
)
rcc_detail_pages = list(self.library_page._get_rcc_detail_pages(sort=constants.SORT_NEWEST_FIRST))
rcc_detail_pages = list(self.library_page.get_sorted_filtered_detail_pages(sort=constants.SORT_NEWEST_FIRST))
newest_page_index = rcc_detail_pages.index(newest_page)
oldest_page_index = rcc_detail_pages.index(oldest_page)
@ -94,7 +97,7 @@ class TestRCCLibraryPage(rcc_test_base.RCCTestCase):
original_publication_date=rcc_test_utils.days_ago(1),
)
rcc_detail_pages = list(self.library_page._get_rcc_detail_pages(sort=constants.SORT_OLDEST_FIRST))
rcc_detail_pages = list(self.library_page.get_sorted_filtered_detail_pages(sort=constants.SORT_OLDEST_FIRST))
newest_page_index = rcc_detail_pages.index(newest_page)
oldest_page_index = rcc_detail_pages.index(oldest_page)
@ -110,7 +113,7 @@ class TestRCCLibraryPage(rcc_test_base.RCCTestCase):
title="Banana",
)
rcc_detail_pages = list(self.library_page._get_rcc_detail_pages(sort=constants.SORT_ALPHABETICAL))
rcc_detail_pages = list(self.library_page.get_sorted_filtered_detail_pages(sort=constants.SORT_ALPHABETICAL))
apple_page_index = rcc_detail_pages.index(apple_page)
banana_page_index = rcc_detail_pages.index(banana_page)
@ -127,13 +130,15 @@ class TestRCCLibraryPage(rcc_test_base.RCCTestCase):
title="Banana",
)
rcc_detail_pages = list(self.library_page._get_rcc_detail_pages(sort=constants.SORT_ALPHABETICAL_REVERSED))
rcc_detail_pages = list(
self.library_page.get_sorted_filtered_detail_pages(sort=constants.SORT_ALPHABETICAL_REVERSED)
)
apple_page_index = rcc_detail_pages.index(apple_page)
banana_page_index = rcc_detail_pages.index(banana_page)
self.assertLess(banana_page_index, apple_page_index)
def test_get_rcc_detail_pages_sort_default(self):
def testget_sorted_filtered_detail_pages_sort_default(self):
detail_page_factory.RCCDetailPageFactory(
parent=self.library_page,
@ -144,8 +149,10 @@ class TestRCCLibraryPage(rcc_test_base.RCCTestCase):
original_publication_date=rcc_test_utils.days_ago(1),
)
default_sort_detail_pages = list(self.library_page._get_rcc_detail_pages())
newest_first_detail_pages = list(self.library_page._get_rcc_detail_pages(sort=constants.SORT_NEWEST_FIRST))
default_sort_detail_pages = list(self.library_page.get_sorted_filtered_detail_pages())
newest_first_detail_pages = list(
self.library_page.get_sorted_filtered_detail_pages(sort=constants.SORT_NEWEST_FIRST)
)
self.assertEqual(default_sort_detail_pages, newest_first_detail_pages)
@ -155,7 +162,7 @@ class TestRCCLibraryPage(rcc_test_base.RCCTestCase):
for _ in range(6):
detail_page_factory.RCCDetailPageFactory(parent=self.library_page)
rcc_detail_pages = self.library_page._get_rcc_detail_pages()
rcc_detail_pages = self.library_page.get_sorted_filtered_detail_pages()
rcc_detail_pages_paginator = paginator.Paginator(
object_list=rcc_detail_pages,
@ -189,7 +196,7 @@ class TestRCCLibraryPageSearch(TestRCCLibraryPage):
collaborators="",
)
rcc_detail_pages = self.library_page._get_rcc_detail_pages(search="Apple")
rcc_detail_pages = self.library_page.get_sorted_filtered_detail_pages(search_query="Apple")
self.assertEqual(len(rcc_detail_pages), 1)
self.assertIn(apple_page, rcc_detail_pages)
self.assertNotIn(banana_page, rcc_detail_pages)
@ -210,7 +217,7 @@ class TestRCCLibraryPageSearch(TestRCCLibraryPage):
collaborators="",
)
rcc_detail_pages = self.library_page._get_rcc_detail_pages(search="Apple")
rcc_detail_pages = self.library_page.get_sorted_filtered_detail_pages(search_query="Apple")
self.assertEqual(len(rcc_detail_pages), 1)
self.assertIn(apple_page, rcc_detail_pages)
@ -232,7 +239,7 @@ class TestRCCLibraryPageSearch(TestRCCLibraryPage):
collaborators="",
)
rcc_detail_pages = self.library_page._get_rcc_detail_pages(search="Apple")
rcc_detail_pages = self.library_page.get_sorted_filtered_detail_pages(search_query="Apple")
self.assertEqual(len(rcc_detail_pages), 1)
self.assertIn(apple_page, rcc_detail_pages)
@ -254,7 +261,7 @@ class TestRCCLibraryPageSearch(TestRCCLibraryPage):
collaborators="Banana",
)
rcc_detail_pages = self.library_page._get_rcc_detail_pages(search="Apple")
rcc_detail_pages = self.library_page.get_sorted_filtered_detail_pages(search_query="Apple")
self.assertEqual(len(rcc_detail_pages), 1)
self.assertIn(apple_page, rcc_detail_pages)
@ -290,7 +297,7 @@ class TestRCCLibraryPageSearch(TestRCCLibraryPage):
relations_factory.RCCAuthorRelationFactory(detail_page=banana_page, author_profile=banana_profile)
self.update_index()
rcc_detail_pages = self.library_page._get_rcc_detail_pages(search="Apple")
rcc_detail_pages = self.library_page.get_sorted_filtered_detail_pages(search_query="Apple")
self.assertEqual(len(rcc_detail_pages), 1)
self.assertIn(apple_page, rcc_detail_pages)
@ -326,7 +333,7 @@ class TestRCCLibraryPageSearch(TestRCCLibraryPage):
)
self.update_index()
rcc_detail_pages = self.library_page._get_rcc_detail_pages(search="Apple")
rcc_detail_pages = self.library_page.get_sorted_filtered_detail_pages(search_query="Apple")
self.assertEqual(len(rcc_detail_pages), 1)
self.assertIn(apple_page, rcc_detail_pages)
@ -362,7 +369,7 @@ class TestRCCLibraryPageSearch(TestRCCLibraryPage):
)
self.update_index()
rcc_detail_pages = self.library_page._get_rcc_detail_pages(search="Apple")
rcc_detail_pages = self.library_page.get_sorted_filtered_detail_pages(search_query="Apple")
self.assertEqual(len(rcc_detail_pages), 1)
self.assertIn(apple_page, rcc_detail_pages)
@ -394,7 +401,7 @@ class TestRCCLibraryPageSearch(TestRCCLibraryPage):
relations_factory.RCCDetailPageRCCTopicRelationFactory(detail_page=banana_page, topic=banana_topic)
self.update_index()
rcc_detail_pages = self.library_page._get_rcc_detail_pages(search="Apple")
rcc_detail_pages = self.library_page.get_sorted_filtered_detail_pages(search_query="Apple")
self.assertEqual(len(rcc_detail_pages), 1)
self.assertIn(apple_page, rcc_detail_pages)
@ -415,7 +422,8 @@ class TestRCCLibraryPageFilters(TestRCCLibraryPage):
detail_page_2.authors.first().author_profile,
)
rcc_detail_pages = self.library_page._get_rcc_detail_pages(author_profile_ids=[author_profile.id])
filter_form = RCCLibraryPageFilterForm(data={"authors": [author_profile.id]})
rcc_detail_pages = self.library_page.get_sorted_filtered_detail_pages(filter_form=filter_form)
self.assertIn(detail_page_1, rcc_detail_pages)
self.assertNotIn(detail_page_2, rcc_detail_pages)
@ -441,7 +449,7 @@ class TestRCCLibraryPageFilters(TestRCCLibraryPage):
)
# Only show the page where both profiles are authors
rcc_detail_pages = response.context["rcc_detail_pages"]
rcc_detail_pages = response.context["detail_pages"]
self.assertNotIn(detail_page_1, rcc_detail_pages)
self.assertIn(detail_page_2, rcc_detail_pages)
@ -474,7 +482,8 @@ class TestRCCLibraryPageFilters(TestRCCLibraryPage):
self.assertEqual(profile.translation_key, profile_fr.translation_key)
translation.activate(self.fr_locale.language_code)
rcc_detail_pages = self.library_page.localized._get_rcc_detail_pages(author_profile_ids=[profile_fr.id])
filter_form = RCCLibraryPageFilterForm(data={"authors": [profile_fr.id]})
rcc_detail_pages = self.library_page.get_sorted_filtered_detail_pages(filter_form=filter_form)
self.assertIn(detail_page_1_fr, rcc_detail_pages)
self.assertIn(detail_page_2_fr, rcc_detail_pages)
@ -493,7 +502,8 @@ class TestRCCLibraryPageFilters(TestRCCLibraryPage):
related_content_types__content_type=content_type_B,
)
rcc_detail_pages = self.library_page._get_rcc_detail_pages(content_type_ids=[content_type_A.id])
filter_form = RCCLibraryPageFilterForm(data={"content_types": [content_type_A.id]})
rcc_detail_pages = self.library_page.get_sorted_filtered_detail_pages(filter_form=filter_form)
self.assertIn(detail_page_A, rcc_detail_pages)
self.assertNotIn(detail_page_B, rcc_detail_pages)
@ -510,7 +520,8 @@ class TestRCCLibraryPageFilters(TestRCCLibraryPage):
related_curricular_areas__curricular_area=curricular_area_B,
)
rcc_detail_pages = self.library_page._get_rcc_detail_pages(curricular_area_ids=[curricular_area_A.id])
filter_form = RCCLibraryPageFilterForm(data={"curricular_areas": [curricular_area_A.id]})
rcc_detail_pages = self.library_page.get_sorted_filtered_detail_pages(filter_form=filter_form)
self.assertIn(detail_page_A, rcc_detail_pages)
self.assertNotIn(detail_page_B, rcc_detail_pages)
@ -527,7 +538,8 @@ class TestRCCLibraryPageFilters(TestRCCLibraryPage):
related_topics__topic=topic_B,
)
rcc_detail_pages = self.library_page._get_rcc_detail_pages(topic_ids=[topic_A.id])
filter_form = RCCLibraryPageFilterForm(data={"topics": [topic_A.id]})
rcc_detail_pages = self.library_page.get_sorted_filtered_detail_pages(filter_form=filter_form)
self.assertIn(detail_page_A, rcc_detail_pages)
self.assertNotIn(detail_page_B, rcc_detail_pages)
@ -547,9 +559,8 @@ class TestRCCLibraryPageFilters(TestRCCLibraryPage):
related_content_types__content_type=content_type_A,
)
rcc_detail_pages = self.library_page._get_rcc_detail_pages(
content_type_ids=[content_type_A.id, content_type_B.id]
)
filter_form = RCCLibraryPageFilterForm(data={"content_types": [content_type_A.id, content_type_B.id]})
rcc_detail_pages = self.library_page.get_sorted_filtered_detail_pages(filter_form=filter_form)
self.assertIn(detail_page_1, rcc_detail_pages)
self.assertNotIn(detail_page_2, rcc_detail_pages)
@ -569,9 +580,8 @@ class TestRCCLibraryPageFilters(TestRCCLibraryPage):
related_curricular_areas__curricular_area=curricular_area_A,
)
rcc_detail_pages = self.library_page._get_rcc_detail_pages(
curricular_area_ids=[curricular_area_A.id, curricular_area_B.id]
)
filter_form = RCCLibraryPageFilterForm(data={"curricular_areas": [curricular_area_A.id, curricular_area_B.id]})
rcc_detail_pages = self.library_page.get_sorted_filtered_detail_pages(filter_form=filter_form)
self.assertIn(detail_page_1, rcc_detail_pages)
self.assertNotIn(detail_page_2, rcc_detail_pages)
@ -589,7 +599,8 @@ class TestRCCLibraryPageFilters(TestRCCLibraryPage):
related_topics__topic=topic_A,
)
rcc_detail_pages = self.library_page._get_rcc_detail_pages(topic_ids=[topic_A.id, topic_B.id])
filter_form = RCCLibraryPageFilterForm(data={"topics": [topic_A.id, topic_B.id]})
rcc_detail_pages = self.library_page.get_sorted_filtered_detail_pages(filter_form=filter_form)
self.assertIn(detail_page_1, rcc_detail_pages)
self.assertNotIn(detail_page_2, rcc_detail_pages)
@ -631,7 +642,8 @@ class TestRCCLibraryPageFilters(TestRCCLibraryPage):
translation.activate(self.fr_locale.language_code)
# Filter for the translated content type
rcc_detail_pages = self.library_page.localized._get_rcc_detail_pages(content_type_ids=[content_type_fr.id])
filter_form = RCCLibraryPageFilterForm(data={"content_types": [content_type_fr.id]})
rcc_detail_pages = self.library_page.get_sorted_filtered_detail_pages(filter_form=filter_form)
# We should see both pages, even though the first one is not associated with the
# translated content type
@ -678,9 +690,8 @@ class TestRCCLibraryPageFilters(TestRCCLibraryPage):
translation.activate(self.fr_locale.language_code)
# Filter for the translated curricular area
rcc_detail_pages = self.library_page.localized._get_rcc_detail_pages(
curricular_area_ids=[curricular_area_fr.id]
)
filter_form = RCCLibraryPageFilterForm(data={"curricular_area": [curricular_area_fr.id]})
rcc_detail_pages = self.library_page.get_sorted_filtered_detail_pages(filter_form=filter_form)
# We should see both pages, even though the first one is not associated with the
# translated curricular area
@ -719,7 +730,8 @@ class TestRCCLibraryPageFilters(TestRCCLibraryPage):
self.assertEqual(topic.translation_key, topic_fr.translation_key)
translation.activate(self.fr_locale.language_code)
rcc_detail_pages = self.library_page.localized._get_rcc_detail_pages(topic_ids=[topic_fr.id])
filter_form = RCCLibraryPageFilterForm(data={"topics": [topic_fr.id]})
rcc_detail_pages = self.library_page.get_sorted_filtered_detail_pages(filter_form=filter_form)
self.assertEqual(len(rcc_detail_pages), 2)
self.assertIn(detail_page_1_fr, rcc_detail_pages)

Просмотреть файл

@ -216,7 +216,7 @@ class ResearchLibraryPageFilterFormTestCase(research_test_base.ResearchHubTestCa
self.assertCountEqual(form.fields["topics"].choices, [(t.id, t.name) for t in topics])
def test_form_years(self):
"""Test that the form years field is populated with the correct choices."""
"""Test that the form year field is populated with the correct choices."""
years = [timezone.now().year, timezone.now().year - 1]
detail_page_factory.ResearchDetailPageFactory(
parent=self.library_page,
@ -228,7 +228,7 @@ class ResearchLibraryPageFilterFormTestCase(research_test_base.ResearchHubTestCa
)
form = ResearchLibraryPageFilterForm()
self.assertCountEqual(form.fields["years"].choices, [("", "Any")] + [(y, y) for y in years])
self.assertCountEqual(form.fields["year"].choices, [("", "Any")] + [(y, y) for y in years])
def test_form_regions(self):
"""Test that the form regions field is populated with the correct choices."""

Просмотреть файл

@ -14,7 +14,10 @@ from networkapi.wagtailpages.factory.libraries.research_hub import (
from networkapi.wagtailpages.factory.libraries.research_hub import (
taxonomies as taxonomies_factory,
)
from networkapi.wagtailpages.pagemodels.libraries.research_hub import constants
from networkapi.wagtailpages.pagemodels.libraries import constants
from networkapi.wagtailpages.pagemodels.libraries.research_hub.forms import (
ResearchLibraryPageFilterForm,
)
from networkapi.wagtailpages.tests.libraries.research_hub import (
base as research_test_base,
)
@ -36,7 +39,7 @@ class TestResearchLibraryPage(research_test_base.ResearchHubTestCase):
parent=self.library_page,
)
research_detail_pages = self.library_page._get_research_detail_pages()
research_detail_pages = self.library_page.get_sorted_filtered_detail_pages()
self.assertEqual(len(research_detail_pages), 2)
self.assertIn(detail_page_1, research_detail_pages)
@ -53,7 +56,7 @@ class TestResearchLibraryPage(research_test_base.ResearchHubTestCase):
fr_detail_page_1 = detail_page_1.get_translation(self.fr_locale)
fr_detail_page_2 = detail_page_2.get_translation(self.fr_locale)
research_detail_pages = self.library_page._get_research_detail_pages()
research_detail_pages = self.library_page.get_sorted_filtered_detail_pages()
self.assertEqual(len(research_detail_pages), 2)
self.assertIn(detail_page_1, research_detail_pages)
@ -70,7 +73,7 @@ class TestResearchLibraryPage(research_test_base.ResearchHubTestCase):
)
self.make_page_private(private_detail_page)
research_detail_pages = self.library_page._get_research_detail_pages()
research_detail_pages = self.library_page.get_sorted_filtered_detail_pages()
self.assertEqual(len(research_detail_pages), 1)
self.assertIn(public_detail_page, research_detail_pages)
self.assertNotIn(private_detail_page, research_detail_pages)
@ -85,7 +88,9 @@ class TestResearchLibraryPage(research_test_base.ResearchHubTestCase):
original_publication_date=research_test_utils.days_ago(1),
)
research_detail_pages = list(self.library_page._get_research_detail_pages(sort=constants.SORT_NEWEST_FIRST))
research_detail_pages = list(
self.library_page.get_sorted_filtered_detail_pages(sort=constants.SORT_NEWEST_FIRST)
)
newest_page_index = research_detail_pages.index(newest_page)
oldest_page_index = research_detail_pages.index(oldest_page)
@ -101,7 +106,9 @@ class TestResearchLibraryPage(research_test_base.ResearchHubTestCase):
original_publication_date=research_test_utils.days_ago(1),
)
research_detail_pages = list(self.library_page._get_research_detail_pages(sort=constants.SORT_OLDEST_FIRST))
research_detail_pages = list(
self.library_page.get_sorted_filtered_detail_pages(sort=constants.SORT_OLDEST_FIRST)
)
newest_page_index = research_detail_pages.index(newest_page)
oldest_page_index = research_detail_pages.index(oldest_page)
@ -117,7 +124,9 @@ class TestResearchLibraryPage(research_test_base.ResearchHubTestCase):
title="Banana",
)
research_detail_pages = list(self.library_page._get_research_detail_pages(sort=constants.SORT_ALPHABETICAL))
research_detail_pages = list(
self.library_page.get_sorted_filtered_detail_pages(sort=constants.SORT_ALPHABETICAL)
)
apple_page_index = research_detail_pages.index(apple_page)
banana_page_index = research_detail_pages.index(banana_page)
@ -135,7 +144,7 @@ class TestResearchLibraryPage(research_test_base.ResearchHubTestCase):
)
research_detail_pages = list(
self.library_page._get_research_detail_pages(sort=constants.SORT_ALPHABETICAL_REVERSED)
self.library_page.get_sorted_filtered_detail_pages(sort=constants.SORT_ALPHABETICAL_REVERSED)
)
apple_page_index = research_detail_pages.index(apple_page)
@ -153,9 +162,9 @@ class TestResearchLibraryPage(research_test_base.ResearchHubTestCase):
original_publication_date=research_test_utils.days_ago(1),
)
default_sort_detail_pages = list(self.library_page._get_research_detail_pages())
default_sort_detail_pages = list(self.library_page.get_sorted_filtered_detail_pages())
newest_first_detail_pages = list(
self.library_page._get_research_detail_pages(sort=constants.SORT_NEWEST_FIRST)
self.library_page.get_sorted_filtered_detail_pages(sort=constants.SORT_NEWEST_FIRST)
)
self.assertEqual(default_sort_detail_pages, newest_first_detail_pages)
@ -166,7 +175,7 @@ class TestResearchLibraryPage(research_test_base.ResearchHubTestCase):
for _ in range(6):
detail_page_factory.ResearchDetailPageFactory(parent=self.library_page)
research_detail_pages = self.library_page._get_research_detail_pages()
research_detail_pages = self.library_page.get_sorted_filtered_detail_pages()
research_detail_pages_paginator = paginator.Paginator(
object_list=research_detail_pages,
@ -200,7 +209,7 @@ class TestResearchLibraryPageSearch(TestResearchLibraryPage):
collaborators="",
)
research_detail_pages = self.library_page._get_research_detail_pages(search="Apple")
research_detail_pages = self.library_page.get_sorted_filtered_detail_pages(search_query="Apple")
self.assertEqual(len(research_detail_pages), 1)
self.assertIn(apple_page, research_detail_pages)
self.assertNotIn(banana_page, research_detail_pages)
@ -221,7 +230,7 @@ class TestResearchLibraryPageSearch(TestResearchLibraryPage):
collaborators="",
)
research_detail_pages = self.library_page._get_research_detail_pages(search="Apple")
research_detail_pages = self.library_page.get_sorted_filtered_detail_pages(search_query="Apple")
self.assertEqual(len(research_detail_pages), 1)
self.assertIn(apple_page, research_detail_pages)
@ -243,7 +252,7 @@ class TestResearchLibraryPageSearch(TestResearchLibraryPage):
collaborators="",
)
research_detail_pages = self.library_page._get_research_detail_pages(search="Apple")
research_detail_pages = self.library_page.get_sorted_filtered_detail_pages(search_query="Apple")
self.assertEqual(len(research_detail_pages), 1)
self.assertIn(apple_page, research_detail_pages)
@ -265,7 +274,7 @@ class TestResearchLibraryPageSearch(TestResearchLibraryPage):
collaborators="Banana",
)
research_detail_pages = self.library_page._get_research_detail_pages(search="Apple")
research_detail_pages = self.library_page.get_sorted_filtered_detail_pages(search_query="Apple")
self.assertEqual(len(research_detail_pages), 1)
self.assertIn(apple_page, research_detail_pages)
@ -301,7 +310,7 @@ class TestResearchLibraryPageSearch(TestResearchLibraryPage):
relations_factory.ResearchAuthorRelationFactory(detail_page=banana_page, author_profile=banana_profile)
self.update_index()
research_detail_pages = self.library_page._get_research_detail_pages(search="Apple")
research_detail_pages = self.library_page.get_sorted_filtered_detail_pages(search_query="Apple")
self.assertEqual(len(research_detail_pages), 1)
self.assertIn(apple_page, research_detail_pages)
@ -335,7 +344,7 @@ class TestResearchLibraryPageSearch(TestResearchLibraryPage):
relations_factory.ResearchDetailPageResearchTopicRelationFactory(detail_page=banana_page, topic=banana_topic)
self.update_index()
research_detail_pages = self.library_page._get_research_detail_pages(search="Apple")
research_detail_pages = self.library_page.get_sorted_filtered_detail_pages(search_query="Apple")
self.assertEqual(len(research_detail_pages), 1)
self.assertIn(apple_page, research_detail_pages)
@ -365,7 +374,7 @@ class TestResearchLibraryPageSearch(TestResearchLibraryPage):
)
self.update_index()
research_detail_pages = self.library_page._get_research_detail_pages(search="Apple")
research_detail_pages = self.library_page.get_sorted_filtered_detail_pages(search_query="Apple")
self.assertEqual(len(research_detail_pages), 1)
self.assertIn(apple_page, research_detail_pages)
@ -386,7 +395,8 @@ class TestResearchLibraryPageFilters(TestResearchLibraryPage):
detail_page_2.authors.first().author_profile,
)
research_detail_pages = self.library_page._get_research_detail_pages(author_profile_ids=[author_profile.id])
filter_form = ResearchLibraryPageFilterForm(data={"authors": [author_profile.id]})
research_detail_pages = self.library_page.get_sorted_filtered_detail_pages(filter_form=filter_form)
self.assertIn(detail_page_1, research_detail_pages)
self.assertNotIn(detail_page_2, research_detail_pages)
@ -412,7 +422,7 @@ class TestResearchLibraryPageFilters(TestResearchLibraryPage):
)
# Only show the page where both profiles are authors
research_detail_pages = response.context["research_detail_pages"]
research_detail_pages = response.context["detail_pages"]
self.assertNotIn(detail_page_1, research_detail_pages)
self.assertIn(detail_page_2, research_detail_pages)
@ -445,9 +455,8 @@ class TestResearchLibraryPageFilters(TestResearchLibraryPage):
self.assertEqual(profile.translation_key, profile_fr.translation_key)
translation.activate(self.fr_locale.language_code)
research_detail_pages = self.library_page.localized._get_research_detail_pages(
author_profile_ids=[profile_fr.id]
)
filter_form = ResearchLibraryPageFilterForm(data={"authors": [profile_fr.id]})
research_detail_pages = self.library_page.get_sorted_filtered_detail_pages(filter_form=filter_form)
self.assertIn(detail_page_1_fr, research_detail_pages)
self.assertIn(detail_page_2_fr, research_detail_pages)
@ -466,7 +475,8 @@ class TestResearchLibraryPageFilters(TestResearchLibraryPage):
related_topics__topic=topic_B,
)
research_detail_pages = self.library_page._get_research_detail_pages(topic_ids=[topic_A.id])
filter_form = ResearchLibraryPageFilterForm(data={"topics": [topic_A.id]})
research_detail_pages = self.library_page.get_sorted_filtered_detail_pages(filter_form=filter_form)
self.assertIn(detail_page_A, research_detail_pages)
self.assertNotIn(detail_page_B, research_detail_pages)
@ -484,7 +494,8 @@ class TestResearchLibraryPageFilters(TestResearchLibraryPage):
related_topics__topic=topic_A,
)
research_detail_pages = self.library_page._get_research_detail_pages(topic_ids=[topic_A.id, topic_B.id])
filter_form = ResearchLibraryPageFilterForm(data={"topics": [topic_A.id, topic_B.id]})
research_detail_pages = self.library_page.get_sorted_filtered_detail_pages(filter_form=filter_form)
self.assertIn(detail_page_1, research_detail_pages)
self.assertNotIn(detail_page_2, research_detail_pages)
@ -518,7 +529,8 @@ class TestResearchLibraryPageFilters(TestResearchLibraryPage):
self.assertEqual(topic.translation_key, topic_fr.translation_key)
translation.activate(self.fr_locale.language_code)
research_detail_pages = self.library_page.localized._get_research_detail_pages(topic_ids=[topic_fr.id])
filter_form = ResearchLibraryPageFilterForm(data={"topics": [topic_fr.id]})
research_detail_pages = self.library_page.get_sorted_filtered_detail_pages(filter_form=filter_form)
self.assertEqual(len(research_detail_pages), 2)
self.assertIn(detail_page_1_fr, research_detail_pages)
@ -538,7 +550,8 @@ class TestResearchLibraryPageFilters(TestResearchLibraryPage):
related_regions__region=region_B,
)
research_detail_pages = self.library_page._get_research_detail_pages(region_ids=[region_A.id])
filter_form = ResearchLibraryPageFilterForm(data={"regions": [region_A.id]})
research_detail_pages = self.library_page.get_sorted_filtered_detail_pages(filter_form=filter_form)
self.assertIn(detail_page_A, research_detail_pages)
self.assertNotIn(detail_page_B, research_detail_pages)
@ -556,7 +569,8 @@ class TestResearchLibraryPageFilters(TestResearchLibraryPage):
related_regions__region=region_A,
)
research_detail_pages = self.library_page._get_research_detail_pages(region_ids=[region_A.id, region_B.id])
filter_form = ResearchLibraryPageFilterForm(data={"regions": [region_A.id, region_B.id]})
research_detail_pages = self.library_page.get_sorted_filtered_detail_pages(filter_form=filter_form)
self.assertIn(detail_page_1, research_detail_pages)
self.assertNotIn(detail_page_2, research_detail_pages)
@ -590,7 +604,8 @@ class TestResearchLibraryPageFilters(TestResearchLibraryPage):
self.assertEqual(region.translation_key, region_fr.translation_key)
translation.activate(self.fr_locale.language_code)
research_detail_pages = self.library_page.localized._get_research_detail_pages(region_ids=[region_fr.id])
filter_form = ResearchLibraryPageFilterForm(data={"regions": [region_fr.id]})
research_detail_pages = self.library_page.get_sorted_filtered_detail_pages(filter_form=filter_form)
self.assertEqual(len(research_detail_pages), 2)
self.assertIn(detail_page_1_fr, research_detail_pages)
@ -608,7 +623,8 @@ class TestResearchLibraryPageFilters(TestResearchLibraryPage):
original_publication_date=datetime.date(year_1 + 1, 6, 1),
)
research_detail_pages = self.library_page._get_research_detail_pages(year=year_1)
filter_form = ResearchLibraryPageFilterForm(data={"year": year_1})
research_detail_pages = self.library_page.get_sorted_filtered_detail_pages(filter_form=filter_form)
self.assertIn(detail_page_1, research_detail_pages)
self.assertNotIn(detail_page_2, research_detail_pages)