mirror of
https://github.com/bookwyrm-social/bookwyrm.git
synced 2024-11-25 19:11:09 +00:00
Adds type checking for templatetags
This commit is contained in:
parent
ef30c7cf65
commit
676a6b4b7f
18 changed files with 93 additions and 68 deletions
|
@ -1,8 +1,9 @@
|
||||||
""" database schema for books and shelves """
|
""" database schema for books and shelves """
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
import re
|
import re
|
||||||
from typing import Any, Dict, Optional, Iterable
|
from typing import Any, Dict, Optional, Iterable, TYPE_CHECKING
|
||||||
from typing_extensions import Self
|
from typing_extensions import Self
|
||||||
|
|
||||||
from django.contrib.postgres.search import SearchVectorField
|
from django.contrib.postgres.search import SearchVectorField
|
||||||
|
@ -33,6 +34,9 @@ from .activitypub_mixin import OrderedCollectionPageMixin, ObjectMixin
|
||||||
from .base_model import BookWyrmModel
|
from .base_model import BookWyrmModel
|
||||||
from . import fields
|
from . import fields
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from bookwyrm.models import Author
|
||||||
|
|
||||||
|
|
||||||
class BookDataModel(ObjectMixin, BookWyrmModel):
|
class BookDataModel(ObjectMixin, BookWyrmModel):
|
||||||
"""fields shared between editable book data (books, works, authors)"""
|
"""fields shared between editable book data (books, works, authors)"""
|
||||||
|
@ -415,7 +419,7 @@ class Work(OrderedCollectionPageMixin, Book):
|
||||||
"""in case the default edition is not set"""
|
"""in case the default edition is not set"""
|
||||||
return self.editions.order_by("-edition_rank").first()
|
return self.editions.order_by("-edition_rank").first()
|
||||||
|
|
||||||
def author_edition(self, author):
|
def author_edition(self, author: Author) -> Any:
|
||||||
"""in case the default edition doesn't have the required author"""
|
"""in case the default edition doesn't have the required author"""
|
||||||
return self.editions.filter(authors=author).order_by("-edition_rank").first()
|
return self.editions.filter(authors=author).order_by("-edition_rank").first()
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
""" template filters """
|
""" template filters """
|
||||||
|
from typing import Any
|
||||||
from django import template
|
from django import template
|
||||||
|
from django.db.models import QuerySet
|
||||||
from bookwyrm import models
|
from bookwyrm import models
|
||||||
|
|
||||||
|
|
||||||
|
@ -7,13 +9,13 @@ register = template.Library()
|
||||||
|
|
||||||
|
|
||||||
@register.filter(name="review_count")
|
@register.filter(name="review_count")
|
||||||
def get_review_count(book):
|
def get_review_count(book: models.Edition) -> int:
|
||||||
"""how many reviews?"""
|
"""how many reviews?"""
|
||||||
return models.Review.objects.filter(deleted=False, book=book).count()
|
return models.Review.objects.filter(deleted=False, book=book).count()
|
||||||
|
|
||||||
|
|
||||||
@register.filter(name="book_description")
|
@register.filter(name="book_description")
|
||||||
def get_book_description(book):
|
def get_book_description(book: models.Edition) -> Any:
|
||||||
"""use the work's text if the book doesn't have it"""
|
"""use the work's text if the book doesn't have it"""
|
||||||
if book.description:
|
if book.description:
|
||||||
return book.description
|
return book.description
|
||||||
|
@ -24,12 +26,12 @@ def get_book_description(book):
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag(takes_context=False)
|
@register.simple_tag(takes_context=False)
|
||||||
def get_book_file_links(book):
|
def get_book_file_links(book: models.Edition) -> QuerySet[models.FileLink]:
|
||||||
"""links for a book"""
|
"""links for a book"""
|
||||||
return book.file_links.filter(domain__status="approved")
|
return book.file_links.filter(domain__status="approved")
|
||||||
|
|
||||||
|
|
||||||
@register.filter(name="author_edition")
|
@register.filter(name="author_edition")
|
||||||
def get_author_edition(book, author):
|
def get_author_edition(book: models.Work, author: models.Author) -> Any:
|
||||||
"""default edition for a book on the author page"""
|
"""default edition for a book on the author page"""
|
||||||
return book.author_edition(author)
|
return book.author_edition(author)
|
||||||
|
|
|
@ -7,18 +7,18 @@ register = template.Library()
|
||||||
|
|
||||||
|
|
||||||
@register.filter(name="uptime")
|
@register.filter(name="uptime")
|
||||||
def uptime(seconds):
|
def uptime(seconds: float) -> str:
|
||||||
"""Seconds uptime to a readable format"""
|
"""Seconds uptime to a readable format"""
|
||||||
return str(datetime.timedelta(seconds=seconds))
|
return str(datetime.timedelta(seconds=seconds))
|
||||||
|
|
||||||
|
|
||||||
@register.filter(name="runtime")
|
@register.filter(name="runtime")
|
||||||
def runtime(timestamp):
|
def runtime(timestamp: float) -> datetime.timedelta:
|
||||||
"""How long has it been?"""
|
"""How long has it been?"""
|
||||||
return datetime.datetime.now() - datetime.datetime.fromtimestamp(timestamp)
|
return datetime.datetime.now() - datetime.datetime.fromtimestamp(timestamp)
|
||||||
|
|
||||||
|
|
||||||
@register.filter(name="shortname")
|
@register.filter(name="shortname")
|
||||||
def shortname(name):
|
def shortname(name: str) -> str:
|
||||||
"""removes bookwyrm.celery..."""
|
"""removes bookwyrm.celery..."""
|
||||||
return ".".join(name.split(".")[-2:])
|
return ".".join(name.split(".")[-2:])
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
""" additional formatting of dates """
|
""" additional formatting of dates """
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from django import template
|
from django import template
|
||||||
from django.template import defaultfilters
|
from django.template import defaultfilters
|
||||||
from django.contrib.humanize.templatetags.humanize import naturalday
|
from django.contrib.humanize.templatetags.humanize import naturalday
|
||||||
|
@ -9,7 +11,7 @@ register = template.Library()
|
||||||
|
|
||||||
|
|
||||||
@register.filter(expects_localtime=True)
|
@register.filter(expects_localtime=True)
|
||||||
def naturalday_partial(date, arg=None):
|
def naturalday_partial(date: Any, arg: Any = None) -> str | None:
|
||||||
"""chooses appropriate precision if date is a PartialDate object
|
"""chooses appropriate precision if date is a PartialDate object
|
||||||
|
|
||||||
If arg is a Django-defined format such as "DATE_FORMAT", it will be adjusted
|
If arg is a Django-defined format such as "DATE_FORMAT", it will be adjusted
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
""" tags used on the feed pages """
|
""" tags used on the feed pages """
|
||||||
|
from typing import Any
|
||||||
from django import template
|
from django import template
|
||||||
from bookwyrm.views.feed import get_suggested_books
|
from bookwyrm.views.feed import get_suggested_books
|
||||||
|
from bookwyrm import models
|
||||||
|
|
||||||
|
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
|
|
||||||
|
|
||||||
@register.filter(name="load_subclass")
|
@register.filter(name="load_subclass")
|
||||||
def load_subclass(status):
|
def load_subclass(status: models.Status) -> models.Status:
|
||||||
"""sometimes you didn't select_subclass"""
|
"""sometimes you didn't select_subclass"""
|
||||||
if hasattr(status, "quotation"):
|
if hasattr(status, "quotation"):
|
||||||
return status.quotation
|
return status.quotation
|
||||||
|
@ -21,7 +23,7 @@ def load_subclass(status):
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag(takes_context=True)
|
@register.simple_tag(takes_context=True)
|
||||||
def suggested_books(context):
|
def suggested_books(context: dict[str, Any]) -> Any:
|
||||||
"""get books for suggested books panel"""
|
"""get books for suggested books panel"""
|
||||||
# this happens here instead of in the view so that the template snippet can
|
# this happens here instead of in the view so that the template snippet can
|
||||||
# be cached in the template
|
# be cached in the template
|
||||||
|
|
|
@ -7,21 +7,21 @@ register = template.Library()
|
||||||
|
|
||||||
|
|
||||||
@register.filter(name="has_groups")
|
@register.filter(name="has_groups")
|
||||||
def has_groups(user):
|
def has_groups(user: models.User) -> bool:
|
||||||
"""whether or not the user has a pending invitation to join this group"""
|
"""whether or not the user has a pending invitation to join this group"""
|
||||||
|
|
||||||
return models.GroupMember.objects.filter(user=user).exists()
|
return models.GroupMember.objects.filter(user=user).exists()
|
||||||
|
|
||||||
|
|
||||||
@register.filter(name="is_member")
|
@register.filter(name="is_member")
|
||||||
def is_member(group, user):
|
def is_member(group: models.Group, user: models.User) -> bool:
|
||||||
"""whether or not the user is a member of this group"""
|
"""whether or not the user is a member of this group"""
|
||||||
|
|
||||||
return models.GroupMember.objects.filter(group=group, user=user).exists()
|
return models.GroupMember.objects.filter(group=group, user=user).exists()
|
||||||
|
|
||||||
|
|
||||||
@register.filter(name="is_invited")
|
@register.filter(name="is_invited")
|
||||||
def is_invited(group, user):
|
def is_invited(group: models.Group, user: models.User) -> bool:
|
||||||
"""whether or not the user has a pending invitation to join this group"""
|
"""whether or not the user has a pending invitation to join this group"""
|
||||||
|
|
||||||
return models.GroupMemberInvitation.objects.filter(group=group, user=user).exists()
|
return models.GroupMemberInvitation.objects.filter(group=group, user=user).exists()
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
""" template filters for status interaction buttons """
|
""" template filters for status interaction buttons """
|
||||||
|
from typing import Any
|
||||||
from django import template
|
from django import template
|
||||||
|
|
||||||
from bookwyrm import models
|
from bookwyrm import models
|
||||||
|
@ -9,7 +10,7 @@ register = template.Library()
|
||||||
|
|
||||||
|
|
||||||
@register.filter(name="liked")
|
@register.filter(name="liked")
|
||||||
def get_user_liked(user, status):
|
def get_user_liked(user: models.User, status: models.Status) -> Any:
|
||||||
"""did the given user fav a status?"""
|
"""did the given user fav a status?"""
|
||||||
return get_or_set(
|
return get_or_set(
|
||||||
f"fav-{user.id}-{status.id}",
|
f"fav-{user.id}-{status.id}",
|
||||||
|
@ -21,7 +22,7 @@ def get_user_liked(user, status):
|
||||||
|
|
||||||
|
|
||||||
@register.filter(name="boosted")
|
@register.filter(name="boosted")
|
||||||
def get_user_boosted(user, status):
|
def get_user_boosted(user: models.User, status: models.Status) -> Any:
|
||||||
"""did the given user fav a status?"""
|
"""did the given user fav a status?"""
|
||||||
return get_or_set(
|
return get_or_set(
|
||||||
f"boost-{user.id}-{status.id}",
|
f"boost-{user.id}-{status.id}",
|
||||||
|
@ -32,13 +33,13 @@ def get_user_boosted(user, status):
|
||||||
|
|
||||||
|
|
||||||
@register.filter(name="saved")
|
@register.filter(name="saved")
|
||||||
def get_user_saved_lists(user, book_list):
|
def get_user_saved_lists(user: models.User, book_list: models.List) -> bool:
|
||||||
"""did the user save a list"""
|
"""did the user save a list"""
|
||||||
return user.saved_lists.filter(id=book_list.id).exists()
|
return user.saved_lists.filter(id=book_list.id).exists()
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag(takes_context=True)
|
@register.simple_tag(takes_context=True)
|
||||||
def get_relationship(context, user_object):
|
def get_relationship(context: dict[str, Any], user_object: models.User) -> Any:
|
||||||
"""caches the relationship between the logged in user and another user"""
|
"""caches the relationship between the logged in user and another user"""
|
||||||
user = context["request"].user
|
user = context["request"].user
|
||||||
return get_or_set(
|
return get_or_set(
|
||||||
|
@ -50,7 +51,9 @@ def get_relationship(context, user_object):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_relationship_name(user, user_object):
|
def get_relationship_name(
|
||||||
|
user: models.User, user_object: models.User
|
||||||
|
) -> dict[str, bool]:
|
||||||
"""returns the relationship type"""
|
"""returns the relationship type"""
|
||||||
types = {
|
types = {
|
||||||
"is_following": False,
|
"is_following": False,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
""" template filters """
|
""" template filters """
|
||||||
|
from typing import Optional
|
||||||
from django import template
|
from django import template
|
||||||
from django.db.models import Avg, StdDev, Count, F, Q
|
from django.db.models import Avg, StdDev, Count, F, Q, QuerySet
|
||||||
|
|
||||||
from bookwyrm import models
|
from bookwyrm import models
|
||||||
|
|
||||||
|
@ -8,7 +9,7 @@ register = template.Library()
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag(takes_context=False)
|
@register.simple_tag(takes_context=False)
|
||||||
def get_book_superlatives():
|
def get_book_superlatives() -> dict[str, Optional[models.Work]]:
|
||||||
"""get book stats for the about page"""
|
"""get book stats for the about page"""
|
||||||
total_ratings = models.Review.objects.filter(local=True, deleted=False).count()
|
total_ratings = models.Review.objects.filter(local=True, deleted=False).count()
|
||||||
data = {}
|
data = {}
|
||||||
|
@ -67,7 +68,7 @@ def get_book_superlatives():
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag(takes_context=False)
|
@register.simple_tag(takes_context=False)
|
||||||
def get_landing_books():
|
def get_landing_books() -> list[QuerySet[models.Edition]]:
|
||||||
"""list of books for the landing page"""
|
"""list of books for the landing page"""
|
||||||
return list(
|
return list(
|
||||||
set(
|
set(
|
||||||
|
|
|
@ -5,7 +5,7 @@ register = template.Library()
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag(takes_context=False)
|
@register.simple_tag(takes_context=False)
|
||||||
def get_lang():
|
def get_lang() -> str:
|
||||||
"""get current language, strip to the first two letters"""
|
"""get current language, strip to the first two letters"""
|
||||||
language = utils.translation.get_language()
|
language = utils.translation.get_language()
|
||||||
return language[0 : language.find("-")]
|
return language[0 : language.find("-")]
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
""" template filters """
|
""" template filters """
|
||||||
|
from typing import Any
|
||||||
from django import template
|
from django import template
|
||||||
from bookwyrm.views.status import to_markdown
|
from bookwyrm.views.status import to_markdown
|
||||||
|
|
||||||
|
@ -7,7 +8,7 @@ register = template.Library()
|
||||||
|
|
||||||
|
|
||||||
@register.filter(name="to_markdown")
|
@register.filter(name="to_markdown")
|
||||||
def get_markdown(content):
|
def get_markdown(content: str) -> Any:
|
||||||
"""convert markdown to html"""
|
"""convert markdown to html"""
|
||||||
if content:
|
if content:
|
||||||
return to_markdown(content)
|
return to_markdown(content)
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
""" tags used on the feed pages """
|
""" tags used on the feed pages """
|
||||||
|
from typing import Optional
|
||||||
from django import template
|
from django import template
|
||||||
|
from bookwyrm import models
|
||||||
from bookwyrm.templatetags.feed_page_tags import load_subclass
|
from bookwyrm.templatetags.feed_page_tags import load_subclass
|
||||||
|
|
||||||
|
|
||||||
|
@ -7,7 +9,7 @@ register = template.Library()
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag(takes_context=False)
|
@register.simple_tag(takes_context=False)
|
||||||
def related_status(notification):
|
def related_status(notification: models.Notification) -> Optional[models.Status]:
|
||||||
"""for notifications"""
|
"""for notifications"""
|
||||||
if not notification.related_status:
|
if not notification.related_status:
|
||||||
return None
|
return None
|
||||||
|
@ -15,6 +17,6 @@ def related_status(notification):
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag(takes_context=False)
|
@register.simple_tag(takes_context=False)
|
||||||
def get_related_users(notification):
|
def get_related_users(notification: models.Notification) -> list[models.User]:
|
||||||
"""Who actually was it who liked your post"""
|
"""Who actually was it who liked your post"""
|
||||||
return list(reversed(list(notification.related_users.distinct())))[:10]
|
return list(reversed(list(notification.related_users.distinct())))[:10]
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
""" template filters """
|
""" template filters """
|
||||||
|
from typing import Any
|
||||||
from django import template
|
from django import template
|
||||||
from django.db.models import Avg
|
from django.db.models import Avg
|
||||||
|
|
||||||
|
@ -10,7 +11,7 @@ register = template.Library()
|
||||||
|
|
||||||
|
|
||||||
@register.filter(name="rating")
|
@register.filter(name="rating")
|
||||||
def get_rating(book, user):
|
def get_rating(book: models.Edition, user: models.User) -> Any:
|
||||||
"""get the overall rating of a book"""
|
"""get the overall rating of a book"""
|
||||||
# this shouldn't happen, but it CAN
|
# this shouldn't happen, but it CAN
|
||||||
if not book.parent_work:
|
if not book.parent_work:
|
||||||
|
@ -29,7 +30,7 @@ def get_rating(book, user):
|
||||||
|
|
||||||
|
|
||||||
@register.filter(name="user_rating")
|
@register.filter(name="user_rating")
|
||||||
def get_user_rating(book, user):
|
def get_user_rating(book: models.Edition, user: models.User) -> Any:
|
||||||
"""get a user's rating of a book"""
|
"""get a user's rating of a book"""
|
||||||
rating = (
|
rating = (
|
||||||
models.Review.objects.filter(
|
models.Review.objects.filter(
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
""" Filters and tags related to shelving books """
|
""" Filters and tags related to shelving books """
|
||||||
|
from typing import Any
|
||||||
from django import template
|
from django import template
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from django_stubs_ext import StrPromise
|
||||||
|
|
||||||
from bookwyrm import models
|
from bookwyrm import models
|
||||||
from bookwyrm.utils import cache
|
from bookwyrm.utils import cache
|
||||||
|
@ -19,7 +21,7 @@ SHELF_NAMES = {
|
||||||
|
|
||||||
|
|
||||||
@register.filter(name="is_book_on_shelf")
|
@register.filter(name="is_book_on_shelf")
|
||||||
def get_is_book_on_shelf(book, shelf):
|
def get_is_book_on_shelf(book: models.Edition, shelf: models.Shelf) -> Any:
|
||||||
"""is a book on a shelf"""
|
"""is a book on a shelf"""
|
||||||
return cache.get_or_set(
|
return cache.get_or_set(
|
||||||
f"book-on-shelf-{book.id}-{shelf.id}",
|
f"book-on-shelf-{book.id}-{shelf.id}",
|
||||||
|
@ -31,7 +33,7 @@ def get_is_book_on_shelf(book, shelf):
|
||||||
|
|
||||||
|
|
||||||
@register.filter(name="next_shelf")
|
@register.filter(name="next_shelf")
|
||||||
def get_next_shelf(current_shelf):
|
def get_next_shelf(current_shelf: str) -> str:
|
||||||
"""shelf you'd use to update reading progress"""
|
"""shelf you'd use to update reading progress"""
|
||||||
if current_shelf == "to-read":
|
if current_shelf == "to-read":
|
||||||
return "reading"
|
return "reading"
|
||||||
|
@ -45,7 +47,7 @@ def get_next_shelf(current_shelf):
|
||||||
|
|
||||||
|
|
||||||
@register.filter(name="translate_shelf_name")
|
@register.filter(name="translate_shelf_name")
|
||||||
def get_translated_shelf_name(shelf):
|
def get_translated_shelf_name(shelf: models.Shelf | dict[str, str]) -> str | StrPromise:
|
||||||
"""produce translated shelf identifiername"""
|
"""produce translated shelf identifiername"""
|
||||||
if not shelf:
|
if not shelf:
|
||||||
return ""
|
return ""
|
||||||
|
@ -59,7 +61,7 @@ def get_translated_shelf_name(shelf):
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag(takes_context=True)
|
@register.simple_tag(takes_context=True)
|
||||||
def active_shelf(context, book):
|
def active_shelf(context: dict[str, Any], book: models.Edition) -> Any:
|
||||||
"""check what shelf a user has a book on, if any"""
|
"""check what shelf a user has a book on, if any"""
|
||||||
user = context["request"].user
|
user = context["request"].user
|
||||||
return cache.get_or_set(
|
return cache.get_or_set(
|
||||||
|
@ -78,7 +80,7 @@ def active_shelf(context, book):
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag(takes_context=False)
|
@register.simple_tag(takes_context=False)
|
||||||
def latest_read_through(book, user):
|
def latest_read_through(book: models.Edition, user: models.User) -> Any:
|
||||||
"""the most recent read activity"""
|
"""the most recent read activity"""
|
||||||
return cache.get_or_set(
|
return cache.get_or_set(
|
||||||
f"latest_read_through-{user.id}-{book.id}",
|
f"latest_read_through-{user.id}-{book.id}",
|
||||||
|
|
|
@ -6,6 +6,6 @@ register = template.Library()
|
||||||
|
|
||||||
|
|
||||||
@register.filter(name="half_star")
|
@register.filter(name="half_star")
|
||||||
def get_half_star(value):
|
def get_half_star(value: str) -> str:
|
||||||
"""one of those things that's weirdly hard with templates"""
|
"""one of those things that's weirdly hard with templates"""
|
||||||
return f"{value}.5"
|
return f"{value}.5"
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
""" template filters """
|
""" template filters """
|
||||||
|
from typing import Any, Optional
|
||||||
from dateutil.relativedelta import relativedelta
|
from dateutil.relativedelta import relativedelta
|
||||||
from django import template
|
from django import template
|
||||||
from django.contrib.humanize.templatetags.humanize import naturaltime, naturalday
|
from django.contrib.humanize.templatetags.humanize import naturaltime, naturalday
|
||||||
|
@ -12,7 +13,7 @@ register = template.Library()
|
||||||
|
|
||||||
|
|
||||||
@register.filter(name="mentions")
|
@register.filter(name="mentions")
|
||||||
def get_mentions(status, user):
|
def get_mentions(status: models.Status, user: models.User) -> str:
|
||||||
"""people to @ in a reply: the parent and all mentions"""
|
"""people to @ in a reply: the parent and all mentions"""
|
||||||
mentions = set([status.user] + list(status.mention_users.all()))
|
mentions = set([status.user] + list(status.mention_users.all()))
|
||||||
return (
|
return (
|
||||||
|
@ -21,7 +22,7 @@ def get_mentions(status, user):
|
||||||
|
|
||||||
|
|
||||||
@register.filter(name="replies")
|
@register.filter(name="replies")
|
||||||
def get_replies(status):
|
def get_replies(status: models.Status) -> Any:
|
||||||
"""get all direct replies to a status"""
|
"""get all direct replies to a status"""
|
||||||
# TODO: this limit could cause problems
|
# TODO: this limit could cause problems
|
||||||
return models.Status.objects.filter(
|
return models.Status.objects.filter(
|
||||||
|
@ -31,7 +32,7 @@ def get_replies(status):
|
||||||
|
|
||||||
|
|
||||||
@register.filter(name="parent")
|
@register.filter(name="parent")
|
||||||
def get_parent(status):
|
def get_parent(status: models.Status) -> Any:
|
||||||
"""get the reply parent for a status"""
|
"""get the reply parent for a status"""
|
||||||
return (
|
return (
|
||||||
models.Status.objects.filter(id=status.reply_parent_id)
|
models.Status.objects.filter(id=status.reply_parent_id)
|
||||||
|
@ -41,7 +42,7 @@ def get_parent(status):
|
||||||
|
|
||||||
|
|
||||||
@register.filter(name="boosted_status")
|
@register.filter(name="boosted_status")
|
||||||
def get_boosted(boost):
|
def get_boosted(boost: models.Boost) -> Any:
|
||||||
"""load a boosted status. have to do this or it won't get foreign keys"""
|
"""load a boosted status. have to do this or it won't get foreign keys"""
|
||||||
return (
|
return (
|
||||||
models.Status.objects.select_subclasses()
|
models.Status.objects.select_subclasses()
|
||||||
|
@ -52,7 +53,7 @@ def get_boosted(boost):
|
||||||
|
|
||||||
|
|
||||||
@register.filter(name="published_date")
|
@register.filter(name="published_date")
|
||||||
def get_published_date(date):
|
def get_published_date(date: str) -> Any:
|
||||||
"""less verbose combo of humanize filters"""
|
"""less verbose combo of humanize filters"""
|
||||||
if not date:
|
if not date:
|
||||||
return ""
|
return ""
|
||||||
|
@ -66,7 +67,7 @@ def get_published_date(date):
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag()
|
@register.simple_tag()
|
||||||
def get_header_template(status):
|
def get_header_template(status: models.Status) -> Any:
|
||||||
"""get the path for the status template"""
|
"""get the path for the status template"""
|
||||||
if isinstance(status, models.Boost):
|
if isinstance(status, models.Boost):
|
||||||
status = status.boosted_status
|
status = status.boosted_status
|
||||||
|
@ -82,6 +83,6 @@ def get_header_template(status):
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag(takes_context=False)
|
@register.simple_tag(takes_context=False)
|
||||||
def load_book(status):
|
def load_book(status: models.Status) -> Optional[models.Book]:
|
||||||
"""how many users that you follow, follow them"""
|
"""how many users that you follow, follow them"""
|
||||||
return status.book if hasattr(status, "book") else status.mention_books.first()
|
return status.book if hasattr(status, "book") else status.mention_books.first()
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
""" template filters """
|
""" template filters """
|
||||||
|
from typing import Any, Optional
|
||||||
from django import template
|
from django import template
|
||||||
|
from bookwyrm import models
|
||||||
|
|
||||||
|
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag(takes_context=True)
|
@register.simple_tag(takes_context=True)
|
||||||
def mutuals_count(context, user):
|
def mutuals_count(context: dict[str, Any], user: models.User) -> Optional[int]:
|
||||||
"""how many users that you follow, follow them"""
|
"""how many users that you follow, follow them"""
|
||||||
viewer = context["request"].user
|
viewer = context["request"].user
|
||||||
if not viewer.is_authenticated:
|
if not viewer.is_authenticated:
|
||||||
|
|
|
@ -1,46 +1,50 @@
|
||||||
""" template filters for really common utilities """
|
""" template filters for really common utilities """
|
||||||
|
from typing import Any, Optional
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
from django import template
|
from django import template
|
||||||
|
from django.contrib.auth.models import Group
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.templatetags.static import static
|
from django.templatetags.static import static
|
||||||
|
from django_stubs_ext import StrPromise
|
||||||
|
|
||||||
from bookwyrm.models import User
|
from bookwyrm.models import Author, Edition, User
|
||||||
from bookwyrm.settings import INSTANCE_ACTOR_USERNAME
|
from bookwyrm.settings import INSTANCE_ACTOR_USERNAME
|
||||||
|
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
|
|
||||||
|
|
||||||
@register.filter(name="uuid")
|
@register.filter(name="uuid")
|
||||||
def get_uuid(identifier):
|
def get_uuid(identifier: str) -> str:
|
||||||
"""for avoiding clashing ids when there are many forms"""
|
"""for avoiding clashing ids when there are many forms"""
|
||||||
return f"{identifier}{uuid4()}"
|
return f"{identifier}{uuid4()}"
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag(takes_context=False)
|
@register.simple_tag(takes_context=False)
|
||||||
def join(*args):
|
def join(*args: tuple[Any]) -> str:
|
||||||
"""concatenate an arbitrary set of values"""
|
"""concatenate an arbitrary set of values"""
|
||||||
return "_".join(str(a) for a in args)
|
return "_".join(str(a) for a in args)
|
||||||
|
|
||||||
|
|
||||||
@register.filter(name="username")
|
@register.filter(name="username")
|
||||||
def get_user_identifier(user):
|
def get_user_identifier(user: User) -> str:
|
||||||
"""use localname for local users, username for remote"""
|
"""use localname for local users, username for remote"""
|
||||||
return user.localname if user.localname else user.username
|
return user.localname if user.localname else user.username or ""
|
||||||
|
|
||||||
|
|
||||||
@register.filter(name="user_from_remote_id")
|
@register.filter(name="user_from_remote_id")
|
||||||
def get_user_identifier_from_remote_id(remote_id):
|
def get_user_identifier_from_remote_id(remote_id: str) -> Optional[User]:
|
||||||
"""get the local user id from their remote id"""
|
"""get the local user id from their remote id"""
|
||||||
user = User.objects.get(remote_id=remote_id)
|
user = User.objects.get(remote_id=remote_id)
|
||||||
return user if user else None
|
return user if user else None
|
||||||
|
|
||||||
|
|
||||||
@register.filter(name="book_title")
|
@register.filter(name="book_title")
|
||||||
def get_title(book, too_short=5):
|
def get_title(book: Edition, too_short: int = 5) -> Any:
|
||||||
"""display the subtitle if the title is short"""
|
"""display the subtitle if the title is short"""
|
||||||
if not book:
|
if not book:
|
||||||
return ""
|
return ""
|
||||||
|
@ -54,7 +58,7 @@ def get_title(book, too_short=5):
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag(takes_context=False)
|
@register.simple_tag(takes_context=False)
|
||||||
def comparison_bool(str1, str2, reverse=False):
|
def comparison_bool(str1: str, str2: str, reverse: bool = False) -> bool:
|
||||||
"""idk why I need to write a tag for this, it returns a bool"""
|
"""idk why I need to write a tag for this, it returns a bool"""
|
||||||
if reverse:
|
if reverse:
|
||||||
return str1 != str2
|
return str1 != str2
|
||||||
|
@ -62,7 +66,7 @@ def comparison_bool(str1, str2, reverse=False):
|
||||||
|
|
||||||
|
|
||||||
@register.filter(is_safe=True)
|
@register.filter(is_safe=True)
|
||||||
def truncatepath(value, arg):
|
def truncatepath(value: Any, arg: Any) -> Any:
|
||||||
"""Truncate a path by removing all directories except the first and truncating"""
|
"""Truncate a path by removing all directories except the first and truncating"""
|
||||||
path = os.path.normpath(value.name)
|
path = os.path.normpath(value.name)
|
||||||
path_list = path.split(os.sep)
|
path_list = path.split(os.sep)
|
||||||
|
@ -74,7 +78,9 @@ def truncatepath(value, arg):
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag(takes_context=False)
|
@register.simple_tag(takes_context=False)
|
||||||
def get_book_cover_thumbnail(book, size="medium", ext="jpg"):
|
def get_book_cover_thumbnail(
|
||||||
|
book: Edition, size: str = "medium", ext: str = "jpg"
|
||||||
|
) -> Any:
|
||||||
"""Returns a book thumbnail at the specified size and extension,
|
"""Returns a book thumbnail at the specified size and extension,
|
||||||
with fallback if needed"""
|
with fallback if needed"""
|
||||||
if size == "":
|
if size == "":
|
||||||
|
@ -87,7 +93,7 @@ def get_book_cover_thumbnail(book, size="medium", ext="jpg"):
|
||||||
|
|
||||||
|
|
||||||
@register.filter(name="get_isni_bio")
|
@register.filter(name="get_isni_bio")
|
||||||
def get_isni_bio(existing, author):
|
def get_isni_bio(existing: int, author: Author) -> str:
|
||||||
"""Returns the isni bio string if an existing author has an isni listed"""
|
"""Returns the isni bio string if an existing author has an isni listed"""
|
||||||
auth_isni = re.sub(r"\D", "", str(author.isni))
|
auth_isni = re.sub(r"\D", "", str(author.isni))
|
||||||
if len(existing) == 0:
|
if len(existing) == 0:
|
||||||
|
@ -101,7 +107,7 @@ def get_isni_bio(existing, author):
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
@register.filter(name="get_isni", needs_autoescape=True)
|
@register.filter(name="get_isni", needs_autoescape=True)
|
||||||
def get_isni(existing, author, autoescape=True):
|
def get_isni(existing: str, author: Author, autoescape: bool = True) -> str:
|
||||||
"""Returns the isni ID if an existing author has an ISNI listing"""
|
"""Returns the isni ID if an existing author has an ISNI listing"""
|
||||||
auth_isni = re.sub(r"\D", "", str(author.isni))
|
auth_isni = re.sub(r"\D", "", str(author.isni))
|
||||||
if len(existing) == 0:
|
if len(existing) == 0:
|
||||||
|
@ -116,7 +122,7 @@ def get_isni(existing, author, autoescape=True):
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag(takes_context=False)
|
@register.simple_tag(takes_context=False)
|
||||||
def id_to_username(user_id):
|
def id_to_username(user_id: str) -> str | StrPromise:
|
||||||
"""given an arbitrary remote id, return the username"""
|
"""given an arbitrary remote id, return the username"""
|
||||||
if user_id:
|
if user_id:
|
||||||
url = urlparse(user_id)
|
url = urlparse(user_id)
|
||||||
|
@ -130,7 +136,7 @@ def id_to_username(user_id):
|
||||||
|
|
||||||
|
|
||||||
@register.filter(name="get_file_size")
|
@register.filter(name="get_file_size")
|
||||||
def get_file_size(nbytes):
|
def get_file_size(nbytes: int) -> str:
|
||||||
"""display the size of a file in human readable terms"""
|
"""display the size of a file in human readable terms"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -148,13 +154,13 @@ def get_file_size(nbytes):
|
||||||
|
|
||||||
|
|
||||||
@register.filter(name="get_user_permission")
|
@register.filter(name="get_user_permission")
|
||||||
def get_user_permission(user):
|
def get_user_permission(user: User) -> Group | str:
|
||||||
"""given a user, return their permission level"""
|
"""given a user, return their permission level"""
|
||||||
|
|
||||||
return user.groups.first() or "User"
|
return user.groups.first() or "User"
|
||||||
|
|
||||||
|
|
||||||
@register.filter(name="is_instance_admin")
|
@register.filter(name="is_instance_admin")
|
||||||
def is_instance_admin(localname):
|
def is_instance_admin(localname: str) -> bool:
|
||||||
"""Returns a boolean indicating whether the user is the instance admin account"""
|
"""Returns a boolean indicating whether the user is the instance admin account"""
|
||||||
return localname == INSTANCE_ACTOR_USERNAME
|
return localname == INSTANCE_ACTOR_USERNAME
|
||||||
|
|
14
mypy.ini
14
mypy.ini
|
@ -10,23 +10,19 @@ django_settings_module = "bookwyrm.settings"
|
||||||
ignore_errors = True
|
ignore_errors = True
|
||||||
implicit_reexport = True
|
implicit_reexport = True
|
||||||
|
|
||||||
[mypy-bookwyrm.connectors.*]
|
|
||||||
ignore_errors = False
|
|
||||||
|
|
||||||
[mypy-bookwyrm.models.author]
|
[mypy-bookwyrm.models.author]
|
||||||
ignore_errors = False
|
ignore_errors = False
|
||||||
allow_untyped_calls = True
|
allow_untyped_calls = True
|
||||||
disable_error_code = import-untyped, assignment
|
disable_error_code = import-untyped, assignment
|
||||||
|
|
||||||
|
[mypy-bookwyrm.connectors.*]
|
||||||
[mypy-bookwyrm.utils.*]
|
[mypy-bookwyrm.utils.*]
|
||||||
ignore_errors = False
|
|
||||||
|
|
||||||
[mypy-bookwyrm.importers.*]
|
[mypy-bookwyrm.importers.*]
|
||||||
ignore_errors = False
|
|
||||||
|
|
||||||
[mypy-bookwyrm.isbn.*]
|
[mypy-bookwyrm.isbn.*]
|
||||||
ignore_errors = False
|
|
||||||
|
|
||||||
[mypy-celerywyrm.*]
|
[mypy-celerywyrm.*]
|
||||||
ignore_errors = False
|
ignore_errors = False
|
||||||
|
|
||||||
|
[mypy-bookwyrm.templatetags.*]
|
||||||
|
ignore_errors = False
|
||||||
|
allow_untyped_calls = True
|
||||||
|
disable_error_code = attr-defined, arg-type, misc
|
||||||
|
|
Loading…
Reference in a new issue