finish independent movie app

This commit is contained in:
doubaniux 2020-10-03 23:27:41 +02:00
parent ac9289c289
commit 8fd0788047
47 changed files with 947 additions and 220 deletions

View file

@ -1,11 +1,8 @@
from django import forms
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, TagField, TagInput
from common.forms import KeyValueInput
from common.forms import PreviewImageInput
from common.forms import *
def BookMarkStatusTranslator(status):
@ -21,6 +18,7 @@ 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())
other_info = JSONField(required=False, label=_("其他信息"))
class Meta:
model = Book
fields = [
@ -66,7 +64,6 @@ class BookForm(forms.ModelForm):
widgets = {
'author': forms.TextInput(attrs={'placeholder': _("多个作者使用英文逗号分隔")}),
'translator': forms.TextInput(attrs={'placeholder': _("多个译者使用英文逗号分隔")}),
'other_info': KeyValueInput(),
# 'cover': forms.FileInput(),
'cover': PreviewImageInput(),
}

View file

@ -10,7 +10,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% trans 'Nicedb - ' %}{{ title }}</title>
<title>{% trans 'NiceDB - ' %}{{ title }}</title>
<script src="https://cdn.staticfile.org/jquery/3.5.0/jquery.min.js"></script>
<!-- <link rel="stylesheet" href="{% static 'lib/css/milligram.css' %}"> -->
<!-- <link rel="stylesheet" href="{% static 'css/boofilsic_edit.css' %}"> -->
@ -27,6 +27,7 @@
<div class="single-section-wrapper" id="main">
<form class="entity-form" action="{{ submit_url }}" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.media }}
{{ form }}
<input class="button" type="submit" value="{% trans '提交' %}">
</form>

View file

@ -10,7 +10,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% trans 'Nicedb - ' %}{{ title }}</title>
<title>{% trans 'NiceDB - ' %}{{ title }}</title>
<script src="https://cdn.staticfile.org/jquery/3.5.0/jquery.min.js"></script>
<script src="{% static 'js/create_update_review.js' %}"></script>
<script src="{% static 'lib/js/rating-star.js' %}"></script>

View file

@ -10,7 +10,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% trans 'Nicedb - 删除图书' %}</title>
<title>{% trans 'NiceDB - 删除图书' %}</title>
<script src="https://cdn.staticfile.org/jquery/3.5.0/jquery.min.js"></script>
<script src="{% static 'lib/js/rating-star.js' %}"></script>
<script src="{% static 'js/rating-star-readonly.js' %}"></script>

View file

@ -10,7 +10,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% trans 'Nicedb - 删除评论' %}</title>
<title>{% trans 'NiceDB - 删除评论' %}</title>
<script src="https://cdn.staticfile.org/jquery/3.5.0/jquery.min.js"></script>
<link rel="stylesheet" href="{% static 'lib/css/rating-star.css' %}">

View file

@ -14,6 +14,7 @@
<meta property="og:type" content="book">
<meta property="og:url" content="{{ request.build_absolute_uri }}">
<meta property="og:image" content="{{ request.scheme }}://{{ request.get_host }}{{ book.cover.url }}">
<meta property="og:site_name" content="NiceDB">
{% if book.author %}
<meta property="og:book:author" content="{% for author in book.author %}{{ author }}{% if not forloop.last %},{% endif %}{% endfor %}">
{% endif %}
@ -21,7 +22,7 @@
<meta property="og:book:isbn" content="{{ book.isbn }}">
{% endif %}
<title>{% trans 'Nicedb - 书籍详情' %} | {{ book.title }}</title>
<title>{% trans 'NiceDB - 书籍详情' %} | {{ book.title }}</title>
<script src="https://cdn.staticfile.org/jquery/3.5.0/jquery.min.js"></script>
<script src="{% static 'lib/js/rating-star.js' %}"></script>
<script src="{% static 'js/detail.js' %}"></script>
@ -62,14 +63,14 @@
<div>{% if book.isbn %}{% trans 'ISBN' %}{{ book.isbn }}{% endif %}</div>
<div>{% if book.author %}{% trans '作者:' %}
{% for author in book.author %}
<span>{{ author }}</span>
<span>{{ author }}</span>{% if not forloop.last %} / {% endif %}
{% endfor %}
{% endif %}</div>
<div>{% if book.pub_house %}{% trans '出版社:' %}{{ book.pub_house }}{% endif %}</div>
<div>{% if book.subtitle %}{% trans '副标题:' %}{{ book.subtitle }}{% endif %}</div>
<div>{% if book.translator %}{% trans '译者:' %}
{% for translator in book.translator %}
<span>{{ translator }}</span>
<span>{{ translator }}</span>{% if not forloop.last %} / {% endif %}
{% endfor %}
{% endif %}</div>
<div>{% if book.orig_title %}{% trans '原作名:' %}{{ book.orig_title }}{% endif %}</div>

View file

@ -11,7 +11,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% trans 'Nicedb - ' %}{{ user.username }}{{ list_title }}</title>
<title>{% trans 'NiceDB - ' %}{{ user.username }}{{ list_title }}</title>
<script src="https://cdn.staticfile.org/jquery/3.5.0/jquery.min.js"></script>
<script src="{% static 'lib/js/rating-star.js' %}"></script>
<script src="{% static 'js/rating-star-readonly.js' %}"></script>

View file

@ -11,7 +11,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% trans 'Nicedb - ' %}{{ book.title }}{% trans '的标记' %}</title>
<title>{% trans 'NiceDB - ' %}{{ book.title }}{% trans '的标记' %}</title>
<script src="https://cdn.staticfile.org/jquery/3.5.0/jquery.min.js"></script>
<script src="{% static 'lib/js/rating-star.js' %}"></script>
<script src="{% static 'js/rating-star-readonly.js' %}"></script>

View file

@ -15,7 +15,7 @@
<meta property="og:article:author" content="{{ review.owner.username }}">
<meta property="og:url" content="{{ request.build_absolute_uri }}">
<meta property="og:image" content="{{ request.scheme }}://{{ request.get_host }}{% static 'img/logo_square.svg' %}">
<title>{% trans 'Nicedb - 评论详情' %}</title>
<title>{% trans 'NiceDB - 评论详情' %}</title>
<script src="https://cdn.staticfile.org/jquery/3.5.0/jquery.min.js"></script>
<script src="{% static 'lib/js/rating-star.js' %}"></script>
<script src="{% static 'js/rating-star-readonly.js' %}"></script>

View file

@ -11,7 +11,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% trans 'Nicedb - ' %}{{ book.title }}{% trans '的评论' %}</title>
<title>{% trans 'NiceDB - ' %}{{ book.title }}{% trans '的评论' %}</title>
<script src="https://cdn.staticfile.org/jquery/3.5.0/jquery.min.js"></script>
<script src="{% static 'lib/js/rating-star.js' %}"></script>
<script src="{% static 'js/rating-star-readonly.js' %}"></script>

View file

@ -10,7 +10,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% trans 'Nicedb - 从豆瓣获取数据' %}</title>
<title>{% trans 'NiceDB - 从豆瓣获取数据' %}</title>
<script src="https://cdn.staticfile.org/jquery/3.5.0/jquery.min.js"></script>
<script src="{% static 'js/scrape.js' %}"></script>
<link rel="stylesheet" href="{% static 'css/boofilsic.css' %}">
@ -68,6 +68,7 @@ ISBN: 9787020104345
<div id="scrapeForm">
<form action="{% url 'books:create' %}" method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ form.media }}
{{ form }}
</form>
<a href="{% url 'books:scrape' %}" class="button add-button submit">{% trans '剽取!' %}</a>
@ -97,7 +98,10 @@ ISBN: 9787020104345
</div>
<script>
// mark required
$("#content input[required]").each(function () {
$(this).prev().prepend("*");
})
</script>
</body>

View file

@ -132,7 +132,7 @@ def retrieve(request, id):
except ObjectDoesNotExist:
mark = None
if mark:
mark_tags = mark.mark_tags.all()
mark_tags = mark.bookmark_tags.all()
mark.get_status_display = BookMarkStatusTranslator(mark.status)
mark_form = BookMarkForm(instance=mark, initial={
'tags': mark_tags
@ -239,7 +239,7 @@ def create_update_mark(request):
if pk:
mark = get_object_or_404(BookMark, pk=pk)
old_rating = mark.rating
old_tags = mark.mark_tags.all()
old_tags = mark.bookmark_tags.all()
# update
form = BookMarkForm(request.POST, instance=mark)
else:

View file

@ -1,5 +1,6 @@
from django import forms
from django.contrib.postgres.forms import JSONField
import django.contrib.postgres.forms as postgres
from django.utils import formats
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _
import json
@ -26,6 +27,21 @@ class KeyValueInput(forms.Widget):
context['widget']['keyvalue_pairs'] = key_value_pairs
return context
class Media:
js = ('js/key_value_input.js',)
class JSONField(postgres.JSONField):
widget = KeyValueInput
def to_python(self, value):
if not value:
return None
json = {}
pairs = eval(value)
for pair in pairs:
json = {**json, **pair}
return super().to_python(json)
class RadioBooleanField(forms.ChoiceField):
widget = forms.RadioSelect
@ -110,3 +126,35 @@ class TagField(forms.CharField):
if not value:
return
return [t.strip() for t in value.split(',')]
class MultiSelect(forms.SelectMultiple):
template_name = 'widgets/multi_select.html'
class HstoreInput(forms.Widget):
template_name = 'widgets/hstore.html'
def format_value(self, value):
"""
Return a value as it should appear when rendered in a template.
"""
if value == '' or value is None:
return None
if self.is_localized:
return formats.localize_input(value)
return value
class Media:
js = ('js/key_value_input.js',)
class HstoreField(forms.CharField):
widget = HstoreInput
def to_python(self, value):
if not value:
return None
pairs = eval(value)
if len(pairs) == 1:
pairs = (pairs,)
return pairs

View file

@ -300,7 +300,7 @@ def scrape_douban_movie(url):
if duration_elem:
duration = duration_elem[0].strip()
if other_duration_elem:
duration += other_duration_elem[0]
duration += other_duration_elem[0].rstrip()
else:
duration = None
@ -324,7 +324,7 @@ def scrape_douban_movie(url):
is_series = True if episodes else False
brief_elem = content.xpath("//span[@property='v:summary']/text()")
brief = brief[0].strip() if brief_elem else None
brief = brief_elem[0].strip() if brief_elem else None
img_url_elem = content.xpath("//img[@rel='v:image']/@src")
img_url = img_url_elem[0].strip() if img_url_elem else None

View file

@ -1193,10 +1193,15 @@ select::placeholder {
font-weight: bold;
}
.entity-detail .entity-detail__title--secondary {
color: #bbb;
}
.entity-detail .entity-detail__fields {
display: inline-block;
vertical-align: top;
width: 48%;
width: 46%;
margin-left: 2%;
}
.entity-detail .entity-detail__fields div, .entity-detail .entity-detail__fields span {
@ -2083,3 +2088,44 @@ select::placeholder {
.rating-star .jq-star {
cursor: unset !important;
}
.ms-parent > .ms-choice {
margin-bottom: 1.5rem;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background-color: transparent;
border: 0.1rem solid #ccc;
border-radius: .4rem;
-webkit-box-shadow: none;
box-shadow: none;
-webkit-box-sizing: inherit;
box-sizing: inherit;
padding: .6rem 1.0rem;
width: 100%;
height: 30.126px;
}
.ms-parent > .ms-choice:focus {
border-color: #00a1cc;
}
.ms-parent > .ms-choice > .icon-caret {
top: 15.5px;
}
.ms-parent > .ms-choice > span {
color: black;
font-weight: initial;
font-size: 13.3333px;
top: 2.5px;
left: 2px;
}
.ms-parent > .ms-choice > span:hover, .ms-parent > .ms-choice > span:focus {
color: black;
}
.ms-parent > .ms-drop > ul > li > label > span {
margin-left: 10px;
}

View file

@ -0,0 +1,63 @@
function keyValueInput(valueKeyWidget, hiddenInput) {
let placeholderKey = valueKeyWidget.attr('placeholder-key');
let placeholderValue = valueKeyWidget.attr('placeholder-value');
if (placeholderKey == null) {
placeholderKey = '';
}
if (placeholderValue == null) {
placeholderValue = '';
}
let newInputPair = $('<input type="text"' + 'placeholder=' + placeholderKey + '><input type="text"' + 'placeholder=' + placeholderValue + '>');
valueKeyWidget.append(newInputPair.clone());
// add new input pair
valueKeyWidget.on('input', ':nth-last-child(1)', function () {
if ($(this).val() && $(this).prev().val()) {
valueKeyWidget.append($(newInputPair).clone());
}
});
valueKeyWidget.on('input', ':nth-last-child(2)', function () {
if ($(this).val() && $(this).next().val()) {
valueKeyWidget.append($(newInputPair).clone());
}
});
valueKeyWidget.on('input', ':nth-last-child(4)', function () {
if (!$(this).val() && !$(this).next().val() && valueKeyWidget.children("input").length > 2) {
$(this).next().remove();
$(this).remove();
}
});
valueKeyWidget.on('input', ':nth-last-child(3)', function () {
if (!$(this).val() && !$(this).prev().val() && valueKeyWidget.children("input").length > 2) {
$(this).prev().remove();
$(this).remove();
}
});
valueKeyWidget.on('input', function () {
let keys = $(this).children(":nth-child(odd)").map(function () {
if ($(this).val()) {
return $(this).val();
}
}).get();
let values = $(this).children(":nth-child(even)").map(function () {
if ($(this).val()) {
return $(this).val();
}
}).get();
if (keys.length == values.length) {
let finalValue = [];
keys.forEach(function (key, i) {
let json = new Object;
json[key] = values[i];
finalValue.push(JSON.stringify(json))
});
hiddenInput.val(finalValue.toString());
} else {
}
});
}

View file

@ -5,7 +5,7 @@
$color-initial: #fff !default
$color-primary: #00a1cc !default
$color-secondary: #606c76 !default
$color-tertiary: #fff !default
$color-tertiary: #bbb !default
$color-quaternary: #d5d5d5 !default
$color-quinary: #e5e5e5 !default

View file

@ -4,5 +4,5 @@
hr
border: 0
border-top: .1rem solid $color-tertiary
border-top: .1rem solid $color-initial
margin: 3.0rem 0

View file

@ -106,11 +106,15 @@ $sub-section-title-margin: 8px
& &__title
font-weight: bold
& &__title--secondary
color: $color-tertiary
& &__fields
display: inline-block
vertical-align: top
width: 48%
width: 46%
margin-left: 2%
// margin-bottom: 5px
& div, & span
margin: 1px 0

View file

@ -2,4 +2,35 @@
min-height: 100px
.rating-star .jq-star
cursor: unset !important
cursor: unset !important
.ms-parent > .ms-choice
margin-bottom: 1.5rem
appearance: none
background-color: transparent
border: 0.1rem solid #ccc
border-radius: .4rem
box-shadow: none
box-sizing: inherit
padding: .6rem 1.0rem
width: 100%
height: 30.126px
&:focus
border-color: $color-primary
& > .icon-caret
top: 15.5px
& > span
color: black
font-weight: initial
// font-size: 1.3rem
font-size: 13.3333px
top: 2.5px
left: 2px
&:hover, &:focus
color: black
.ms-parent > .ms-drop > ul > li > label > span
margin-left: 10px

View file

@ -10,7 +10,13 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% trans 'Nicedb - 主页' %}</title>
{% if user == request.user %}
<title>{% trans 'NiceDB - 我的主页' %}</title>
{% else %}
<title>{% trans 'NiceDB - ' %}{{user.username}}{% trans '的主页' %}</title>
{% endif %}
<script src="https://cdn.staticfile.org/jquery/3.5.0/jquery.min.js"></script>
<script src="{% static 'js/mastodon.js' %}"></script>
<script src="{% static 'js/home.js' %}"></script>
@ -109,6 +115,84 @@
</ul>
</div>
<div class="entity-sort" id="movieWish">
<h5 class="entity-sort__label">
{% trans '想看的电影/剧集' %}
</h5>
{% if wish_movies_more %}
<a href="{% url 'users:movie_list' user.id 'wish' %}"
class="entity-sort__more-link">{% trans '更多' %}</a>
{% endif %}
<ul class="entity-sort__entity-list">
{% for wish_movie_mark in wish_movie_marks %}
<li class="entity-sort__entity">
<a href="{% url 'movies:retrieve' wish_movie_mark.movie.id %}">
<img src="{{ wish_movie_mark.movie.cover.url }}"
alt="{{wish_movie_mark.movie.title}}" class="entity-sort__entity-img">
<div class="entity-sort__entity-name" title="wish_movie_mark.movie.title">
{{ wish_movie_mark.movie.title }}</div>
</a>
</li>
{% empty %}
<div>暂无记录</div>
{% endfor %}
</ul>
</div>
<div class="entity-sort" id="movieWish">
<h5 class="entity-sort__label">
{% trans '在看的电影/剧集' %}
</h5>
{% if do_movies_more %}
<a href="{% url 'users:movie_list' user.id 'do' %}"
class="entity-sort__more-link">{% trans '更多' %}</a>
{% endif %}
<ul class="entity-sort__entity-list">
{% for do_movie_mark in do_movie_marks %}
<li class="entity-sort__entity">
<a href="{% url 'movies:retrieve' do_movie_mark.movie.id %}">
<img src="{{ do_movie_mark.movie.cover.url }}"
alt="{{do_movie_mark.movie.title}}" class="entity-sort__entity-img">
<div class="entity-sort__entity-name" title="{{do_movie_mark.movie.title}}">
{{ do_movie_mark.movie.title }}</div>
</a>
</li>
{% empty %}
<div>暂无记录</div>
{% endfor %}
</ul>
</div>
<div class="entity-sort" id="movieCollect">
<h5 class="entity-sort__label">
{% trans '看过的电影/剧集' %}
</h5>
{% if collect_movies_more %}
<a href="{% url 'users:movie_list' user.id 'collect' %}"
class="entity-sort__more-link">{% trans '更多' %}</a>
{% endif %}
<ul class="entity-sort__entity-list">
{% for collect_movie_mark in collect_movie_marks %}
<li class="entity-sort__entity">
<a href="{% url 'movies:retrieve' collect_movie_mark.movie.id %}">
<img src="{{ collect_movie_mark.movie.cover.url }}"
alt="{{collect_movie_mark.movie.title}}" class="entity-sort__entity-img">
<span class="entity-sort__entity-name"
title="{{collect_movie_mark.movie.title}}">{{ collect_movie_mark.movie.title }}</span>
</a>
</li>
{% empty %}
<div>暂无记录</div>
{% endfor %}
</ul>
</div>
</div>
</div>

View file

@ -11,7 +11,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% trans 'Nicedb - 搜索结果' %}</title>
<title>{% trans 'NiceDB - 搜索结果' %}</title>
<script src="https://cdn.staticfile.org/jquery/3.5.0/jquery.min.js"></script>
<script src="{% static 'lib/js/rating-star.js' %}"></script>
<script src="{% static 'js/rating-star-readonly.js' %}"></script>

View file

@ -0,0 +1,31 @@
<style>
.widget-value-key-input input:nth-child(odd) {
width: 49%;
margin-right: 1%;
}
.widget-value-key-input input:nth-child(even) {
width: 49%;
margin-left: 1%;
}
</style>
<div class="widget-value-key-input" name='{{ widget.name }}'{% include "django/forms/widgets/attrs.html" %}>
{% if widget.value != None %}
{% for pair in widget.value %}
{% for k, v in pair.items %}
<input type="text" value="{{ k }}"><input type="text" value="{{ v }}">
{% endfor %}
{% endfor %}
{% endif %}
</div>
<input type="text" class="widget-value-key-input-data" hidden name="{{ widget.name }}">
<script>
keyValueInput(
$(".widget-value-key-input[name='{{ widget.name }}']"),
$(".widget-value-key-input-data[name='{{ widget.name }}']")
);
</script>

View file

@ -8,7 +8,7 @@
margin-left: 1%;
}
</style>
<div class="widget-value-key-input">
<div class="widget-value-key-input" name='{{ widget.name }}'{% include "django/forms/widgets/attrs.html" %}>
{% if widget.value != None %}
{% for k, v in widget.keyvalue_pairs.items %}
@ -19,53 +19,8 @@
</div>
<input type="text" class="widget-value-key-input-data" hidden name="{{ widget.name }}">
<script>
//init
$(".widget-value-key-input").append('<input type="text"><input type="text">');
// add new input pair
$(".widget-value-key-input").on('input', ':nth-last-child(1)', function() {
let newInputPair = $('<input type="text"><input type="text">');
if ($(this).val() && $(this).prev().val()) {
$(".widget-value-key-input").append($(newInputPair).clone());
}
});
$(".widget-value-key-input").on('input', ':nth-last-child(2)', function() {
let newInputPair = $('<input type="text"><input type="text">');
if ($(this).val() && $(this).next().val()) {
$(".widget-value-key-input").append($(newInputPair).clone());
}
});
$(".widget-value-key-input").on('input', ':nth-last-child(4)', function() {
if (!$(this).val() && !$(this).next().val() && $(".widget-value-key-input input").length > 2) {
$(this).next().remove();
$(this).remove();
}
});
$(".widget-value-key-input").on('input', ':nth-last-child(3)', function() {
if (!$(this).val() && !$(this).prev().val() && $(".widget-value-key-input input").length > 2) {
$(this).prev().remove();
$(this).remove();
}
});
$(".widget-value-key-input").on('input', function() {
let keys = $(this).children(":nth-child(odd)").map(function() {
if ($(this).val()) {
return $(this).val();
}
}).get();
let values = $(this).children(":nth-child(even)").map(function() {
if ($(this).val()) {
return $(this).val();
}
}).get();
if (keys.length == values.length) {
let json = new Object;
keys.forEach(function(key, i) {
json[key] = values[i];
});
$("input.widget-value-key-input-data").val(JSON.stringify(json));
} else {
}
});
keyValueInput(
$(".widget-value-key-input[name='{{ widget.name }}']"),
$(".widget-value-key-input-data[name='{{ widget.name }}']")
);
</script>

View file

@ -0,0 +1,18 @@
<select name="{{ widget.name }}"
{% include "django/forms/widgets/attrs.html" %}>{% for group_name, group_choices, group_index in widget.optgroups %}{% if group_name %}
<optgroup label="{{ group_name }}">{% endif %}{% for option in group_choices %}
{% include option.template_name with widget=option %}{% endfor %}{% if group_name %}
</optgroup>{% endif %}{% endfor %}
</select>
<link rel="stylesheet" href="https://unpkg.com/multiple-select@1.5.2/dist/multiple-select.min.css">
<script src="https://unpkg.com/multiple-select@1.5.2/dist/multiple-select.min.js"></script>
<script>
$('select[name="{{ widget.name }}"]').multipleSelect({
selectAll: false,
width: "100%",
displayTitle: true,
// any value greater than number of genres
minimumCountSelected: 50,
filter: true,
})
</script>

View file

@ -54,4 +54,12 @@ class PageLinksGenerator:
self.first_page = 1
self.last_page = total_pages
self.page_range = range(self.start_page, self.end_page + 1)
# assert self.has_prev is not None and self.has_next is not None
# assert self.has_prev is not None and self.has_next is not None
def ChoicesDictGenerator(choices_enum):
choices_dict = {}
for attr in dir(choices_enum):
if not '__' in attr:
choices_dict[getattr(choices_enum, attr).value] = getattr(choices_enum, attr).label
return choices_dict

View file

@ -12,6 +12,9 @@ from django.http import HttpResponseBadRequest
# how many books have in each set at the home page
BOOKS_PER_SET = 5
# how many movies have in each set at the home page
MOVIES_PER_SET = 5
# how many items are showed in one search result page
ITEMS_PER_PAGE = 20
@ -38,6 +41,19 @@ def home(request):
status=MarkStatusEnum.COLLECT).order_by("-edited_time")
collect_books_more = True if collect_book_marks.count() > BOOKS_PER_SET else False
do_movie_marks = request.user.user_moviemarks.filter(
status=MarkStatusEnum.DO).order_by("-edited_time")
do_movies_more = True if do_movie_marks.count() > MOVIES_PER_SET else False
wish_movie_marks = request.user.user_moviemarks.filter(
status=MarkStatusEnum.WISH).order_by("-edited_time")
wish_movies_more = True if wish_movie_marks.count() > MOVIES_PER_SET else False
collect_movie_marks = request.user.user_moviemarks.filter(
status=MarkStatusEnum.COLLECT).order_by("-edited_time")
collect_movies_more = True if collect_movie_marks.count() > MOVIES_PER_SET else False
reports = Report.objects.order_by('-submitted_time').filter(is_read=False)
# reports = Report.objects.latest('submitted_time').filter(is_read=False)
@ -51,6 +67,12 @@ def home(request):
'do_books_more': do_books_more,
'wish_books_more': wish_books_more,
'collect_books_more': collect_books_more,
'do_movie_marks': do_movie_marks[:MOVIES_PER_SET],
'wish_movie_marks': wish_movie_marks[:MOVIES_PER_SET],
'collect_movie_marks': collect_movie_marks[:MOVIES_PER_SET],
'do_movies_more': do_movies_more,
'wish_movies_more': wish_movies_more,
'collect_movies_more': collect_movies_more,
'reports': reports,
}
)

View file

@ -1,11 +1,9 @@
from django import forms
from django.contrib.postgres.forms import SimpleArrayField
from django.utils.translation import gettext_lazy as _
from .models import Movie, MovieMark, MovieReview
from .models import Movie, MovieMark, MovieReview, MovieGenreEnum
from common.models import MarkStatusEnum
from common.forms import RadioBooleanField, RatingValidator, TagField, TagInput
from common.forms import KeyValueInput
from common.forms import PreviewImageInput
from common.forms import *
def MovieMarkStatusTranslator(status):
@ -22,7 +20,24 @@ class MovieForm(forms.ModelForm):
# 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())
genre = forms.MultipleChoiceField(
choices=MovieGenreEnum.choices,
widget= MultiSelect,
label=_("类型")
)
showtime = HstoreField(
required=False,
label=_("上映时间"),
widget=HstoreInput(
attrs={
'placeholder-key': _("日期"),
'placeholder-value': _("地区"),
}
)
)
other_info = JSONField(required=False, label=_("其他信息"))
class Meta:
model = Movie
@ -31,7 +46,7 @@ class MovieForm(forms.ModelForm):
'title',
'orig_title',
'other_title',
'imbd_code',
'imdb_code',
'director',
'playwright',
'actor',
@ -46,14 +61,15 @@ class MovieForm(forms.ModelForm):
'episodes',
'single_episode_length',
'cover',
'brief',
'is_series',
'brief',
'other_info',
]
labels = {
'title': _("标题"),
'orig_title': _("原名"),
'other_title': _("又名"),
'imbd_code': _("IMBd编号"),
'imdb_code': _("IMDb编号"),
'director': _("导演"),
'playwright': _("编剧"),
'actor': _("主演"),
@ -69,16 +85,20 @@ class MovieForm(forms.ModelForm):
'single_episode_length': _("单集片长"),
'cover': _("封面"),
'brief': _("简介"),
'is_series': _("是否是剧集"),
'other_info': _("其他信息"),
'is_series': _("是否为剧集"),
}
# widgets = {
# 'author': forms.TextInput(attrs={'placeholder': _("多个作者使用英文逗号分隔")}),
# 'translator': forms.TextInput(attrs={'placeholder': _("多个译者使用英文逗号分隔")}),
# 'other_info': KeyValueInput(),
# # 'cover': forms.FileInput(),
# 'cover': PreviewImageInput(),
# }
widgets = {
'other_title': forms.TextInput(attrs={'placeholder': _("多个别名使用英文逗号分隔")}),
'director': forms.TextInput(attrs={'placeholder': _("多个导演使用英文逗号分隔")}),
'actor': forms.TextInput(attrs={'placeholder': _("多个主演使用英文逗号分隔")}),
'playwright': forms.TextInput(attrs={'placeholder': _("多个编剧使用英文逗号分隔")}),
'area': forms.TextInput(attrs={'placeholder': _("多个国家/地区使用英文逗号分隔")}),
'language': forms.TextInput(attrs={'placeholder': _("多种语言使用英文逗号分隔")}),
'cover': PreviewImageInput(),
'is_series': forms.CheckboxInput(attrs={'style': 'width: auto; position: relative; top: 2px'})
}
# def clean_isbn(self):
# isbn = self.cleaned_data.get('isbn')

View file

@ -4,6 +4,7 @@ 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, Tag
from common.utils import ChoicesDictGenerator
from boofilsic.settings import MOVIE_MEDIA_PATH_ROOT, DEFAULT_MOVIE_IMAGE
from django.utils import timezone
@ -54,6 +55,9 @@ class MovieGenreEnum(models.TextChoices):
TALK_SHOW = 'Talk-Show', _('脱口秀')
MovieGenreTranslator = ChoicesDictGenerator(MovieGenreEnum)
class Movie(Resource):
'''
Can either be movie or series.
@ -70,7 +74,7 @@ class Movie(Resource):
blank=True,
default=list,
)
imbd_code = models.CharField(
imdb_code = models.CharField(
blank=True, max_length=10, null=True, unique=True, db_index=True)
director = postgres.ArrayField(
models.CharField(_("director"), blank=True,
@ -93,12 +97,17 @@ class Movie(Resource):
blank=True,
default=list,
)
genre = models.CharField(
_("genre"),
genre = postgres.ArrayField(
models.CharField(
_("genre"),
blank=True,
default='',
choices=MovieGenreEnum.choices,
max_length=50
),
null=True,
blank=True,
default='',
choices=MovieGenreEnum.choices,
max_length=50
default=list,
)
showtime = postgres.ArrayField(
# HStoreField stores showtime-region pair
@ -107,7 +116,7 @@ class Movie(Resource):
blank=True,
default=list,
)
site = models.URLField(_('site url'), max_length=200)
site = models.URLField(_('site url'), blank=True, default='', max_length=200)
# country or region
area = postgres.ArrayField(
@ -162,6 +171,13 @@ class Movie(Resource):
return self.movie_tags
def get_genre_display(self):
translated_genre = []
for g in self.genre:
translated_genre.append(MovieGenreTranslator[g])
return translated_genre
class MovieMark(Mark):
movie = models.ForeignKey(Movie, on_delete=models.CASCADE, related_name='movie_marks', null=True)
class Meta:

View file

@ -10,7 +10,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% trans 'Nicedb - ' %}{{ title }}</title>
<title>{% trans 'NiceDB - ' %}{{ title }}</title>
<script src="https://cdn.staticfile.org/jquery/3.5.0/jquery.min.js"></script>
<!-- <link rel="stylesheet" href="{% static 'lib/css/milligram.css' %}"> -->
<!-- <link rel="stylesheet" href="{% static 'css/boofilsic_edit.css' %}"> -->
@ -27,7 +27,22 @@
<div class="single-section-wrapper" id="main">
<form class="entity-form" action="{{ submit_url }}" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form }}
{{ form.media }}
{% for field in form %}
{% if field.id_for_label == 'id_is_series' %}
<label for="{{ field.id_for_label }}"
style="display: inline-block; position: relative; left: -4px;">{{ field.label }}</label>
{{ field }}
{% else %}
{% if field.id_for_label != 'id_id' %}
<label for="{{ field.id_for_label }}">{{ field.label }}</label>
{% endif %}
{{ field }}
{% endif %}
{% endfor %}
<input class="button" type="submit" value="{% trans '提交' %}">
</form>
</div>

View file

@ -1,5 +1,7 @@
{% load static %}
{% load i18n %}
{% load l10n %}
{% load humanize %}
{% load admin_url %}
{% load mastodon %}
{% load oauth_token %}
@ -10,7 +12,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% trans 'Nicedb - ' %}{{ title }}</title>
<title>{% trans 'NiceDB - ' %}{{ title }}</title>
<script src="https://cdn.staticfile.org/jquery/3.5.0/jquery.min.js"></script>
<script src="{% static 'js/create_update_review.js' %}"></script>
<script src="{% static 'lib/js/rating-star.js' %}"></script>
@ -36,16 +38,61 @@
</div>
<div class="entity-card__info-wrapper entity-card__info-wrapper--horizontal">
<h5 class="entity-card__title"><a href="{% url 'movies:retrieve' movie.id %}">{{ movie.title }}</a></h5>
<div>{% if movie.isbn %}{% trans 'ISBN' %}{{ movie.isbn }}{% endif %}</div>
<div>{% if movie.author %}{% trans '作者:' %}
{% for author in movie.author %}
<span>{{ author }}</span>
<h5 class="entity-card__title"><a href="{% url 'movies:retrieve' movie.id %}">
{% if movie.season %}
{{ movie.title }} {% trans '第' %}{{ movie.season|apnumber }}{% trans '季' %} {{ movie.orig_title }} Season
{{ movie.season }}
<span class="entity-detail__title entity-detail__title--secondary">
({{ movie.year }})
</span>
{% else %}
{{ movie.title }} {{ movie.orig_title }}
<span class="entity-detail__title entity-detail__title--secondary">
({{ movie.year }})
</span>
{% endif %}
</a></h5>
<div>{% if movie.director %}{% trans '导演:' %}
{% for director in movie.director %}
<span>{{ director }}</span>{% if not forloop.last %} / {% endif %}
{% endfor %}
{% endif %}</div>
<div>{% if movie.genre %}{% trans '类型:' %}
{% for genre in movie.get_genre_display %}
<span>{{ genre }}</span>{% if not forloop.last %} / {% endif %}
{% endfor %}
{% endif %}</div>
<div>{% if movie.actor %}{% trans '主演:' %}
{% for actor in movie.actor %}
<span {% if forloop.counter > 5 %}style="display: none;"{% endif %}>{{ actor }}</span>
{% if forloop.counter <= 5 %}
{% if not forloop.last %} / {% endif %}
{% endif %}
{% endfor %}
{% if movie.actor|length > 5 %}
<a href="javascript:void(0);" id="actorMore">{% trans '更多' %}</a>
<script>
$("#actorMore").click(function(e) {
$(this).siblings("span:not(:visible)").each(function(e){
$(this).removeAttr('style');
});
$(this).remove();
})
</script>
{% endif %}
{% endif %}</div>
<div>{% if movie.pub_house %}{% trans '出版社:' %}{{ movie.pub_house }}{% endif %}</div>
<div>{%if movie.pub_year %}{% trans '出版时间:' %}{{ movie.pub_year }}{% trans '年' %}{% if movie.pub_month %}{{ movie.pub_month }}{% trans '月' %}{% endif %}{% endif %}</div>
<div>{% if movie.showtime %}{% trans '上映时间:' %}
{% for showtime in movie.showtime %}
{% for time, region in showtime.items %}
<span>{{ time }}({{ region }})</span>
{% endfor %}
{% if not forloop.last %} / {% endif %}
{% endfor %}
{% endif %}</div>
{% if movie.rating %}
{% trans '评分:' %}<span class="entity-card__rating-star rating-star" data-rating-score="{{ movie.rating | floatformat:"0" }}"> </span>
<span class="entity-card__rating-score rating-score"> {{ movie.rating }} </span>

View file

@ -1,5 +1,7 @@
{% load static %}
{% load i18n %}
{% load l10n %}
{% load humanize %}
{% load admin_url %}
{% load mastodon %}
{% load oauth_token %}
@ -10,7 +12,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% trans 'Nicedb - 删除电影/剧集' %}</title>
<title>{% trans 'NiceDB - 删除电影/剧集' %}</title>
<script src="https://cdn.staticfile.org/jquery/3.5.0/jquery.min.js"></script>
<script src="{% static 'lib/js/rating-star.js' %}"></script>
<script src="{% static 'js/rating-star-readonly.js' %}"></script>
@ -39,7 +41,18 @@
<div class="entity-card__info-wrapper entity-card__info-wrapper--horizontal">
<a href="{% url 'movies:retrieve' movie.id %}">
<h5 class="entity-card__title">
{{ movie.title }}
{% if movie.season %}
{{ movie.title }} {% trans '第' %}{{ movie.season|apnumber }}{% trans '季' %} {{ movie.orig_title }} Season
{{ movie.season }}
<span class="entity-detail__title entity-detail__title--secondary">
({{ movie.year }})
</span>
{% else %}
{{ movie.title }} {{ movie.orig_title }}
<span class="entity-detail__title entity-detail__title--secondary">
({{ movie.year }})
</span>
{% endif %}
</h5>
</a>
{% if movie.rating %}

View file

@ -10,7 +10,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% trans 'Nicedb - 删除评论' %}</title>
<title>{% trans 'NiceDB - 删除评论' %}</title>
<script src="https://cdn.staticfile.org/jquery/3.5.0/jquery.min.js"></script>
<link rel="stylesheet" href="{% static 'lib/css/rating-star.css' %}">

View file

@ -1,5 +1,7 @@
{% load static %}
{% load i18n %}
{% load l10n %}
{% load humanize %}
{% load admin_url %}
{% load mastodon %}
{% load oauth_token %}
@ -10,18 +12,28 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta property="og:title" content="NiceDB - {{ movie.title }}">
<meta property="og:type" content="movie">
<meta property="og:title" content="NiceDB电影 - {{ movie.title }}">
<meta property="og:type" content="video.movie">
<meta property="og:url" content="{{ request.build_absolute_uri }}">
<meta property="og:image" content="{{ request.scheme }}://{{ request.get_host }}{{ movie.cover.url }}">
{% if movie.author %}
<meta property="og:movie:author" content="{% for author in movie.author %}{{ author }}{% if not forloop.last %},{% endif %}{% endfor %}">
{% endif %}
{% if movie.isbn %}
<meta property="og:movie:isbn" content="{{ movie.isbn }}">
{% endif %}
<meta property="og:site_name" content="NiceDB">
<meta property="og:description"content="{{ movie.brief }}">
<!--
video:actor - profile array - Actors in the movie.
video:actor:role - string - The role they played.
video:director - profile array - Directors of the movie.
video:writer - profile array - Writers of the movie.
video:duration - integer >=1 - The movie's length in seconds.
video:release_date - datetime - The date the movie was released.
video:tag - string array - Tag words associated with this movie.
-->
<title>{% trans 'Nicedb - 电影/剧集详情' %} | {{ movie.title }}</title>
{% if movie.is_series %}
<title>{% trans 'NiceDB - 剧集详情' %} | {{ movie.title }}</title>
{% else %}
<title>{% trans 'NiceDB - 电影详情' %} | {{ movie.title }}</title>
{% endif %}
<script src="https://cdn.staticfile.org/jquery/3.5.0/jquery.min.js"></script>
<script src="{% static 'lib/js/rating-star.js' %}"></script>
<script src="{% static 'js/detail.js' %}"></script>
@ -47,7 +59,18 @@
<div class="entity-detail__info">
<h5 class="entity-detail__title">
{{ movie.title }}
{% if movie.season %}
{{ movie.title }} {% trans '第' %}{{ movie.season|apnumber }}{% trans '季' %} {{ movie.orig_title }} Season {{ movie.season }}
<span class="entity-detail__title entity-detail__title--secondary">
({{ movie.year }})
</span>
{% else %}
{{ movie.title }} {{ movie.orig_title }}
<span class="entity-detail__title entity-detail__title--secondary">
({{ movie.year }})
</span>
{% endif %}
</h5>
<div class="entity-detail__fields">
@ -59,28 +82,80 @@
<span> {% trans '评分:暂无评分' %}</span>
{% endif %}
</div>
<div>{% if movie.isbn %}{% trans 'ISBN' %}{{ movie.isbn }}{% endif %}</div>
<div>{% if movie.author %}{% trans '作者:' %}
{% for author in movie.author %}
<span>{{ author }}</span>
<div>{% if movie.imdb_code %}
{% trans 'IMDb' %}<a href="https://www.imdb.com/title/{{ movie.imdb_code }}/" target="_blank">{{ movie.imdb_code }}</a>
{% endif %}
</div>
<div>{% if movie.director %}{% trans '导演:' %}
{% for director in movie.director %}
<span>{{ director }}</span>{% if not forloop.last %} / {% endif %}
{% endfor %}
{% endif %}</div>
<div>{% if movie.pub_house %}{% trans '出版社:' %}{{ movie.pub_house }}{% endif %}</div>
<div>{% if movie.subtitle %}{% trans '副标题:' %}{{ movie.subtitle }}{% endif %}</div>
<div>{% if movie.translator %}{% trans '译者:' %}
{% for translator in movie.translator %}
<span>{{ translator }}</span>
{% endfor %}
{% endif %}</div>
<div>{% if movie.orig_title %}{% trans '原作名:' %}{{ movie.orig_title }}{% endif %}</div>
<div>{% if movie.language %}{% trans '语言:' %}{{ movie.language }}{% endif %}</div>
<div>{%if movie.pub_year %}{% trans '出版时间:' %}{{ movie.pub_year }}{% trans '年' %}{% if movie.pub_month %}{{ movie.pub_month }}{% trans '月' %}{% endif %}{% endif %}</div>
<div>{% if movie.playwright %}{% trans '编剧:' %}
{% for playwright in movie.playwright %}
<span>{{ playwright }}</span>{% if not forloop.last %} / {% endif %}
{% endfor %}
{% endif %}</div>
<div>{% if movie.actor %}{% trans '主演:' %}
{% for actor in movie.actor %}
<span {% if forloop.counter > 5 %}style="display: none;"{% endif %}>{{ actor }}</span>
{% if forloop.counter <= 5 %}
{% if not forloop.last %} / {% endif %}
{% endif %}
{% endfor %}
{% if movie.actor|length > 5 %}
<a href="javascript:void(0);" id="actorMore">{% trans '更多' %}</a>
<script>
$("#actorMore").click(function(e) {
$(this).siblings("span:not(:visible)").each(function(e){
$(this).removeAttr('style');
});
$(this).remove();
})
</script>
{% endif %}
{% endif %}</div>
<div>{% if movie.genre %}{% trans '类型:' %}
{% for genre in movie.get_genre_display %}
<span>{{ genre }}</span>{% if not forloop.last %} / {% endif %}
{% endfor %}
{% endif %}</div>
<div>{% if movie.area %}{% trans '制片国家/地区:' %}
{% for area in movie.area %}
<span>{{ area }}</span>{% if not forloop.last %} / {% endif %}
{% endfor %}
{% endif %}</div>
<div>{% if movie.language %}{% trans '语言:' %}
{% for language in movie.language %}
<span>{{ language }}</span>{% if not forloop.last %} / {% endif %}
{% endfor %}
{% endif %}</div>
</div>
<div class="entity-detail__fields">
<div>{% if movie.binding %}{% trans '装帧:' %}{{ movie.binding }}{% endif %}</div>
<div>{% if movie.price %}{% trans '定价:' %}{{ movie.price }}{% endif %}</div>
<div>{% if movie.pages %}{% trans '页数:' %}{{ movie.pages }}{% endif %}</div>
<div>{% if movie.duration %}{% trans '片长:' %}{{ movie.duration }}{% endif %}</div>
<div>{% if movie.season %}{% trans '季数:' %}{{ movie.season }}{% endif %}</div>
<div>{% if movie.episodes %}{% trans '集数:' %}{{ movie.episodes }}{% endif %}</div>
<div>{% if movie.single_episode_length %}{% trans '单集长度:' %}{{ movie.single_episode_length }}{% endif %}</div>
<div>{% if movie.showtime %}{% trans '上映时间:' %}
{% for showtime in movie.showtime %}
{% for time, region in showtime.items %}
<span>{{ time }}({{ region }})</span>
{% endfor %}
{% if not forloop.last %} / {% endif %}
{% endfor %}
{% endif %}</div>
<div>{% if movie.other_title %}{% trans '又名:' %}
{% for other_title in movie.other_title %}
<span>{{ other_title }}</span>{% if not forloop.last %} / {% endif %}
{% endfor %}
{% endif %}</div>
<div>{% if movie.site %}{% trans '网站:' %}
<a href="{{ movie.site }}" target="_blank">{{ movie.site }}</a>
{% endif %}</div>
{% if movie.other_info %}
{% for k, v in movie.other_info.items %}
<div>
@ -93,9 +168,13 @@
{% if movie.last_editor %}
<div>{% trans '最近编辑者:' %}<a href="{% url 'users:home' movie.last_editor.id %}">{{ movie.last_editor | default:"" }}</a></div>
{% endif %}
<div>
<a href="{% url 'movies:update' movie.id %}">{% trans '编辑这部电影/剧集' %}</a>
{% if movie.is_series %}
<a href="{% url 'movies:update' movie.id %}">{% trans '编辑这部剧集' %}</a>
{% else %}
<a href="{% url 'movies:update' movie.id %}">{% trans '编辑这部电影' %}</a>
{% endif %}
{% if user.is_staff %}
<a href="{% url 'movies:delete' movie.id %}"> / {% trans '删除' %}</a>
{% endif %}
@ -144,7 +223,12 @@
{% endif %}
<div class="entity-marks">
<h5 class="entity-marks__title">{% trans '这部电影/剧集的标记' %}</h5>
{% if movie.is_series %}
<h5 class="entity-marks__title">{% trans '这部剧集的标记' %}</h5>
{% else %}
<h5 class="entity-marks__title">{% trans '这部电影的标记' %}</h5>
{% endif %}
{% if mark_list_more %}
<a href="{% url 'movies:retrieve_mark_list' movie.id %}" class="entity-marks__more-link">{% trans '更多' %}</a>
{% endif %}
@ -172,7 +256,12 @@
{% endif %}
</div>
<div class="entity-reviews">
<h5 class="entity-reviews__title">{% trans '这部电影/剧集的评论' %}</h5>
{% if movie.is_series %}
<h5 class="entity-reviews__title">{% trans '这部剧集的评论' %}</h5>
{% else %}
<h5 class="entity-reviews__title">{% trans '这部电影的评论' %}</h5>
{% endif %}
{% if review_list_more %}
<a href="{% url 'movies:retrieve_review_list' movie.id %}" class="entity-reviews__more-link">{% trans '更多' %}</a>
{% endif %}
@ -235,7 +324,12 @@
</div>
{% else %}
<div class="action-panel" id="addMarkPanel">
<div class="action-panel__label">{% trans '标记这部电影/剧集' %}</div>
{% if movie.is_series %}
<div class="action-panel__label">{% trans '标记这部剧集' %}</div>
{% else %}
<div class="action-panel__label">{% trans '标记这部电影' %}</div>
{% endif %}
<div class="action-panel__button-group">
<button class="action-panel__button" data-status="{{ status_enum.WISH.value }}" id="wishButton">{% trans '想读' %}</button>
<button class="action-panel__button" data-status="{{ status_enum.DO.value }}">{% trans '在读' %}</button>
@ -294,11 +388,21 @@
<div class="mark-modal__head">
{% if not mark %}
{% if movie.is_series %}
<style>
.mark-modal__title::after {
content: "{% trans '这部电影/剧集' %}";
content: "{% trans '这部剧集' %}";
}
</style>
{% else %}
<style>
.mark-modal__title::after {
content: "{% trans '这部电影' %}";
}
</style>
{% endif %}
<span class="mark-modal__title"></span>
{% else %}
<span class="mark-modal__title">{% trans '我的标记' %}</span>

View file

@ -1,5 +1,7 @@
{% load static %}
{% load i18n %}
{% load l10n %}
{% load humanize %}
{% load admin_url %}
{% load mastodon %}
{% load oauth_token %}
@ -11,7 +13,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% trans 'Nicedb - ' %}{{ user.username }}{{ list_title }}</title>
<title>{% trans 'NiceDB - ' %}{{ user.username }}{{ list_title }}</title>
<script src="https://cdn.staticfile.org/jquery/3.5.0/jquery.min.js"></script>
<script src="{% static 'lib/js/rating-star.js' %}"></script>
<script src="{% static 'js/rating-star-readonly.js' %}"></script>
@ -52,7 +54,18 @@
<div class="entity-list__entity-text">
<div class="entity-list__entity-title">
<a href="{% url 'movies:retrieve' mark.movie.id %}" class="entity-list__entity-link">
{{ mark.movie.title }}
{% if mark.movie.season %}
{{ mark.movie.title }} {% trans '第' %}{{ mark.movie.season|apnumber }}{% trans '季' %} {{ mark.movie.orig_title }} Season
{{ mark.movie.season }}
<span class="entity-detail__title entity-detail__title--secondary">
({{ mark.movie.year }})
</span>
{% else %}
{{ mark.movie.title }} {{ mark.movie.orig_title }}
<span class="entity-detail__title entity-detail__title--secondary">
({{ mark.movie.year }})
</span>
{% endif %}
</a>
</div>
{% comment %}
@ -66,32 +79,36 @@
{% endif %} -->
{% endcomment %}
<span class="entity-list__entity-info entity-list__entity-info--full-length">
{% if mark.movie.pub_year %}
{{ mark.movie.pub_year }}{% trans '年' %} /
{% if mark.movie.pub_month %}
{{ mark.movie.pub_month }}{% trans '月' %} /
{% endif %}
{% endif %}
{% if mark.movie.author %}
{% trans '作者' %}
{% for author in mark.movie.author %}
{{ author }}{% if not forloop.last %},{% endif %}
{% endfor %}/
{% endif %}
{% if mark.movie.director %}{% trans '导演' %}
{% for director in mark.movie.director %}
{{ director }}{% if not forloop.last %} {% endif %}
{% endfor %}/
{% endif %}
{% if mark.movie.translator %}
{% trans '译者' %}
{% for translator in mark.movie.translator %}
{{ translator }}{% if not forloop.last %},{% endif %}
{% endfor %}/
{% endif %}
{% if mark.movie.orig_title %}
&nbsp;{% trans '原名' %}
{{ mark.movie.orig_title }}
{% if mark.movie.genre %}{% trans '类型' %}
{% for genre in mark.movie.get_genre_display %}
{{ genre }}{% if not forloop.last %} {% endif %}
{% endfor %}/
{% endif %}
{% if mark.movie.other_title %}{% trans '又名' %}
{% for other_title in mark.movie.other_title %}
{{ other_title }}{% if not forloop.last %} {% endif %}
{% endfor %}
{% endif %}
</span>
<span class="entity-list__entity-info entity-list__entity-info--full-length">
{% if mark.movie.actor %}{% trans '主演' %}
{% for actor in mark.movie.actor %}
<span {% if forloop.counter > 5 %}style="display: none;" {% endif %}>{{ actor }}</span>
{% if forloop.counter <= 5 %}
{% if not forloop.last %} / {% endif %}
{% endif %}
</span>
{% endfor %}
{% endif %}
</span>
<p class="entity-list__entity-brief">
{{ mark.movie.brief | truncate:170 }}
</p>

View file

@ -1,5 +1,7 @@
{% load static %}
{% load i18n %}
{% load l10n %}
{% load humanize %}
{% load admin_url %}
{% load mastodon %}
{% load oauth_token %}
@ -11,7 +13,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% trans 'Nicedb - ' %}{{ movie.title }}{% trans '的标记' %}</title>
<title>{% trans 'NiceDB - ' %}{{ movie.title }}{% trans '的标记' %}</title>
<script src="https://cdn.staticfile.org/jquery/3.5.0/jquery.min.js"></script>
<script src="{% static 'lib/js/rating-star.js' %}"></script>
<script src="{% static 'js/rating-star-readonly.js' %}"></script>
@ -106,13 +108,40 @@
<a href="{% url 'movies:retrieve' movie.id %}"><img src="{{ movie.cover.url }}" alt="" class="entity-card__img"></a>
</div>
<div class="entity-card__info-wrapper">
<h5 class="entity-card__title"><a href="{% url 'movies:retrieve' movie.id %}">{{ movie.title }}</a></h5>
<h5 class="entity-card__title"><a href="{% url 'movies:retrieve' movie.id %}">
{% if movie.season %}
{{ movie.title }} {% trans '第' %}{{ movie.season|apnumber }}{% trans '季' %} {{ movie.orig_title }} Season
{{ movie.season }}
<span class="entity-detail__title entity-detail__title--secondary">
({{ movie.year }})
</span>
{% else %}
{{ movie.title }} {{ movie.orig_title }}
<span class="entity-detail__title entity-detail__title--secondary">
({{ movie.year }})
</span>
{% endif %}
</a></h5>
{% if movie.isbn %}
<div>ISBN: {{ movie.isbn }}</div>
{% endif %}
<div>{% if movie.pub_house %}{% trans '出版社:' %}{{ movie.pub_house }}{% endif %}</div>
<div>{% if movie.director %}{% trans '导演:' %}
{% for director in movie.director %}
<span>{{ director }}</span>{% if not forloop.last %} / {% endif %}
{% endfor %}
{% endif %}</div>
<div>{% if movie.genre %}{% trans '类型:' %}
{% for genre in movie.get_genre_display %}
<span>{{ genre }}</span>{% if not forloop.last %} / {% endif %}
{% endfor %}
{% endif %}</div>
<div>{% if movie.showtime %}{% trans '上映时间:' %}
{% for showtime in movie.showtime %}
{% for time, region in showtime.items %}
<span>{{ time }}({{ region }})</span>
{% endfor %}
{% if not forloop.last %} / {% endif %}
{% endfor %}
{% endif %}</div>
{% if movie.rating %}
{% trans '评分: ' %}<span class="entity-card__rating-star rating-star" data-rating-score="{{ movie.rating | floatformat:"0" }}"></span>
<span class="entity-card__rating-score rating-score">{{ movie.rating }}</span>

View file

@ -1,5 +1,7 @@
{% load static %}
{% load i18n %}
{% load l10n %}
{% load humanize %}
{% load admin_url %}
{% load mastodon %}
{% load oauth_token %}
@ -15,7 +17,7 @@
<meta property="og:article:author" content="{{ review.owner.username }}">
<meta property="og:url" content="{{ request.build_absolute_uri }}">
<meta property="og:image" content="{{ request.scheme }}://{{ request.get_host }}{% static 'img/logo_square.svg' %}">
<title>{% trans 'Nicedb - 评论详情' %}</title>
<title>{% trans 'NiceDB - 评论详情' %}</title>
<script src="https://cdn.staticfile.org/jquery/3.5.0/jquery.min.js"></script>
<script src="{% static 'lib/js/rating-star.js' %}"></script>
<script src="{% static 'js/rating-star-readonly.js' %}"></script>
@ -85,13 +87,40 @@
class="entity-card__img"></a>
</div>
<div class="entity-card__info-wrapper">
<h5 class="entity-card__title"><a href="{% url 'movies:retrieve' movie.id %}">{{ movie.title }}</a></h5>
{% if movie.isbn %}
<div>ISBN: {{ movie.isbn }}</div>
{% endif %}
<div>{% if movie.pub_house %}{% trans '出版社:' %}{{ movie.pub_house }}{% endif %}</div>
<h5 class="entity-card__title"><a href="{% url 'movies:retrieve' movie.id %}">
{% if movie.season %}
{{ movie.title }} {% trans '第' %}{{ movie.season|apnumber }}{% trans '季' %} {{ movie.orig_title }} Season
{{ movie.season }}
<span class="entity-detail__title entity-detail__title--secondary">
({{ movie.year }})
</span>
{% else %}
{{ movie.title }} {{ movie.orig_title }}
<span class="entity-detail__title entity-detail__title--secondary">
({{ movie.year }})
</span>
{% endif %}
</a></h5>
<div>{% if movie.director %}{% trans '导演:' %}
{% for director in movie.director %}
<span>{{ director }}</span>{% if not forloop.last %} / {% endif %}
{% endfor %}
{% endif %}</div>
<div>{% if movie.genre %}{% trans '类型:' %}
{% for genre in movie.get_genre_display %}
<span>{{ genre }}</span>{% if not forloop.last %} / {% endif %}
{% endfor %}
{% endif %}</div>
<div>{% if movie.showtime %}{% trans '上映时间:' %}
{% for showtime in movie.showtime %}
{% for time, region in showtime.items %}
<span>{{ time }}({{ region }})</span>
{% endfor %}
{% if not forloop.last %} / {% endif %}
{% endfor %}
{% endif %}</div>
{% if movie.rating %}
{% trans '评分: ' %}<span class="entity-card__rating-star rating-star"
data-rating-score="{{ movie.rating | floatformat:"0" }}"></span>

View file

@ -1,5 +1,7 @@
{% load static %}
{% load i18n %}
{% load l10n %}
{% load humanize %}
{% load admin_url %}
{% load mastodon %}
{% load oauth_token %}
@ -11,7 +13,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% trans 'Nicedb - ' %}{{ movie.title }}{% trans '的评论' %}</title>
<title>{% trans 'NiceDB - ' %}{{ movie.title }}{% trans '的评论' %}</title>
<script src="https://cdn.staticfile.org/jquery/3.5.0/jquery.min.js"></script>
<script src="{% static 'lib/js/rating-star.js' %}"></script>
<script src="{% static 'js/rating-star-readonly.js' %}"></script>
@ -92,13 +94,40 @@
class="entity-card__img"></a>
</div>
<div class="entity-card__info-wrapper">
<h5 class="entity-card__title"><a href="{% url 'movies:retrieve' movie.id %}">{{ movie.title }}</a></h5>
{% if movie.isbn %}
<div>ISBN: {{ movie.isbn }}</div>
{% endif %}
<div>{% if movie.pub_house %}{% trans '出版社:' %}{{ movie.pub_house }}{% endif %}</div>
<h5 class="entity-card__title"><a href="{% url 'movies:retrieve' movie.id %}">
{% if movie.season %}
{{ movie.title }} {% trans '第' %}{{ movie.season|apnumber }}{% trans '季' %} {{ movie.orig_title }} Season
{{ movie.season }}
<span class="entity-detail__title entity-detail__title--secondary">
({{ movie.year }})
</span>
{% else %}
{{ movie.title }} {{ movie.orig_title }}
<span class="entity-detail__title entity-detail__title--secondary">
({{ movie.year }})
</span>
{% endif %}
</a></h5>
<div>{% if movie.director %}{% trans '导演:' %}
{% for director in movie.director %}
<span>{{ director }}</span>{% if not forloop.last %} / {% endif %}
{% endfor %}
{% endif %}</div>
<div>{% if movie.genre %}{% trans '类型:' %}
{% for genre in movie.get_genre_display %}
<span>{{ genre }}</span>{% if not forloop.last %} / {% endif %}
{% endfor %}
{% endif %}</div>
<div>{% if movie.showtime %}{% trans '上映时间:' %}
{% for showtime in movie.showtime %}
{% for time, region in showtime.items %}
<span>{{ time }}({{ region }})</span>
{% endfor %}
{% if not forloop.last %} / {% endif %}
{% endfor %}
{% endif %}</div>
{% if movie.rating %}
{% trans '评分: ' %}<span class="entity-card__rating-star rating-star"
data-rating-score="{{ movie.rating | floatformat:"0" }}"></span>

View file

@ -10,7 +10,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% trans 'Nicedb - 从豆瓣获取数据' %}</title>
<title>{% trans 'NiceDB - 从豆瓣获取数据' %}</title>
<script src="https://cdn.staticfile.org/jquery/3.5.0/jquery.min.js"></script>
<script src="{% static 'js/scrape.js' %}"></script>
<link rel="stylesheet" href="{% static 'css/boofilsic.css' %}">
@ -51,7 +51,23 @@
<div id="scrapeForm">
<form action="{% url 'movies:create' %}" method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ form }}
{{ form.media }}
{% for field in form %}
{% if field.id_for_label == 'id_is_series' %}
<label for="{{ field.id_for_label }}" style="display: inline-block; position: relative; left: -4px;">{{ field.label }}</label>
{{ field }}
{% else %}
{% if field.id_for_label != 'id_id' %}
<label for="{{ field.id_for_label }}">{{ field.label }}</label>
{% endif %}
{{ field }}
{% endif %}
{% endfor %}
</form>
<a href="{% url 'movies:scrape' %}" class="button add-button submit">{% trans '剽取!' %}</a>
</div>
@ -80,7 +96,10 @@
</div>
<script>
// mark required
$("#content input[required]").each(function () {
$(this).prev().prepend("*");
})
</script>
</body>

View file

@ -47,7 +47,7 @@ def create(request):
'movies/create_update.html',
{
'form': form,
'title': _('添加书籍'),
'title': _('添加电影/剧集'),
'submit_url': reverse("movies:create")
}
)
@ -65,7 +65,7 @@ def create(request):
'movies/create_update.html',
{
'form': form,
'title': _('添加书籍'),
'title': _('添加电影/剧集'),
'submit_url': reverse("movies:create")
}
)
@ -80,18 +80,20 @@ def update(request, id):
if request.method == 'GET':
movie = get_object_or_404(Movie, pk=id)
form = MovieForm(instance=movie)
page_title = _('修改剧集') if movie.is_series else _("修改电影")
return render(
request,
'movies/create_update.html',
{
'form': form,
'title': _('修改书籍'),
'title': page_title,
'submit_url': reverse("movies:update", args=[movie.id])
}
)
elif request.method == 'POST':
movie = get_object_or_404(Movie, pk=id)
form = MovieForm(request.POST, request.FILES, instance=movie)
page_title = _('修改剧集') if movie.is_series else _("修改电影")
if form.is_valid():
form.instance.last_editor = request.user
form.instance.edited_time = timezone.now()
@ -102,7 +104,7 @@ def update(request, id):
'movies/create_update.html',
{
'form': form,
'title': _('修改书籍'),
'title': page_title,
'submit_url': reverse("movies:update", args=[movie.id])
}
)
@ -132,7 +134,7 @@ def retrieve(request, id):
except ObjectDoesNotExist:
mark = None
if mark:
mark_tags = mark.mark_tags.all()
mark_tags = mark.moviemark_tags.all()
mark.get_status_display = MovieMarkStatusTranslator(mark.status)
mark_form = MovieMarkForm(instance=mark, initial={
'tags': mark_tags
@ -239,7 +241,7 @@ def create_update_mark(request):
if pk:
mark = get_object_or_404(MovieMark, pk=pk)
old_rating = mark.rating
old_tags = mark.mark_tags.all()
old_tags = mark.moviemark_tags.all()
# update
form = MovieMarkForm(request.POST, instance=mark)
else:
@ -568,10 +570,7 @@ def click_to_scrape(request):
form.save()
return redirect(reverse('movies:retrieve', args=[form.instance.id]))
else:
if 'isbn' in form.errors:
msg = _("ISBN与现有图书重复")
else:
msg = _("爬取数据失败😫")
msg = _("爬取数据失败😫")
return render(request, 'common/error.html', {'msg': msg})
else:
return HttpResponseBadRequest()

View file

@ -6,14 +6,14 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta property="og:title" content="{% trans 'Nicedb - 登录' %}">
<meta property="og:title" content="{% trans 'NiceDB - 登录' %}">
<meta property="og:type" content="website">
<meta property="og:url" content="{{ request.build_absolute_uri }}">
<meta property="og:image" content="{{ request.scheme }}://{{ request.get_host }}{% static 'img/logo_square.jpg' %}">
<link rel="stylesheet" href="{% static 'lib/css/milligram.css' %}">
<link rel="stylesheet" href="{% static 'css/boofilsic_edit.css' %}">
<link rel="stylesheet" href="{% static 'css/boofilsic_box.css' %}">
<title>{% trans 'Nicedb - 登录' %}</title>
<title>{% trans 'NiceDB - 登录' %}</title>
</head>
<body>
<div id="loginBox" class="box">

View file

@ -10,7 +10,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% trans 'Nicedb - 管理举报' %}</title>
<title>{% trans 'NiceDB - 管理举报' %}</title>
<script src="https://cdn.staticfile.org/jquery/3.5.0/jquery.min.js"></script>
<script src="{% static 'js/create_update.js' %}"></script>
<link rel="stylesheet" href="{% static 'css/boofilsic.css' %}">

View file

@ -9,7 +9,7 @@
<link rel="stylesheet" href="{% static 'lib/css/milligram.css' %}">
<link rel="stylesheet" href="{% static 'css/boofilsic_edit.css' %}">
<link rel="stylesheet" href="{% static 'css/boofilsic_box.css' %}">
<title>{% trans 'Nicedb - 注册' %}</title>
<title>{% trans 'NiceDB - 注册' %}</title>
</head>
<body>

View file

@ -11,9 +11,9 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{% if is_followers_page %}
<title>{% trans 'Nicedb - 被他们关注' %}</title>
<title>{% trans 'NiceDB - 被他们关注' %}</title>
{% else %}
<title>{% trans 'Nicedb - 关注的人' %}</title>
<title>{% trans 'NiceDB - 关注的人' %}</title>
{% endif %}
<script src="https://cdn.staticfile.org/jquery/3.5.0/jquery.min.js"></script>
<script src="{% static 'js/mastodon.js' %}"></script>

View file

@ -10,7 +10,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% trans 'Nicedb - 举报用户' %}</title>
<title>{% trans 'NiceDB - 举报用户' %}</title>
<script src="https://cdn.staticfile.org/jquery/3.5.0/jquery.min.js"></script>
<script src="{% static 'js/create_update.js' %}"></script>
<link rel="stylesheet" href="{% static 'css/boofilsic.css' %}">

View file

@ -12,6 +12,7 @@ urlpatterns = [
path('<int:id>/followers/', followers, name='followers'),
path('<int:id>/following/', following, name='following'),
path('<int:id>/book/<str:status>/', book_list, name='book_list'),
path('<int:id>/movie/<str:status>/', movie_list, name='movie_list'),
path('report/', report, name='report'),
path('manage_report/', manage_report, name='manage_report'),
]

View file

@ -12,12 +12,14 @@ from .forms import ReportForm
from common.mastodon.auth import *
from common.mastodon.api import *
from common.mastodon import mastodon_request_included
from common.views import BOOKS_PER_SET, ITEMS_PER_PAGE, PAGE_LINK_NUMBER, TAG_NUMBER_ON_LIST
from common.views import BOOKS_PER_SET, ITEMS_PER_PAGE, PAGE_LINK_NUMBER, TAG_NUMBER_ON_LIST, MOVIES_PER_SET
from common.models import MarkStatusEnum
from common.utils import PageLinksGenerator
from books.models import *
from movies.models import *
from boofilsic.settings import MASTODON_DOMAIN_NAME, CLIENT_ID, CLIENT_SECRET
from books.forms import BookMarkStatusTranslator
from movies.forms import MovieMarkStatusTranslator
# Views
@ -172,6 +174,17 @@ def home(request, id):
collect_book_marks = book_marks.filter(status=MarkStatusEnum.COLLECT)
collect_books_more = True if collect_book_marks.count() > BOOKS_PER_SET else False
movie_marks = MovieMark.get_available_user_data(user, relation['following'])
do_movie_marks = movie_marks.filter(status=MarkStatusEnum.DO)
do_movies_more = True if do_movie_marks.count() > BOOKS_PER_SET else False
wish_movie_marks = movie_marks.filter(status=MarkStatusEnum.WISH)
wish_movies_more = True if wish_movie_marks.count() > BOOKS_PER_SET else False
collect_movie_marks = movie_marks.filter(status=MarkStatusEnum.COLLECT)
collect_movies_more = True if collect_movie_marks.count() > BOOKS_PER_SET else False
return render(
request,
'common/home.html',
@ -183,6 +196,12 @@ def home(request, id):
'do_books_more': do_books_more,
'wish_books_more': wish_books_more,
'collect_books_more': collect_books_more,
'do_movie_marks': do_movie_marks[:MOVIES_PER_SET],
'wish_movie_marks': wish_movie_marks[:MOVIES_PER_SET],
'collect_movie_marks': collect_movie_marks[:MOVIES_PER_SET],
'do_movies_more': do_movies_more,
'wish_movies_more': wish_movies_more,
'collect_movies_more': collect_movies_more,
}
)
else:
@ -326,6 +345,63 @@ def book_list(request, id, status):
)
else:
return HttpResponseBadRequest()
@mastodon_request_included
@login_required
def movie_list(request, id, status):
if request.method == 'GET':
if not status.upper() in MarkStatusEnum.names:
return HttpResponseBadRequest()
try:
user = User.objects.get(pk=id)
except ObjectDoesNotExist:
msg = _("😖哎呀这位老师还没有注册书影音呢快去长毛象喊TA来吧")
sec_msg = _("目前只开放本站用户注册")
return render(
request,
'common/error.html',
{
'msg': msg,
'secondary_msg': sec_msg,
}
)
if not user == request.user:
# mastodon request
relation = get_relationships([user.mastodon_id], request.session['oauth_token'])[0]
if relation['blocked_by']:
msg = _("你没有访问TA主页的权限😥")
return render(
request,
'common/error.html',
{
'msg': msg,
}
)
queryset = MovieMark.get_available_user_data(user, relation['following']).filter(
status=MarkStatusEnum[status.upper()]).order_by("-edited_time")
else:
queryset = MovieMark.objects.filter(
owner=user, status=MarkStatusEnum[status.upper()]).order_by("-edited_time")
paginator = Paginator(queryset, ITEMS_PER_PAGE)
page_number = request.GET.get('page', default=1)
marks = paginator.get_page(page_number)
for mark in marks:
mark.movie.tag_list = mark.movie.get_tags_manager().values('content').annotate(
tag_frequency=Count('content')).order_by('-tag_frequency')[:TAG_NUMBER_ON_LIST]
marks.pagination = PageLinksGenerator(PAGE_LINK_NUMBER, page_number, paginator.num_pages)
list_title = str(MovieMarkStatusTranslator(MarkStatusEnum[status.upper()])) + str(_("的电影和剧集"))
return render(
request,
'movies/list.html',
{
'marks': marks,
'user': user,
'list_title' : list_title,
}
)
else:
return HttpResponseBadRequest()
@login_required