show book review/mark from same isbn

This commit is contained in:
Your Name 2022-04-03 12:05:48 -04:00
parent fe76112cce
commit 7b18ef9c3b
6 changed files with 87 additions and 46 deletions

View file

@ -1,12 +1,9 @@
import uuid
import django.contrib.postgres.fields as postgres
from django.utils.translation import gettext_lazy as _
from django.db import models
from django.core.serializers.json import DjangoJSONEncoder
from django.shortcuts import reverse
from common.models import Entity, Mark, Review, Tag
from common.utils import GenerateDateUUIDMediaFilePath
from django.utils import timezone
from django.conf import settings
from django.db.models import Q
@ -18,9 +15,11 @@ def book_cover_path(instance, filename):
class Book(Entity):
# widely recognized name, usually in Chinese
title = models.CharField(_("title"), max_length=200)
subtitle = models.CharField(_("subtitle"), blank=True, default='', max_length=200)
subtitle = models.CharField(
_("subtitle"), blank=True, default='', max_length=200)
# original name, for books in foreign language
orig_title = models.CharField(_("original title"), blank=True, default='', max_length=200)
orig_title = models.CharField(
_("original title"), blank=True, default='', max_length=200)
author = postgres.ArrayField(
models.CharField(_("author"), blank=True, default='', max_length=100),
@ -29,40 +28,45 @@ class Book(Entity):
default=list,
)
translator = postgres.ArrayField(
models.CharField(_("translator"), blank=True, default='', max_length=100),
models.CharField(_("translator"), blank=True,
default='', max_length=100),
null=True,
blank=True,
default=list,
)
language = models.CharField(_("language"), blank=True, default='', max_length=10)
pub_house = models.CharField(_("publishing house"), blank=True, default='', max_length=200)
language = models.CharField(
_("language"), blank=True, default='', max_length=10)
pub_house = models.CharField(
_("publishing house"), blank=True, default='', max_length=200)
pub_year = models.IntegerField(_("published year"), null=True, blank=True)
pub_month = models.IntegerField(_("published month"), null=True, blank=True)
binding = models.CharField(_("binding"), blank=True, default='', max_length=50)
pub_month = models.IntegerField(
_("published month"), null=True, blank=True)
binding = models.CharField(
_("binding"), blank=True, default='', max_length=50)
# since data origin is not formatted and might be CNY USD or other currency, use char instead
price = models.CharField(_("pricing"), blank=True, default='', max_length=50)
price = models.CharField(_("pricing"), blank=True,
default='', max_length=50)
pages = models.PositiveIntegerField(_("pages"), null=True, blank=True)
isbn = models.CharField(_("ISBN"), blank=True, null=False, max_length=20, db_index=True, default='')
# to store previously scrapped data
cover = models.ImageField(_("cover picture"), upload_to=book_cover_path, default=settings.DEFAULT_BOOK_IMAGE, blank=True)
isbn = models.CharField(_("ISBN"), blank=True, null=False,
max_length=20, db_index=True, default='')
# to store previously scrapped data
cover = models.ImageField(_("cover picture"), upload_to=book_cover_path,
default=settings.DEFAULT_BOOK_IMAGE, blank=True)
contents = models.TextField(blank=True, default="")
class Meta:
# more info: https://docs.djangoproject.com/en/2.2/ref/models/options/
# set managed=False if the model represents an existing table or
# a database view that has been created by some other means.
# check the link above for further info
# managed = True
# db_table = 'book'
constraints = [
models.CheckConstraint(check=models.Q(pub_year__gte=0), name='pub_year_lowerbound'),
models.CheckConstraint(check=models.Q(pub_month__lte=12), name='pub_month_upperbound'),
models.CheckConstraint(check=models.Q(pub_month__gte=1), name='pub_month_lowerbound'),
models.CheckConstraint(check=models.Q(
pub_year__gte=0), name='pub_year_lowerbound'),
models.CheckConstraint(check=models.Q(
pub_month__lte=12), name='pub_month_upperbound'),
models.CheckConstraint(check=models.Q(
pub_month__gte=1), name='pub_month_lowerbound'),
]
def __str__(self):
return self.title
def get_json(self):
r = {
'subtitle': self.subtitle,
@ -85,40 +89,59 @@ class Book(Entity):
return self.book_tags
def get_related_books(self):
qs = Q(orig_title = self.title)
qs = Q(orig_title=self.title)
if self.isbn:
qs = qs | Q(isbn = self.isbn)
qs = qs | Q(isbn=self.isbn)
if self.orig_title:
qs = qs | Q(title = self.orig_title)
qs = qs | Q(orig_title = self.orig_title)
qs = qs & ~Q(id = self.id)
qs = qs | Q(title=self.orig_title)
qs = qs | Q(orig_title=self.orig_title)
qs = qs & ~Q(id=self.id)
return Book.objects.filter(qs)
def get_identicals(self):
qs = Q(orig_title=self.title)
if self.isbn:
qs = Q(isbn=self.isbn)
# qs = qs & ~Q(id=self.id)
return Book.objects.filter(qs)
else:
return [self] # Book.objects.filter(id=self.id)
@property
def verbose_category_name(self):
return _("书籍")
class BookMark(Mark):
book = models.ForeignKey(Book, on_delete=models.CASCADE, related_name='book_marks', null=True)
book = models.ForeignKey(
Book, on_delete=models.CASCADE, related_name='book_marks', null=True)
class Meta:
constraints = [
models.UniqueConstraint(fields=['owner', 'book'], name="unique_book_mark")
models.UniqueConstraint(
fields=['owner', 'book'], name="unique_book_mark")
]
class BookReview(Review):
book = models.ForeignKey(Book, on_delete=models.CASCADE, related_name='book_reviews', null=True)
book = models.ForeignKey(
Book, on_delete=models.CASCADE, related_name='book_reviews', null=True)
class Meta:
constraints = [
models.UniqueConstraint(fields=['owner', 'book'], name="unique_book_review")
]
models.UniqueConstraint(
fields=['owner', 'book'], name="unique_book_review")
]
class BookTag(Tag):
book = models.ForeignKey(Book, on_delete=models.CASCADE, related_name='book_tags', null=True)
mark = models.ForeignKey(BookMark, on_delete=models.CASCADE, related_name='bookmark_tags', null=True)
book = models.ForeignKey(
Book, on_delete=models.CASCADE, related_name='book_tags', null=True)
mark = models.ForeignKey(
BookMark, on_delete=models.CASCADE, related_name='bookmark_tags', null=True)
class Meta:
constraints = [
models.UniqueConstraint(fields=['content', 'mark'], name="unique_bookmark_tag")
models.UniqueConstraint(
fields=['content', 'mark'], name="unique_bookmark_tag")
]

View file

@ -155,9 +155,7 @@
<div class="entity-marks">
<h5 class="entity-marks__title">{% trans '这本书的标记' %}</h5>
{% if mark_list_more %}
<a href="{% url 'books:retrieve_mark_list' book.id %}" class="entity-marks__more-link">{% trans '全部标记' %}</a>
{% endif %}
<a href="{% url 'books:retrieve_mark_list' book.id 1 %}" class="entity-marks__more-link">关注的人的标记</a>
{% if mark_list %}
<ul class="entity-marks__mark-list">
@ -172,6 +170,9 @@
<span class="icon-lock"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M17,8.48h-.73V6.27a6.27,6.27,0,1,0-12.53,0V8.48H3a.67.67,0,0,0-.67.67V19.33A.67.67,0,0,0,3,20H17a.67.67,0,0,0,.67-.67V9.15A.67.67,0,0,0,17,8.48ZM6.42,6.27h0a3.57,3.57,0,0,1,7.14,0h0V8.48H6.42Z"/></svg></span>
{% endif %}
<span class="entity-marks__mark-time">{{ others_mark.edited_time }}</span>
{% if others_mark.book != book %}
<span class="entity-marks__mark-time source-label"><a class="entity-marks__mark-time" href="{% url 'books:retrieve' others_mark.book.id %}">{{ others_mark.book.get_source_site_display }}</a></span>
{% endif %}
{% if others_mark.text %}
<p class="entity-marks__mark-content">{{ others_mark.text }}</p>
{% endif %}
@ -196,6 +197,9 @@
<span class="icon-lock"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M17,8.48h-.73V6.27a6.27,6.27,0,1,0-12.53,0V8.48H3a.67.67,0,0,0-.67.67V19.33A.67.67,0,0,0,3,20H17a.67.67,0,0,0,.67-.67V9.15A.67.67,0,0,0,17,8.48ZM6.42,6.27h0a3.57,3.57,0,0,1,7.14,0h0V8.48H6.42Z"/></svg></span>
{% endif %}
<span class="entity-reviews__review-time">{{ others_review.edited_time }}</span>
{% if others_review.book != book %}
<span class="entity-reviews__review-time source-label"><a class="entity-reviews__review-time" href="{% url 'books:retrieve' others_review.book.id %}">{{ others_review.book.get_source_site_display }}</a></span>
{% endif %}
<span class="entity-reviews__review-title"> <a href="{% url 'books:retrieve_review' others_review.id %}">{{ others_review.title }}</a></span>
<span>{{ others_review.get_plain_content | truncate:100 }}</span>
</li>

View file

@ -53,6 +53,10 @@
</svg></span>
{% endif %}
<span class="entity-marks__mark-time">{{ mark.edited_time }}</span>
{% if mark.book != book %}
<span class="entity-marks__mark-time source-label"><a class="entity-marks__mark-time" href="{% url 'books:retrieve' mark.book.id %}">{{ mark.book.get_source_site_display }}</a></span>
{% endif %}
{% if mark.text %}
<p class="entity-marks__mark-content">{{ mark.text }}</p>
{% endif %}

View file

@ -44,7 +44,9 @@
<span class="icon-lock"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M17,8.48h-.73V6.27a6.27,6.27,0,1,0-12.53,0V8.48H3a.67.67,0,0,0-.67.67V19.33A.67.67,0,0,0,3,20H17a.67.67,0,0,0,.67-.67V9.15A.67.67,0,0,0,17,8.48ZM6.42,6.27h0a3.57,3.57,0,0,1,7.14,0h0V8.48H6.42Z"/></svg></span>
{% endif %}
<span class="entity-reviews__review-time">{{ review.edited_time }}</span>
{% if review.book != book %}
<span class="entity-reviews__review-time source-label"><a href="{% url 'books:retrieve' review.book.id %}" class="entity-reviews__review-time">{{ review.book.get_source_site_display }}</a></span>
{% endif %}
<span href="{% url 'books:retrieve_review' review.id %}" class="entity-reviews__review-title"><a href="{% url 'books:retrieve_review' review.id %}">{{ review.title }}</a></span>

View file

@ -186,8 +186,8 @@ def retrieve(request, id):
mark_list_more = None
review_list_more = None
else:
mark_list = BookMark.get_available(book, request.user)
review_list = BookReview.get_available(book, request.user)
mark_list = BookMark.get_available_for_identicals(book, request.user)
review_list = BookReview.get_available_for_identicals(book, request.user)
mark_list_more = True if len(mark_list) > MARK_NUMBER else False
mark_list = mark_list[:MARK_NUMBER]
for m in mark_list:
@ -284,7 +284,7 @@ def create_update_mark(request):
form.instance.owner = request.user
form.instance.edited_time = timezone.now()
book = form.instance.book
try:
with transaction.atomic():
# update book rating
@ -340,7 +340,7 @@ def create_update_mark(request):
def retrieve_mark_list(request, book_id, following_only=False):
if request.method == 'GET':
book = get_object_or_404(Book, pk=book_id)
queryset = BookMark.get_available(book, request.user, following_only=following_only)
queryset = BookMark.get_available_for_identicals(book, request.user, following_only=following_only)
paginator = Paginator(queryset, MARK_PER_PAGE)
page_number = request.GET.get('page', default=1)
marks = paginator.get_page(page_number)
@ -546,7 +546,7 @@ def retrieve_review(request, id):
def retrieve_review_list(request, book_id):
if request.method == 'GET':
book = get_object_or_404(Book, pk=book_id)
queryset = BookReview.get_available(book, request.user)
queryset = BookReview.get_available_for_identicals(book, request.user)
paginator = Paginator(queryset, REVIEW_PER_PAGE)
page_number = request.GET.get('page', default=1)
reviews = paginator.get_page(page_number)

View file

@ -203,12 +203,20 @@ class UserOwnedEntity(models.Model):
@classmethod
def get_available(cls, entity, request_user, following_only=False):
# e.g. SongMark.get_available(song, request.user, request.user.mastodon_token)
# e.g. SongMark.get_available(song, request.user)
query_kwargs = {entity.__class__.__name__.lower(): entity}
all_entities = cls.objects.filter(**query_kwargs).order_by("-edited_time") # get all marks for song
visible_entities = list(filter(lambda _entity: _entity.is_visible_to(request_user) and (_entity.owner.mastodon_username in request_user.mastodon_following if following_only else True), all_entities))
return visible_entities
@classmethod
def get_available_for_identicals(cls, entity, request_user, following_only=False):
# e.g. SongMark.get_available(song, request.user)
query_kwargs = {entity.__class__.__name__.lower() + '__in': entity.get_identicals()}
all_entities = cls.objects.filter(**query_kwargs).order_by("-edited_time") # get all marks for song
visible_entities = list(filter(lambda _entity: _entity.is_visible_to(request_user) and (_entity.owner.mastodon_username in request_user.mastodon_following if following_only else True), all_entities))
return visible_entities
@classmethod
def get_available_by_user(cls, owner, is_following): # FIXME
"""