export marks via rq; pref to send public toot; move import to data page
This commit is contained in:
parent
0c785320e7
commit
4f504288ca
17 changed files with 647 additions and 20 deletions
|
@ -33,6 +33,10 @@ urlpatterns = [
|
|||
|
||||
]
|
||||
|
||||
urlpatterns += [
|
||||
path('django-rq/', include('django_rq.urls'))
|
||||
]
|
||||
|
||||
if settings.DEBUG:
|
||||
from django.conf.urls.static import static
|
||||
urlpatterns += static(settings.MEDIA_URL,
|
||||
|
|
|
@ -308,7 +308,7 @@ def create_update_mark(request):
|
|||
if form.cleaned_data['is_private']:
|
||||
visibility = TootVisibilityEnum.PRIVATE
|
||||
else:
|
||||
visibility = TootVisibilityEnum.UNLISTED
|
||||
visibility = TootVisibilityEnum.PUBLIC if request.user.preference.mastodon_publish_public else TootVisibilityEnum.UNLISTED
|
||||
url = "https://" + request.get_host() + reverse("books:retrieve",
|
||||
args=[book.id])
|
||||
words = BookMarkStatusTranslator(form.cleaned_data['status']) +\
|
||||
|
@ -402,7 +402,7 @@ def create_review(request, book_id):
|
|||
if form.cleaned_data['is_private']:
|
||||
visibility = TootVisibilityEnum.PRIVATE
|
||||
else:
|
||||
visibility = TootVisibilityEnum.UNLISTED
|
||||
visibility = TootVisibilityEnum.PUBLIC if request.user.preference.mastodon_publish_public else TootVisibilityEnum.UNLISTED
|
||||
url = "https://" + request.get_host() + reverse("books:retrieve_review",
|
||||
args=[form.instance.id])
|
||||
words = "发布了关于" + f"《{form.instance.book.title}》" + "的评论"
|
||||
|
@ -454,7 +454,7 @@ def update_review(request, id):
|
|||
if form.cleaned_data['is_private']:
|
||||
visibility = TootVisibilityEnum.PRIVATE
|
||||
else:
|
||||
visibility = TootVisibilityEnum.UNLISTED
|
||||
visibility = TootVisibilityEnum.PUBLIC if request.user.preference.mastodon_publish_public else TootVisibilityEnum.UNLISTED
|
||||
url = "https://" + request.get_host() + reverse("books:retrieve_review",
|
||||
args=[form.instance.id])
|
||||
words = "发布了关于" + f"《{form.instance.book.title}》" + "的评论"
|
||||
|
|
|
@ -1164,6 +1164,10 @@ select::placeholder {
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
.tools-section-wrapper input, .tools-section-wrapperl select {
|
||||
width: unset;
|
||||
}
|
||||
|
||||
.entity-list .entity-list__title {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
|
4
common/static/css/boofilsic.min.css
vendored
4
common/static/css/boofilsic.min.css
vendored
File diff suppressed because one or more lines are too long
|
@ -26,8 +26,10 @@
|
|||
|
||||
{% if request.user.is_authenticated %}
|
||||
|
||||
<a class="navbar__link" id="logoutLink" href="{% url 'users:logout' %}">{% trans '登出' %}</a>
|
||||
<a class="navbar__link" href="{% url 'common:home' %}">{% trans '主页' %}</a>
|
||||
<a class="navbar__link" id="logoutLink" href="{% url 'users:data' %}">{% trans '数据' %}</a>
|
||||
<a class="navbar__link" id="logoutLink" href="{% url 'users:preferences' %}">{% trans '设置' %}</a>
|
||||
<a class="navbar__link" id="logoutLink" href="{% url 'users:logout' %}">{% trans '登出' %}</a>
|
||||
{% if request.user.is_staff %}
|
||||
<a class="navbar__link" href="{% admin_url %}">{% trans '后台' %}</a>
|
||||
{% endif %}
|
||||
|
|
|
@ -310,7 +310,7 @@ def create_update_mark(request):
|
|||
if form.cleaned_data['is_private']:
|
||||
visibility = TootVisibilityEnum.PRIVATE
|
||||
else:
|
||||
visibility = TootVisibilityEnum.UNLISTED
|
||||
visibility = TootVisibilityEnum.PUBLIC if request.user.preference.mastodon_publish_public else TootVisibilityEnum.UNLISTED
|
||||
url = "https://" + request.get_host() + reverse("games:retrieve",
|
||||
args=[game.id])
|
||||
words = GameMarkStatusTranslator(form.cleaned_data['status']) +\
|
||||
|
@ -405,7 +405,7 @@ def create_review(request, game_id):
|
|||
if form.cleaned_data['is_private']:
|
||||
visibility = TootVisibilityEnum.PRIVATE
|
||||
else:
|
||||
visibility = TootVisibilityEnum.UNLISTED
|
||||
visibility = TootVisibilityEnum.PUBLIC if request.user.preference.mastodon_publish_public else TootVisibilityEnum.UNLISTED
|
||||
url = "https://" + request.get_host() + reverse("games:retrieve_review",
|
||||
args=[form.instance.id])
|
||||
words = "发布了关于" + f"《{form.instance.game.title}》" + "的评论"
|
||||
|
@ -457,7 +457,7 @@ def update_review(request, id):
|
|||
if form.cleaned_data['is_private']:
|
||||
visibility = TootVisibilityEnum.PRIVATE
|
||||
else:
|
||||
visibility = TootVisibilityEnum.UNLISTED
|
||||
visibility = TootVisibilityEnum.PUBLIC if request.user.preference.mastodon_publish_public else TootVisibilityEnum.UNLISTED
|
||||
url = "https://" + request.get_host() + reverse("games:retrieve_review",
|
||||
args=[form.instance.id])
|
||||
words = "发布了关于" + f"《{form.instance.game.title}》" + "的评论"
|
||||
|
|
|
@ -309,7 +309,7 @@ def create_update_mark(request):
|
|||
if form.cleaned_data['is_private']:
|
||||
visibility = TootVisibilityEnum.PRIVATE
|
||||
else:
|
||||
visibility = TootVisibilityEnum.UNLISTED
|
||||
visibility = TootVisibilityEnum.PUBLIC if request.user.preference.mastodon_publish_public else TootVisibilityEnum.UNLISTED
|
||||
url = "https://" + request.get_host() + reverse("movies:retrieve",
|
||||
args=[movie.id])
|
||||
words = MovieMarkStatusTranslator(form.cleaned_data['status']) +\
|
||||
|
@ -404,7 +404,7 @@ def create_review(request, movie_id):
|
|||
if form.cleaned_data['is_private']:
|
||||
visibility = TootVisibilityEnum.PRIVATE
|
||||
else:
|
||||
visibility = TootVisibilityEnum.UNLISTED
|
||||
visibility = TootVisibilityEnum.PUBLIC if request.user.preference.mastodon_publish_public else TootVisibilityEnum.UNLISTED
|
||||
url = "https://" + request.get_host() + reverse("movies:retrieve_review",
|
||||
args=[form.instance.id])
|
||||
words = "发布了关于" + f"《{form.instance.movie.title}》" + "的评论"
|
||||
|
@ -456,7 +456,7 @@ def update_review(request, id):
|
|||
if form.cleaned_data['is_private']:
|
||||
visibility = TootVisibilityEnum.PRIVATE
|
||||
else:
|
||||
visibility = TootVisibilityEnum.UNLISTED
|
||||
visibility = TootVisibilityEnum.PUBLIC if request.user.preference.mastodon_publish_public else TootVisibilityEnum.UNLISTED
|
||||
url = "https://" + request.get_host() + reverse("movies:retrieve_review",
|
||||
args=[form.instance.id])
|
||||
words = "发布了关于" + f"《{form.instance.movie.title}》" + "的评论"
|
||||
|
|
|
@ -328,7 +328,7 @@ def create_update_song_mark(request):
|
|||
if form.cleaned_data['is_private']:
|
||||
visibility = TootVisibilityEnum.PRIVATE
|
||||
else:
|
||||
visibility = TootVisibilityEnum.UNLISTED
|
||||
visibility = TootVisibilityEnum.PUBLIC if request.user.preference.mastodon_publish_public else TootVisibilityEnum.UNLISTED
|
||||
url = "https://" + request.get_host() + reverse("music:retrieve_song",
|
||||
args=[song.id])
|
||||
words = MusicMarkStatusTranslator(form.cleaned_data['status']) +\
|
||||
|
@ -423,7 +423,7 @@ def create_song_review(request, song_id):
|
|||
if form.cleaned_data['is_private']:
|
||||
visibility = TootVisibilityEnum.PRIVATE
|
||||
else:
|
||||
visibility = TootVisibilityEnum.UNLISTED
|
||||
visibility = TootVisibilityEnum.PUBLIC if request.user.preference.mastodon_publish_public else TootVisibilityEnum.UNLISTED
|
||||
url = "https://" + request.get_host() + reverse("music:retrieve_song_review",
|
||||
args=[form.instance.id])
|
||||
words = "发布了关于" + f"《{form.instance.song.title}》" + "的评论"
|
||||
|
@ -475,7 +475,7 @@ def update_song_review(request, id):
|
|||
if form.cleaned_data['is_private']:
|
||||
visibility = TootVisibilityEnum.PRIVATE
|
||||
else:
|
||||
visibility = TootVisibilityEnum.UNLISTED
|
||||
visibility = TootVisibilityEnum.PUBLIC if request.user.preference.mastodon_publish_public else TootVisibilityEnum.UNLISTED
|
||||
url = "https://" + request.get_host() + reverse("music:retrieve_song_review",
|
||||
args=[form.instance.id])
|
||||
words = "发布了关于" + f"《{form.instance.song.title}》" + "的评论"
|
||||
|
@ -899,7 +899,7 @@ def create_update_album_mark(request):
|
|||
if form.cleaned_data['is_private']:
|
||||
visibility = TootVisibilityEnum.PRIVATE
|
||||
else:
|
||||
visibility = TootVisibilityEnum.UNLISTED
|
||||
visibility = TootVisibilityEnum.PUBLIC if request.user.preference.mastodon_publish_public else TootVisibilityEnum.UNLISTED
|
||||
url = "https://" + request.get_host() + reverse("music:retrieve_album",
|
||||
args=[album.id])
|
||||
words = MusicMarkStatusTranslator(form.cleaned_data['status']) +\
|
||||
|
@ -994,7 +994,7 @@ def create_album_review(request, album_id):
|
|||
if form.cleaned_data['is_private']:
|
||||
visibility = TootVisibilityEnum.PRIVATE
|
||||
else:
|
||||
visibility = TootVisibilityEnum.UNLISTED
|
||||
visibility = TootVisibilityEnum.PUBLIC if request.user.preference.mastodon_publish_public else TootVisibilityEnum.UNLISTED
|
||||
url = "https://" + request.get_host() + reverse("music:retrieve_album_review",
|
||||
args=[form.instance.id])
|
||||
words = "发布了关于" + f"《{form.instance.album.title}》" + "的评论"
|
||||
|
@ -1046,7 +1046,7 @@ def update_album_review(request, id):
|
|||
if form.cleaned_data['is_private']:
|
||||
visibility = TootVisibilityEnum.PRIVATE
|
||||
else:
|
||||
visibility = TootVisibilityEnum.UNLISTED
|
||||
visibility = TootVisibilityEnum.PUBLIC if request.user.preference.mastodon_publish_public else TootVisibilityEnum.UNLISTED
|
||||
url = "https://" + request.get_host() + reverse("music:retrieve_album_review",
|
||||
args=[form.instance.id])
|
||||
words = "发布了关于" + f"《{form.instance.album.title}》" + "的评论"
|
||||
|
|
|
@ -3,6 +3,7 @@ django
|
|||
django-hstore
|
||||
django-markdownx
|
||||
django-sass
|
||||
django-rq
|
||||
easy-thumbnails
|
||||
lxml
|
||||
openpyxl
|
||||
|
|
120
users/export.py
Normal file
120
users/export.py
Normal file
|
@ -0,0 +1,120 @@
|
|||
from django.shortcuts import reverse, redirect, render, get_object_or_404
|
||||
from django.http import HttpResponseBadRequest, HttpResponse
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib import auth
|
||||
from django.contrib.auth import authenticate
|
||||
from django.core.paginator import Paginator
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.db.models import Count
|
||||
from .models import User, Report, Preference
|
||||
from .forms import ReportForm
|
||||
from mastodon.auth import *
|
||||
from mastodon.api import *
|
||||
from mastodon import mastodon_request_included
|
||||
from common.config import *
|
||||
from common.models import MarkStatusEnum
|
||||
from common.utils import PageLinksGenerator
|
||||
from management.models import Announcement
|
||||
from books.models import *
|
||||
from movies.models import *
|
||||
from music.models import *
|
||||
from games.models import *
|
||||
from books.forms import BookMarkStatusTranslator
|
||||
from movies.forms import MovieMarkStatusTranslator
|
||||
from music.forms import MusicMarkStatusTranslator
|
||||
from games.forms import GameMarkStatusTranslator
|
||||
from mastodon.models import MastodonApplication
|
||||
from django.conf import settings
|
||||
from urllib.parse import quote
|
||||
from openpyxl import Workbook
|
||||
from common.utils import GenerateDateUUIDMediaFilePath
|
||||
from datetime import datetime
|
||||
import os
|
||||
|
||||
|
||||
def export_marks_task(user):
|
||||
user.preference.export_status['marks_pending'] = True
|
||||
user.preference.save()
|
||||
filename = GenerateDateUUIDMediaFilePath(None, 'f.xlsx', settings.MEDIA_ROOT + settings.EXPORT_FILE_PATH_ROOT)
|
||||
if not os.path.exists(os.path.dirname(filename)):
|
||||
os.makedirs(os.path.dirname(filename))
|
||||
heading = ['标题', '简介', '豆瓣评分', '链接', '创建时间', '我的评分', '标签', '评论', 'NeoDB链接', '其它ID']
|
||||
wb = Workbook() # adding write_only=True will speed up but corrupt the xlsx and won't be importable
|
||||
for status, label in [('collect', '看过'), ('do', '在看'), ('wish', '想看')]:
|
||||
ws = wb.create_sheet(title=label)
|
||||
marks = MovieMark.objects.filter(owner=user, status=status).order_by("-edited_time")
|
||||
ws.append(heading)
|
||||
for mark in marks:
|
||||
movie = mark.movie
|
||||
title = movie.title
|
||||
summary = str(movie.year) + ' / ' + ','.join(movie.area) + ' / ' + ','.join(map(lambda x: str(MovieGenreTranslator[x]), movie.genre)) + ' / ' + ','.join(movie.director) + ' / ' + ','.join(movie.actor)
|
||||
tags = ','.join(map(lambda t: t['content'], movie.get_tags_manager().values()))
|
||||
world_rating = (movie.rating / 2) if movie.rating else None
|
||||
timestamp = mark.edited_time.strftime('%Y-%m-%d %H:%M:%S')
|
||||
my_rating = (mark.rating / 2) if mark.rating else None
|
||||
text = mark.text
|
||||
source_url = movie.source_url
|
||||
url = settings.APP_WEBSITE + movie.get_absolute_url()
|
||||
line = [title, summary, world_rating, source_url, timestamp, my_rating, tags, text, url, movie.imdb_code]
|
||||
ws.append(line)
|
||||
|
||||
for status, label in [('collect', '听过'), ('do', '在听'), ('wish', '想听')]:
|
||||
ws = wb.create_sheet(title=label)
|
||||
marks = AlbumMark.objects.filter(owner=user, status=status).order_by("-edited_time")
|
||||
ws.append(heading)
|
||||
for mark in marks:
|
||||
album = mark.album
|
||||
title = album.title
|
||||
summary = ','.join(album.artist) + ' / ' + (album.release_date.strftime('%Y') if album.release_date else '')
|
||||
tags = ','.join(map(lambda t: t['content'], album.get_tags_manager().values()))
|
||||
world_rating = (album.rating / 2) if album.rating else None
|
||||
timestamp = mark.edited_time.strftime('%Y-%m-%d %H:%M:%S')
|
||||
my_rating = (mark.rating / 2) if mark.rating else None
|
||||
text = mark.text
|
||||
source_url = album.source_url
|
||||
url = settings.APP_WEBSITE + album.get_absolute_url()
|
||||
line = [title, summary, world_rating, source_url, timestamp, my_rating, tags, text, url, '']
|
||||
ws.append(line)
|
||||
|
||||
for status, label in [('collect', '读过'), ('do', '在读'), ('wish', '想读')]:
|
||||
ws = wb.create_sheet(title=label)
|
||||
marks = BookMark.objects.filter(owner=user, status=status).order_by("-edited_time")
|
||||
ws.append(heading)
|
||||
for mark in marks:
|
||||
book = mark.book
|
||||
title = book.title
|
||||
summary = ','.join(book.author) + ' / ' + str(book.pub_year) + ' / ' + book.pub_house
|
||||
tags = ','.join(map(lambda t: t['content'], book.get_tags_manager().values()))
|
||||
world_rating = (book.rating / 2) if book.rating else None
|
||||
timestamp = mark.edited_time.strftime('%Y-%m-%d %H:%M:%S')
|
||||
my_rating = (mark.rating / 2) if mark.rating else None
|
||||
text = mark.text
|
||||
source_url = book.source_url
|
||||
url = settings.APP_WEBSITE + book.get_absolute_url()
|
||||
line = [title, summary, world_rating, source_url, timestamp, my_rating, tags, text, url, book.isbn]
|
||||
ws.append(line)
|
||||
|
||||
for status, label in [('collect', '玩过'), ('do', '在玩'), ('wish', '想玩')]:
|
||||
ws = wb.create_sheet(title=label)
|
||||
marks = GameMark.objects.filter(owner=user, status=status).order_by("-edited_time")
|
||||
ws.append(heading)
|
||||
for mark in marks:
|
||||
game = mark.game
|
||||
title = game.title
|
||||
summary = ','.join(game.genre) + ' / ' + ','.join(game.platform) + ' / ' + game.release_date.strftime('%Y-%m-%d')
|
||||
tags = ','.join(map(lambda t: t['content'], game.get_tags_manager().values()))
|
||||
world_rating = (game.rating / 2) if game.rating else None
|
||||
timestamp = mark.edited_time.strftime('%Y-%m-%d %H:%M:%S')
|
||||
my_rating = (mark.rating / 2) if mark.rating else None
|
||||
text = mark.text
|
||||
source_url = game.source_url
|
||||
url = settings.APP_WEBSITE + game.get_absolute_url()
|
||||
line = [title, summary, world_rating, source_url, timestamp, my_rating, tags, text, url, '']
|
||||
ws.append(line)
|
||||
|
||||
wb.save(filename=filename)
|
||||
user.preference.export_status['marks_pending'] = False
|
||||
user.preference.export_status['marks_file'] = filename
|
||||
user.preference.export_status['marks_date'] = datetime.now().strftime("%Y-%m-%d %H:%M")
|
||||
user.preference.save()
|
|
@ -50,9 +50,11 @@ class Preference(models.Model):
|
|||
blank=True,
|
||||
default=list,
|
||||
)
|
||||
export_status = models.JSONField(blank=True, null=True, encoder=DjangoJSONEncoder, default=dict)
|
||||
mastodon_publish_public = models.BooleanField(null=False, default=False)
|
||||
|
||||
def get_serialized_home_layout(self):
|
||||
return str(self.home_layout).replace("\'","\"")
|
||||
return str(self.home_layout).replace("\'", "\"")
|
||||
|
||||
def __str__(self):
|
||||
return str(self.user)
|
||||
|
|
274
users/templates/users/data.html
Normal file
274
users/templates/users/data.html
Normal file
|
@ -0,0 +1,274 @@
|
|||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load admin_url %}
|
||||
{% load mastodon %}
|
||||
{% load oauth_token %}
|
||||
{% load truncate %}
|
||||
{% load thumb %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{ site_name }} - 数据管理</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||
<script src="{% static 'js/mastodon.js' %}"></script>
|
||||
<script src="{% static 'js/home.js' %}"></script>
|
||||
<link rel="stylesheet" href="{% static 'css/boofilsic.min.css' %}">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="page-wrapper">
|
||||
<div id="content-wrapper">
|
||||
{% include "partial/_navbar.html" %}
|
||||
|
||||
<section id="content">
|
||||
<div class="grid grid--reverse-order">
|
||||
<div class="grid__main grid__main--reverse-order">
|
||||
<div class="main-section-wrapper">
|
||||
<div class="tools-section-wrapper">
|
||||
<div class="import-panel">
|
||||
<h5 class="import-panel__label">{% trans '导入豆瓣标记数据' %}</h5>
|
||||
<span id="importHelp" class="import-panel__help">?</span>
|
||||
<div class="import-panel__body">
|
||||
<form action="{% url 'sync:douban' %}" method="POST" enctype="multipart/form-data" >
|
||||
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="user" value="{{ request.user.id }}">
|
||||
<span>{% trans '导入:' %}</span>
|
||||
<div class="import-panel__checkbox">
|
||||
<input type="checkbox" name="sync_book" id="syncBook">
|
||||
<label for="syncBook">{% trans '书' %}</label>
|
||||
</div>
|
||||
<div class="import-panel__checkbox">
|
||||
<input type="checkbox" name="sync_movie" id="syncMovie">
|
||||
<label for="syncMovie">{% trans '电影' %}</label>
|
||||
</div>
|
||||
<div class="import-panel__checkbox">
|
||||
<input type="checkbox" name="sync_music" id="syncMusic">
|
||||
<label for="syncMusic">{% trans '音乐' %}</label>
|
||||
</div>
|
||||
<div class="import-panel__checkbox">
|
||||
<input type="checkbox" name="sync_game" id="syncGame">
|
||||
<label for="syncGame">{% trans '游戏' %}</label>
|
||||
</div>
|
||||
<div></div>
|
||||
<span>{% trans '覆盖:' %}</span>
|
||||
<div class="import-panel__checkbox import-panel__checkbox--last">
|
||||
<input type="checkbox" name="overwrite" id="overwrite">
|
||||
<label for="overwrite">{% trans '覆盖原有标记' %}</label>
|
||||
</div>
|
||||
<span id="overwriteHelp" class="import-panel__help">?</span>
|
||||
<div></div>
|
||||
<span>{% trans '可见性:' %}</span>
|
||||
<div class="import-panel__checkbox import-panel__checkbox--last">
|
||||
<input type="checkbox" name="default_public" id="visibility">
|
||||
<label for="visibility">{% trans '公开' %}</label>
|
||||
</div>
|
||||
<span id="visibilityHelp" class="import-panel__help">?</span>
|
||||
<div></div>
|
||||
<div class="import-panel__file-input">
|
||||
<input type="file" name="file" id="excelFile" required accept=".xlsx">
|
||||
</div>
|
||||
<input type="submit" class="import-panel__button" value="{% trans '导入' %}" id="uploadBtn"
|
||||
{% if not latest_task is None and not latest_task.is_finished %}
|
||||
disabled
|
||||
{% endif %}
|
||||
>
|
||||
</form>
|
||||
<div class="import-panel__progress"
|
||||
{% if latest_task.is_finished or latest_task is None %}
|
||||
style="display: none;"
|
||||
{% endif %}
|
||||
>
|
||||
<label for="importProgress">{% trans '进度' %}</label>
|
||||
<progress id="importProgress" value="{{ latest_task.finished_items }}" max="{{ latest_task.total_items }}"></progress>
|
||||
<span class="float-right" id="progressPercent">{{ latest_task.get_progress | floatformat:"0" }}%</span>
|
||||
<span class="clearfix"></span>
|
||||
</div>
|
||||
<div class="import-panel__last-task"
|
||||
{% if not latest_task.is_finished %}`
|
||||
style="display: none;"
|
||||
{% endif %}
|
||||
>
|
||||
{% trans '上次导入:' %}
|
||||
<span class="index">{% trans '总数' %} <span id="lastTaskTotalItems">{{ latest_task.total_items }}</span></span>
|
||||
<span class="index">{% trans '同步' %} <span id="lastTaskSuccessItems">{{ latest_task.success_items }}</span></span>
|
||||
<span class="index">{% trans '状态' %} <span id="lastTaskStatus">{{ latest_task.get_status_emoji }}</span></span>
|
||||
<div class="import-panel__fail-urls"
|
||||
{% if not latest_task.failed_urls %}
|
||||
style="display: none;"
|
||||
{% endif %}
|
||||
>
|
||||
<span>
|
||||
{% trans '失败条目链接' %}
|
||||
</span>
|
||||
<a class="float-right" style="cursor: pointer;" id="failedUrlsBtn">
|
||||
▶
|
||||
</a>
|
||||
<script>
|
||||
$("#failedUrlsBtn").data("collapse", true);
|
||||
$("#failedUrlsBtn").click(()=>{
|
||||
const btn = $("#failedUrlsBtn");
|
||||
if(btn.data("collapse") == true) {
|
||||
btn.data("collapse", false);
|
||||
btn.text("▼");
|
||||
$("#failedUrls").show();
|
||||
} else {
|
||||
btn.data("collapse", true);
|
||||
btn.text("▶");
|
||||
$("#failedUrls").hide();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<span class="clearfix"></span>
|
||||
<ul id="failedUrls" style="display: none;">
|
||||
{% for url in latest_task.failed_urls %}
|
||||
<li>{{ url }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="main-section-wrapper">
|
||||
<div class="tools-section-wrapper">
|
||||
<div class="import-panel">
|
||||
<h5 class="import-panel__label">{% trans '导出个人数据' %}</h5>
|
||||
<div class="import-panel__body">
|
||||
<form action="{% url 'users:export_marks' %}" method="POST" enctype="multipart/form-data" >
|
||||
{% csrf_token %}
|
||||
{% if export_status.marks_pending %}
|
||||
<input type="submit" class="import-panel__button" value="{% trans '正在导出兼容豆伴(doufen)和NiceDB的标记和短评' %}" id="uploadBtn" disabled />
|
||||
{% else %}
|
||||
<input type="submit" class="import-panel__button" value="{% trans '导出兼容豆伴(doufen)和NiceDB的标记和短评' %}" id="uploadBtn" />
|
||||
{% endif %}
|
||||
{% if export_status.marks_file %}
|
||||
<a href="{% url 'users:export_marks' %}" download>下载 {{ export_status.marks_date }} 的导出</a>
|
||||
{% endif %}
|
||||
</form>
|
||||
<!-- <form action="{% url 'users:export_reviews' %}" method="POST" enctype="multipart/form-data" >
|
||||
{% csrf_token %}
|
||||
<input type="submit" class="import-panel__button" value="{% trans '导出评论' %}" id="uploadBtn"
|
||||
>
|
||||
</form> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid__aside grid__aside--reverse-order grid__aside--tablet-column">
|
||||
<div class="aside-section-wrapper aside-section-wrapper--no-margin">
|
||||
<div class="user-profile" id="userInfoCard">
|
||||
<div class="user-profile__header">
|
||||
<!-- <img src="" class="user-profile__avatar mast-avatar" alt="{{ user.username }}"> -->
|
||||
<img src="" class="user-profile__avatar mast-avatar">
|
||||
<a href="{% url 'users:home' user.id %}">
|
||||
<h5 class="user-profile__username mast-displayname"></h5>
|
||||
</a>
|
||||
</div>
|
||||
<p><a class="user-profile__link mast-acct" target="_blank" href="https://{{ user.mastodon_site }}/@{{ user.username }}">@{{ user.username }}@{{ user.mastodon_site }}</a></p>
|
||||
<p class="user-profile__bio mast-brief"></p>
|
||||
<!-- <a href="#" class="follow">{% trans '关注TA' %}</a> -->
|
||||
|
||||
{% if request.user != user %}
|
||||
<a href="{% url 'users:report' %}?user_id={{ user.id }}"
|
||||
class="user-profile__report-link">{% trans '举报用户' %}</a>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- contains relations, reports, and upload excel entry -->
|
||||
<div class="relation-dropdown">
|
||||
<div class="relation-dropdown__body">
|
||||
<!-- import douban data -->
|
||||
{% if user == request.user %}
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
{% include "partial/_footer.html" %}
|
||||
</div>
|
||||
|
||||
<div id="oauth2Token" hidden="true">{% oauth_token %}</div>
|
||||
<div id="mastodonURI" hidden="true">{% mastodon request.user.mastodon_site %}</div>
|
||||
<div id="queryProgressURL" data-url="{% url 'sync:progress' %}"></div>
|
||||
<div id="querySyncInfoURL" data-url="{% url 'sync:last' %}"></div>
|
||||
<!--current user mastodon id-->
|
||||
|
||||
{% if user == request.user %}
|
||||
<div id="userMastodonID" hidden="true">{{ user.mastodon_id }}</div>
|
||||
{% else %}
|
||||
<div id="userMastodonID" hidden="true">{{ user.target_site_id }}</div>
|
||||
{% endif %}
|
||||
|
||||
<div id="userPageURL" hidden="true">{% url 'users:home' 0 %}</div>
|
||||
|
||||
<div id="spinner" hidden>
|
||||
<div class="spinner">
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// because the modal and mask elements only exist when there are new announcements
|
||||
$(".bg-mask").show();
|
||||
$(".modal-close").on('click', function () {
|
||||
$(this).parents(".modal").hide();
|
||||
$(".bg-mask").hide();
|
||||
});
|
||||
|
||||
</script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/2.9.2/umd/popper.min.js"
|
||||
integrity="sha512-2rNj2KJ+D8s1ceNasTIex6z4HWyOnEYLVC3FigGOmyQCZc2eBXKgOxQmo3oKLHyfcj53uz4QMsRCWNbLd32Q1g=="
|
||||
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/tippy.js/6.3.1/tippy.umd.min.js"
|
||||
integrity="sha512-Ns7w8bjVjVcBVa+k3XLt0ObfsG2LQfr573HoIYtC4wh8gUKLvCx+rlggxfvsHqup6jvMAEmBtYXmhcKHL+6R5A=="
|
||||
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<script>
|
||||
tippy('#importHelp', {
|
||||
content: "{% trans '上传导入由<a href=\"https://github.com/doufen-org/tofu\" target=\"_blank\">豆伴</a>(豆坟)导出的Excel文件,<strong>请勿手动修改该文件</strong>。部分条目由于需要登陆无法自动同步。' %}",
|
||||
interactive: true,
|
||||
allowHTML: true,
|
||||
duration: 0,
|
||||
});
|
||||
tippy('#overwriteHelp', {
|
||||
content: "{% trans '在导入之前如果已经在本站标记了某一个条目,是否使用来自豆瓣的标记覆盖原有的。' %}",
|
||||
interactive: true,
|
||||
allowHTML: true,
|
||||
duration: 0,
|
||||
});
|
||||
tippy('#visibilityHelp', {
|
||||
content: "{% trans '所同步的标记可见性是否为公开;对于已有的标记即便覆盖也不会改变可见性。' %}",
|
||||
interactive: true,
|
||||
allowHTML: true,
|
||||
duration: 0,
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
||||
</html>
|
|
@ -550,7 +550,7 @@
|
|||
</div>
|
||||
|
||||
<!-- import douban data -->
|
||||
{% if user == request.user %}
|
||||
<!-- {% if user == request.user %}
|
||||
<div class="aside-section-wrapper aside-section-wrapper--transparent aside-section-wrapper--collapse">
|
||||
<div class="import-panel">
|
||||
<h5 class="import-panel__label">{% trans '导入豆瓣标记数据' %}</h5>
|
||||
|
@ -658,7 +658,7 @@
|
|||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
-->
|
||||
<div
|
||||
class="aside-section-wrapper aside-section-wrapper--transparent aside-section-wrapper--collapse">
|
||||
{% if request.user.is_staff and request.user == user%}
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta property="og:title" content="{{ site_name }} - {% trans '登录' %}">
|
||||
<meta property="og:title" content="{{ site_name }} - 联邦宇宙书影音游戏标注平台">
|
||||
<meta name="description" property="og:description" content="NeoDB致力于为联邦宇宙居民提供一个自由、开放、互联的书籍、电影、音乐和游戏收藏评论空间">
|
||||
<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' %}">
|
||||
|
|
164
users/templates/users/preferences.html
Normal file
164
users/templates/users/preferences.html
Normal file
|
@ -0,0 +1,164 @@
|
|||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load admin_url %}
|
||||
{% load mastodon %}
|
||||
{% load oauth_token %}
|
||||
{% load truncate %}
|
||||
{% load thumb %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{ site_name }} - 设置</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||
<script src="{% static 'js/mastodon.js' %}"></script>
|
||||
<script src="{% static 'js/home.js' %}"></script>
|
||||
<link rel="stylesheet" href="{% static 'css/boofilsic.min.css' %}">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="page-wrapper">
|
||||
<div id="content-wrapper">
|
||||
{% include "partial/_navbar.html" %}
|
||||
|
||||
<section id="content">
|
||||
<div class="grid grid--reverse-order">
|
||||
<div class="grid__main grid__main--reverse-order">
|
||||
<div class="main-section-wrapper">
|
||||
<div class="tools-section-wrapper">
|
||||
<div class="import-panel">
|
||||
<h5 class="import-panel__label">{% trans '联邦网络(长毛象)相关设置' %}</h5>
|
||||
<div class="import-panel__body">
|
||||
<form action="{% url 'users:preferences' %}" method="POST" enctype="multipart/form-data" >
|
||||
{% csrf_token %}
|
||||
<span>{% trans '以公开方式分享的嘟文是否发布到公共时间轴上:' %}</span>
|
||||
<div class="import-panel__checkbox import-panel__checkbox--last">
|
||||
<input type="checkbox" name="mastodon_publish_public" id="visibility" {%if mastodon_publish_public%}checked{% endif %}>
|
||||
<label for="visibility">{% trans '选中时为public,未选中时为unlisted' %}</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="submit" class="import-panel__button" value="{% trans '保存' %}" id="uploadBtn"
|
||||
{% if not latest_task is None and not latest_task.is_finished %}
|
||||
disabled
|
||||
{% endif %}
|
||||
>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid__aside grid__aside--reverse-order grid__aside--tablet-column">
|
||||
<div class="aside-section-wrapper aside-section-wrapper--no-margin">
|
||||
<div class="user-profile" id="userInfoCard">
|
||||
<div class="user-profile__header">
|
||||
<!-- <img src="" class="user-profile__avatar mast-avatar" alt="{{ user.username }}"> -->
|
||||
<img src="" class="user-profile__avatar mast-avatar">
|
||||
<a href="{% url 'users:home' user.id %}">
|
||||
<h5 class="user-profile__username mast-displayname"></h5>
|
||||
</a>
|
||||
</div>
|
||||
<p><a class="user-profile__link mast-acct" target="_blank" href="https://{{ user.mastodon_site }}/@{{ user.username }}">@{{ user.username }}@{{ user.mastodon_site }}</a></p>
|
||||
<p class="user-profile__bio mast-brief"></p>
|
||||
<!-- <a href="#" class="follow">{% trans '关注TA' %}</a> -->
|
||||
|
||||
{% if request.user != user %}
|
||||
<a href="{% url 'users:report' %}?user_id={{ user.id }}"
|
||||
class="user-profile__report-link">{% trans '举报用户' %}</a>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- contains relations, reports, and upload excel entry -->
|
||||
<div class="relation-dropdown">
|
||||
<div class="relation-dropdown__body">
|
||||
<!-- import douban data -->
|
||||
{% if user == request.user %}
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
{% include "partial/_footer.html" %}
|
||||
</div>
|
||||
|
||||
<div id="oauth2Token" hidden="true">{% oauth_token %}</div>
|
||||
<div id="mastodonURI" hidden="true">{% mastodon request.user.mastodon_site %}</div>
|
||||
<div id="queryProgressURL" data-url="{% url 'sync:progress' %}"></div>
|
||||
<div id="querySyncInfoURL" data-url="{% url 'sync:last' %}"></div>
|
||||
<!--current user mastodon id-->
|
||||
|
||||
{% if user == request.user %}
|
||||
<div id="userMastodonID" hidden="true">{{ user.mastodon_id }}</div>
|
||||
{% else %}
|
||||
<div id="userMastodonID" hidden="true">{{ user.target_site_id }}</div>
|
||||
{% endif %}
|
||||
|
||||
<div id="userPageURL" hidden="true">{% url 'users:home' 0 %}</div>
|
||||
|
||||
<div id="spinner" hidden>
|
||||
<div class="spinner">
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// because the modal and mask elements only exist when there are new announcements
|
||||
$(".bg-mask").show();
|
||||
$(".modal-close").on('click', function () {
|
||||
$(this).parents(".modal").hide();
|
||||
$(".bg-mask").hide();
|
||||
});
|
||||
|
||||
</script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/2.9.2/umd/popper.min.js"
|
||||
integrity="sha512-2rNj2KJ+D8s1ceNasTIex6z4HWyOnEYLVC3FigGOmyQCZc2eBXKgOxQmo3oKLHyfcj53uz4QMsRCWNbLd32Q1g=="
|
||||
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/tippy.js/6.3.1/tippy.umd.min.js"
|
||||
integrity="sha512-Ns7w8bjVjVcBVa+k3XLt0ObfsG2LQfr573HoIYtC4wh8gUKLvCx+rlggxfvsHqup6jvMAEmBtYXmhcKHL+6R5A=="
|
||||
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<script>
|
||||
tippy('#importHelp', {
|
||||
content: "{% trans '上传导入由<a href=\"https://github.com/doufen-org/tofu\" target=\"_blank\">豆伴</a>(豆坟)导出的Excel文件,<strong>请勿手动修改该文件</strong>。部分条目由于需要登陆无法自动同步。' %}",
|
||||
interactive: true,
|
||||
allowHTML: true,
|
||||
duration: 0,
|
||||
});
|
||||
tippy('#overwriteHelp', {
|
||||
content: "{% trans '在导入之前如果已经在本站标记了某一个条目,是否使用来自豆瓣的标记覆盖原有的。' %}",
|
||||
interactive: true,
|
||||
allowHTML: true,
|
||||
duration: 0,
|
||||
});
|
||||
tippy('#visibilityHelp', {
|
||||
content: "{% trans '所同步的标记可见性是否为公开;对于已有的标记即便覆盖也不会改变可见性。' %}",
|
||||
interactive: true,
|
||||
allowHTML: true,
|
||||
duration: 0,
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
||||
</html>
|
|
@ -6,6 +6,10 @@ urlpatterns = [
|
|||
path('login/', login, name='login'),
|
||||
path('register/', register, name='register'),
|
||||
path('connect/', connect, name='connect'),
|
||||
path('data/', data, name='data'),
|
||||
path('data/export_reviews', export_reviews, name='export_reviews'),
|
||||
path('data/export_marks', export_marks, name='export_marks'),
|
||||
path('preferences/', preferences, name='preferences'),
|
||||
path('logout/', logout, name='logout'),
|
||||
path('delete/', delete, name='delete'),
|
||||
path('layout/', set_layout, name='set_layout'),
|
||||
|
|
|
@ -27,6 +27,9 @@ from games.forms import GameMarkStatusTranslator
|
|||
from mastodon.models import MastodonApplication
|
||||
from django.conf import settings
|
||||
from urllib.parse import quote
|
||||
import django_rq
|
||||
from .export import *
|
||||
|
||||
|
||||
# Views
|
||||
########################################
|
||||
|
@ -782,8 +785,10 @@ def set_layout(request):
|
|||
|
||||
@login_required
|
||||
def report(request):
|
||||
print(request.a.a)
|
||||
if request.method == 'GET':
|
||||
user_id = request.GET.get('user_id')
|
||||
print(user_id)
|
||||
if user_id:
|
||||
user = get_object_or_404(User, pk=user_id)
|
||||
form = ReportForm(initial={'reported_user': user})
|
||||
|
@ -845,3 +850,45 @@ def auth_logout(request):
|
|||
""" Decorates django ``logout()``. Release token in session."""
|
||||
del request.session['oauth_token']
|
||||
auth.logout(request)
|
||||
|
||||
|
||||
@mastodon_request_included
|
||||
@login_required
|
||||
def preferences(request):
|
||||
if request.method == 'POST':
|
||||
request.user.preference.mastodon_publish_public = bool(request.POST.get('mastodon_publish_public'))
|
||||
request.user.preference.save()
|
||||
return render(request, 'users/preferences.html', {'mastodon_publish_public': request.user.preference.mastodon_publish_public})
|
||||
|
||||
|
||||
@mastodon_request_included
|
||||
@login_required
|
||||
def data(request):
|
||||
return render(request, 'users/data.html', {
|
||||
'latest_task': request.user.user_synctasks.order_by("-id").first(),
|
||||
'export_status': request.user.preference.export_status
|
||||
})
|
||||
|
||||
|
||||
@mastodon_request_included
|
||||
@login_required
|
||||
def export_reviews(request):
|
||||
if request.method != 'POST':
|
||||
return redirect(reverse("users:data"))
|
||||
return render(request, 'users/data.html')
|
||||
|
||||
|
||||
@mastodon_request_included
|
||||
@login_required
|
||||
def export_marks(request):
|
||||
if request.method == 'POST':
|
||||
if not request.user.preference.export_status.get('marks_pending'):
|
||||
django_rq.enqueue(export_marks_task, request.user)
|
||||
request.user.preference.export_status['marks_pending'] = True
|
||||
request.user.preference.save()
|
||||
return redirect(reverse("users:data"))
|
||||
else:
|
||||
with open(request.user.preference.export_status['marks_file'], 'rb') as fh:
|
||||
response = HttpResponse(fh.read(), content_type="application/vnd.ms-excel")
|
||||
response['Content-Disposition'] = 'attachment;filename="marks.xlsx"'
|
||||
return response
|
||||
|
|
Loading…
Add table
Reference in a new issue