diff --git a/.platform.app.yaml b/.platform.app.yaml index c4e588bec..249c3bdca 100644 --- a/.platform.app.yaml +++ b/.platform.app.yaml @@ -102,10 +102,10 @@ mounts: source_path: logs crons: - # Publish scheduled pages once an hour + # Publish scheduled pages once every half an hour # https://docs.wagtail.org/en/stable/reference/management_commands.html#publish-scheduled publish_scheduled: - spec: 0 * * * * + spec: */30 * * * * cmd: poetry run python manage.py publish_scheduled # Take snapshot at 5am every morning diff --git a/etna/api/tests/expected_results/article.json b/etna/api/tests/expected_results/article.json index abd155019..27524d42b 100644 --- a/etna/api/tests/expected_results/article.json +++ b/etna/api/tests/expected_results/article.json @@ -114,10 +114,16 @@ }, "display_content_warning": false, "custom_warning_text": "", - "is_newly_published": false, "tags": [ "Witchcraft" ], + "published_date": { + "value": "2000-01-01T00:00:00Z", + "year": 2000, + "month": 1, + "day": 1 + }, + "is_newly_published": false, "body": [ { "type": "content_section", @@ -332,7 +338,7 @@ } }, "last_published_at": "2000-01-02T00:00:00Z", - "is_newly_published": true + "is_newly_published": false } ], "latest_items": [], diff --git a/etna/api/tests/expected_results/article_index.json b/etna/api/tests/expected_results/article_index.json index bd2c2b6f9..6fe6740f9 100644 --- a/etna/api/tests/expected_results/article_index.json +++ b/etna/api/tests/expected_results/article_index.json @@ -152,7 +152,7 @@ } }, "last_published_at": "2000-01-02T00:00:00Z", - "is_newly_published": true + "is_newly_published": false } ] }, diff --git a/etna/api/tests/expected_results/arts.json b/etna/api/tests/expected_results/arts.json index 0cbb06fc9..0efb9c209 100644 --- a/etna/api/tests/expected_results/arts.json +++ b/etna/api/tests/expected_results/arts.json @@ -140,7 +140,7 @@ } }, "last_published_at": "2000-01-02T00:00:00Z", - "is_newly_published": true + "is_newly_published": false }, { "id": ARTICLE_ID, diff --git a/etna/api/tests/expected_results/author.json b/etna/api/tests/expected_results/author.json index baa249204..b2ce93b7a 100644 --- a/etna/api/tests/expected_results/author.json +++ b/etna/api/tests/expected_results/author.json @@ -142,7 +142,7 @@ } }, "last_published_at": "2000-01-02T00:00:00Z", - "is_newly_published": true + "is_newly_published": false } ], "shop_items": [] diff --git a/etna/api/tests/expected_results/early_modern.json b/etna/api/tests/expected_results/early_modern.json index 79dcd9fc4..38338900e 100644 --- a/etna/api/tests/expected_results/early_modern.json +++ b/etna/api/tests/expected_results/early_modern.json @@ -139,7 +139,7 @@ } }, "last_published_at": "2000-01-02T00:00:00Z", - "is_newly_published": true + "is_newly_published": false }, { "id": ARTICLE_ID, diff --git a/etna/api/tests/expected_results/focused_article.json b/etna/api/tests/expected_results/focused_article.json index 8133b7769..6b52acfbe 100644 --- a/etna/api/tests/expected_results/focused_article.json +++ b/etna/api/tests/expected_results/focused_article.json @@ -114,11 +114,17 @@ }, "display_content_warning": false, "custom_warning_text": "", - "is_newly_published": true, "tags": [ "Medicine", "Witchcraft" ], + "is_newly_published": false, + "published_date": { + "value": "2000-01-02T00:00:00Z", + "year": 2000, + "month": 1, + "day": 2 + }, "body": [], "similar_items": [ { diff --git a/etna/api/tests/expected_results/pages.json b/etna/api/tests/expected_results/pages.json index 6977d02d6..ad579aba5 100644 --- a/etna/api/tests/expected_results/pages.json +++ b/etna/api/tests/expected_results/pages.json @@ -281,7 +281,7 @@ } }, "last_published_at": "2000-01-02T00:00:00Z", - "is_newly_published": true + "is_newly_published": false } ] } \ No newline at end of file diff --git a/etna/api/tests/test_pages.py b/etna/api/tests/test_pages.py index 4e6fcc738..b5dae96f6 100644 --- a/etna/api/tests/test_pages.py +++ b/etna/api/tests/test_pages.py @@ -146,8 +146,7 @@ def setUpTestData(cls): PageTimePeriod(time_period=cls.postwar), ], first_published_at=DATE_1, - newly_published_at=DATE_1, - mark_new_on_next_publish=False, + published_date=DATE_1, ) cls.focused_article = FocusedArticlePageFactory( @@ -157,6 +156,7 @@ def setUpTestData(cls): page_time_periods=[PageTimePeriod(time_period=cls.early_modern)], author_tags=[AuthorTag(author=cls.author_page)], first_published_at=DATE_2, + published_date=DATE_2, ) cls.BODY_JSON = [ diff --git a/etna/articles/migrations/0113_remove_articlepage_mark_new_on_next_publish_and_more.py b/etna/articles/migrations/0113_remove_articlepage_mark_new_on_next_publish_and_more.py new file mode 100644 index 000000000..e31d41908 --- /dev/null +++ b/etna/articles/migrations/0113_remove_articlepage_mark_new_on_next_publish_and_more.py @@ -0,0 +1,71 @@ +# Generated by Django 5.1.2 on 2024-12-19 16:48 + +import django.utils.timezone +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("articles", "0112_alter_articlepage_body_alter_focusedarticlepage_body"), + ] + + def migrate_data(apps, schema_editor): + Article = apps.get_model("articles.ArticlePage") + FocusedArticle = apps.get_model("articles.FocusedArticlePage") + RecordArticle = apps.get_model("articles.RecordArticlePage") + for page in Article.objects.all(): + if page.newly_published_at: + page.published_date = ( + page.newly_published_at + or page.first_published_at + or django.utils.timezone.now() + ) + page.save() + for page in FocusedArticle.objects.all(): + if page.newly_published_at: + page.published_date = ( + page.newly_published_at + or page.first_published_at + or django.utils.timezone.now() + ) + page.save() + for page in RecordArticle.objects.all(): + if page.newly_published_at: + page.published_date = ( + page.newly_published_at + or page.first_published_at + or django.utils.timezone.now() + ) + page.save() + + operations = [ + migrations.AddField( + model_name="articlepage", + name="published_date", + field=models.DateTimeField( + default=django.utils.timezone.now, + help_text="The date the page was published to the public.", + verbose_name="Published date", + ), + ), + migrations.AddField( + model_name="focusedarticlepage", + name="published_date", + field=models.DateTimeField( + default=django.utils.timezone.now, + help_text="The date the page was published to the public.", + verbose_name="Published date", + ), + ), + migrations.AddField( + model_name="recordarticlepage", + name="published_date", + field=models.DateTimeField( + default=django.utils.timezone.now, + help_text="The date the page was published to the public.", + verbose_name="Published date", + ), + ), + migrations.RunPython(migrate_data), + ] diff --git a/etna/articles/migrations/0114_remove_articlepage_mark_new_on_next_publish_and_more.py b/etna/articles/migrations/0114_remove_articlepage_mark_new_on_next_publish_and_more.py new file mode 100644 index 000000000..891ca3e44 --- /dev/null +++ b/etna/articles/migrations/0114_remove_articlepage_mark_new_on_next_publish_and_more.py @@ -0,0 +1,38 @@ +# Generated by Django 5.1.2 on 2024-12-19 17:51 +# etna:allowRemoveField + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("articles", "0113_remove_articlepage_mark_new_on_next_publish_and_more"), + ] + + operations = [ + migrations.RemoveField( + model_name="articlepage", + name="mark_new_on_next_publish", + ), + migrations.RemoveField( + model_name="articlepage", + name="newly_published_at", + ), + migrations.RemoveField( + model_name="focusedarticlepage", + name="mark_new_on_next_publish", + ), + migrations.RemoveField( + model_name="focusedarticlepage", + name="newly_published_at", + ), + migrations.RemoveField( + model_name="recordarticlepage", + name="mark_new_on_next_publish", + ), + migrations.RemoveField( + model_name="recordarticlepage", + name="newly_published_at", + ), + ] diff --git a/etna/articles/models.py b/etna/articles/models.py index b12675b13..938c6af1b 100755 --- a/etna/articles/models.py +++ b/etna/articles/models.py @@ -32,7 +32,7 @@ BasePageWithRequiredIntro, ContentWarningMixin, HeroImageMixin, - NewLabelMixin, + PublishedDateMixin, RequiredHeroImageMixin, ) from etna.core.serializers import ( @@ -149,12 +149,6 @@ class ArticleIndexPage(BasePageWithRequiredIntro): serializer=DefaultPageSerializer(required_api_fields=["teaser_image"]), ), APIField("featured_pages"), - # APIField( TODO: Commented out until we have a way to paginate the child pages effectively - # "article_pages", - # serializer=DefaultPageSerializer( - # required_api_fields=["teaser_image"], many=True - # ), - # ), ] # DataLayerMixin overrides @@ -171,9 +165,9 @@ def article_pages(self): .live() .order_by( Coalesce( - "recordarticlepage__newly_published_at", - "focusedarticlepage__newly_published_at", - "articlepage__newly_published_at", + "recordarticlepage__published_date", + "focusedarticlepage__published_date", + "articlepage__published_date", ) ) .reverse() @@ -205,7 +199,7 @@ class ArticlePage( TopicalPageMixin, RequiredHeroImageMixin, ContentWarningMixin, - NewLabelMixin, + PublishedDateMixin, ArticleTagMixin, BasePageWithRequiredIntro, ): @@ -236,7 +230,7 @@ class Meta: ) promote_panels = ( - NewLabelMixin.promote_panels + PublishedDateMixin.promote_panels + BasePageWithRequiredIntro.promote_panels + ArticleTagMixin.promote_panels + [ @@ -259,16 +253,17 @@ class Meta: ) default_api_fields = BasePageWithRequiredIntro.default_api_fields + [ - APIField("is_newly_published"), + PublishedDateMixin.get_is_newly_published_apifield(), ] api_fields = ( BasePageWithRequiredIntro.api_fields + RequiredHeroImageMixin.api_fields + ContentWarningMixin.api_fields - + NewLabelMixin.api_fields + ArticleTagMixin.api_fields + [ + PublishedDateMixin.get_published_date_apifield(), + PublishedDateMixin.get_is_newly_published_apifield(), APIField("body"), APIField( "similar_items", @@ -358,9 +353,9 @@ def latest_items( .prefetch_related("teaser_image__renditions") ) - return sorted( - latest_query_set, key=lambda x: x.newly_published_at, reverse=True - )[:3] + return sorted(latest_query_set, key=lambda x: x.published_date, reverse=True)[ + :3 + ] class FocusedArticlePage( @@ -368,7 +363,7 @@ class FocusedArticlePage( AuthorPageMixin, HeroImageMixin, ContentWarningMixin, - NewLabelMixin, + PublishedDateMixin, ArticleTagMixin, BasePageWithRequiredIntro, ): @@ -399,7 +394,7 @@ class Meta: ) promote_panels = ( - NewLabelMixin.promote_panels + PublishedDateMixin.promote_panels + BasePageWithRequiredIntro.promote_panels + ArticleTagMixin.promote_panels + [ @@ -424,16 +419,17 @@ class Meta: ) default_api_fields = BasePageWithRequiredIntro.default_api_fields + [ - APIField("is_newly_published"), + PublishedDateMixin.get_is_newly_published_apifield(), ] api_fields = ( BasePageWithRequiredIntro.api_fields + HeroImageMixin.api_fields + ContentWarningMixin.api_fields - + NewLabelMixin.api_fields + ArticleTagMixin.api_fields + [ + PublishedDateMixin.get_is_newly_published_apifield(), + PublishedDateMixin.get_published_date_apifield(), APIField("type_label"), APIField("body"), APIField( @@ -525,9 +521,9 @@ def latest_items( .prefetch_related("teaser_image__renditions") ) - return sorted( - latest_query_set, key=lambda x: x.newly_published_at, reverse=True - )[:3] + return sorted(latest_query_set, key=lambda x: x.published_date, reverse=True)[ + :3 + ] class PageGalleryImage(Orderable): @@ -577,7 +573,7 @@ class Meta: class RecordArticlePage( TopicalPageMixin, ContentWarningMixin, - NewLabelMixin, + PublishedDateMixin, ArticleTagMixin, BasePageWithRequiredIntro, ): @@ -709,7 +705,7 @@ class Meta: ) promote_panels = ( - NewLabelMixin.promote_panels + PublishedDateMixin.promote_panels + BasePageWithRequiredIntro.promote_panels + ArticleTagMixin.promote_panels + [ @@ -731,15 +727,16 @@ class Meta: ) default_api_fields = BasePageWithRequiredIntro.default_api_fields + [ - APIField("is_newly_published"), + PublishedDateMixin.get_is_newly_published_apifield(), ] api_fields = ( BasePageWithRequiredIntro.api_fields + ContentWarningMixin.api_fields - + NewLabelMixin.api_fields + ArticleTagMixin.api_fields + [ + PublishedDateMixin.get_is_newly_published_apifield(), + PublishedDateMixin.get_published_date_apifield(), APIField("type_label"), APIField("date_text"), APIField("about", serializer=RichTextSerializer()), diff --git a/etna/articles/tests/test_models.py b/etna/articles/tests/test_models.py index d6aea2f61..15b0d146e 100644 --- a/etna/articles/tests/test_models.py +++ b/etna/articles/tests/test_models.py @@ -1,9 +1,17 @@ +from datetime import datetime, timezone + from django.test import TestCase from wagtail.models import Site from ..models import ArticleIndexPage, ArticlePage, ArticleTag +DATE_1 = datetime(2000, 1, 1, tzinfo=timezone.utc) + +DATE_2 = datetime(2000, 1, 2, tzinfo=timezone.utc) + +DATE_3 = datetime(2000, 1, 3, tzinfo=timezone.utc) + class TestArticleTagClean(TestCase): def test_clean_preserves_existing_skos_ids(self): @@ -45,6 +53,7 @@ def setUp(self): title="Article page 1", intro="test", teaser_text="test", + published_date=DATE_3, ) self.article_index_page.add_child(instance=self.article_page1) @@ -52,6 +61,7 @@ def setUp(self): title="Article page 2", intro="test", teaser_text="test", + published_date=DATE_2, ) self.article_index_page.add_child(instance=self.article_page2) @@ -59,6 +69,7 @@ def setUp(self): title="Article page 3", intro="test", teaser_text="test", + published_date=DATE_1, ) self.article_index_page.add_child(instance=self.article_page3) diff --git a/etna/blog/migrations/0007_remove_blogindexpage_hero_image_and_more.py b/etna/blog/migrations/0007_remove_blogindexpage_hero_image_and_more.py new file mode 100644 index 000000000..9ce4ba20b --- /dev/null +++ b/etna/blog/migrations/0007_remove_blogindexpage_hero_image_and_more.py @@ -0,0 +1,33 @@ +# Generated by Django 5.1.2 on 2024-12-19 16:48 +# etna:allowRemoveField +# etna:allowAlterField + +import django.utils.timezone +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("blog", "0006_alter_blogpostpage_body"), + ] + + operations = [ + migrations.RemoveField( + model_name="blogindexpage", + name="hero_image", + ), + migrations.RemoveField( + model_name="blogindexpage", + name="hero_image_caption", + ), + migrations.AlterField( + model_name="blogpostpage", + name="published_date", + field=models.DateTimeField( + default=django.utils.timezone.now, + help_text="The date the page was published to the public.", + verbose_name="Published date", + ), + ), + ] diff --git a/etna/blog/models.py b/etna/blog/models.py index 8245c1514..8919ce81e 100644 --- a/etna/blog/models.py +++ b/etna/blog/models.py @@ -1,7 +1,3 @@ -from django.db import models -from django.utils import timezone -from django.utils.functional import cached_property - from wagtail.admin.panels import FieldPanel from wagtail.api import APIField from wagtail.fields import StreamField @@ -10,14 +6,14 @@ BasePageWithRequiredIntro, ContentWarningMixin, HeroImageMixin, + PublishedDateMixin, ) -from etna.core.serializers import DateTimeSerializer, DefaultPageSerializer from etna.people.models import AuthorPageMixin, ExternalAuthorMixin from .blocks import BlogPostPageStreamBlock -class BlogIndexPage(HeroImageMixin, BasePageWithRequiredIntro): +class BlogIndexPage(BasePageWithRequiredIntro): """Blog index page This is the parent page for all blog posts. It is used to @@ -25,23 +21,8 @@ class BlogIndexPage(HeroImageMixin, BasePageWithRequiredIntro): """ subpage_types = ["blog.BlogPage"] - parent_page_types = ["home.HomePage"] - content_panels = ( - BasePageWithRequiredIntro.content_panels + HeroImageMixin.content_panels - ) - - promote_panels = BasePageWithRequiredIntro.promote_panels - - @cached_property - def blog_pages(self): - return BlogPage.objects.all().live().public().specific().order_by("title") - - api_fields = BasePageWithRequiredIntro.api_fields + [ - APIField("blog_pages", serializer=DefaultPageSerializer(many=True)) - ] - max_count = 1 @@ -70,42 +51,14 @@ class BlogPage(HeroImageMixin, BasePageWithRequiredIntro): promote_panels = BasePageWithRequiredIntro.promote_panels - @cached_property - def blog_posts(self): - return ( - self.get_children() - .type(BlogPostPage) - .live() - .public() - .specific() - .order_by("-blogpostpage__published_date") - ) - - @cached_property - def blog_pages(self): - return ( - self.get_children() - .type(BlogPage) - .live() - .public() - .specific() - .order_by("title") - ) - - api_fields = ( - BasePageWithRequiredIntro.api_fields - + HeroImageMixin.api_fields - + [ - APIField("blog_posts", serializer=DefaultPageSerializer(many=True)), - APIField("blog_pages", serializer=DefaultPageSerializer(many=True)), - ] - ) + api_fields = BasePageWithRequiredIntro.api_fields + HeroImageMixin.api_fields class BlogPostPage( AuthorPageMixin, ExternalAuthorMixin, ContentWarningMixin, + PublishedDateMixin, HeroImageMixin, BasePageWithRequiredIntro, ): @@ -120,12 +73,6 @@ class BlogPostPage( BlogPostPageStreamBlock(), ) - published_date = models.DateTimeField( - verbose_name="Published date", - help_text="The date the blog post was published.", - default=timezone.now, - ) - content_panels = ( BasePageWithRequiredIntro.content_panels + HeroImageMixin.content_panels @@ -134,17 +81,21 @@ class BlogPostPage( ] ) - promote_panels = BasePageWithRequiredIntro.promote_panels + [ - FieldPanel("published_date"), - AuthorPageMixin.get_authors_inlinepanel(), - ExternalAuthorMixin.get_authors_inlinepanel(), - ] + promote_panels = ( + BasePageWithRequiredIntro.promote_panels + + PublishedDateMixin.promote_panels + + [ + AuthorPageMixin.get_authors_inlinepanel(), + ExternalAuthorMixin.get_authors_inlinepanel(), + ] + ) default_api_fields = ( BasePageWithRequiredIntro.default_api_fields + AuthorPageMixin.default_api_fields + [ - APIField("published_date", serializer=DateTimeSerializer()), + PublishedDateMixin.get_published_date_apifield(), + PublishedDateMixin.get_is_newly_published_apifield(), APIField("last_published_at"), ] ) @@ -156,7 +107,8 @@ class BlogPostPage( + AuthorPageMixin.api_fields + ExternalAuthorMixin.api_fields + [ - APIField("published_date", serializer=DateTimeSerializer()), + PublishedDateMixin.get_published_date_apifield(), + PublishedDateMixin.get_is_newly_published_apifield(), APIField("body"), ] ) diff --git a/etna/collections/models.py b/etna/collections/models.py index c662f7e68..7b616ba8e 100644 --- a/etna/collections/models.py +++ b/etna/collections/models.py @@ -377,7 +377,7 @@ def related_articles(self): .prefetch_related("teaser_image__renditions") ) - return sorted(page_list, key=lambda x: x.newly_published_at, reverse=True) + return sorted(page_list, key=lambda x: x.published_date, reverse=True) @cached_property def related_highlight_gallery_pages(self): @@ -583,7 +583,7 @@ def related_articles(self): .prefetch_related("teaser_image__renditions") ) - return sorted(page_list, key=lambda x: x.newly_published_at, reverse=True) + return sorted(page_list, key=lambda x: x.published_date, reverse=True) @cached_property def related_highlight_gallery_pages(self): diff --git a/etna/core/models/mixins.py b/etna/core/models/mixins.py index ed7d1231b..3cdb70e69 100644 --- a/etna/core/models/mixins.py +++ b/etna/core/models/mixins.py @@ -12,6 +12,7 @@ from wagtail.images import get_image_model_string from etna.core.serializers import ( + DateTimeSerializer, DetailedImageSerializer, ImageSerializer, RichTextSerializer, @@ -23,10 +24,10 @@ __all__ = [ "AccentColourMixin", "ContentWarningMixin", - "NewLabelMixin", "HeroImageMixin", "HeroLayoutMixin", "HeroStyleMixin", + "PublishedDateMixin", "RequiredHeroImageMixin", "SidebarMixin", "SocialMixin", @@ -71,82 +72,42 @@ class Meta: abstract = True -class NewLabelMixin(models.Model): - """Mixin to allow editors to toggle 'new' label to be applied on-publish""" - - mark_new_on_next_publish = models.BooleanField( - verbose_name="mark this page as 'new' when published", - default=True, - help_text="This will set the 'new' label for 21 days", - ) - - newly_published_at = models.DateField( - editable=False, - verbose_name="Page marked as new on", - default=None, - null=True, - ) +class PublishedDateMixin(models.Model): + """Mixin to add a published date to a Page.""" new_label_display_for_days = 21 - def with_content_json(self, content): - """ - Overrides Page.with_content_json() to ensure page's `newly_published_at` - value is always preserved between revisions. - """ - obj = super().with_content_json(content) - obj.newly_published_at = self.newly_published_at - return obj - - def save(self, *args, **kwargs): - """ - Overrides Page.save() to set `newly_published_at` under the right - circumstances, and to ensure `mark_new_on_next_publish` is unset - once that wish has been fulfilled. - """ - # Set/reset newly_published_at where requested - if self.live and self.mark_new_on_next_publish: - self.newly_published_at = timezone.now().date() - self.mark_new_on_next_publish = False - - # Save page changes to the database - super().save(*args, **kwargs) - - if self.live and self.mark_new_on_next_publish and self.latest_revision: - # If `mark_new_on_next_publish` is still 'True' in the latest revision, - # The checkbox will remain checked when the page is next edited in Wagtail. - # Checking the box has had the desired effect now, so we 'uncheck' it - # in the revision content to avoid unexpected resetting. - self.latest_revision.content["mark_new_on_next_publish"] = False - self.latest_revision.save() + published_date = models.DateTimeField( + verbose_name="Published date", + help_text="The date the page was published to the public.", + default=timezone.now, + ) @cached_property def is_newly_published(self): expiry_date = timezone.now().date() - timedelta( days=self.new_label_display_for_days ) - if self.newly_published_at: - if self.newly_published_at > expiry_date: + if self.published_date: + if self.published_date.date() > expiry_date: return True return False - promote_panels = [ - MultiFieldPanel( - [ - FieldPanel("mark_new_on_next_publish"), - FieldPanel("newly_published_at", read_only=True), - ], - heading="New label", - ) - ] - class Meta: abstract = True - api_fields = [ - APIField("is_newly_published"), + promote_panels = [ + FieldPanel("published_date"), ] + @classmethod + def get_published_date_apifield(cls) -> APIField: + return APIField("published_date", serializer=DateTimeSerializer()) + + @classmethod + def get_is_newly_published_apifield(cls) -> APIField: + return APIField("is_newly_published") + class HeroImageMixin(models.Model): """Mixin to add hero_image attribute to a Page.""" diff --git a/etna/records/tests/test_blocks.py b/etna/records/tests/test_blocks.py index 7078587ed..6571c3647 100644 --- a/etna/records/tests/test_blocks.py +++ b/etna/records/tests/test_blocks.py @@ -1,5 +1,7 @@ import json +from datetime import datetime, timezone + from django.conf import settings from django.urls import reverse @@ -21,6 +23,8 @@ BLOCK_TITLE_OVERRIDE = "This record is sooooo featured!" +DATE_1 = datetime(2000, 1, 1, tzinfo=timezone.utc) + class TestFeaturedRecordBlockIntegration(WagtailPageTestCase): def setUp(self): @@ -64,6 +68,7 @@ def test_add_record_links(self): "intro": rich_text("test"), "hero_image": test_image.id, "teaser_text": "test", + "published_date": DATE_1, "body": streamfield( [ (