website/website/common/models.py

130 lines
3.9 KiB
Python
Raw Normal View History

2022-06-26 19:25:30 +01:00
import math
from typing import Any, Optional
2022-06-19 13:23:41 +01:00
2022-06-14 20:57:43 +01:00
from django.db import models
2022-06-19 16:35:56 +01:00
from django.http.request import HttpRequest
2022-06-19 20:13:19 +01:00
from django.utils.functional import cached_property, classproperty
from django.utils.text import slugify
2022-06-14 20:57:43 +01:00
from wagtail.admin.panels import FieldPanel
2022-06-26 18:37:04 +01:00
from wagtail.fields import StreamField
from wagtail.images import get_image_model_string
from wagtail.images.views.serve import generate_image_url
from wagtail.models import Page, PageQuerySet
2022-07-26 08:41:40 +01:00
from wagtail.search import index
2022-07-14 21:41:43 +01:00
from wagtail.snippets.models import register_snippet
from website.common.utils import count_words
from website.contrib.unsplash.widgets import UnsplashPhotoChooser
from .streamfield import add_heading_anchors, get_blocks, get_content_html
from .utils import TocEntry, extract_text, get_table_of_contents, truncate_string
2022-06-19 20:13:19 +01:00
class BasePage(Page):
2022-06-17 14:03:43 +01:00
show_in_menus_default = True
class Meta:
abstract = True
2022-06-10 15:54:31 +01:00
@classproperty
2022-06-12 15:17:28 +01:00
def body_class(cls) -> str:
return "page-" + slugify(cls.__name__)
2022-06-14 20:57:43 +01:00
def get_parent_pages(self) -> PageQuerySet:
2022-06-19 16:56:47 +01:00
"""
Shim over the fact everything is in 1 tree
"""
return self.get_ancestors().exclude(depth__lte=2)
2022-06-19 16:56:47 +01:00
2022-06-14 20:57:43 +01:00
2022-06-19 13:23:41 +01:00
class BaseContentMixin(models.Model):
2022-06-14 20:57:43 +01:00
subtitle = models.CharField(max_length=255, blank=True)
hero_image = models.ForeignKey(
2022-06-19 11:36:15 +01:00
get_image_model_string(), null=True, blank=True, on_delete=models.SET_NULL
)
hero_unsplash_photo = models.ForeignKey(
"unsplash.UnsplashPhoto", null=True, blank=True, on_delete=models.SET_NULL
)
2022-06-26 18:37:04 +01:00
body = StreamField(get_blocks(), blank=True, use_json_field=True)
2022-06-14 20:57:43 +01:00
2022-06-19 13:23:41 +01:00
content_panels = [
FieldPanel("subtitle"),
FieldPanel("hero_image"),
FieldPanel("hero_unsplash_photo", widget=UnsplashPhotoChooser),
2022-06-26 18:37:04 +01:00
FieldPanel("body"),
]
2022-06-19 13:23:41 +01:00
class Meta:
abstract = True
@cached_property
def table_of_contents(self) -> list[TocEntry]:
return get_table_of_contents(self.content_html)
@cached_property
def reading_time(self) -> int:
2022-06-26 19:25:30 +01:00
"""
https://help.medium.com/hc/en-us/articles/214991667-Read-time
"""
return int(math.ceil(self.word_count / 265))
@cached_property
def word_count(self) -> int:
return count_words(self.plain_text)
2022-06-26 19:52:20 +01:00
@cached_property
def summary(self) -> str:
return truncate_string(self.plain_text, 50)
2022-06-26 19:52:20 +01:00
2022-07-01 09:25:57 +01:00
@cached_property
2022-07-03 22:00:52 +01:00
def body_html(self) -> str:
2022-07-01 09:25:57 +01:00
return add_heading_anchors(str(self.body))
@cached_property
def content_html(self) -> str:
return get_content_html(str(self.body))
@cached_property
def plain_text(self) -> str:
return extract_text(self.content_html)
@cached_property
def hero_image_url(self) -> Optional[str]:
if self.hero_unsplash_photo_id is not None:
return self.hero_unsplash_photo.get_hero_image_url()
elif self.hero_image_id is not None:
return generate_image_url(self.hero_image, "width-1200")
return None
2022-06-19 13:23:41 +01:00
class ContentPage(BasePage, BaseContentMixin): # type: ignore[misc]
subpage_types: list[Any] = []
content_panels = BasePage.content_panels + BaseContentMixin.content_panels
class ListingPage(BasePage, BaseContentMixin): # type: ignore[misc]
content_panels = BasePage.content_panels + BaseContentMixin.content_panels
2022-06-19 16:35:56 +01:00
def get_context(self, request: HttpRequest) -> dict:
context = super().get_context(request)
context["child_pages"] = (
self.get_children().live().specific().select_related("hero_image")
)
return context
2022-07-14 21:41:43 +01:00
@register_snippet
2022-07-26 08:41:40 +01:00
class ReferralLink(models.Model, index.Indexed):
2022-07-14 21:41:43 +01:00
url = models.URLField()
name = models.CharField(max_length=64, unique=True)
panels = [
FieldPanel("name"),
FieldPanel("url"),
]
2022-07-26 08:41:40 +01:00
search_fields = [index.AutocompleteField("name"), index.SearchField("url")]
2022-07-14 21:41:43 +01:00
def __str__(self) -> str:
return self.name