diff --git a/boofilsic/settings.py b/boofilsic/settings.py
index 2708bbbe..a72782ce 100644
--- a/boofilsic/settings.py
+++ b/boofilsic/settings.py
@@ -86,7 +86,7 @@ if DEBUG:
'NAME': 'test',
'USER': 'donotban',
'PASSWORD': 'donotbansilvousplait',
- 'HOST': '192.168.136.5',
+ 'HOST': '192.168.144.2',
'OPTIONS': {
'client_encoding': 'UTF8',
# 'isolation_level': psycopg2.extensions.ISOLATION_LEVEL_DEFAULT,
@@ -112,7 +112,7 @@ else:
# https://docs.djangoproject.com/en/3.0/topics/auth/customizing/#authentication-backends
AUTHENTICATION_BACKENDS = [
- 'users.auth.OAuth2Backend',
+ 'common.mastodon.auth.OAuth2Backend',
# for admin to login admin site
# 'django.contrib.auth.backends.ModelBackend'
]
@@ -155,6 +155,9 @@ DEFAULT_BOOK_IMAGE = os.path.join(MEDIA_ROOT, BOOK_MEDIA_PATH_ROOT, 'default.jpg
# Mastodon domain name
MASTODON_DOMAIN_NAME = 'cmx-im.work'
+# Timeout of requests to Mastodon, in seconds
+MASTODON_TIMEOUT = 30
+
# Default password for each user. since assword is not used any way,
# any string that is not empty is ok
DEFAULT_PASSWORD = 'eBRM1DETkYgiqPgq'
diff --git a/books/admin.py b/books/admin.py
index 8c38f3f3..9b58e6bb 100644
--- a/books/admin.py
+++ b/books/admin.py
@@ -1,3 +1,7 @@
from django.contrib import admin
+from .models import *
+
+admin.site.register(Book)
+admin.site.register(BookMark)
+admin.site.register(BookReview)
-# Register your models here.
diff --git a/books/forms.py b/books/forms.py
index 2e07a45e..b605924c 100644
--- a/books/forms.py
+++ b/books/forms.py
@@ -3,14 +3,27 @@ from common.forms import KeyValueInput
from django.contrib.postgres.forms import SimpleArrayField
from django.utils.translation import gettext_lazy as _
from .models import Book, BookMark, BookReview
+from common.models import MarkStatusEnum
+from common.forms import RadioBooleanField, RatingValidator
+
+
+def BookMarkStatusTranslator(status):
+ trans_dict = {
+ MarkStatusEnum.DO.value: _("在看"),
+ MarkStatusEnum.WISH.value: _("想看"),
+ MarkStatusEnum.COLLECT.value: _("看过")
+ }
+ return trans_dict[status]
class BookForm(forms.ModelForm):
pub_year = forms.IntegerField(required=False, max_value=9999, min_value=0, label=_("出版年份"))
pub_month = forms.IntegerField(required=False, max_value=12, min_value=1, label=_("出版月份"))
+ id = forms.IntegerField(required=False, widget=forms.HiddenInput())
class Meta:
model = Book
fields = [
+ 'id',
'title',
'isbn',
'author',
@@ -50,27 +63,78 @@ class BookForm(forms.ModelForm):
'author': forms.TextInput(attrs={'placeholder': _("多个作者使用英文逗号分隔")}),
'translator': forms.TextInput(attrs={'placeholder': _("多个译者使用英文逗号分隔")}),
'other_info': KeyValueInput(),
+ # 'cover': forms.FileInput(),
}
class BookMarkForm(forms.ModelForm):
+ IS_PRIVATE_CHOICES = [
+ (True, _("仅关注者")),
+ (False, _("公开")),
+ ]
+ STATUS_CHOICES = [(v, BookMarkStatusTranslator(v)) for v in MarkStatusEnum.values]
+ id = forms.IntegerField(required=False, widget=forms.HiddenInput())
+ share_to_mastodon = forms.BooleanField(label=_("分享到长毛象"), initial=True, required=False)
+ rating = forms.IntegerField(validators=[RatingValidator()], widget=forms.HiddenInput(), required=False)
+ status = forms.ChoiceField(
+ label=_(""),
+ widget=forms.RadioSelect(),
+ choices=STATUS_CHOICES
+ )
+ is_private = RadioBooleanField(
+ label=_("可见性"),
+ initial=True,
+ choices=IS_PRIVATE_CHOICES
+ )
class Meta:
model = BookMark
fields = [
+ 'id',
'book',
'status',
'rating',
'text',
'is_private',
]
+ labels = {
+ 'rating': _("评分"),
+ 'text': _("短评"),
+ }
+ widgets = {
+ 'book': forms.Select(attrs={"hidden": ""}),
+ 'text': forms.Textarea(attrs={"placeholder": _("最多只能写500字哦~")}),
+ }
class BookReviewForm(forms.ModelForm):
+ IS_PRIVATE_CHOICES = [
+ (True, _("仅关注者")),
+ (False, _("公开")),
+ ]
+ share_to_mastodon = forms.BooleanField(label=_("分享到长毛象"), initial=True, required=False)
+ id = forms.IntegerField(required=False, widget=forms.HiddenInput())
+ is_private = RadioBooleanField(
+ label=_("可见性"),
+ initial=True,
+ choices=IS_PRIVATE_CHOICES
+ )
class Meta:
model = BookReview
fields = [
+ 'id',
'book',
'title',
'content',
'is_private'
- ]
\ No newline at end of file
+ ]
+ labels = {
+ 'book': "",
+ 'title': _("标题"),
+ 'content': _("正文"),
+ 'share_to_mastodon': _("分享到长毛象")
+ }
+ widgets = {
+ 'book': forms.Select(attrs={"hidden": ""}),
+ }
+
+
diff --git a/books/models.py b/books/models.py
index ab699bd4..f3b0f721 100644
--- a/books/models.py
+++ b/books/models.py
@@ -1,20 +1,22 @@
+import uuid
import django.contrib.postgres.fields as postgres
from django.utils.translation import ugettext_lazy as _
from django.db import models
from django.core.serializers.json import DjangoJSONEncoder
from common.models import Resource, Mark, Review
from boofilsic.settings import BOOK_MEDIA_PATH_ROOT, DEFAULT_BOOK_IMAGE
-from datetime import datetime
+from django.utils import timezone
def book_cover_path(instance, filename):
- raise NotImplementedError("UUID!!!!!!!!!!!")
+ ext = filename.split('.')[-1]
+ filename = "%s.%s" % (uuid.uuid4(), ext)
root = ''
if BOOK_MEDIA_PATH_ROOT.endswith('/'):
root = BOOK_MEDIA_PATH_ROOT
else:
root = BOOK_MEDIA_PATH_ROOT + '/'
- return root + datetime.now().strftime('%Y/%m/%d') + f'{filename}'
+ return root + timezone.now().strftime('%Y/%m/%d') + f'{filename}'
class Book(Resource):
@@ -44,9 +46,8 @@ class Book(Resource):
# 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)
pages = models.PositiveIntegerField(_("pages"), null=True, blank=True)
- isbn = models.CharField(_("ISBN"), blank=True, max_length=20, unique=True, db_index=True)
+ isbn = models.CharField(_("ISBN"), blank=True, null=True, max_length=20, unique=True, db_index=True)
# to store previously scrapped data
- img_url = models.URLField(max_length=300)
cover = models.ImageField(_("cover picture"), upload_to=book_cover_path, default=DEFAULT_BOOK_IMAGE, blank=True)
class Meta:
@@ -67,7 +68,7 @@ class Book(Resource):
class BookMark(Mark):
- book = models.ForeignKey(Book, on_delete=models.SET_NULL, 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")
@@ -75,7 +76,7 @@ class BookMark(Mark):
class BookReview(Review):
- book = models.ForeignKey(Book, on_delete=models.SET_NULL, 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")
diff --git a/books/static/js/scrape.js b/books/static/js/scrape.js
new file mode 100644
index 00000000..b36d2468
--- /dev/null
+++ b/books/static/js/scrape.js
@@ -0,0 +1,7 @@
+$(document).ready( function() {
+ $("#submit").click(function(e) {
+ e.preventDefault();
+ $("#scrapeForm form").submit();
+ });
+
+});
\ No newline at end of file
diff --git a/books/templates/books/create_update.html b/books/templates/books/create_update.html
index 776e0663..b9d0882c 100644
--- a/books/templates/books/create_update.html
+++ b/books/templates/books/create_update.html
@@ -10,7 +10,7 @@
- {% trans 'Boofilsic - 添加图书' %}
+ {% trans 'Boofilsic - ' %}{{ title }}
@@ -30,7 +30,9 @@
{% trans '登出' %}
{% trans '主页' %}
+ {% if user.is_staff %}
{% trans '后台' %}
+ {% endif %}
@@ -38,10 +40,10 @@
-
diff --git a/books/templates/books/create_update_review.html b/books/templates/books/create_update_review.html
new file mode 100644
index 00000000..c19e72db
--- /dev/null
+++ b/books/templates/books/create_update_review.html
@@ -0,0 +1,128 @@
+{% load static %}
+{% load i18n %}
+{% load admin_url %}
+{% load mastodon %}
+{% load oauth_token %}
+{% load truncate %}
+
+
+
+
+
+
+
{% trans 'Boofilsic - ' %}{{ title }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
{% if book.isbn %}{% trans 'ISBN:' %}{{ book.isbn }}{% endif %}
+
{% if book.author %}{% trans '作者:' %}
+ {% for author in book.author %}
+ {{ author }}
+ {% endfor %}
+ {% endif %}
+
{% if book.pub_house %}{% trans '出版社:' %}{{ book.pub_house }}{% endif %}
+
{%if book.pub_year %}{% trans '出版时间:' %}{{ book.pub_year }}{% trans '年' %}{% if book.pub_month %}{{ book.pub_month }}{% trans '月' %}{% endif %}{% endif %}
+
+ {% if book.rating %}
+
+
{{ book.rating }}
+ {% endif %}
+
+
+
+
+
+
+
+
+
+
+
+
+ {% comment %}
+
{% oauth_token %}
+
{% mastodon %}
+
+
{{ user.mastodon_id }}
+ {% endcomment %}
+
+
+
+
+
+
diff --git a/books/templates/books/delete.html b/books/templates/books/delete.html
index e69de29b..f5ac6bfb 100644
--- a/books/templates/books/delete.html
+++ b/books/templates/books/delete.html
@@ -0,0 +1,107 @@
+{% load static %}
+{% load i18n %}
+{% load admin_url %}
+{% load mastodon %}
+{% load oauth_token %}
+{% load truncate %}
+
+
+
+
+
+
+
{% trans 'Boofilsic - 删除图书' %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{% trans '确认删除这本书吗?相关评论和标记将一并删除。' %}
+
+
+
+

+
+
+
+ {{ book.title }}
+
+
+ {% if book.rating %}
+ {% trans '评分:' %}
+
+
+
{{ book.rating }}
+ {% else %}
+
{% trans '评分:暂无评分' %}
+ {% endif %}
+
{% trans '最近编辑者:' %}{{ book.last_editor | default:"" }}
+
{% trans '上次编辑时间:' %}{{ book.edited_time }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {% comment %}
+
{% oauth_token %}
+
{% mastodon %}
+
+
{{ user.mastodon_id }}
+ {% endcomment %}
+
+
+
+
+
+
diff --git a/books/templates/books/delete_review.html b/books/templates/books/delete_review.html
new file mode 100644
index 00000000..e3d8404b
--- /dev/null
+++ b/books/templates/books/delete_review.html
@@ -0,0 +1,109 @@
+{% load static %}
+{% load i18n %}
+{% load admin_url %}
+{% load mastodon %}
+{% load oauth_token %}
+{% load truncate %}
+
+
+
+
+
+
+
{% trans 'Boofilsic - 删除评论' %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{% trans '确认删除这篇评论吗?' %}
+
+
+
+
+
+
+ {{ form.content }}
+
+ {{ form.media }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {% comment %}
+
{% oauth_token %}
+
{% mastodon %}
+
+
{{ user.mastodon_id }}
+ {% endcomment %}
+
+
+
+
+
+
diff --git a/books/templates/books/detail.html b/books/templates/books/detail.html
index a26a814c..ce64005b 100644
--- a/books/templates/books/detail.html
+++ b/books/templates/books/detail.html
@@ -15,6 +15,7 @@
+
@@ -31,7 +32,9 @@
{% trans '登出' %}
{% trans '主页' %}
+ {% if user.is_staff %}
{% trans '后台' %}
+ {% endif %}
@@ -40,7 +43,9 @@
-

+
+

+
{{ book.title }}
@@ -58,7 +63,7 @@
{% if book.translator %}{% trans '译者:' %}
{% for translator in book.translator %}
{{ translator }}
- {% endfor %}
+ {% endfor %}
{% endif %}
{% if book.orig_title %}{% trans '原作名:' %}{{ book.orig_title }}{% endif %}
{% if book.language %}{% trans '语言:' %}{{ book.language }}{% endif %}
@@ -88,7 +93,13 @@
{% url 'users:home' book.last_editor %}
{% endcomment %}
-
+
+
@@ -104,16 +115,136 @@
{% endif %}
+
+
{% trans '这本书的标记' %}
+ {% if mark_list_more %}
+
{% trans '更多' %}
+ {% endif %}
+ {% if mark_list %}
+ {% for others_mark in mark_list %}
+
+
+
{{ others_mark.owner.username }}
+
{{ others_mark.get_status_display }}
+ {% if others_mark.rating %}
+
+ {% endif %}
+ {% if others_mark.is_private %}
+
+ {% endif %}
+
{{ others_mark.edited_time }}
+
+
+ {% if others_mark.text %}
+
{{ others_mark.text }}
+ {% endif %}
+
+
+ {% endfor %}
+ {% else %}
+
{% trans '暂无标记' %}
+ {% endif %}
+
+
+
{% trans '这本书的评论' %}
+ {% if review_list_more %}
+
{% trans '更多' %}
+ {% endif %}
+ {% if review_list %}
+ {% for others_review in review_list %}
+
+ {% endfor %}
+ {% else %}
+
{% trans '暂无评论' %}
+ {% endif %}
+
-
+
-
-
-
-
+
+
+ {% if mark %}
+
+
+
{% trans '我' %}{{ mark.get_status_display }}
+ {% if mark.status == status_enum.DO.value or mark.status == status_enum.COLLECT.value%}
+ {% if mark.rating %}
+
+ {% endif %}
+ {% endif %}
+ {% if mark.is_private %}
+
+ {% endif %}
+
+ {% trans '修改' %}
+
+
+
+
+ {{ mark.edited_time }}
+
+ {% if mark.text %}
+
{{ mark.text }}
+ {% endif %}
+
+ {% else %}
+
+
+ {% trans '标记这本书' %}
+
+
+
+ {% endif %}
+
-
+
+ {% if review %}
+
+ {% else %}
+
+ {% trans '我的评论' %}
+
+
{% trans '去写评论' %}
+ {% endif %}
+
+
@@ -124,6 +255,73 @@
+
+
+
+
+ {% if not mark %}
+
+
+ {% else %}
+
{% trans '我的标记' %}
+ {% endif %}
+
+
+
+
+
+
+
+
+
+
+
{% trans '确定要删除你的标记吗?' %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {% for mark in marks %}
+
+
+
+
{{ mark.owner.username }}
+
{{ mark.get_status_display }}
+ {% if mark.rating %}
+
+ {% endif %}
+ {% if mark.is_private %}
+
+ {% endif %}
+
{{ mark.edited_time }}
+
+
+ {% if mark.text %}
+
{{ mark.text }}
+ {% endif %}
+
+
+ {% empty %}
+ {% trans '无结果' %}
+ {% endfor %}
+
+
+
+
+
+
+
+
+

+
+
+ {% if book.isbn %}
+
ISBN: {{ book.isbn }}
+ {% endif %}
+
+
{% if book.pub_house %}{% trans '出版社:' %}{{ book.pub_house }}{% endif %}
+ {% if book.rating %}
+ {% trans '评分: ' %}
+ {% endif %}
+
+
+
+
+
+
+
+
+
+
+ {% comment %}
+
{% oauth_token %}
+
{% mastodon %}
+
+
{{ user.mastodon_id }}
+ {% endcomment %}
+
+
+
+
+
+