diff --git a/bookwyrm/templates/about/about.html b/bookwyrm/templates/about/about.html index d39d70486..dd3417abb 100644 --- a/bookwyrm/templates/about/about.html +++ b/bookwyrm/templates/about/about.html @@ -2,7 +2,7 @@ {% load humanize %} {% load i18n %} {% load utilities %} -{% load bookwyrm_tags %} +{% load landing_page_tags %} {% load cache %} {% block title %} diff --git a/bookwyrm/templates/book/book.html b/bookwyrm/templates/book/book.html index d2ab99b4b..43f2171c3 100644 --- a/bookwyrm/templates/book/book.html +++ b/bookwyrm/templates/book/book.html @@ -1,6 +1,6 @@ {% extends 'layout.html' %} {% load i18n %} -{% load bookwyrm_tags %} +{% load book_display_tags %} {% load humanize %} {% load utilities %} {% load static %} diff --git a/bookwyrm/templates/book/file_links/links.html b/bookwyrm/templates/book/file_links/links.html index fbc95b566..2147bf6e0 100644 --- a/bookwyrm/templates/book/file_links/links.html +++ b/bookwyrm/templates/book/file_links/links.html @@ -1,5 +1,5 @@ {% load i18n %} -{% load bookwyrm_tags %} +{% load book_display_tags %} {% load utilities %} {% get_book_file_links book as links %} diff --git a/bookwyrm/templates/discover/large-book.html b/bookwyrm/templates/discover/large-book.html index 1fa0afb92..a6ff0aca0 100644 --- a/bookwyrm/templates/discover/large-book.html +++ b/bookwyrm/templates/discover/large-book.html @@ -1,4 +1,4 @@ -{% load bookwyrm_tags %} +{% load rating_tags %} {% load i18n %} {% load utilities %} {% load status_display %} diff --git a/bookwyrm/templates/discover/small-book.html b/bookwyrm/templates/discover/small-book.html index 76732ca14..2da93d522 100644 --- a/bookwyrm/templates/discover/small-book.html +++ b/bookwyrm/templates/discover/small-book.html @@ -1,4 +1,4 @@ -{% load bookwyrm_tags %} +{% load landing_page_tags %} {% load utilities %} {% load i18n %} {% load status_display %} diff --git a/bookwyrm/templates/feed/status.html b/bookwyrm/templates/feed/status.html index e7b9280d7..ed828ae01 100644 --- a/bookwyrm/templates/feed/status.html +++ b/bookwyrm/templates/feed/status.html @@ -1,6 +1,6 @@ {% extends 'feed/layout.html' %} +{% load feed_page_tags %} {% load i18n %} -{% load bookwyrm_tags %} {% block opengraph_images %} diff --git a/bookwyrm/templates/feed/suggested_books.html b/bookwyrm/templates/feed/suggested_books.html index a3d3f1fad..435d4f513 100644 --- a/bookwyrm/templates/feed/suggested_books.html +++ b/bookwyrm/templates/feed/suggested_books.html @@ -1,5 +1,5 @@ {% load i18n %} -{% load bookwyrm_tags %} +{% load feed_page_tags %} {% suggested_books as suggested_books %}
diff --git a/bookwyrm/templates/groups/group.html b/bookwyrm/templates/groups/group.html index 1a71bda89..5f5b58601 100644 --- a/bookwyrm/templates/groups/group.html +++ b/bookwyrm/templates/groups/group.html @@ -1,7 +1,6 @@ {% extends 'groups/layout.html' %} {% load i18n %} -{% load bookwyrm_tags %} -{% load bookwyrm_group_tags %} +{% load group_tags %} {% load markdown %} {% block panel %} diff --git a/bookwyrm/templates/groups/layout.html b/bookwyrm/templates/groups/layout.html index a25c10850..e688bc771 100644 --- a/bookwyrm/templates/groups/layout.html +++ b/bookwyrm/templates/groups/layout.html @@ -1,6 +1,6 @@ {% extends 'layout.html' %} {% load i18n %} -{% load bookwyrm_group_tags %} +{% load group_tags %} {% block title %}{{ group.name }}{% endblock %} diff --git a/bookwyrm/templates/groups/members.html b/bookwyrm/templates/groups/members.html index 8b06d178a..90236843f 100644 --- a/bookwyrm/templates/groups/members.html +++ b/bookwyrm/templates/groups/members.html @@ -1,8 +1,7 @@ {% load i18n %} {% load utilities %} {% load humanize %} -{% load bookwyrm_tags %} -{% load bookwyrm_group_tags %} +{% load group_tags %}

Group Members

{% if group.user == request.user %} diff --git a/bookwyrm/templates/landing/landing.html b/bookwyrm/templates/landing/landing.html index c37717597..ec8bcee06 100644 --- a/bookwyrm/templates/landing/landing.html +++ b/bookwyrm/templates/landing/landing.html @@ -1,7 +1,7 @@ {% extends 'landing/layout.html' %} {% load i18n %} {% load cache %} -{% load bookwyrm_tags %} +{% load landing_page_tags %} {% block panel %} diff --git a/bookwyrm/templates/landing/large-book.html b/bookwyrm/templates/landing/large-book.html index 03ec718ba..9b4fd1f93 100644 --- a/bookwyrm/templates/landing/large-book.html +++ b/bookwyrm/templates/landing/large-book.html @@ -1,4 +1,5 @@ -{% load bookwyrm_tags %} +{% load book_display_tags %} +{% load rating_tags %} {% load markdown %} {% load i18n %} diff --git a/bookwyrm/templates/landing/small-book.html b/bookwyrm/templates/landing/small-book.html index 813fb797d..31b095803 100644 --- a/bookwyrm/templates/landing/small-book.html +++ b/bookwyrm/templates/landing/small-book.html @@ -1,4 +1,4 @@ -{% load bookwyrm_tags %} +{% load rating_tags %} {% load i18n %} {% if book %} diff --git a/bookwyrm/templates/lists/embed-list.html b/bookwyrm/templates/lists/embed-list.html index 54dc80ab2..186681670 100644 --- a/bookwyrm/templates/lists/embed-list.html +++ b/bookwyrm/templates/lists/embed-list.html @@ -1,7 +1,8 @@ {% extends 'embed-layout.html' %} {% load i18n %} -{% load bookwyrm_tags %} -{% load bookwyrm_group_tags %} +{% load book_display_tags %} +{% load rating_tags %} +{% load group_tags %} {% load markdown %} {% block title %}{% blocktrans with list_name=list.name owner=list.user.display_name %}{{ list_name }}, a list by {{owner}}{% endblocktrans %}{% endblock title %} diff --git a/bookwyrm/templates/lists/list.html b/bookwyrm/templates/lists/list.html index 880413cdb..c44d3fe36 100644 --- a/bookwyrm/templates/lists/list.html +++ b/bookwyrm/templates/lists/list.html @@ -1,7 +1,8 @@ {% extends 'lists/layout.html' %} {% load i18n %} -{% load bookwyrm_tags %} -{% load bookwyrm_group_tags %} +{% load rating_tags %} +{% load book_display_tags %} +{% load group_tags %} {% load markdown %} {% block breadcrumbs %} diff --git a/bookwyrm/templates/notifications/items/layout.html b/bookwyrm/templates/notifications/items/layout.html index 6ddbdcc31..d595bf20e 100644 --- a/bookwyrm/templates/notifications/items/layout.html +++ b/bookwyrm/templates/notifications/items/layout.html @@ -1,4 +1,4 @@ -{% load bookwyrm_tags %} +{% load notification_page_tags %} {% related_status notification as related_status %}
diff --git a/bookwyrm/templates/shelf/shelf.html b/bookwyrm/templates/shelf/shelf.html index 0e295a873..a630918a6 100644 --- a/bookwyrm/templates/shelf/shelf.html +++ b/bookwyrm/templates/shelf/shelf.html @@ -1,5 +1,5 @@ {% extends 'layout.html' %} -{% load bookwyrm_tags %} +{% load shelf_tags %} {% load utilities %} {% load humanize %} {% load i18n %} diff --git a/bookwyrm/templates/snippets/add_to_group_button.html b/bookwyrm/templates/snippets/add_to_group_button.html index 2785d7c01..20194623a 100644 --- a/bookwyrm/templates/snippets/add_to_group_button.html +++ b/bookwyrm/templates/snippets/add_to_group_button.html @@ -1,5 +1,6 @@ {% load i18n %} -{% load bookwyrm_group_tags %} +{% load group_tags %} + {% if request.user == user or not request.user == group.user or not request.user.is_authenticated %} {% elif user in request.user.blocks.all %} {% include 'snippets/block_button.html' with blocks=True %} diff --git a/bookwyrm/templates/snippets/create_status/comment.html b/bookwyrm/templates/snippets/create_status/comment.html index 10cdf639f..65b322699 100644 --- a/bookwyrm/templates/snippets/create_status/comment.html +++ b/bookwyrm/templates/snippets/create_status/comment.html @@ -1,5 +1,5 @@ {% extends "snippets/create_status/layout.html" %} -{% load bookwyrm_tags %} +{% load shelf_tags %} {% load i18n %} {% load utilities %} {% load status_display %} diff --git a/bookwyrm/templates/snippets/create_status/layout.html b/bookwyrm/templates/snippets/create_status/layout.html index 8d24fa021..0585638d9 100644 --- a/bookwyrm/templates/snippets/create_status/layout.html +++ b/bookwyrm/templates/snippets/create_status/layout.html @@ -1,4 +1,3 @@ -{% load bookwyrm_tags %} {% load i18n %} {% load utilities %} {% load status_display %} diff --git a/bookwyrm/templates/snippets/create_status/quotation.html b/bookwyrm/templates/snippets/create_status/quotation.html index cf472c113..a9ddb17f4 100644 --- a/bookwyrm/templates/snippets/create_status/quotation.html +++ b/bookwyrm/templates/snippets/create_status/quotation.html @@ -1,5 +1,4 @@ {% extends "snippets/create_status/layout.html" %} -{% load bookwyrm_tags %} {% load utilities %} {% load status_display %} {% load i18n %} diff --git a/bookwyrm/templates/snippets/create_status/review.html b/bookwyrm/templates/snippets/create_status/review.html index 13d349caf..e52a7c1c4 100644 --- a/bookwyrm/templates/snippets/create_status/review.html +++ b/bookwyrm/templates/snippets/create_status/review.html @@ -1,5 +1,4 @@ {% extends "snippets/create_status/layout.html" %} -{% load bookwyrm_tags %} {% load utilities %} {% load status_display %} {% load i18n %} diff --git a/bookwyrm/templates/snippets/join_invitation_buttons.html b/bookwyrm/templates/snippets/join_invitation_buttons.html index 46c4071d4..b77ce43cb 100644 --- a/bookwyrm/templates/snippets/join_invitation_buttons.html +++ b/bookwyrm/templates/snippets/join_invitation_buttons.html @@ -1,5 +1,6 @@ {% load i18n %} -{% load bookwyrm_group_tags %} +{% load group_tags %} + {% if group|is_invited:request.user %}
diff --git a/bookwyrm/templates/snippets/rate_action.html b/bookwyrm/templates/snippets/rate_action.html index 767039a3d..6ecbceffc 100644 --- a/bookwyrm/templates/snippets/rate_action.html +++ b/bookwyrm/templates/snippets/rate_action.html @@ -1,5 +1,6 @@ {% load i18n %} -{% load bookwyrm_tags %} +{% load rating_tags %} + {% if request.user.is_authenticated %} {% trans "Leave a rating" %}
diff --git a/bookwyrm/templates/snippets/remove_from_group_button.html b/bookwyrm/templates/snippets/remove_from_group_button.html index 1672e0388..2e08760f3 100644 --- a/bookwyrm/templates/snippets/remove_from_group_button.html +++ b/bookwyrm/templates/snippets/remove_from_group_button.html @@ -1,5 +1,6 @@ {% load i18n %} -{% load bookwyrm_group_tags %} +{% load group_tags %} + {% if request.user == user or not request.user == group.user or not request.user.is_authenticated %} {% else %} {% if user in request.user.blocks.all %} diff --git a/bookwyrm/templates/snippets/shelf_selector.html b/bookwyrm/templates/snippets/shelf_selector.html index 323e04a27..ea6096668 100644 --- a/bookwyrm/templates/snippets/shelf_selector.html +++ b/bookwyrm/templates/snippets/shelf_selector.html @@ -1,7 +1,7 @@ {% extends 'components/dropdown.html' %} -{% load i18n %} -{% load bookwyrm_tags %} +{% load shelf_tags %} {% load utilities %} +{% load i18n %} {% block dropdown-trigger %} {% trans "Move book" %} diff --git a/bookwyrm/templates/snippets/shelve_button/shelve_button.html b/bookwyrm/templates/snippets/shelve_button/shelve_button.html index 37c57fc23..04dc4e4b0 100644 --- a/bookwyrm/templates/snippets/shelve_button/shelve_button.html +++ b/bookwyrm/templates/snippets/shelve_button/shelve_button.html @@ -1,5 +1,5 @@ -{% load bookwyrm_tags %} {% load utilities %} +{% load shelf_tags %} {% if request.user.is_authenticated %} diff --git a/bookwyrm/templates/snippets/shelve_button/shelve_button_dropdown_options.html b/bookwyrm/templates/snippets/shelve_button/shelve_button_dropdown_options.html index 15e2bb519..5a29f9ad7 100644 --- a/bookwyrm/templates/snippets/shelve_button/shelve_button_dropdown_options.html +++ b/bookwyrm/templates/snippets/shelve_button/shelve_button_dropdown_options.html @@ -1,5 +1,5 @@ -{% load bookwyrm_tags %} {% load utilities %} +{% load shelf_tags %} {% load i18n %} {% with next_shelf_identifier=active_shelf.shelf.identifier|next_shelf %} diff --git a/bookwyrm/templates/snippets/shelve_button/shelve_button_options.html b/bookwyrm/templates/snippets/shelve_button/shelve_button_options.html index d6ca99337..04f4bdc26 100644 --- a/bookwyrm/templates/snippets/shelve_button/shelve_button_options.html +++ b/bookwyrm/templates/snippets/shelve_button/shelve_button_options.html @@ -1,5 +1,5 @@ -{% load bookwyrm_tags %} {% load utilities %} +{% load shelf_tags %} {% load i18n %} {% with next_shelf_identifier=active_shelf.shelf.identifier|next_shelf %} diff --git a/bookwyrm/templates/snippets/status/content_status.html b/bookwyrm/templates/snippets/status/content_status.html index 01734cc78..fed325841 100644 --- a/bookwyrm/templates/snippets/status/content_status.html +++ b/bookwyrm/templates/snippets/status/content_status.html @@ -1,4 +1,5 @@ -{% load bookwyrm_tags %} +{% load book_display_tags %} +{% load rating_tags %} {% load markdown %} {% load i18n %} {% load static %} diff --git a/bookwyrm/templates/snippets/status/generated_status.html b/bookwyrm/templates/snippets/status/generated_status.html index 1234ae7ce..f91751445 100644 --- a/bookwyrm/templates/snippets/status/generated_status.html +++ b/bookwyrm/templates/snippets/status/generated_status.html @@ -1,6 +1,6 @@ {% spaceless %} -{% load bookwyrm_tags %} +{% load book_display_tags %} {% load markdown %} {% load i18n %} {% load cache %} diff --git a/bookwyrm/templates/user/layout.html b/bookwyrm/templates/user/layout.html index 03e3dfce8..65b6a9ac9 100755 --- a/bookwyrm/templates/user/layout.html +++ b/bookwyrm/templates/user/layout.html @@ -4,7 +4,7 @@ {% load utilities %} {% load markdown %} {% load layout %} -{% load bookwyrm_group_tags %} +{% load group_tags %} {% block title %}{{ user.display_name }}{% endblock %} diff --git a/bookwyrm/templates/user/user_preview.html b/bookwyrm/templates/user/user_preview.html index c46563e59..23dd3ab55 100755 --- a/bookwyrm/templates/user/user_preview.html +++ b/bookwyrm/templates/user/user_preview.html @@ -1,7 +1,7 @@ {% load i18n %} {% load humanize %} {% load utilities %} -{% load bookwyrm_tags %} +{% load user_page_tags %}
diff --git a/bookwyrm/templatetags/book_display_tags.py b/bookwyrm/templatetags/book_display_tags.py new file mode 100644 index 000000000..9db79f8e4 --- /dev/null +++ b/bookwyrm/templatetags/book_display_tags.py @@ -0,0 +1,17 @@ +""" template filters """ +from django import template + + +register = template.Library() + + +@register.filter(name="book_description") +def get_book_description(book): + """use the work's text if the book doesn't have it""" + return book.description or book.parent_work.description + + +@register.simple_tag(takes_context=False) +def get_book_file_links(book): + """links for a book""" + return book.file_links.filter(domain__status="approved") diff --git a/bookwyrm/templatetags/bookwyrm_tags.py b/bookwyrm/templatetags/bookwyrm_tags.py deleted file mode 100644 index 4eff0d826..000000000 --- a/bookwyrm/templatetags/bookwyrm_tags.py +++ /dev/null @@ -1,226 +0,0 @@ -""" template filters """ -from django import template -from django.db.models import Avg, StdDev, Count, F, Q - -from bookwyrm import models -from bookwyrm.utils import cache -from bookwyrm.views.feed import get_suggested_books - - -register = template.Library() - - -@register.filter(name="rating") -def get_rating(book, user): - """get the overall rating of a book""" - return cache.get_or_set( - f"book-rating-{book.parent_work.id}-{user.id}", - lambda u, b: models.Review.privacy_filter(u) - .filter(book__parent_work__editions=b) - .aggregate(Avg("rating"))["rating__avg"] - or 0, - user, - book, - timeout=15552000, - ) - - -@register.filter(name="user_rating") -def get_user_rating(book, user): - """get a user's rating of a book""" - rating = ( - models.Review.objects.filter( - user=user, - book=book, - rating__isnull=False, - deleted=False, - ) - .order_by("-published_date") - .first() - ) - if rating: - return rating.rating - return 0 - - -@register.filter(name="is_book_on_shelf") -def get_is_book_on_shelf(book, shelf): - """is a book on a shelf""" - return cache.get_or_set( - f"book-on-shelf-{book.id}-{shelf.id}", - lambda b, s: s.books.filter(id=b.id).exists(), - book, - shelf, - timeout=15552000, - ) - - -@register.filter(name="book_description") -def get_book_description(book): - """use the work's text if the book doesn't have it""" - return book.description or book.parent_work.description - - -@register.filter(name="next_shelf") -def get_next_shelf(current_shelf): - """shelf you'd use to update reading progress""" - if current_shelf == "to-read": - return "reading" - if current_shelf == "reading": - return "read" - if current_shelf == "read": - return "complete" - return "to-read" - - -@register.filter(name="load_subclass") -def load_subclass(status): - """sometimes you didn't select_subclass""" - if hasattr(status, "quotation"): - return status.quotation - if hasattr(status, "review"): - return status.review - if hasattr(status, "comment"): - return status.comment - if hasattr(status, "generatednote"): - return status.generatednote - return status - - -@register.simple_tag(takes_context=False) -def get_book_superlatives(): - """get book stats for the about page""" - total_ratings = models.Review.objects.filter(local=True, deleted=False).count() - data = {} - data["top_rated"] = ( - models.Work.objects.annotate( - rating=Avg( - "editions__review__rating", - filter=Q(editions__review__local=True, editions__review__deleted=False), - ), - rating_count=Count( - "editions__review", - filter=Q(editions__review__local=True, editions__review__deleted=False), - ), - ) - .annotate(weighted=F("rating") * F("rating_count") / total_ratings) - .filter(rating__gt=4, weighted__gt=0) - .order_by("-weighted") - .first() - ) - - data["controversial"] = ( - models.Work.objects.annotate( - deviation=StdDev( - "editions__review__rating", - filter=Q(editions__review__local=True, editions__review__deleted=False), - ), - rating_count=Count( - "editions__review", - filter=Q(editions__review__local=True, editions__review__deleted=False), - ), - ) - .annotate(weighted=F("deviation") * F("rating_count") / total_ratings) - .filter(weighted__gt=0) - .order_by("-weighted") - .first() - ) - - data["wanted"] = ( - models.Work.objects.annotate( - shelf_count=Count( - "editions__shelves", filter=Q(editions__shelves__identifier="to-read") - ) - ) - .order_by("-shelf_count") - .first() - ) - return data - - -@register.simple_tag(takes_context=False) -def related_status(notification): - """for notifications""" - if not notification.related_status: - return None - return load_subclass(notification.related_status) - - -@register.simple_tag(takes_context=True) -def active_shelf(context, book): - """check what shelf a user has a book on, if any""" - user = context["request"].user - return ( - cache.get_or_set( - f"active_shelf-{user.id}-{book.id}", - lambda u, b: ( - models.ShelfBook.objects.filter( - shelf__user=u, - book__parent_work__editions=b, - ).first() - or False - ), - user, - book, - timeout=15552000, - ) - or {"book": book} - ) - - -@register.simple_tag(takes_context=False) -def latest_read_through(book, user): - """the most recent read activity""" - return cache.get_or_set( - f"latest_read_through-{user.id}-{book.id}", - lambda u, b: ( - models.ReadThrough.objects.filter(user=u, book=b, is_active=True) - .order_by("-start_date") - .first() - or False - ), - user, - book, - timeout=15552000, - ) - - -@register.simple_tag(takes_context=False) -def get_landing_books(): - """list of books for the landing page""" - return list( - set( - models.Edition.objects.filter( - review__published_date__isnull=False, - review__deleted=False, - review__user__local=True, - review__privacy__in=["public", "unlisted"], - ) - .exclude(cover__exact="") - .distinct() - .order_by("-review__published_date")[:6] - ) - ) - - -@register.simple_tag(takes_context=True) -def mutuals_count(context, user): - """how many users that you follow, follow them""" - viewer = context["request"].user - if not viewer.is_authenticated: - return None - return user.followers.filter(followers=viewer).count() - - -@register.simple_tag(takes_context=True) -def suggested_books(context): - """get books for suggested books panel""" - # this happens here instead of in the view so that the template snippet can - # be cached in the template - return get_suggested_books(context["request"].user) - - -@register.simple_tag(takes_context=False) -def get_book_file_links(book): - """links for a book""" - return book.file_links.filter(domain__status="approved") diff --git a/bookwyrm/templatetags/feed_page_tags.py b/bookwyrm/templatetags/feed_page_tags.py new file mode 100644 index 000000000..3d346b9a2 --- /dev/null +++ b/bookwyrm/templatetags/feed_page_tags.py @@ -0,0 +1,28 @@ +""" tags used on the feed pages """ +from django import template +from bookwyrm.views.feed import get_suggested_books + + +register = template.Library() + + +@register.filter(name="load_subclass") +def load_subclass(status): + """sometimes you didn't select_subclass""" + if hasattr(status, "quotation"): + return status.quotation + if hasattr(status, "review"): + return status.review + if hasattr(status, "comment"): + return status.comment + if hasattr(status, "generatednote"): + return status.generatednote + return status + + +@register.simple_tag(takes_context=True) +def suggested_books(context): + """get books for suggested books panel""" + # this happens here instead of in the view so that the template snippet can + # be cached in the template + return get_suggested_books(context["request"].user) diff --git a/bookwyrm/templatetags/bookwyrm_group_tags.py b/bookwyrm/templatetags/group_tags.py similarity index 100% rename from bookwyrm/templatetags/bookwyrm_group_tags.py rename to bookwyrm/templatetags/group_tags.py diff --git a/bookwyrm/templatetags/landing_page_tags.py b/bookwyrm/templatetags/landing_page_tags.py new file mode 100644 index 000000000..e7d943603 --- /dev/null +++ b/bookwyrm/templatetags/landing_page_tags.py @@ -0,0 +1,76 @@ +""" template filters """ +from django import template +from django.db.models import Avg, StdDev, Count, F, Q + +from bookwyrm import models + +register = template.Library() + + +@register.simple_tag(takes_context=False) +def get_book_superlatives(): + """get book stats for the about page""" + total_ratings = models.Review.objects.filter(local=True, deleted=False).count() + data = {} + data["top_rated"] = ( + models.Work.objects.annotate( + rating=Avg( + "editions__review__rating", + filter=Q(editions__review__local=True, editions__review__deleted=False), + ), + rating_count=Count( + "editions__review", + filter=Q(editions__review__local=True, editions__review__deleted=False), + ), + ) + .annotate(weighted=F("rating") * F("rating_count") / total_ratings) + .filter(rating__gt=4, weighted__gt=0) + .order_by("-weighted") + .first() + ) + + data["controversial"] = ( + models.Work.objects.annotate( + deviation=StdDev( + "editions__review__rating", + filter=Q(editions__review__local=True, editions__review__deleted=False), + ), + rating_count=Count( + "editions__review", + filter=Q(editions__review__local=True, editions__review__deleted=False), + ), + ) + .annotate(weighted=F("deviation") * F("rating_count") / total_ratings) + .filter(weighted__gt=0) + .order_by("-weighted") + .first() + ) + + data["wanted"] = ( + models.Work.objects.annotate( + shelf_count=Count( + "editions__shelves", filter=Q(editions__shelves__identifier="to-read") + ) + ) + .order_by("-shelf_count") + .first() + ) + return data + + +@register.simple_tag(takes_context=False) +def get_landing_books(): + """list of books for the landing page""" + return list( + set( + models.Edition.objects.filter( + review__published_date__isnull=False, + review__deleted=False, + review__user__local=True, + review__privacy__in=["public", "unlisted"], + ) + .exclude(cover__exact="") + .distinct() + .order_by("-review__published_date")[:6] + ) + ) diff --git a/bookwyrm/templatetags/notification_page_tags.py b/bookwyrm/templatetags/notification_page_tags.py new file mode 100644 index 000000000..28fa2afb5 --- /dev/null +++ b/bookwyrm/templatetags/notification_page_tags.py @@ -0,0 +1,14 @@ +""" tags used on the feed pages """ +from django import template +from bookwyrm.templatetags.feed_page_tags import load_subclass + + +register = template.Library() + + +@register.simple_tag(takes_context=False) +def related_status(notification): + """for notifications""" + if not notification.related_status: + return None + return load_subclass(notification.related_status) diff --git a/bookwyrm/templatetags/rating_tags.py b/bookwyrm/templatetags/rating_tags.py new file mode 100644 index 000000000..670599e25 --- /dev/null +++ b/bookwyrm/templatetags/rating_tags.py @@ -0,0 +1,42 @@ +""" template filters """ +from django import template +from django.db.models import Avg + +from bookwyrm import models +from bookwyrm.utils import cache + + +register = template.Library() + + +@register.filter(name="rating") +def get_rating(book, user): + """get the overall rating of a book""" + return cache.get_or_set( + f"book-rating-{book.parent_work.id}-{user.id}", + lambda u, b: models.Review.privacy_filter(u) + .filter(book__parent_work__editions=b, rating__gt=0) + .aggregate(Avg("rating"))["rating__avg"] + or 0, + user, + book, + timeout=15552000, + ) + + +@register.filter(name="user_rating") +def get_user_rating(book, user): + """get a user's rating of a book""" + rating = ( + models.Review.objects.filter( + user=user, + book=book, + rating__isnull=False, + deleted=False, + ) + .order_by("-published_date") + .first() + ) + if rating: + return rating.rating + return 0 diff --git a/bookwyrm/templatetags/shelf_tags.py b/bookwyrm/templatetags/shelf_tags.py new file mode 100644 index 000000000..7aef638f4 --- /dev/null +++ b/bookwyrm/templatetags/shelf_tags.py @@ -0,0 +1,71 @@ +""" Filters and tags related to shelving books """ +from django import template + +from bookwyrm import models +from bookwyrm.utils import cache + + +register = template.Library() + + +@register.filter(name="is_book_on_shelf") +def get_is_book_on_shelf(book, shelf): + """is a book on a shelf""" + return cache.get_or_set( + f"book-on-shelf-{book.id}-{shelf.id}", + lambda b, s: s.books.filter(id=b.id).exists(), + book, + shelf, + timeout=15552000, + ) + + +@register.filter(name="next_shelf") +def get_next_shelf(current_shelf): + """shelf you'd use to update reading progress""" + if current_shelf == "to-read": + return "reading" + if current_shelf == "reading": + return "read" + if current_shelf == "read": + return "complete" + return "to-read" + + +@register.simple_tag(takes_context=True) +def active_shelf(context, book): + """check what shelf a user has a book on, if any""" + user = context["request"].user + return ( + cache.get_or_set( + f"active_shelf-{user.id}-{book.id}", + lambda u, b: ( + models.ShelfBook.objects.filter( + shelf__user=u, + book__parent_work__editions=b, + ).first() + or False + ), + user, + book, + timeout=15552000, + ) + or {"book": book} + ) + + +@register.simple_tag(takes_context=False) +def latest_read_through(book, user): + """the most recent read activity""" + return cache.get_or_set( + f"latest_read_through-{user.id}-{book.id}", + lambda u, b: ( + models.ReadThrough.objects.filter(user=u, book=b, is_active=True) + .order_by("-start_date") + .first() + or False + ), + user, + book, + timeout=15552000, + ) diff --git a/bookwyrm/templatetags/user_page_tags.py b/bookwyrm/templatetags/user_page_tags.py new file mode 100644 index 000000000..b3a82597e --- /dev/null +++ b/bookwyrm/templatetags/user_page_tags.py @@ -0,0 +1,14 @@ +""" template filters """ +from django import template + + +register = template.Library() + + +@register.simple_tag(takes_context=True) +def mutuals_count(context, user): + """how many users that you follow, follow them""" + viewer = context["request"].user + if not viewer.is_authenticated: + return None + return user.followers.filter(followers=viewer).count() diff --git a/bookwyrm/tests/templatetags/test_book_display_tags.py b/bookwyrm/tests/templatetags/test_book_display_tags.py new file mode 100644 index 000000000..54ae8806b --- /dev/null +++ b/bookwyrm/tests/templatetags/test_book_display_tags.py @@ -0,0 +1,62 @@ +""" style fixes and lookups for templates """ +from unittest.mock import patch + +from django.test import TestCase + +from bookwyrm import models +from bookwyrm.templatetags import book_display_tags + + +@patch("bookwyrm.activitystreams.add_status_task.delay") +@patch("bookwyrm.activitystreams.remove_status_task.delay") +@patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async") +class BookDisplayTags(TestCase): + """lotta different things here""" + + def setUp(self): + """create some filler objects""" + with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch( + "bookwyrm.activitystreams.populate_stream_task.delay" + ), patch("bookwyrm.lists_stream.populate_lists_task.delay"): + self.user = models.User.objects.create_user( + "mouse@example.com", + "mouse@mouse.mouse", + "mouseword", + local=True, + localname="mouse", + ) + self.book = models.Edition.objects.create(title="Test Book") + + def test_get_book_description(self, *_): + """grab it from the edition or the parent""" + work = models.Work.objects.create(title="Test Work") + self.book.parent_work = work + self.book.save() + + self.assertIsNone(book_display_tags.get_book_description(self.book)) + + work.description = "hi" + work.save() + self.assertEqual(book_display_tags.get_book_description(self.book), "hi") + + self.book.description = "hello" + self.book.save() + self.assertEqual(book_display_tags.get_book_description(self.book), "hello") + + def test_get_book_file_links(self, *_): + """load approved links""" + link = models.FileLink.objects.create( + book=self.book, + url="https://web.site/hello", + ) + links = book_display_tags.get_book_file_links(self.book) + # the link is pending + self.assertFalse(links.exists()) + + domain = link.domain + domain.status = "approved" + domain.save() + + links = book_display_tags.get_book_file_links(self.book) + self.assertTrue(links.exists()) + self.assertEqual(links[0], link) diff --git a/bookwyrm/tests/templatetags/test_bookwyrm_tags.py b/bookwyrm/tests/templatetags/test_bookwyrm_tags.py deleted file mode 100644 index 7b8d199de..000000000 --- a/bookwyrm/tests/templatetags/test_bookwyrm_tags.py +++ /dev/null @@ -1,101 +0,0 @@ -""" style fixes and lookups for templates """ -from unittest.mock import patch - -from django.test import TestCase - -from bookwyrm import models -from bookwyrm.templatetags import bookwyrm_tags - - -@patch("bookwyrm.activitystreams.add_status_task.delay") -@patch("bookwyrm.activitystreams.remove_status_task.delay") -class BookWyrmTags(TestCase): - """lotta different things here""" - - def setUp(self): - """create some filler objects""" - with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch( - "bookwyrm.activitystreams.populate_stream_task.delay" - ), patch("bookwyrm.lists_stream.populate_lists_task.delay"): - self.user = models.User.objects.create_user( - "mouse@example.com", - "mouse@mouse.mouse", - "mouseword", - local=True, - localname="mouse", - ) - with patch("bookwyrm.models.user.set_remote_server.delay"): - self.remote_user = models.User.objects.create_user( - "rat", - "rat@rat.rat", - "ratword", - remote_id="http://example.com/rat", - local=False, - ) - self.book = models.Edition.objects.create(title="Test Book") - - def test_get_user_rating(self, *_): - """get a user's most recent rating of a book""" - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): - models.Review.objects.create(user=self.user, book=self.book, rating=3) - self.assertEqual(bookwyrm_tags.get_user_rating(self.book, self.user), 3) - - def test_get_user_rating_doesnt_exist(self, *_): - """there is no rating available""" - self.assertEqual(bookwyrm_tags.get_user_rating(self.book, self.user), 0) - - def test_get_book_description(self, *_): - """grab it from the edition or the parent""" - work = models.Work.objects.create(title="Test Work") - self.book.parent_work = work - self.book.save() - - self.assertIsNone(bookwyrm_tags.get_book_description(self.book)) - - work.description = "hi" - work.save() - self.assertEqual(bookwyrm_tags.get_book_description(self.book), "hi") - - self.book.description = "hello" - self.book.save() - self.assertEqual(bookwyrm_tags.get_book_description(self.book), "hello") - - def test_get_next_shelf(self, *_): - """self progress helper""" - self.assertEqual(bookwyrm_tags.get_next_shelf("to-read"), "reading") - self.assertEqual(bookwyrm_tags.get_next_shelf("reading"), "read") - self.assertEqual(bookwyrm_tags.get_next_shelf("read"), "complete") - self.assertEqual(bookwyrm_tags.get_next_shelf("blooooga"), "to-read") - - @patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async") - def test_load_subclass(self, *_): - """get a status' real type""" - review = models.Review.objects.create(user=self.user, book=self.book, rating=3) - status = models.Status.objects.get(id=review.id) - self.assertIsInstance(status, models.Status) - self.assertIsInstance(bookwyrm_tags.load_subclass(status), models.Review) - - quote = models.Quotation.objects.create( - user=self.user, book=self.book, content="hi" - ) - status = models.Status.objects.get(id=quote.id) - self.assertIsInstance(status, models.Status) - self.assertIsInstance(bookwyrm_tags.load_subclass(status), models.Quotation) - - comment = models.Comment.objects.create( - user=self.user, book=self.book, content="hi" - ) - status = models.Status.objects.get(id=comment.id) - self.assertIsInstance(status, models.Status) - self.assertIsInstance(bookwyrm_tags.load_subclass(status), models.Comment) - - def test_related_status(self, *_): - """gets the subclass model for a notification status""" - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): - status = models.Status.objects.create(content="hi", user=self.user) - notification = models.Notification.objects.create( - user=self.user, notification_type="MENTION", related_status=status - ) - - result = bookwyrm_tags.related_status(notification) - self.assertIsInstance(result, models.Status) diff --git a/bookwyrm/tests/templatetags/test_feed_page_tags.py b/bookwyrm/tests/templatetags/test_feed_page_tags.py new file mode 100644 index 000000000..5e5dc2357 --- /dev/null +++ b/bookwyrm/tests/templatetags/test_feed_page_tags.py @@ -0,0 +1,49 @@ +""" style fixes and lookups for templates """ +from unittest.mock import patch + +from django.test import TestCase + +from bookwyrm import models +from bookwyrm.templatetags import feed_page_tags + + +@patch("bookwyrm.activitystreams.add_status_task.delay") +@patch("bookwyrm.activitystreams.remove_status_task.delay") +class FeedPageTags(TestCase): + """lotta different things here""" + + def setUp(self): + """create some filler objects""" + with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch( + "bookwyrm.activitystreams.populate_stream_task.delay" + ), patch("bookwyrm.lists_stream.populate_lists_task.delay"): + self.user = models.User.objects.create_user( + "mouse@example.com", + "mouse@mouse.mouse", + "mouseword", + local=True, + localname="mouse", + ) + self.book = models.Edition.objects.create(title="Test Book") + + @patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async") + def test_load_subclass(self, *_): + """get a status' real type""" + review = models.Review.objects.create(user=self.user, book=self.book, rating=3) + status = models.Status.objects.get(id=review.id) + self.assertIsInstance(status, models.Status) + self.assertIsInstance(feed_page_tags.load_subclass(status), models.Review) + + quote = models.Quotation.objects.create( + user=self.user, book=self.book, content="hi" + ) + status = models.Status.objects.get(id=quote.id) + self.assertIsInstance(status, models.Status) + self.assertIsInstance(feed_page_tags.load_subclass(status), models.Quotation) + + comment = models.Comment.objects.create( + user=self.user, book=self.book, content="hi" + ) + status = models.Status.objects.get(id=comment.id) + self.assertIsInstance(status, models.Status) + self.assertIsInstance(feed_page_tags.load_subclass(status), models.Comment) diff --git a/bookwyrm/tests/templatetags/test_notification_page_tags.py b/bookwyrm/tests/templatetags/test_notification_page_tags.py new file mode 100644 index 000000000..3c92181b2 --- /dev/null +++ b/bookwyrm/tests/templatetags/test_notification_page_tags.py @@ -0,0 +1,37 @@ +""" style fixes and lookups for templates """ +from unittest.mock import patch + +from django.test import TestCase + +from bookwyrm import models +from bookwyrm.templatetags import notification_page_tags + + +@patch("bookwyrm.activitystreams.add_status_task.delay") +@patch("bookwyrm.activitystreams.remove_status_task.delay") +class NotificationPageTags(TestCase): + """lotta different things here""" + + def setUp(self): + """create some filler objects""" + with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch( + "bookwyrm.activitystreams.populate_stream_task.delay" + ), patch("bookwyrm.lists_stream.populate_lists_task.delay"): + self.user = models.User.objects.create_user( + "mouse@example.com", + "mouse@mouse.mouse", + "mouseword", + local=True, + localname="mouse", + ) + + def test_related_status(self, *_): + """gets the subclass model for a notification status""" + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): + status = models.Status.objects.create(content="hi", user=self.user) + notification = models.Notification.objects.create( + user=self.user, notification_type="MENTION", related_status=status + ) + + result = notification_page_tags.related_status(notification) + self.assertIsInstance(result, models.Status) diff --git a/bookwyrm/tests/templatetags/test_rating_tags.py b/bookwyrm/tests/templatetags/test_rating_tags.py new file mode 100644 index 000000000..c00f20726 --- /dev/null +++ b/bookwyrm/tests/templatetags/test_rating_tags.py @@ -0,0 +1,80 @@ +""" Gettings book ratings """ +from unittest.mock import patch + +from django.test import TestCase + +from bookwyrm import models +from bookwyrm.templatetags import rating_tags + + +@patch("bookwyrm.activitystreams.add_status_task.delay") +@patch("bookwyrm.activitystreams.remove_status_task.delay") +class RatingTags(TestCase): + """lotta different things here""" + + def setUp(self): + """create some filler objects""" + with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch( + "bookwyrm.activitystreams.populate_stream_task.delay" + ), patch("bookwyrm.lists_stream.populate_lists_task.delay"): + self.local_user = models.User.objects.create_user( + "mouse@example.com", + "mouse@mouse.mouse", + "mouseword", + local=True, + localname="mouse", + ) + with patch("bookwyrm.models.user.set_remote_server.delay"): + self.remote_user = models.User.objects.create_user( + "rat", + "rat@rat.rat", + "ratword", + remote_id="http://example.com/rat", + local=False, + ) + work = models.Work.objects.create(title="Work title") + self.book = models.Edition.objects.create( + title="Test Book", + parent_work=work, + ) + + @patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async") + def test_get_rating(self, *_): + """privacy filtered rating""" + # follows-only: not included + models.ReviewRating.objects.create( + user=self.remote_user, + rating=5, + book=self.book, + privacy="followers", + ) + self.assertEqual(rating_tags.get_rating(self.book, self.local_user), 0) + + # public: included + models.ReviewRating.objects.create( + user=self.remote_user, + rating=5, + book=self.book, + privacy="public", + ) + self.assertEqual(rating_tags.get_rating(self.book, self.local_user), 5) + + # rating unset: not included + models.Review.objects.create( + name="blah", + user=self.local_user, + rating=0, + book=self.book, + privacy="public", + ) + self.assertEqual(rating_tags.get_rating(self.book, self.local_user), 5) + + def test_get_user_rating(self, *_): + """get a user's most recent rating of a book""" + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): + models.Review.objects.create(user=self.local_user, book=self.book, rating=3) + self.assertEqual(rating_tags.get_user_rating(self.book, self.local_user), 3) + + def test_get_user_rating_doesnt_exist(self, *_): + """there is no rating available""" + self.assertEqual(rating_tags.get_user_rating(self.book, self.local_user), 0) diff --git a/bookwyrm/tests/templatetags/test_shelf_tags.py b/bookwyrm/tests/templatetags/test_shelf_tags.py new file mode 100644 index 000000000..5a88604dd --- /dev/null +++ b/bookwyrm/tests/templatetags/test_shelf_tags.py @@ -0,0 +1,70 @@ +""" style fixes and lookups for templates """ +from unittest.mock import patch + +from django.test import TestCase +from django.test.client import RequestFactory + +from bookwyrm import models +from bookwyrm.templatetags import shelf_tags + + +@patch("bookwyrm.activitystreams.add_status_task.delay") +@patch("bookwyrm.activitystreams.remove_status_task.delay") +@patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async") +@patch("bookwyrm.activitystreams.add_book_statuses_task.delay") +class ShelfTags(TestCase): + """lotta different things here""" + + def setUp(self): + """create some filler objects""" + self.factory = RequestFactory() + with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch( + "bookwyrm.activitystreams.populate_stream_task.delay" + ), patch("bookwyrm.lists_stream.populate_lists_task.delay"): + self.local_user = models.User.objects.create_user( + "mouse@example.com", + "mouse@mouse.mouse", + "mouseword", + local=True, + localname="mouse", + ) + with patch("bookwyrm.models.user.set_remote_server.delay"): + self.remote_user = models.User.objects.create_user( + "rat", + "rat@rat.rat", + "ratword", + remote_id="http://example.com/rat", + local=False, + ) + self.book = models.Edition.objects.create( + title="Test Book", + parent_work=models.Work.objects.create(title="Test work"), + ) + + def test_get_is_book_on_shelf(self, *_): + """check if a book is on a shelf""" + shelf = self.local_user.shelf_set.first() + self.assertFalse(shelf_tags.get_is_book_on_shelf(self.book, shelf)) + models.ShelfBook.objects.create( + shelf=shelf, book=self.book, user=self.local_user + ) + self.assertTrue(shelf_tags.get_is_book_on_shelf(self.book, shelf)) + + def test_get_next_shelf(self, *_): + """self progress helper""" + self.assertEqual(shelf_tags.get_next_shelf("to-read"), "reading") + self.assertEqual(shelf_tags.get_next_shelf("reading"), "read") + self.assertEqual(shelf_tags.get_next_shelf("read"), "complete") + self.assertEqual(shelf_tags.get_next_shelf("blooooga"), "to-read") + + def test_active_shelf(self, *_): + """get the shelf a book is on""" + shelf = self.local_user.shelf_set.first() + request = self.factory.get("") + request.user = self.local_user + context = {"request": request} + self.assertIsInstance(shelf_tags.active_shelf(context, self.book), dict) + models.ShelfBook.objects.create( + shelf=shelf, book=self.book, user=self.local_user + ) + self.assertEqual(shelf_tags.active_shelf(context, self.book).shelf, shelf) diff --git a/bookwyrm/tests/templatetags/test_status_display.py b/bookwyrm/tests/templatetags/test_status_display.py index 50c5571e2..af2fc9420 100644 --- a/bookwyrm/tests/templatetags/test_status_display.py +++ b/bookwyrm/tests/templatetags/test_status_display.py @@ -1,4 +1,5 @@ """ style fixes and lookups for templates """ +from datetime import datetime from unittest.mock import patch from django.test import TestCase @@ -35,6 +36,12 @@ class StatusDisplayTags(TestCase): ) self.book = models.Edition.objects.create(title="Test Book") + def test_get_mentions(self, *_): + """list of people mentioned""" + status = models.Status.objects.create(content="hi", user=self.remote_user) + result = status_display.get_mentions(status, self.user) + self.assertEqual(result, "@rat@example.com ") + @patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async") def test_get_replies(self, *_): """direct replies to a status""" @@ -83,8 +90,16 @@ class StatusDisplayTags(TestCase): self.assertIsInstance(boosted, models.Review) self.assertEqual(boosted, status) - def test_get_mentions(self, *_): - """list of people mentioned""" - status = models.Status.objects.create(content="hi", user=self.remote_user) - result = status_display.get_mentions(status, self.user) - self.assertEqual(result, "@rat@example.com ") + def test_get_published_date(self, *_): + """date formatting""" + date = datetime(2020, 1, 1, 0, 0, tzinfo=timezone.utc) + with patch("django.utils.timezone.now") as timezone_mock: + timezone_mock.return_value = datetime(2022, 1, 1, 0, 0, tzinfo=timezone.utc) + result = status_display.get_published_date(date) + self.assertEqual(result, "Jan. 1, 2020") + + date = datetime(2022, 1, 1, 0, 0, tzinfo=timezone.utc) + with patch("django.utils.timezone.now") as timezone_mock: + timezone_mock.return_value = datetime(2022, 1, 8, 0, 0, tzinfo=timezone.utc) + result = status_display.get_published_date(date) + self.assertEqual(result, "Jan 1") diff --git a/bookwyrm/tests/templatetags/test_utilities.py b/bookwyrm/tests/templatetags/test_utilities.py index e41cd21ad..0136ca8cd 100644 --- a/bookwyrm/tests/templatetags/test_utilities.py +++ b/bookwyrm/tests/templatetags/test_utilities.py @@ -35,6 +35,15 @@ class UtilitiesTags(TestCase): ) self.book = models.Edition.objects.create(title="Test Book") + def test_get_uuid(self, *_): + """uuid functionality""" + uuid = utilities.get_uuid("hi") + self.assertTrue(re.match(r"hi[A-Za-z0-9\-]", uuid)) + + def test_join(self, *_): + """concats things with underscores""" + self.assertEqual(utilities.join("hi", 5, "blah", 0.75), "hi_5_blah_0.75") + def test_get_user_identifer_local(self, *_): """fall back to the simplest uid available""" self.assertNotEqual(self.user.username, self.user.localname) @@ -46,11 +55,6 @@ class UtilitiesTags(TestCase): utilities.get_user_identifier(self.remote_user), "rat@example.com" ) - def test_get_uuid(self, *_): - """uuid functionality""" - uuid = utilities.get_uuid("hi") - self.assertTrue(re.match(r"hi[A-Za-z0-9\-]", uuid)) - def test_get_title(self, *_): """the title of a book""" self.assertEqual(utilities.get_title(None), "")