diff --git a/bookwyrm/static/css/bookwyrm.css b/bookwyrm/static/css/bookwyrm.css index f385e6295..087f8d756 100644 --- a/bookwyrm/static/css/bookwyrm.css +++ b/bookwyrm/static/css/bookwyrm.css @@ -555,6 +555,35 @@ ol.ordered-list li::before { padding: 0 0.75em; } +/* Breadcrumbs + ******************************************************************************/ + +.books-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(10em, 1fr)); + gap: 2em; + align-items: end; + justify-items: center; +} + +.books-grid > .is-big { + grid-column: span 2; + grid-row: span 2; + justify-self: stretch; +} + +.books-grid .book-cover { + width: 100%; +} + +.books-grid .book-title { + --height-basis: 1.35rem; + display: block; + margin-top: 0.5rem; + line-height: var(--height-basis); + min-height: calc(2 * var(--height-basis)); +} + /* Dimensions * @todo These could be in rem. ******************************************************************************/ diff --git a/bookwyrm/templates/annual_summary/layout.html b/bookwyrm/templates/annual_summary/layout.html new file mode 100644 index 000000000..602fd752b --- /dev/null +++ b/bookwyrm/templates/annual_summary/layout.html @@ -0,0 +1,152 @@ +{% extends 'layout.html' %} +{% load i18n %} +{% load static %} + + +{% block title %}{% blocktrans %}{{ year }} in the books{% endblocktrans %}{% endblock %} + +{% block content %} +

{% blocktrans %}{{ year }} in the books{% endblocktrans %}

+ + {% if not books %} + {% blocktrans %}Sadly you didn't finish any book in {{ year }}{% endblocktrans %} + {% else %} + +
+
+

+ {% blocktrans %}In {{ year }}, you read {{ books_total }} books
for a total of {{ pages_total }} pages!{% endblocktrans %} +

+

{% trans "That’s great!" %}

+ +

+ {% blocktrans %}That makes an average of {{ pages_average }} pages per book.{% endblocktrans %} +

+ + {% if no_page_number %} +

{% blocktrans %}({{ no_page_number }} books don’t have pages){% endblocktrans %}

+ {% endif %} +
+
+ +
+
+ {% include 'snippets/book_cover.html' with book=book_pages_lowest cover_class='is-w-auto-tablet is-h-l-mobile' %} +
+
+ {% trans "Your shortest read this year" %} +

+ + {{ book_pages_lowest.title }} + +

+ {% if book_pages_lowest.authors.exists %} +

{% trans "by" %} + {% include 'snippets/authors.html' with book=book_pages_lowest %} +

+ {% endif %} +

+ {% with pages=book_pages_lowest.pages %} + {% blocktrans %}{{ pages }} pages{% endblocktrans%} + {% endwith %} +

+
+
+ {% include 'snippets/book_cover.html' with book=book_pages_highest cover_class='is-w-auto-tablet is-h-l-mobile' %} +
+
+ {% trans "and the longest read" %} +

+ + {{ book_pages_highest.title }} + +

+ {% if book_pages_highest.authors.exists %} +

{% trans "by" %} + {% include 'snippets/authors.html' with book=book_pages_highest %} +

+ {% endif %} +

+ {% with pages=book_pages_highest.pages %} + {% blocktrans %}{{ pages }} pages{% endblocktrans%} + {% endwith %} +

+
+
+ +
+
+
+
+
+ +
+
+

+ {% blocktrans %}You rated {{ ratings_total }} books and your average rating is {{ rating_average }}{% endblocktrans %} +

+
+
+
+
+ {% include 'snippets/book_cover.html' with book=book_rating_highest.book cover_class='is-w-auto-tablet is-h-l-mobile' %} +
+
+ {% trans "Your best rated review" %} +

+ + {{ book_rating_highest.book.title }} + +

+ {% if book_rating_highest.book.authors.exists %} +

{% trans "by" %} + {% include 'snippets/authors.html' with book=book_rating_highest.book %} +

+ {% endif %} +

+ {% with rating=book_rating_highest.rating|floatformat %} + {% blocktrans %}Your rating: {{ rating }}{% endblocktrans%} + {% endwith %} +

+
+
+ +
+
+
+
+
+ +
+
+

+ {% blocktrans %}All the books you read in 2021{% endblocktrans %} +

+
+
+ +
+
+ +
+
+ {% endif %} +{% endblock %} diff --git a/bookwyrm/urls.py b/bookwyrm/urls.py index 2829d2034..4992d7fe6 100644 --- a/bookwyrm/urls.py +++ b/bookwyrm/urls.py @@ -477,4 +477,6 @@ urlpatterns = [ re_path( r"^ostatus_success/?$", views.ostatus_follow_success, name="ostatus-success" ), + # annual summary + re_path(r"^my-year-in-the-books/(?P\d{4})/?$", views.AnnualSummary.as_view()), ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) diff --git a/bookwyrm/views/__init__.py b/bookwyrm/views/__init__.py index fb9db2105..30195fad6 100644 --- a/bookwyrm/views/__init__.py +++ b/bookwyrm/views/__init__.py @@ -96,3 +96,4 @@ from .status import edit_readthrough from .updates import get_notification_count, get_unread_status_count from .user import User, Followers, Following, hide_suggestions from .wellknown import * +from .annual_summary import AnnualSummary diff --git a/bookwyrm/views/annual_summary.py b/bookwyrm/views/annual_summary.py new file mode 100644 index 000000000..21ecb041d --- /dev/null +++ b/bookwyrm/views/annual_summary.py @@ -0,0 +1,79 @@ +from django.db.models import Case, When +from django.shortcuts import get_object_or_404 +from django.template.response import TemplateResponse +from django.views import View + +from bookwyrm import models + + +class AnnualSummary(View): + """display a summary of the year""" + + def get(self, request, year): + """get response""" + + user = request.user + read_shelf = get_object_or_404(user.shelf_set, identifier="read") + read_shelf_books_in_year = ( + models.ShelfBook.objects.filter(shelf=read_shelf) + .filter(user=user) + .filter(shelved_date__year=year) + .order_by("shelved_date", "created_date", "updated_date") + ) + read_book_ids_in_year = [i.book.id for i in read_shelf_books_in_year] + preserved = Case( + *[When(pk=pk, then=pos) for pos, pk in enumerate(read_book_ids_in_year)] + ) + read_books_in_year = models.Edition.objects.filter( + id__in=read_book_ids_in_year + ).order_by(preserved) + + """pages stats queries""" + read_books_with_pages = read_books_in_year.filter(pages__gte=0).order_by( + "pages" + ) + pages_list = [p.pages for p in read_books_with_pages] + + pages_total = 0 + pages_average = 0 + book_pages_lowest = 0 + book_pages_highest = 0 + if len(pages_list) > 0: + pages_total = sum(pages_list) + pages_average = round(sum(pages_list) / len(pages_list)) + book_pages_lowest = read_books_with_pages.first() + book_pages_highest = read_books_with_pages.last() + + """books with no pages""" + no_page_list = len(read_books_in_year.filter(pages__exact=None)) + + """rating stats queries""" + ratings = ( + models.Review.objects.filter(user=user) + .exclude(deleted=True) + .exclude(rating=None) + .filter(book_id__in=read_book_ids_in_year) + ) + best_ratings_books_ids = [review.book.id for review in ratings.filter(rating=5)] + + rating_average = 0 + if len(ratings) > 0: + ratings_list = [review.rating for review in ratings] + rating_average = round(sum(ratings_list) / len(ratings_list), 2) + + data = { + "year": year, + "books_total": len(read_books_in_year), + "books": read_books_in_year, + "pages_total": pages_total, + "pages_average": pages_average, + "book_pages_lowest": book_pages_lowest, + "book_pages_highest": book_pages_highest, + "no_page_number": no_page_list, + "ratings_total": len(ratings), + "rating_average": rating_average, + "book_rating_highest": ratings.order_by("-rating").first(), + "best_ratings_books_ids": best_ratings_books_ids, + } + + return TemplateResponse(request, "annual_summary/layout.html", data)