mirror of
https://github.com/janeczku/calibre-web
synced 2024-12-18 06:00:32 +00:00
Merge branch 'master' into Develop (goodreads)
This commit is contained in:
commit
f1ceff2b52
@ -186,7 +186,6 @@ def create_app():
|
|||||||
services.ldap.init_app(app, config)
|
services.ldap.init_app(app, config)
|
||||||
if services.goodreads_support:
|
if services.goodreads_support:
|
||||||
services.goodreads_support.connect(config.config_goodreads_api_key,
|
services.goodreads_support.connect(config.config_goodreads_api_key,
|
||||||
config.config_goodreads_api_secret_e,
|
|
||||||
config.config_use_goodreads)
|
config.config_use_goodreads)
|
||||||
config.store_calibre_uuid(calibre_db, db.Library_Id)
|
config.store_calibre_uuid(calibre_db, db.Library_Id)
|
||||||
# Configure rate limiter
|
# Configure rate limiter
|
||||||
|
@ -1631,7 +1631,10 @@ def import_ldap_users():
|
|||||||
|
|
||||||
imported = 0
|
imported = 0
|
||||||
for username in new_users:
|
for username in new_users:
|
||||||
user = username.decode('utf-8')
|
if isinstance(username, bytes):
|
||||||
|
user = username.decode('utf-8')
|
||||||
|
else:
|
||||||
|
user = username
|
||||||
if '=' in user:
|
if '=' in user:
|
||||||
# if member object field is empty take user object as filter
|
# if member object field is empty take user object as filter
|
||||||
if config.config_ldap_member_user_object:
|
if config.config_ldap_member_user_object:
|
||||||
@ -1806,11 +1809,8 @@ def _configuration_update_helper():
|
|||||||
# Goodreads configuration
|
# Goodreads configuration
|
||||||
_config_checkbox(to_save, "config_use_goodreads")
|
_config_checkbox(to_save, "config_use_goodreads")
|
||||||
_config_string(to_save, "config_goodreads_api_key")
|
_config_string(to_save, "config_goodreads_api_key")
|
||||||
if to_save.get("config_goodreads_api_secret_e", ""):
|
|
||||||
_config_string(to_save, "config_goodreads_api_secret_e")
|
|
||||||
if services.goodreads_support:
|
if services.goodreads_support:
|
||||||
services.goodreads_support.connect(config.config_goodreads_api_key,
|
services.goodreads_support.connect(config.config_goodreads_api_key,
|
||||||
config.config_goodreads_api_secret_e,
|
|
||||||
config.config_use_goodreads)
|
config.config_use_goodreads)
|
||||||
|
|
||||||
_config_int(to_save, "config_updatechannel")
|
_config_int(to_save, "config_updatechannel")
|
||||||
|
53
cps/clean_html.py
Normal file
53
cps/clean_html.py
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# This file is part of the Calibre-Web (https://github.com/janeczku/calibre-web)
|
||||||
|
# Copyright (C) 2018-2019 OzzieIsaacs
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from . import logger
|
||||||
|
from lxml.etree import ParserError
|
||||||
|
|
||||||
|
try:
|
||||||
|
# at least bleach 6.0 is needed -> incomplatible change from list arguments to set arguments
|
||||||
|
from bleach import clean_text as clean_html
|
||||||
|
BLEACH = True
|
||||||
|
except ImportError:
|
||||||
|
try:
|
||||||
|
BLEACH = False
|
||||||
|
from nh3 import clean as clean_html
|
||||||
|
except ImportError:
|
||||||
|
try:
|
||||||
|
BLEACH = False
|
||||||
|
from lxml.html.clean import clean_html
|
||||||
|
except ImportError:
|
||||||
|
clean_html = None
|
||||||
|
|
||||||
|
|
||||||
|
log = logger.create()
|
||||||
|
|
||||||
|
|
||||||
|
def clean_string(unsafe_text, book_id=0):
|
||||||
|
try:
|
||||||
|
if BLEACH:
|
||||||
|
safe_text = clean_html(unsafe_text, tags=set(), attributes=set())
|
||||||
|
else:
|
||||||
|
safe_text = clean_html(unsafe_text)
|
||||||
|
except ParserError as e:
|
||||||
|
log.error("Comments of book {} are corrupted: {}".format(book_id, e))
|
||||||
|
safe_text = ""
|
||||||
|
except TypeError as e:
|
||||||
|
log.error("Comments can't be parsed, maybe 'lxml' is too new, try installing 'bleach': {}".format(e))
|
||||||
|
safe_text = ""
|
||||||
|
return safe_text
|
@ -114,8 +114,6 @@ class _Settings(_Base):
|
|||||||
|
|
||||||
config_use_goodreads = Column(Boolean, default=False)
|
config_use_goodreads = Column(Boolean, default=False)
|
||||||
config_goodreads_api_key = Column(String)
|
config_goodreads_api_key = Column(String)
|
||||||
config_goodreads_api_secret_e = Column(String)
|
|
||||||
config_goodreads_api_secret = Column(String)
|
|
||||||
config_register_email = Column(Boolean, default=False)
|
config_register_email = Column(Boolean, default=False)
|
||||||
config_login_type = Column(Integer, default=0)
|
config_login_type = Column(Integer, default=0)
|
||||||
|
|
||||||
@ -422,19 +420,13 @@ def _encrypt_fields(session, secret_key):
|
|||||||
except OperationalError:
|
except OperationalError:
|
||||||
with session.bind.connect() as conn:
|
with session.bind.connect() as conn:
|
||||||
conn.execute(text("ALTER TABLE settings ADD column 'mail_password_e' String"))
|
conn.execute(text("ALTER TABLE settings ADD column 'mail_password_e' String"))
|
||||||
conn.execute(text("ALTER TABLE settings ADD column 'config_goodreads_api_secret_e' String"))
|
|
||||||
conn.execute(text("ALTER TABLE settings ADD column 'config_ldap_serv_password_e' String"))
|
conn.execute(text("ALTER TABLE settings ADD column 'config_ldap_serv_password_e' String"))
|
||||||
session.commit()
|
session.commit()
|
||||||
crypter = Fernet(secret_key)
|
crypter = Fernet(secret_key)
|
||||||
settings = session.query(_Settings.mail_password, _Settings.config_goodreads_api_secret,
|
settings = session.query(_Settings.mail_password, _Settings.config_ldap_serv_password).first()
|
||||||
_Settings.config_ldap_serv_password).first()
|
|
||||||
if settings.mail_password:
|
if settings.mail_password:
|
||||||
session.query(_Settings).update(
|
session.query(_Settings).update(
|
||||||
{_Settings.mail_password_e: crypter.encrypt(settings.mail_password.encode())})
|
{_Settings.mail_password_e: crypter.encrypt(settings.mail_password.encode())})
|
||||||
if settings.config_goodreads_api_secret:
|
|
||||||
session.query(_Settings).update(
|
|
||||||
{_Settings.config_goodreads_api_secret_e:
|
|
||||||
crypter.encrypt(settings.config_goodreads_api_secret.encode())})
|
|
||||||
if settings.config_ldap_serv_password:
|
if settings.config_ldap_serv_password:
|
||||||
session.query(_Settings).update(
|
session.query(_Settings).update(
|
||||||
{_Settings.config_ldap_serv_password_e:
|
{_Settings.config_ldap_serv_password_e:
|
||||||
|
@ -27,22 +27,22 @@ from shutil import copyfile
|
|||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
from markupsafe import escape, Markup # dependency of flask
|
from markupsafe import escape, Markup # dependency of flask
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from lxml.etree import ParserError
|
# from lxml.etree import ParserError
|
||||||
|
|
||||||
try:
|
#try:
|
||||||
# at least bleach 6.0 is needed -> incomplatible change from list arguments to set arguments
|
# # at least bleach 6.0 is needed -> incomplatible change from list arguments to set arguments
|
||||||
from bleach import clean_text as clean_html
|
# from bleach import clean_text as clean_html
|
||||||
BLEACH = True
|
# BLEACH = True
|
||||||
except ImportError:
|
#except ImportError:
|
||||||
try:
|
# try:
|
||||||
BLEACH = False
|
# BLEACH = False
|
||||||
from nh3 import clean as clean_html
|
# from nh3 import clean as clean_html
|
||||||
except ImportError:
|
# except ImportError:
|
||||||
try:
|
# try:
|
||||||
BLEACH = False
|
# BLEACH = False
|
||||||
from lxml.html.clean import clean_html
|
# from lxml.html.clean import clean_html
|
||||||
except ImportError:
|
# except ImportError:
|
||||||
clean_html = None
|
# clean_html = None
|
||||||
|
|
||||||
from flask import Blueprint, request, flash, redirect, url_for, abort, Response
|
from flask import Blueprint, request, flash, redirect, url_for, abort, Response
|
||||||
from flask_babel import gettext as _
|
from flask_babel import gettext as _
|
||||||
@ -54,6 +54,7 @@ from sqlalchemy.orm.exc import StaleDataError
|
|||||||
from sqlalchemy.sql.expression import func
|
from sqlalchemy.sql.expression import func
|
||||||
|
|
||||||
from . import constants, logger, isoLanguages, gdriveutils, uploader, helper, kobo_sync_status
|
from . import constants, logger, isoLanguages, gdriveutils, uploader, helper, kobo_sync_status
|
||||||
|
from .clean_html import clean_string
|
||||||
from . import config, ub, db, calibre_db
|
from . import config, ub, db, calibre_db
|
||||||
from .services.worker import WorkerThread
|
from .services.worker import WorkerThread
|
||||||
from .tasks.upload import TaskUpload
|
from .tasks.upload import TaskUpload
|
||||||
@ -1004,17 +1005,18 @@ def edit_book_series_index(series_index, book):
|
|||||||
def edit_book_comments(comments, book):
|
def edit_book_comments(comments, book):
|
||||||
modify_date = False
|
modify_date = False
|
||||||
if comments:
|
if comments:
|
||||||
try:
|
comments = clean_string(comments, book.id)
|
||||||
if BLEACH:
|
#try:
|
||||||
comments = clean_html(comments, tags=set(), attributes=set())
|
# if BLEACH:
|
||||||
else:
|
# comments = clean_html(comments, tags=set(), attributes=set())
|
||||||
comments = clean_html(comments)
|
# else:
|
||||||
except ParserError as e:
|
# comments = clean_html(comments)
|
||||||
log.error("Comments of book {} are corrupted: {}".format(book.id, e))
|
#except ParserError as e:
|
||||||
comments = ""
|
# log.error("Comments of book {} are corrupted: {}".format(book.id, e))
|
||||||
except TypeError as e:
|
# comments = ""
|
||||||
log.error("Comments can't be parsed, maybe 'lxml' is too new, try installing 'bleach': {}".format(e))
|
#except TypeError as e:
|
||||||
comments = ""
|
# log.error("Comments can't be parsed, maybe 'lxml' is too new, try installing 'bleach': {}".format(e))
|
||||||
|
# comments = ""
|
||||||
if len(book.comments):
|
if len(book.comments):
|
||||||
if book.comments[0].text != comments:
|
if book.comments[0].text != comments:
|
||||||
book.comments[0].text = comments
|
book.comments[0].text = comments
|
||||||
@ -1072,18 +1074,19 @@ def edit_cc_data_value(book_id, book, c, to_save, cc_db_value, cc_string):
|
|||||||
elif c.datatype == 'comments':
|
elif c.datatype == 'comments':
|
||||||
to_save[cc_string] = Markup(to_save[cc_string]).unescape()
|
to_save[cc_string] = Markup(to_save[cc_string]).unescape()
|
||||||
if to_save[cc_string]:
|
if to_save[cc_string]:
|
||||||
try:
|
to_save[cc_string] = clean_string(to_save[cc_string], book_id)
|
||||||
if BLEACH:
|
#try:
|
||||||
to_save[cc_string] = clean_html(to_save[cc_string], tags=set(), attributes=set())
|
# if BLEACH:
|
||||||
else:
|
# to_save[cc_string] = clean_html(to_save[cc_string], tags=set(), attributes=set())
|
||||||
to_save[cc_string] = clean_html(to_save[cc_string])
|
# else:
|
||||||
except ParserError as e:
|
# to_save[cc_string] = clean_html(to_save[cc_string])
|
||||||
log.error("Customs Comments of book {} are corrupted: {}".format(book_id, e))
|
#except ParserError as e:
|
||||||
to_save[cc_string] = ""
|
# log.error("Customs Comments of book {} are corrupted: {}".format(book_id, e))
|
||||||
except TypeError as e:
|
# to_save[cc_string] = ""
|
||||||
to_save[cc_string] = ""
|
#except TypeError as e:
|
||||||
log.error("Customs Comments can't be parsed, maybe 'lxml' is too new, "
|
# to_save[cc_string] = ""
|
||||||
"try installing 'bleach': {}".format(e))
|
# log.error("Customs Comments can't be parsed, maybe 'lxml' is too new, "
|
||||||
|
# "try installing 'bleach': {}".format(e))
|
||||||
elif c.datatype == 'datetime':
|
elif c.datatype == 'datetime':
|
||||||
try:
|
try:
|
||||||
to_save[cc_string] = datetime.strptime(to_save[cc_string], "%Y-%m-%d")
|
to_save[cc_string] = datetime.strptime(to_save[cc_string], "%Y-%m-%d")
|
||||||
|
@ -18,16 +18,49 @@
|
|||||||
|
|
||||||
import time
|
import time
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
|
import requests
|
||||||
|
import xmltodict
|
||||||
|
|
||||||
|
from goodreads.client import GoodreadsClient
|
||||||
|
from goodreads.request import GoodreadsRequest
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from goodreads.client import GoodreadsClient
|
import Levenshtein
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from betterreads.client import GoodreadsClient
|
Levenshtein = False
|
||||||
|
|
||||||
try: import Levenshtein
|
|
||||||
except ImportError: Levenshtein = False
|
|
||||||
|
|
||||||
from .. import logger
|
from .. import logger
|
||||||
|
from ..clean_html import clean_string
|
||||||
|
|
||||||
|
class my_GoodreadsClient(GoodreadsClient):
|
||||||
|
|
||||||
|
def request(self, *args, **kwargs):
|
||||||
|
"""Create a GoodreadsRequest object and make that request"""
|
||||||
|
req = my_GoodreadsRequest(self, *args, **kwargs)
|
||||||
|
return req.request()
|
||||||
|
|
||||||
|
class GoodreadsRequestException(Exception):
|
||||||
|
def __init__(self, error_msg, url):
|
||||||
|
self.error_msg = error_msg
|
||||||
|
self.url = url
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.url, ':', self.error_msg
|
||||||
|
|
||||||
|
|
||||||
|
class my_GoodreadsRequest(GoodreadsRequest):
|
||||||
|
|
||||||
|
def request(self):
|
||||||
|
resp = requests.get(self.host+self.path, params=self.params,
|
||||||
|
headers={"User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:125.0) "
|
||||||
|
"Gecko/20100101 Firefox/125.0"})
|
||||||
|
if resp.status_code != 200:
|
||||||
|
raise GoodreadsRequestException(resp.reason, self.path)
|
||||||
|
if self.req_format == 'xml':
|
||||||
|
data_dict = xmltodict.parse(resp.content)
|
||||||
|
return data_dict['GoodreadsResponse']
|
||||||
|
else:
|
||||||
|
raise Exception("Invalid format")
|
||||||
|
|
||||||
|
|
||||||
log = logger.create()
|
log = logger.create()
|
||||||
@ -38,20 +71,20 @@ _CACHE_TIMEOUT = 23 * 60 * 60 # 23 hours (in seconds)
|
|||||||
_AUTHORS_CACHE = {}
|
_AUTHORS_CACHE = {}
|
||||||
|
|
||||||
|
|
||||||
def connect(key=None, secret=None, enabled=True):
|
def connect(key=None, enabled=True):
|
||||||
global _client
|
global _client
|
||||||
|
|
||||||
if not enabled or not key or not secret:
|
if not enabled or not key:
|
||||||
_client = None
|
_client = None
|
||||||
return
|
return
|
||||||
|
|
||||||
if _client:
|
if _client:
|
||||||
# make sure the configuration has not changed since last we used the client
|
# make sure the configuration has not changed since last we used the client
|
||||||
if _client.client_key != key or _client.client_secret != secret:
|
if _client.client_key != key:
|
||||||
_client = None
|
_client = None
|
||||||
|
|
||||||
if not _client:
|
if not _client:
|
||||||
_client = GoodreadsClient(key, secret)
|
_client = my_GoodreadsClient(key, None)
|
||||||
|
|
||||||
|
|
||||||
def get_author_info(author_name):
|
def get_author_info(author_name):
|
||||||
@ -76,6 +109,7 @@ def get_author_info(author_name):
|
|||||||
|
|
||||||
if author_info:
|
if author_info:
|
||||||
author_info._timestamp = now
|
author_info._timestamp = now
|
||||||
|
author_info.safe_about = clean_string(author_info.about)
|
||||||
_AUTHORS_CACHE[author_name] = author_info
|
_AUTHORS_CACHE[author_name] = author_info
|
||||||
return author_info
|
return author_info
|
||||||
|
|
||||||
|
@ -8,8 +8,8 @@
|
|||||||
<img title="{{author.name}}" src="{{author.image_url}}" alt="{{author.name}}" class="author-photo pull-left">
|
<img title="{{author.name}}" src="{{author.image_url}}" alt="{{author.name}}" class="author-photo pull-left">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{%if author.about is not none %}
|
{%if author.safe_about is not none %}
|
||||||
<p>{{author.about}}</p>
|
<p>{{author.safe_about|safe}}</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
- {{_("via")}} <a href="{{author.link}}" class="author-link" target="_blank" rel="noopener">Goodreads</a>
|
- {{_("via")}} <a href="{{author.link}}" class="author-link" target="_blank" rel="noopener">Goodreads</a>
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<h2>{{title}}</h2>
|
<h2>{{title}}</h2>
|
||||||
<form role="form" method="POST" autocomplete="off">
|
<form role="form" method="POST" autocomplete="off">
|
||||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||||
<div class="panel-group col-md-10 col-lg-8">
|
<div class="panel-group col-md-11 col-lg-8">
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
<h4 class="panel-title">
|
<h4 class="panel-title">
|
||||||
@ -155,17 +155,12 @@
|
|||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input type="checkbox" id="config_use_goodreads" name="config_use_goodreads" data-control="goodreads-settings" {% if config.config_use_goodreads %}checked{% endif %}>
|
<input type="checkbox" id="config_use_goodreads" name="config_use_goodreads" data-control="goodreads-settings" {% if config.config_use_goodreads %}checked{% endif %}>
|
||||||
<label for="config_use_goodreads">{{_('Use Goodreads')}}</label>
|
<label for="config_use_goodreads">{{_('Use Goodreads')}}</label>
|
||||||
<a href="https://www.goodreads.com/api/keys" target="_blank" style="margin-left: 5px">{{_('Create an API Key')}}</a>
|
|
||||||
</div>
|
</div>
|
||||||
<div data-related="goodreads-settings">
|
<div data-related="goodreads-settings">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="config_goodreads_api_key">{{_('Goodreads API Key')}}</label>
|
<label for="config_goodreads_api_key">{{_('Goodreads API Key')}}</label>
|
||||||
<input type="text" class="form-control" id="config_goodreads_api_key" name="config_goodreads_api_key" value="{% if config.config_goodreads_api_key != None %}{{ config.config_goodreads_api_key }}{% endif %}" autocomplete="off">
|
<input type="text" class="form-control" id="config_goodreads_api_key" name="config_goodreads_api_key" value="{% if config.config_goodreads_api_key != None %}{{ config.config_goodreads_api_key }}{% endif %}" autocomplete="off">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
|
||||||
<label for="config_goodreads_api_secret_e">{{_('Goodreads API Secret')}}</label>
|
|
||||||
<input type="password" class="form-control" id="config_goodreads_api_secret_e" name="config_goodreads_api_secret_e" value="" autocomplete="off">
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
@ -21,7 +21,7 @@ python-Levenshtein>=0.12.0,<0.26.0
|
|||||||
|
|
||||||
# ldap login
|
# ldap login
|
||||||
python-ldap>=3.0.0,<3.5.0
|
python-ldap>=3.0.0,<3.5.0
|
||||||
Flask-SimpleLDAP>=1.4.0,<1.5.0
|
Flask-SimpleLDAP>=1.4.0,<2.1.0
|
||||||
|
|
||||||
# oauth
|
# oauth
|
||||||
Flask-Dance>=2.0.0,<7.1.0
|
Flask-Dance>=2.0.0,<7.1.0
|
||||||
|
@ -58,6 +58,7 @@ install_requires =
|
|||||||
chardet>=3.0.0,<4.1.0
|
chardet>=3.0.0,<4.1.0
|
||||||
advocate>=1.0.0,<1.1.0
|
advocate>=1.0.0,<1.1.0
|
||||||
Flask-Limiter>=2.3.0,<3.6.0
|
Flask-Limiter>=2.3.0,<3.6.0
|
||||||
|
regex>=2022.3.2,<2024.2.25
|
||||||
|
|
||||||
|
|
||||||
[options.packages.find]
|
[options.packages.find]
|
||||||
@ -85,7 +86,7 @@ goodreads =
|
|||||||
python-Levenshtein>=0.12.0,<0.26.0
|
python-Levenshtein>=0.12.0,<0.26.0
|
||||||
ldap =
|
ldap =
|
||||||
python-ldap>=3.0.0,<3.5.0
|
python-ldap>=3.0.0,<3.5.0
|
||||||
Flask-SimpleLDAP>=1.4.0,<1.5.0
|
Flask-SimpleLDAP>=1.4.0,<2.1.0
|
||||||
oauth =
|
oauth =
|
||||||
Flask-Dance>=2.0.0,<7.1.0
|
Flask-Dance>=2.0.0,<7.1.0
|
||||||
SQLAlchemy-Utils>=0.33.5,<0.42.0
|
SQLAlchemy-Utils>=0.33.5,<0.42.0
|
||||||
|
@ -37,20 +37,20 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-6 col-md-6 col-sm-offset-3" style="margin-top:50px;">
|
<div class="col-xs-6 col-md-6 col-sm-offset-3" style="margin-top:50px;">
|
||||||
|
|
||||||
<p class='text-justify attribute'><strong>Start Time: </strong>2024-02-26 20:07:24</p>
|
<p class='text-justify attribute'><strong>Start Time: </strong>2024-05-10 20:24:40</p>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-6 col-md-6 col-sm-offset-3">
|
<div class="col-xs-6 col-md-6 col-sm-offset-3">
|
||||||
|
|
||||||
<p class='text-justify attribute'><strong>Stop Time: </strong>2024-02-27 03:19:17</p>
|
<p class='text-justify attribute'><strong>Stop Time: </strong>2024-05-11 03:33:47</p>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-6 col-md-6 col-sm-offset-3">
|
<div class="col-xs-6 col-md-6 col-sm-offset-3">
|
||||||
<p class='text-justify attribute'><strong>Duration: </strong>6h 0 min</p>
|
<p class='text-justify attribute'><strong>Duration: </strong>5h 58 min</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -320,38 +320,30 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<tr id='pt2.9' class='hiddenRow bg-success'>
|
<tr id="ft2.9" class="none bg-danger">
|
||||||
<td>
|
<td>
|
||||||
<div class='testcase'>TestBackupMetadata - test_backup_change_book_series_index</div>
|
<div class='testcase'>TestBackupMetadata - test_backup_change_book_series_index</div>
|
||||||
</td>
|
</td>
|
||||||
<td colspan='6' align='center'>PASS</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<tr id="ft2.10" class="none bg-danger">
|
|
||||||
<td>
|
|
||||||
<div class='testcase'>TestBackupMetadata - test_backup_change_book_tags</div>
|
|
||||||
</td>
|
|
||||||
<td colspan='6'>
|
<td colspan='6'>
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<a class="popup_link text-center" onfocus='blur()' onclick="showTestDetail('div_ft2.10')">FAIL</a>
|
<a class="popup_link text-center" onfocus='blur()' onclick="showTestDetail('div_ft2.9')">FAIL</a>
|
||||||
</div>
|
</div>
|
||||||
<!--css div popup start-->
|
<!--css div popup start-->
|
||||||
<div id="div_ft2.10" class="popup_window test_output" style="display:block;">
|
<div id="div_ft2.9" class="popup_window test_output" style="display:block;">
|
||||||
<div class='close_button pull-right'>
|
<div class='close_button pull-right'>
|
||||||
<button type="button" class="close" aria-label="Close" onfocus="this.blur();"
|
<button type="button" class="close" aria-label="Close" onfocus="this.blur();"
|
||||||
onclick="document.getElementById('div_ft2.10').style.display='none'"><span
|
onclick="document.getElementById('div_ft2.9').style.display='none'"><span
|
||||||
aria-hidden="true">×</span></button>
|
aria-hidden="true">×</span></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-left pull-left">
|
<div class="text-left pull-left">
|
||||||
<pre class="text-left">Traceback (most recent call last):
|
<pre class="text-left">Traceback (most recent call last):
|
||||||
File "/home/ozzie/Development/calibre-web-test/test/test_backup_metadata.py", line 243, in test_backup_change_book_tags
|
File "/home/ozzie/Development/calibre-web-test/test/test_backup_metadata.py", line 135, in test_backup_change_book_series_index
|
||||||
self.assertCountEqual(metadata['tags'], ['Ku','kOl'])
|
self.assertEqual(metadata['series']['content'], "tEst")
|
||||||
AssertionError: Element counts were not equal:
|
AssertionError: 'test' != 'tEst'
|
||||||
First has 1, Second has 0: 'Lo执|1u'
|
- test
|
||||||
First has 0, Second has 1: 'Ku'
|
? ^
|
||||||
First has 0, Second has 1: 'kOl'</pre>
|
+ tEst
|
||||||
|
? ^</pre>
|
||||||
</div>
|
</div>
|
||||||
<div class="clearfix"></div>
|
<div class="clearfix"></div>
|
||||||
</div>
|
</div>
|
||||||
@ -361,6 +353,15 @@ First has 0, Second has 1: 'kOl'</pre>
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<tr id='pt2.10' class='hiddenRow bg-success'>
|
||||||
|
<td>
|
||||||
|
<div class='testcase'>TestBackupMetadata - test_backup_change_book_tags</div>
|
||||||
|
</td>
|
||||||
|
<td colspan='6' align='center'>PASS</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<tr id='pt2.11' class='hiddenRow bg-success'>
|
<tr id='pt2.11' class='hiddenRow bg-success'>
|
||||||
<td>
|
<td>
|
||||||
<div class='testcase'>TestBackupMetadata - test_backup_change_book_title</div>
|
<div class='testcase'>TestBackupMetadata - test_backup_change_book_title</div>
|
||||||
@ -1028,11 +1029,11 @@ First has 0, Second has 1: 'kOl'</pre>
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<tr id="su" class="skipClass">
|
<tr id="su" class="failClass">
|
||||||
<td>TestEditAdditionalBooks</td>
|
<td>TestEditAdditionalBooks</td>
|
||||||
<td class="text-center">20</td>
|
<td class="text-center">20</td>
|
||||||
<td class="text-center">18</td>
|
<td class="text-center">17</td>
|
||||||
<td class="text-center">0</td>
|
<td class="text-center">1</td>
|
||||||
<td class="text-center">0</td>
|
<td class="text-center">0</td>
|
||||||
<td class="text-center">2</td>
|
<td class="text-center">2</td>
|
||||||
<td class="text-center">
|
<td class="text-center">
|
||||||
@ -1150,11 +1151,33 @@ First has 0, Second has 1: 'kOl'</pre>
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<tr id='pt12.13' class='hiddenRow bg-success'>
|
<tr id="ft12.13" class="none bg-danger">
|
||||||
<td>
|
<td>
|
||||||
<div class='testcase'>TestEditAdditionalBooks - test_upload_metadata_cb7</div>
|
<div class='testcase'>TestEditAdditionalBooks - test_upload_metadata_cb7</div>
|
||||||
</td>
|
</td>
|
||||||
<td colspan='6' align='center'>PASS</td>
|
<td colspan='6'>
|
||||||
|
<div class="text-center">
|
||||||
|
<a class="popup_link text-center" onfocus='blur()' onclick="showTestDetail('div_ft12.13')">FAIL</a>
|
||||||
|
</div>
|
||||||
|
<!--css div popup start-->
|
||||||
|
<div id="div_ft12.13" class="popup_window test_output" style="display:block;">
|
||||||
|
<div class='close_button pull-right'>
|
||||||
|
<button type="button" class="close" aria-label="Close" onfocus="this.blur();"
|
||||||
|
onclick="document.getElementById('div_ft12.13').style.display='none'"><span
|
||||||
|
aria-hidden="true">×</span></button>
|
||||||
|
</div>
|
||||||
|
<div class="text-left pull-left">
|
||||||
|
<pre class="text-left">Traceback (most recent call last):
|
||||||
|
File "/home/ozzie/Development/calibre-web-test/test/test_edit_additional_books.py", line 246, in test_upload_metadata_cb7
|
||||||
|
self.assertEqual('Test 执 to', details['title'])
|
||||||
|
AssertionError: 'Test 执 to' != 'book'
|
||||||
|
- Test 执 to
|
||||||
|
+ book</pre>
|
||||||
|
</div>
|
||||||
|
<div class="clearfix"></div>
|
||||||
|
</div>
|
||||||
|
<!--css div popup end-->
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
|
|
||||||
@ -2571,11 +2594,11 @@ IndexError: list index out of range</pre>
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<tr id="su" class="passClass">
|
<tr id="su" class="failClass">
|
||||||
<td>TestGoodreads</td>
|
<td>TestGoodreads</td>
|
||||||
<td class="text-center">3</td>
|
<td class="text-center">3</td>
|
||||||
<td class="text-center">3</td>
|
<td class="text-center">2</td>
|
||||||
<td class="text-center">0</td>
|
<td class="text-center">1</td>
|
||||||
<td class="text-center">0</td>
|
<td class="text-center">0</td>
|
||||||
<td class="text-center">0</td>
|
<td class="text-center">0</td>
|
||||||
<td class="text-center">
|
<td class="text-center">
|
||||||
@ -2585,11 +2608,31 @@ IndexError: list index out of range</pre>
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<tr id='pt28.1' class='hiddenRow bg-success'>
|
<tr id="ft28.1" class="none bg-danger">
|
||||||
<td>
|
<td>
|
||||||
<div class='testcase'>TestGoodreads - test_author_page</div>
|
<div class='testcase'>TestGoodreads - test_author_page</div>
|
||||||
</td>
|
</td>
|
||||||
<td colspan='6' align='center'>PASS</td>
|
<td colspan='6'>
|
||||||
|
<div class="text-center">
|
||||||
|
<a class="popup_link text-center" onfocus='blur()' onclick="showTestDetail('div_ft28.1')">FAIL</a>
|
||||||
|
</div>
|
||||||
|
<!--css div popup start-->
|
||||||
|
<div id="div_ft28.1" class="popup_window test_output" style="display:block;">
|
||||||
|
<div class='close_button pull-right'>
|
||||||
|
<button type="button" class="close" aria-label="Close" onfocus="this.blur();"
|
||||||
|
onclick="document.getElementById('div_ft28.1').style.display='none'"><span
|
||||||
|
aria-hidden="true">×</span></button>
|
||||||
|
</div>
|
||||||
|
<div class="text-left pull-left">
|
||||||
|
<pre class="text-left">Traceback (most recent call last):
|
||||||
|
File "/home/ozzie/Development/calibre-web-test/test/test_goodreads.py", line 100, in test_author_page
|
||||||
|
self.assertTrue(self.check_element_on_page((By.CLASS_NAME, "author-photo")))
|
||||||
|
AssertionError: False is not true</pre>
|
||||||
|
</div>
|
||||||
|
<div class="clearfix"></div>
|
||||||
|
</div>
|
||||||
|
<!--css div popup end-->
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
|
|
||||||
@ -3435,7 +3478,7 @@ IndexError: list index out of range</pre>
|
|||||||
</div>
|
</div>
|
||||||
<div class="text-left pull-left">
|
<div class="text-left pull-left">
|
||||||
<pre class="text-left">Traceback (most recent call last):
|
<pre class="text-left">Traceback (most recent call last):
|
||||||
File "/home/ozzie/Development/calibre-web-test/test/test_login.py", line 532, in test_proxy_login_multi_user
|
File "/home/ozzie/Development/calibre-web-test/test/test_login.py", line 575, in test_proxy_login_multi_user
|
||||||
self.assertTrue('<input type="text" class="form-control" name="name" id="name" value="new_user1" autocomplete="off">' in resp.text)
|
self.assertTrue('<input type="text" class="form-control" name="name" id="name" value="new_user1" autocomplete="off">' in resp.text)
|
||||||
AssertionError: False is not true</pre>
|
AssertionError: False is not true</pre>
|
||||||
</div>
|
</div>
|
||||||
@ -5569,8 +5612,8 @@ AssertionError: False is not true</pre>
|
|||||||
<tr id='total_row' class="text-center bg-grey">
|
<tr id='total_row' class="text-center bg-grey">
|
||||||
<td>Total</td>
|
<td>Total</td>
|
||||||
<td>492</td>
|
<td>492</td>
|
||||||
<td>479</td>
|
<td>477</td>
|
||||||
<td>2</td>
|
<td>4</td>
|
||||||
<td>1</td>
|
<td>1</td>
|
||||||
<td>10</td>
|
<td>10</td>
|
||||||
<td> </td>
|
<td> </td>
|
||||||
@ -5600,7 +5643,7 @@ AssertionError: False is not true</pre>
|
|||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<th>Platform</th>
|
<th>Platform</th>
|
||||||
<td>Linux 6.5.0-21-generic #21~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Fri Feb 9 13:32:52 UTC 2 x86_64 x86_64</td>
|
<td>Linux 6.5.0-28-generic #29~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Thu Apr 4 14:39:20 UTC 2 x86_64 x86_64</td>
|
||||||
<td>Basic</td>
|
<td>Basic</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
@ -5624,7 +5667,7 @@ AssertionError: False is not true</pre>
|
|||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<th>Babel</th>
|
<th>Babel</th>
|
||||||
<td>2.14.0</td>
|
<td>2.15.0</td>
|
||||||
<td>Basic</td>
|
<td>Basic</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
@ -5684,19 +5727,19 @@ AssertionError: False is not true</pre>
|
|||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<th>Jinja2</th>
|
<th>Jinja2</th>
|
||||||
<td>3.1.3</td>
|
<td>3.1.4</td>
|
||||||
<td>Basic</td>
|
<td>Basic</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<th>lxml</th>
|
<th>lxml</th>
|
||||||
<td>5.1.0</td>
|
<td>5.1.1</td>
|
||||||
<td>Basic</td>
|
<td>Basic</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<th>pyasn1</th>
|
<th>pyasn1</th>
|
||||||
<td>0.5.1</td>
|
<td>0.6.0</td>
|
||||||
<td>Basic</td>
|
<td>Basic</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
@ -5712,6 +5755,12 @@ AssertionError: False is not true</pre>
|
|||||||
<td>Basic</td>
|
<td>Basic</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<th>regex</th>
|
||||||
|
<td>2023.12.25</td>
|
||||||
|
<td>Basic</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<th>requests</th>
|
<th>requests</th>
|
||||||
<td>2.31.0</td>
|
<td>2.31.0</td>
|
||||||
@ -5720,7 +5769,7 @@ AssertionError: False is not true</pre>
|
|||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<th>SQLAlchemy</th>
|
<th>SQLAlchemy</th>
|
||||||
<td>2.0.27</td>
|
<td>2.0.30</td>
|
||||||
<td>Basic</td>
|
<td>Basic</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
@ -5750,7 +5799,7 @@ AssertionError: False is not true</pre>
|
|||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<th>google-api-python-client</th>
|
<th>google-api-python-client</th>
|
||||||
<td>2.119.0</td>
|
<td>2.128.0</td>
|
||||||
<td>TestBackupMetadataGdrive</td>
|
<td>TestBackupMetadataGdrive</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
@ -5780,7 +5829,7 @@ AssertionError: False is not true</pre>
|
|||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<th>google-api-python-client</th>
|
<th>google-api-python-client</th>
|
||||||
<td>2.119.0</td>
|
<td>2.128.0</td>
|
||||||
<td>TestCliGdrivedb</td>
|
<td>TestCliGdrivedb</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
@ -5810,7 +5859,7 @@ AssertionError: False is not true</pre>
|
|||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<th>google-api-python-client</th>
|
<th>google-api-python-client</th>
|
||||||
<td>2.119.0</td>
|
<td>2.128.0</td>
|
||||||
<td>TestEbookConvertCalibreGDrive</td>
|
<td>TestEbookConvertCalibreGDrive</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
@ -5840,7 +5889,7 @@ AssertionError: False is not true</pre>
|
|||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<th>google-api-python-client</th>
|
<th>google-api-python-client</th>
|
||||||
<td>2.119.0</td>
|
<td>2.129.0</td>
|
||||||
<td>TestEbookConvertGDriveKepubify</td>
|
<td>TestEbookConvertGDriveKepubify</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
@ -5876,25 +5925,25 @@ AssertionError: False is not true</pre>
|
|||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<th>py7zr</th>
|
<th>py7zr</th>
|
||||||
<td>0.20.8</td>
|
<td>0.21.0</td>
|
||||||
<td>TestEditAdditionalBooks</td>
|
<td>TestEditAdditionalBooks</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<th>rarfile</th>
|
<th>rarfile</th>
|
||||||
<td>4.1</td>
|
<td>4.2</td>
|
||||||
<td>TestEditAdditionalBooks</td>
|
<td>TestEditAdditionalBooks</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<th>py7zr</th>
|
<th>py7zr</th>
|
||||||
<td>0.20.8</td>
|
<td>0.21.0</td>
|
||||||
<td>TestEditBooks</td>
|
<td>TestEditBooks</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<th>google-api-python-client</th>
|
<th>google-api-python-client</th>
|
||||||
<td>2.119.0</td>
|
<td>2.129.0</td>
|
||||||
<td>TestEditAuthorsGdrive</td>
|
<td>TestEditAuthorsGdrive</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
@ -5930,7 +5979,7 @@ AssertionError: False is not true</pre>
|
|||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<th>google-api-python-client</th>
|
<th>google-api-python-client</th>
|
||||||
<td>2.119.0</td>
|
<td>2.129.0</td>
|
||||||
<td>TestEditBooksOnGdrive</td>
|
<td>TestEditBooksOnGdrive</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
@ -5972,7 +6021,7 @@ AssertionError: False is not true</pre>
|
|||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<th>google-api-python-client</th>
|
<th>google-api-python-client</th>
|
||||||
<td>2.119.0</td>
|
<td>2.129.0</td>
|
||||||
<td>TestEmbedMetadataGdrive</td>
|
<td>TestEmbedMetadataGdrive</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
@ -6002,7 +6051,7 @@ AssertionError: False is not true</pre>
|
|||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<th>google-api-python-client</th>
|
<th>google-api-python-client</th>
|
||||||
<td>2.119.0</td>
|
<td>2.129.0</td>
|
||||||
<td>TestSetupGdrive</td>
|
<td>TestSetupGdrive</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
@ -6038,31 +6087,31 @@ AssertionError: False is not true</pre>
|
|||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<th>python-Levenshtein</th>
|
<th>python-Levenshtein</th>
|
||||||
<td>0.25.0</td>
|
<td>0.25.1</td>
|
||||||
<td>TestGoodreads</td>
|
<td>TestGoodreads</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<th>jsonschema</th>
|
<th>jsonschema</th>
|
||||||
<td>4.21.1</td>
|
<td>4.22.0</td>
|
||||||
<td>TestKoboSync</td>
|
<td>TestKoboSync</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<th>jsonschema</th>
|
<th>jsonschema</th>
|
||||||
<td>4.21.1</td>
|
<td>4.22.0</td>
|
||||||
<td>TestKoboSyncBig</td>
|
<td>TestKoboSyncBig</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<th>Flask-SimpleLDAP</th>
|
<th>Flask-SimpleLDAP</th>
|
||||||
<td>1.4.0</td>
|
<td>2.0.0</td>
|
||||||
<td>TestLdapLogin</td>
|
<td>TestLdapLogin</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<th>jsonschema</th>
|
<th>jsonschema</th>
|
||||||
<td>4.21.1</td>
|
<td>4.22.0</td>
|
||||||
<td>TestLdapLogin</td>
|
<td>TestLdapLogin</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
@ -6074,13 +6123,13 @@ AssertionError: False is not true</pre>
|
|||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<th>Flask-Dance</th>
|
<th>Flask-Dance</th>
|
||||||
<td>7.0.1</td>
|
<td>7.1.0</td>
|
||||||
<td>TestOAuthLogin</td>
|
<td>TestOAuthLogin</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<th>SQLAlchemy-Utils</th>
|
<th>SQLAlchemy-Utils</th>
|
||||||
<td>0.41.1</td>
|
<td>0.41.2</td>
|
||||||
<td>TestOAuthLogin</td>
|
<td>TestOAuthLogin</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
@ -6092,7 +6141,7 @@ AssertionError: False is not true</pre>
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
drawCircle(479, 2, 1, 10);
|
drawCircle(477, 4, 1, 10);
|
||||||
showCase(5);
|
showCase(5);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user