show book review/mark from same isbn
This commit is contained in:
parent
fe76112cce
commit
7b18ef9c3b
6 changed files with 87 additions and 46 deletions
|
@ -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")
|
||||
]
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
"""
|
||||
|
|
Loading…
Add table
Reference in a new issue