From 1de94bce8ff08b76c60a7e2154607f3308f0f343 Mon Sep 17 00:00:00 2001 From: Christoph Volkert Date: Thu, 21 Nov 2024 22:18:13 +0100 Subject: [PATCH 1/7] Wiki, task `update_related_pages`: update metadata of related pages Move the logic of the celery task to the class `Page`, so tests can be easier written. --- inyoka/wiki/models.py | 26 ++++++++++++++++++++++++++ inyoka/wiki/tasks.py | 18 ++++-------------- 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/inyoka/wiki/models.py b/inyoka/wiki/models.py index 01695ce30..797de4891 100644 --- a/inyoka/wiki/models.py +++ b/inyoka/wiki/models.py @@ -988,6 +988,32 @@ def update_meta(self): continue MetaData(page=self, key=key, value=value[:MAX_METADATA]).save() + def update_related_pages(self, update_meta: bool=True) -> None: + """ + Removes the content of page from the cache and its related pages. + + It also updates the metadata of all pages. This is f.e. relevant for page + templates that emit tags. + + Intended to be run in a celery task. + """ + related_pages = MetaData.objects.only('page') \ + .filter(key__in=('X-Link', 'X-Attach'), value=self.name) + + for meta in related_pages: + p = meta.page + cache.delete(f'wiki/page/{p.name.lower()}') + p.last_rev.text.remove_value_from_cache() + if update_meta: + p.update_meta() + + cache.delete(f'wiki/page/{self.name.lower()}') + self.last_rev.text.remove_value_from_cache() + if update_meta: + self.update_meta() + + ## TODO test number of queries used + def save(self, update_meta=True, *args, **kwargs): """ This not only saves the page but also a revision that is diff --git a/inyoka/wiki/tasks.py b/inyoka/wiki/tasks.py index 54f9e6a54..0448ceccb 100644 --- a/inyoka/wiki/tasks.py +++ b/inyoka/wiki/tasks.py @@ -63,20 +63,10 @@ def update_page_by_slug(): @shared_task -def update_related_pages(page, update_meta=True): - from inyoka.wiki.models import MetaData, Page - page = Page.objects.get(id=page) - related_pages = set() - values = ('value', 'page__last_rev__text_id') - linked = MetaData.objects.values_list(*values) \ - .filter(key__in=('X-Link', 'X-Attach'), value=page.name) - for value, text_id in linked.all(): - cache.delete(f'wiki/page/{value.lower()}') - related_pages.add(text_id) - cache.delete(f'wiki/page/{page.name.lower()}') - - if update_meta: - page.update_meta() +def update_related_pages(page_id: int, update_meta: bool=True) -> None: + from inyoka.wiki.models import Page + page = Page.objects.get(id=page_id) + page.update_related_pages(update_meta=update_meta) @shared_task From 4917f2e04f5f59f0af124fdbb50a38ecad342672 Mon Sep 17 00:00:00 2001 From: Christoph Volkert Date: Thu, 21 Nov 2024 22:29:41 +0100 Subject: [PATCH 2/7] Remove management command `regenerate_metadata`, as it is not needed anymore --- .../commands/regenerate_metadata.py | 38 ------------------- 1 file changed, 38 deletions(-) delete mode 100755 inyoka/wiki/management/commands/regenerate_metadata.py diff --git a/inyoka/wiki/management/commands/regenerate_metadata.py b/inyoka/wiki/management/commands/regenerate_metadata.py deleted file mode 100755 index f0593068c..000000000 --- a/inyoka/wiki/management/commands/regenerate_metadata.py +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env python -""" - inyoka.wiki.management.commands.regenerate_metadata - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Allows to regenerate the MetaData for wiki pages with a specific tested tag. - - :copyright: (c) 2007-2024 by the Inyoka Team, see AUTHORS for more details. - :license: BSD, see LICENSE for more details. -""" - -from django.core.management.base import BaseCommand - -from inyoka.wiki.models import MetaData, Page - - -class Command(BaseCommand): - help = "Allows to regenerate the MetaData for wiki pages with a specific tested tag." - - def add_arguments(self, parser): - parser.add_argument('--tested-tag', - action='store', - dest='tested_tag', - help='Metadata for all wiki pages that have this tested tag will be regenerated.') - - def handle(self, *args, **options): - to_clean = [] - for d in MetaData.objects.filter(key="getestet", value=options['tested_tag']): - p = d.page - print(p) - p.update_meta() - to_clean.append(p.name) - - Page.objects.clean_cache(to_clean) - - # drop overview pages from the cache - Page.objects.get_by_name('Wiki/ungetestet').rev.text.remove_value_from_cache() - Page.objects.get_by_name('Wiki/nur_getestet_bionic').rev.text.remove_value_from_cache() From a81576a05c4db348d81a79e1037699224981d440 Mon Sep 17 00:00:00 2001 From: Christoph Volkert Date: Mon, 25 Nov 2024 23:42:00 +0100 Subject: [PATCH 3/7] Tests for `update_related_pages` --- tests/apps/wiki/test_models.py | 48 ++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/tests/apps/wiki/test_models.py b/tests/apps/wiki/test_models.py index 45e2a8ef0..52438dc61 100644 --- a/tests/apps/wiki/test_models.py +++ b/tests/apps/wiki/test_models.py @@ -12,6 +12,54 @@ BASE_PATH = path.dirname(__file__) +class TestPage(TestCase): + + def test_update_related_pages__content_updated(self): + """ + Test, that content of a embedder page will be updated. + """ + template = Page.objects.create('Wiki/Templates/template', 'Foo') + page = Page.objects.create('test1', '[:test1:] content [[Vorlage(template, "Hello World")]]') + + self.assertEqual(page.last_rev.text.value_rendered, + '

test1 content Foo

') + + # included `template` was changed + # `page` should have a different content, but it's yet not updated + template.edit('Bar', note='changed content') + self.assertEqual(page.last_rev.text.value_rendered, + '

test1 content Foo

') + + template.update_related_pages() + + self.assertEqual(page.last_rev.text.value_rendered, + '

test1 content Bar

') + + def test_update_related_pages__metadata_updated(self): + """ + Test, that metadata will be updated. + """ + template = Page.objects.create('Wiki/Templates/template', '#tag: untested') + page = Page.objects.create('test1', '[:test1:] content [[Vorlage(template, "Hello World")]]') + + self.assertEqual(len(page.metadata), 3) + self.assertEqual(page.metadata['X-Link'], ['test1']) + self.assertEqual(page.metadata['X-Attach'], ['Wiki/Templates/template']) + self.assertEqual(page.metadata['tag'], ['untested']) + + # included `template` was changed + # `page` should have a different metadata, but it's yet not updated + template.edit('# tag: foo', note='changed tag') + page = Page.objects.get(id=page.id) # defer of page.meta caches the metadata once we accessed it + self.assertEqual(len(page.metadata), 3) + self.assertEqual(page.metadata['tag'], ['untested']) + + template.update_related_pages() + page = Page.objects.get(id=page.id) # defer of page.meta caches the metadata once we accessed it + self.assertEqual(len(page.metadata), 3) + self.assertEqual(page.metadata['tag'], ['foo']) + + class TestPageManager(TestCase): def test_get_by_name_case_sensitive(self): """ From 3cb9588e1e1b93f7b6505b86abbfa2c9a20e62d5 Mon Sep 17 00:00:00 2001 From: Christoph Volkert Date: Mon, 25 Nov 2024 22:55:10 +0100 Subject: [PATCH 4/7] Reduce number of db queries --- inyoka/wiki/models.py | 4 +--- tests/apps/wiki/test_models.py | 10 ++++++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/inyoka/wiki/models.py b/inyoka/wiki/models.py index 797de4891..e3382c604 100644 --- a/inyoka/wiki/models.py +++ b/inyoka/wiki/models.py @@ -997,7 +997,7 @@ def update_related_pages(self, update_meta: bool=True) -> None: Intended to be run in a celery task. """ - related_pages = MetaData.objects.only('page') \ + related_pages = MetaData.objects.select_related('page__last_rev__text') \ .filter(key__in=('X-Link', 'X-Attach'), value=self.name) for meta in related_pages: @@ -1012,8 +1012,6 @@ def update_related_pages(self, update_meta: bool=True) -> None: if update_meta: self.update_meta() - ## TODO test number of queries used - def save(self, update_meta=True, *args, **kwargs): """ This not only saves the page but also a revision that is diff --git a/tests/apps/wiki/test_models.py b/tests/apps/wiki/test_models.py index 52438dc61..b9b4d9795 100644 --- a/tests/apps/wiki/test_models.py +++ b/tests/apps/wiki/test_models.py @@ -59,6 +59,16 @@ def test_update_related_pages__metadata_updated(self): self.assertEqual(len(page.metadata), 3) self.assertEqual(page.metadata['tag'], ['foo']) + def test_update_related_pages__queries_used(self): + """ + Test, how many database queries are needed. + """ + template = Page.objects.create('Wiki/Templates/template', 'Foo') + Page.objects.create('test1', '[:test1:] content [[Vorlage(template, "Hello World")]]') + + with self.assertNumQueries(7): + template.update_related_pages() + class TestPageManager(TestCase): def test_get_by_name_case_sensitive(self): From fc1c201e55b6013b7ebf4e70e1533356c1e2180b Mon Sep 17 00:00:00 2001 From: Christoph Volkert Date: Mon, 25 Nov 2024 23:33:09 +0100 Subject: [PATCH 5/7] Rewrite DB query to return Pages directly --- inyoka/wiki/models.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/inyoka/wiki/models.py b/inyoka/wiki/models.py index e3382c604..8e117705a 100644 --- a/inyoka/wiki/models.py +++ b/inyoka/wiki/models.py @@ -997,11 +997,12 @@ def update_related_pages(self, update_meta: bool=True) -> None: Intended to be run in a celery task. """ - related_pages = MetaData.objects.select_related('page__last_rev__text') \ - .filter(key__in=('X-Link', 'X-Attach'), value=self.name) - for meta in related_pages: - p = meta.page + related_pages = Page.objects.select_related('last_rev__text') \ + .filter(metadata__key__in=('X-Link', 'X-Attach'), + metadata__value=self.name) + + for p in related_pages: cache.delete(f'wiki/page/{p.name.lower()}') p.last_rev.text.remove_value_from_cache() if update_meta: From eda202b499be5cfb24cb578efc237ea645cb7d09 Mon Sep 17 00:00:00 2001 From: Christoph Volkert Date: Mon, 25 Nov 2024 23:45:58 +0100 Subject: [PATCH 6/7] Typo --- inyoka/wiki/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inyoka/wiki/models.py b/inyoka/wiki/models.py index 8e117705a..ac9589924 100644 --- a/inyoka/wiki/models.py +++ b/inyoka/wiki/models.py @@ -1300,7 +1300,7 @@ class Revision(models.Model): `rendered_text`. user - The user that created this revision. If an anoymous user created + The user that created this revision. If an anonymous user created the revision this will be `None`. change_date From 75065a8d4631b4c28ea32229f22347c56996b6a9 Mon Sep 17 00:00:00 2001 From: Christoph Volkert Date: Sat, 21 Dec 2024 20:12:54 +0100 Subject: [PATCH 7/7] Update Changelog --- ChangeLog.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/ChangeLog.rst b/ChangeLog.rst index 0c75c0cd3..bacd716ae 100644 --- a/ChangeLog.rst +++ b/ChangeLog.rst @@ -35,6 +35,7 @@ Deployment notes ✨ New features --------------- +* Wiki: Update metadata and content of related pages after a edit 🏗 Changes ----------