forked from mirrors/bookwyrm
stars filter
This commit is contained in:
parent
668e714c31
commit
093f2d4fdd
8 changed files with 141 additions and 26 deletions
|
@ -28,6 +28,7 @@ INSTALLED_APPS = [
|
|||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'django.contrib.humanize',
|
||||
'fedireads',
|
||||
]
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ h2 {
|
|||
background-color: #B2DBBF;
|
||||
padding: 0.5rem 0.2rem;
|
||||
margin-bottom: 1rem;
|
||||
height: 1rem;
|
||||
}
|
||||
|
||||
#top-bar {
|
||||
|
@ -51,18 +52,21 @@ h2 {
|
|||
#feed, #content, #sidebar {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 0 1rem 0 0;
|
||||
margin: 0 1rem 1rem 0;
|
||||
}
|
||||
|
||||
#sidebar {
|
||||
min-width: 20rem;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.user-pic {
|
||||
width: 2rem;
|
||||
height: auto;
|
||||
border-radius: 50%;
|
||||
vertical-align: middle;
|
||||
vertical-align: top;
|
||||
position: relative;
|
||||
bottom: 0.5em;
|
||||
}
|
||||
|
||||
.book-preview {
|
||||
|
@ -72,7 +76,6 @@ h2 {
|
|||
.book-preview img {
|
||||
float: left;
|
||||
margin-right: 1em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.book-cover.small {
|
||||
|
@ -110,3 +113,23 @@ blockquote {
|
|||
clear: both;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
margin: 1em;
|
||||
}
|
||||
tr {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
tr:nth-child(even) {
|
||||
background-color: #DDD;
|
||||
}
|
||||
|
||||
th {
|
||||
font-weight: bold;
|
||||
}
|
||||
th, td {
|
||||
padding: 1em;
|
||||
text-align: left;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
{% extends 'layout.html' %}
|
||||
{% load fr_display %}
|
||||
{% block content %}
|
||||
<div id="content">
|
||||
<div>
|
||||
|
@ -6,13 +7,14 @@
|
|||
<img class="book-cover" src="{% if book.cover %}/images/{{ book.cover }}{% else %}/static/images/no_cover.jpg{% endif %}">
|
||||
<h1>{{ book.data.title }}</h1>
|
||||
by {{ book.authors.first.data.name }}
|
||||
{{ rating }} stars
|
||||
{{ rating | stars }} {{ rating }}
|
||||
<blockquote>{{ book.data.description }}</blockquote>
|
||||
</div>
|
||||
|
||||
<h3>Leave a review</h3>
|
||||
|
||||
<form class="review-form" name="review" action="/review/" method="post">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="book" value="{{ book.openlibrary_key }}"></input>
|
||||
{{ review_form.as_p }}
|
||||
<button type="submit">Post review</button>
|
||||
|
@ -27,7 +29,7 @@
|
|||
{% for review in reviews %}
|
||||
<div class="review">
|
||||
<h4>{{ review.name }}
|
||||
<small>{{ review.rating }} stars, by {{ review.user.username }}</small>
|
||||
<small>{{ review.rating | stars }} stars, by {{ review.user.username }}</small>
|
||||
</h4>
|
||||
<blockquote>{{ review.review_content }}</blockquote>
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
{% extends 'layout.html' %}
|
||||
{% load fr_display %}
|
||||
{% block content %}
|
||||
<div id="sidebar">
|
||||
<div>
|
||||
|
@ -103,7 +104,7 @@
|
|||
</p>
|
||||
|
||||
<h3>{{ activity.name }}</h3>
|
||||
<p>{{ activity.rating }} stars</p>
|
||||
<p>{{ activity.rating | stars }} stars</p>
|
||||
<p>{{ activity.review_content }}</p>
|
||||
</div>
|
||||
<div class="interaction"><button>⭐️ Like</button></div>
|
||||
|
|
|
@ -1,51 +1,122 @@
|
|||
{% extends 'layout.html' %}
|
||||
{% load humanize %}
|
||||
{% load fr_display %}
|
||||
{% block content %}
|
||||
<div id="content">
|
||||
<div id="sidebar">
|
||||
<div class="user-profile">
|
||||
<img class="user-pic" src="{% if user.avatar %}/images/{{ user.avatar }}{% else %}/static/images/default_avi.jpg{% endif %}">
|
||||
<h1>{% if user.name %}{{ user.name }}<br>{% endif %}{% if user.localname %}{{ user.localname }}{% else %}{{ user.username }}{% endif %}</h1>
|
||||
<h2>
|
||||
<img class="user-pic" src="{% if user.avatar %}/images/{{ user.avatar }}{% else %}/static/images/default_avi.jpg{% endif %}">
|
||||
{% if user.name %}{{ user.name }}{% endif %}
|
||||
<small>{{ user.username }}</small>
|
||||
</h2>
|
||||
{% if user.summary %}
|
||||
<blockquote>{{ user.summary }}</blockquote>
|
||||
{% endif %}
|
||||
{% if is_self %}<a href="/user/{{ user.localname }}/edit">Edit profile</a>
|
||||
<p>Since {{ user.created_date }}</p>
|
||||
{% endif %}
|
||||
{% if not is_self %}
|
||||
{% if not following %}
|
||||
{% if not request.user in user.followers.all %}
|
||||
<form action="/follow/" method="post">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="user" value="{{ user.id }}"></input>
|
||||
<input type="submit" value="Follow"></input>
|
||||
</form>
|
||||
{% else %}
|
||||
<form action="/unfollow/" method="post">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="user" value="{{ user.id }}"></input>
|
||||
<input type="submit" value="Unfollow"></input>
|
||||
</form>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2>Books</h2>
|
||||
{% for book in books.all %}
|
||||
<div class="book">
|
||||
{{ book.data.title }} by {{ book.authors.first.data.name }}
|
||||
{% if is_self %}
|
||||
<div class="interaction">
|
||||
<a href="/user/{{ user.localname }}/edit">Edit profile</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2>Followers</h2>
|
||||
{% for follower in user.followers.all %}
|
||||
<div>
|
||||
<a href="{{ follower.actor }}">{{ follower.username }}</a>
|
||||
{% if not request.user in follower.followers.all %}
|
||||
<form action="/follow/" method="post">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="user" value="{{ follower.id }}"></input>
|
||||
<input type="submit" value="Follow"></input>
|
||||
</form>
|
||||
{% else %}
|
||||
<form action="/unfollow/" method="post">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="user" value="{{ follower.id }}"></input>
|
||||
<input type="submit" value="Unfollow"></input>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="content">
|
||||
{% for shelf in shelves %}
|
||||
<div>
|
||||
<h2>{{ shelf.shelf.name }}</h2>
|
||||
{% if shelf.books %}
|
||||
<table>
|
||||
<tr class="book-preview">
|
||||
<th>
|
||||
Cover
|
||||
</th>
|
||||
<th>
|
||||
Title
|
||||
</th>
|
||||
<th>
|
||||
Author
|
||||
</th>
|
||||
<th>
|
||||
Published
|
||||
</th>
|
||||
<th>
|
||||
Shelved
|
||||
</th>
|
||||
<th>
|
||||
External links
|
||||
</th>
|
||||
<th>
|
||||
Rating
|
||||
</th>
|
||||
</tr>
|
||||
{% for book in shelf.books.all %}
|
||||
<tr class="book-preview">
|
||||
<td>
|
||||
<img src="/images/{{ book.cover }}" class="book-cover small">
|
||||
</td>
|
||||
<td>
|
||||
<a href="{{ book.openlibrary_key }}">{{ book.data.title }}</a>
|
||||
</td>
|
||||
<td>
|
||||
{{ book.authors.first.data.name }}
|
||||
</td>
|
||||
<td>
|
||||
{{ book.data.first_publish_date }}
|
||||
</td>
|
||||
<td>
|
||||
{{ book.added_date | naturalday }}
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://openlibrary.org{{ book.data.key }}" target="_blank">OpenLibrary</a>
|
||||
</td>
|
||||
<td>
|
||||
{{ ratings | dict_key:book.id | stars}}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% else %}
|
||||
<p>This shelf is empty.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
|
0
fedireads/templatetags/__init__.py
Normal file
0
fedireads/templatetags/__init__.py
Normal file
15
fedireads/templatetags/fr_display.py
Normal file
15
fedireads/templatetags/fr_display.py
Normal file
|
@ -0,0 +1,15 @@
|
|||
''' template filters '''
|
||||
from django import template
|
||||
|
||||
register = template.Library()
|
||||
|
||||
@register.filter(name='dict_key')
|
||||
def dict_key(d, k):
|
||||
'''Returns the given key from a dictionary.'''
|
||||
return d.get(k) or 0
|
||||
|
||||
@register.filter(name='stars')
|
||||
def stars(number):
|
||||
''' turn integers into stars '''
|
||||
number = int(number)
|
||||
return ('★' * number) + '☆' * (5 - number)
|
|
@ -1,11 +1,10 @@
|
|||
''' application views/pages '''
|
||||
from django.contrib.auth import authenticate, login, logout
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.db.models import Avg, Q
|
||||
from django.db.models import Avg, FilteredRelation, Q
|
||||
from django.http import HttpResponseNotFound
|
||||
from django.shortcuts import redirect
|
||||
from django.template.response import TemplateResponse
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
import re
|
||||
|
||||
from fedireads import forms, models, openlibrary, outgoing, incoming
|
||||
|
@ -120,10 +119,13 @@ def user_profile(request, username):
|
|||
except models.User.DoesNotExist:
|
||||
return HttpResponseNotFound()
|
||||
|
||||
books = models.Book.objects.filter(shelves__user=user)
|
||||
shelves = models.Shelf.objects.filter(user=user)
|
||||
ratings = {r.book.id: r.rating for r in models.Review.objects.filter(user=user, book__shelves__user=user)}
|
||||
|
||||
data = {
|
||||
'user': user,
|
||||
'books': books,
|
||||
'shelves': shelves,
|
||||
'ratings': ratings,
|
||||
'is_self': request.user.id == user.id,
|
||||
}
|
||||
return TemplateResponse(request, 'user.html', data)
|
||||
|
|
Loading…
Reference in a new issue