1
0
mirror of https://github.com/janeczku/calibre-web synced 2024-06-30 17:13:16 +00:00

Code cosmetics

This commit is contained in:
Ozzieisaacs 2020-04-19 19:08:58 +02:00
parent 7bb5afa585
commit 24c743d23d
15 changed files with 262 additions and 192 deletions

View File

@ -25,7 +25,7 @@ import ast
from sqlalchemy import create_engine from sqlalchemy import create_engine
from sqlalchemy import Table, Column, ForeignKey from sqlalchemy import Table, Column, ForeignKey
from sqlalchemy import String, Integer, Boolean, TIMESTAMP, Float from sqlalchemy import String, Integer, Boolean, TIMESTAMP, Float, DateTime
from sqlalchemy.orm import relationship, sessionmaker, scoped_session from sqlalchemy.orm import relationship, sessionmaker, scoped_session
from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.ext.declarative import declarative_base

View File

@ -146,31 +146,47 @@ def check_send_to_kindle(entry):
# no converter - only for mobi and pdf formats # no converter - only for mobi and pdf formats
for ele in iter(entry.data): for ele in iter(entry.data):
if 'MOBI' in ele.format: if 'MOBI' in ele.format:
bookformats.append({'format':'Mobi','convert':0,'text':_('Send %(format)s to Kindle',format='Mobi')}) bookformats.append({'format': 'Mobi',
'convert': 0,
'text': _('Send %(format)s to Kindle', format='Mobi')})
if 'PDF' in ele.format: if 'PDF' in ele.format:
bookformats.append({'format':'Pdf','convert':0,'text':_('Send %(format)s to Kindle',format='Pdf')}) bookformats.append({'format': 'Pdf',
'convert': 0,
'text': _('Send %(format)s to Kindle', format='Pdf')})
if 'AZW' in ele.format: if 'AZW' in ele.format:
bookformats.append({'format':'Azw','convert':0,'text':_('Send %(format)s to Kindle',format='Azw')}) bookformats.append({'format': 'Azw',
'''if 'AZW3' in ele.format: 'convert': 0,
bookformats.append({'format':'Azw3','convert':0,'text':_('Send %(format)s to Kindle',format='Azw3')})''' 'text': _('Send %(format)s to Kindle', format='Azw')})
else: else:
formats = list() formats = list()
for ele in iter(entry.data): for ele in iter(entry.data):
formats.append(ele.format) formats.append(ele.format)
if 'MOBI' in formats: if 'MOBI' in formats:
bookformats.append({'format': 'Mobi','convert':0,'text':_('Send %(format)s to Kindle',format='Mobi')}) bookformats.append({'format': 'Mobi',
'convert': 0,
'text': _('Send %(format)s to Kindle', format='Mobi')})
if 'AZW' in formats: if 'AZW' in formats:
bookformats.append({'format': 'Azw','convert':0,'text':_('Send %(format)s to Kindle',format='Azw')}) bookformats.append({'format': 'Azw',
'convert': 0,
'text': _('Send %(format)s to Kindle', format='Azw')})
if 'PDF' in formats: if 'PDF' in formats:
bookformats.append({'format': 'Pdf','convert':0,'text':_('Send %(format)s to Kindle',format='Pdf')}) bookformats.append({'format': 'Pdf',
'convert': 0,
'text': _('Send %(format)s to Kindle', format='Pdf')})
if config.config_ebookconverter >= 1: if config.config_ebookconverter >= 1:
if 'EPUB' in formats and not 'MOBI' in formats: if 'EPUB' in formats and not 'MOBI' in formats:
bookformats.append({'format': 'Mobi','convert':1, bookformats.append({'format': 'Mobi',
'text':_('Convert %(orig)s to %(format)s and send to Kindle',orig='Epub',format='Mobi')}) 'convert':1,
'text': _('Convert %(orig)s to %(format)s and send to Kindle',
orig='Epub',
format='Mobi')})
if config.config_ebookconverter == 2: if config.config_ebookconverter == 2:
if 'AZW3' in formats and not 'MOBI' in formats: if 'AZW3' in formats and not 'MOBI' in formats:
bookformats.append({'format': 'Mobi','convert':2, bookformats.append({'format': 'Mobi',
'text':_('Convert %(orig)s to %(format)s and send to Kindle',orig='Azw3',format='Mobi')}) 'convert': 2,
'text': _('Convert %(orig)s to %(format)s and send to Kindle',
orig='Azw3',
format='Mobi')})
return bookformats return bookformats
else: else:
log.error(u'Cannot find book entry %d', entry.id) log.error(u'Cannot find book entry %d', entry.id)
@ -204,7 +220,6 @@ def send_mail(book_id, book_format, convert, kindle_mail, calibrepath, user_id):
# returns None if success, otherwise errormessage # returns None if success, otherwise errormessage
return convert_book_format(book_id, calibrepath, u'azw3', book_format.lower(), user_id, kindle_mail) return convert_book_format(book_id, calibrepath, u'azw3', book_format.lower(), user_id, kindle_mail)
for entry in iter(book.data): for entry in iter(book.data):
if entry.format.upper() == book_format.upper(): if entry.format.upper() == book_format.upper():
converted_file_name = entry.name + '.' + book_format.lower() converted_file_name = entry.name + '.' + book_format.lower()
@ -417,14 +432,14 @@ def reset_password(user_id):
password = generate_random_password() password = generate_random_password()
existing_user.password = generate_password_hash(password) existing_user.password = generate_password_hash(password)
if not config.get_mail_server_configured(): if not config.get_mail_server_configured():
return (2, None) return 2, None
try: try:
ub.session.commit() ub.session.commit()
send_registration_mail(existing_user.email, existing_user.nickname, password, True) send_registration_mail(existing_user.email, existing_user.nickname, password, True)
return (1, existing_user.nickname) return 1, existing_user.nickname
except Exception: except Exception:
ub.session.rollback() ub.session.rollback()
return (0, None) return 0, None
def generate_random_password(): def generate_random_password():
@ -434,6 +449,7 @@ def generate_random_password():
################################## External interface ################################## External interface
def update_dir_stucture(book_id, calibrepath, first_author=None): def update_dir_stucture(book_id, calibrepath, first_author=None):
if config.config_use_google_drive: if config.config_use_google_drive:
return update_dir_structure_gdrive(book_id, first_author) return update_dir_structure_gdrive(book_id, first_author)
@ -454,15 +470,18 @@ def get_cover_on_failure(use_generic_cover):
else: else:
return None return None
def get_book_cover(book_id): def get_book_cover(book_id):
book = db.session.query(db.Books).filter(db.Books.id == book_id).filter(common_filters()).first() book = db.session.query(db.Books).filter(db.Books.id == book_id).filter(common_filters()).first()
return get_book_cover_internal(book, use_generic_cover_on_failure=True) return get_book_cover_internal(book, use_generic_cover_on_failure=True)
def get_book_cover_with_uuid(book_uuid, def get_book_cover_with_uuid(book_uuid,
use_generic_cover_on_failure=True): use_generic_cover_on_failure=True):
book = db.session.query(db.Books).filter(db.Books.uuid == book_uuid).first() book = db.session.query(db.Books).filter(db.Books.uuid == book_uuid).first()
return get_book_cover_internal(book, use_generic_cover_on_failure) return get_book_cover_internal(book, use_generic_cover_on_failure)
def get_book_cover_internal(book, def get_book_cover_internal(book,
use_generic_cover_on_failure): use_generic_cover_on_failure):
if book and book.has_cover: if book and book.has_cover:
@ -539,7 +558,7 @@ def save_cover(img, book_path):
im.save(tmp_bytesio, format='JPEG') im.save(tmp_bytesio, format='JPEG')
img._content = tmp_bytesio.getvalue() img._content = tmp_bytesio.getvalue()
else: else:
if content_type not in ('image/jpeg'): if content_type not in 'image/jpeg':
log.error("Only jpg/jpeg files are supported as coverfile") log.error("Only jpg/jpeg files are supported as coverfile")
return False, _("Only jpg/jpeg files are supported as coverfile") return False, _("Only jpg/jpeg files are supported as coverfile")
@ -557,7 +576,6 @@ def save_cover(img, book_path):
return save_cover_from_filestorage(os.path.join(config.config_calibre_dir, book_path), "cover.jpg", img) return save_cover_from_filestorage(os.path.join(config.config_calibre_dir, book_path), "cover.jpg", img)
def do_download_file(book, book_format, data, headers): def do_download_file(book, book_format, data, headers):
if config.config_use_google_drive: if config.config_use_google_drive:
startTime = time.time() startTime = time.time()
@ -579,7 +597,6 @@ def do_download_file(book, book_format, data, headers):
################################## ##################################
def check_unrar(unrarLocation): def check_unrar(unrarLocation):
if not unrarLocation: if not unrarLocation:
return return
@ -601,13 +618,12 @@ def check_unrar(unrarLocation):
return 'Error excecuting UnRar' return 'Error excecuting UnRar'
def json_serial(obj): def json_serial(obj):
"""JSON serializer for objects not serializable by default json code""" """JSON serializer for objects not serializable by default json code"""
if isinstance(obj, (datetime)): if isinstance(obj, datetime):
return obj.isoformat() return obj.isoformat()
if isinstance(obj, (timedelta)): if isinstance(obj, timedelta):
return { return {
'__type__': 'timedelta', '__type__': 'timedelta',
'days': obj.days, 'days': obj.days,
@ -733,7 +749,8 @@ def tags_filters():
# Creates for all stored languages a translated speaking name in the array for the UI # Creates for all stored languages a translated speaking name in the array for the UI
def speaking_language(languages=None): def speaking_language(languages=None):
if not languages: if not languages:
languages = db.session.query(db.Languages).join(db.books_languages_link).join(db.Books).filter(common_filters())\ languages = db.session.query(db.Languages).join(db.books_languages_link).join(db.Books)\
.filter(common_filters())\
.group_by(text('books_languages_link.lang_code')).all() .group_by(text('books_languages_link.lang_code')).all()
for lang in languages: for lang in languages:
try: try:
@ -743,6 +760,7 @@ def speaking_language(languages=None):
lang.name = _(isoLanguages.get(part3=lang.lang_code).name) lang.name = _(isoLanguages.get(part3=lang.lang_code).name)
return languages return languages
# checks if domain is in database (including wildcards) # checks if domain is in database (including wildcards)
# example SELECT * FROM @TABLE WHERE 'abcdefg' LIKE Name; # example SELECT * FROM @TABLE WHERE 'abcdefg' LIKE Name;
# from https://code.luasoftware.com/tutorials/flask/execute-raw-sql-in-flask-sqlalchemy/ # from https://code.luasoftware.com/tutorials/flask/execute-raw-sql-in-flask-sqlalchemy/
@ -787,9 +805,11 @@ def fill_indexpage_with_archived_books(page, database, db_filter, order, allow_s
randm = false() randm = false()
off = int(int(config.config_books_per_page) * (page - 1)) off = int(int(config.config_books_per_page) * (page - 1))
pagination = Pagination(page, config.config_books_per_page, pagination = Pagination(page, config.config_books_per_page,
len(db.session.query(database).filter(db_filter).filter(common_filters(allow_show_archived)).all())) len(db.session.query(database).filter(db_filter)
entries = db.session.query(database).join(*join, isouter=True).filter(db_filter).filter(common_filters(allow_show_archived)).\ .filter(common_filters(allow_show_archived)).all()))
order_by(*order).offset(off).limit(config.config_books_per_page).all() entries = db.session.query(database).join(*join, isouter=True).filter(db_filter)\
.filter(common_filters(allow_show_archived))\
.order_by(*order).offset(off).limit(config.config_books_per_page).all()
for book in entries: for book in entries:
book = order_authors(book) book = order_authors(book)
return entries, randm, pagination return entries, randm, pagination
@ -798,10 +818,12 @@ def fill_indexpage_with_archived_books(page, database, db_filter, order, allow_s
def get_typeahead(database, query, replace=('', ''), tag_filter=true()): def get_typeahead(database, query, replace=('', ''), tag_filter=true()):
query = query or '' query = query or ''
db.session.connection().connection.connection.create_function("lower", 1, lcase) db.session.connection().connection.connection.create_function("lower", 1, lcase)
entries = db.session.query(database).filter(tag_filter).filter(func.lower(database.name).ilike("%" + query + "%")).all() entries = db.session.query(database).filter(tag_filter).\
filter(func.lower(database.name).ilike("%" + query + "%")).all()
json_dumps = json.dumps([dict(name=r.name.replace(*replace)) for r in entries]) json_dumps = json.dumps([dict(name=r.name.replace(*replace)) for r in entries])
return json_dumps return json_dumps
# read search results from calibre-database and return it (function is used for feed and simple search # read search results from calibre-database and return it (function is used for feed and simple search
def get_search_results(term): def get_search_results(term):
db.session.connection().connection.connection.create_function("lower", 1, lcase) db.session.connection().connection.connection.create_function("lower", 1, lcase)
@ -820,6 +842,7 @@ def get_search_results(term):
func.lower(db.Books.title).ilike("%" + term + "%") func.lower(db.Books.title).ilike("%" + term + "%")
)).all() )).all()
def get_cc_columns(): def get_cc_columns():
tmpcc = db.session.query(db.Custom_Columns).filter(db.Custom_Columns.datatype.notin_(db.cc_exceptions)).all() tmpcc = db.session.query(db.Custom_Columns).filter(db.Custom_Columns.datatype.notin_(db.cc_exceptions)).all()
if config.config_columns_to_ignore: if config.config_columns_to_ignore:
@ -832,6 +855,7 @@ def get_cc_columns():
cc = tmpcc cc = tmpcc
return cc return cc
def get_download_link(book_id, book_format): def get_download_link(book_id, book_format):
book_format = book_format.split(".")[0] book_format = book_format.split(".")[0]
book = db.session.query(db.Books).filter(db.Books.id == book_id).filter(common_filters()).first() book = db.session.query(db.Books).filter(db.Books.id == book_id).filter(common_filters()).first()
@ -856,6 +880,7 @@ def get_download_link(book_id, book_format):
else: else:
abort(404) abort(404)
def check_exists_book(authr, title): def check_exists_book(authr, title):
db.session.connection().connection.connection.create_function("lower", 1, lcase) db.session.connection().connection.connection.create_function("lower", 1, lcase)
q = list() q = list()
@ -870,6 +895,7 @@ def check_exists_book(authr,title):
############### Database Helper functions ############### Database Helper functions
def lcase(s): def lcase(s):
try: try:
return unidecode.unidecode(s.lower()) return unidecode.unidecode(s.lower())

View File

@ -80,9 +80,13 @@ def formatdate_filter(val):
formatdate = datetime.datetime.strptime(conformed_timestamp[:15], "%Y%m%d %H%M%S") formatdate = datetime.datetime.strptime(conformed_timestamp[:15], "%Y%m%d %H%M%S")
return format_date(formatdate, format='medium', locale=get_locale()) return format_date(formatdate, format='medium', locale=get_locale())
except AttributeError as e: except AttributeError as e:
log.error('Babel error: %s, Current user locale: %s, Current User: %s', e, current_user.locale, current_user.nickname) log.error('Babel error: %s, Current user locale: %s, Current User: %s', e,
current_user.locale,
current_user.nickname
)
return formatdate return formatdate
@jinjia.app_template_filter('formatdateinput') @jinjia.app_template_filter('formatdateinput')
def format_date_input(val): def format_date_input(val):
conformed_timestamp = re.sub(r"[:]|([-](?!((\d{2}[:]\d{2})|(\d{4}))$))", '', val) conformed_timestamp = re.sub(r"[:]|([-](?!((\d{2}[:]\d{2})|(\d{4}))$))", '', val)

View File

@ -385,7 +385,7 @@ def get_metadata(book):
name = get_series(book) name = get_series(book)
metadata["Series"] = { metadata["Series"] = {
"Name": get_series(book), "Name": get_series(book),
"Number": book.series_index, "Number": book.series_index, # ToDo Check int() ?
"NumberFloat": float(book.series_index), "NumberFloat": float(book.series_index),
# Get a deterministic id based on the series name. # Get a deterministic id based on the series name.
"Id": uuid.uuid3(uuid.NAMESPACE_DNS, name), "Id": uuid.uuid3(uuid.NAMESPACE_DNS, name),
@ -407,8 +407,10 @@ def HandleTagCreate():
log.debug("Received malformed v1/library/tags request.") log.debug("Received malformed v1/library/tags request.")
abort(400, description="Malformed tags POST request. Data is missing 'Name' or 'Items' field") abort(400, description="Malformed tags POST request. Data is missing 'Name' or 'Items' field")
# ToDO: Names are not unique ! -> filter only private shelfs
shelf = ub.session.query(ub.Shelf).filter(and_(ub.Shelf.name) == name, ub.Shelf.user_id == shelf = ub.session.query(ub.Shelf).filter(and_(ub.Shelf.name) == name, ub.Shelf.user_id ==
current_user.id).one_or_none() current_user.id).one_or_none() # ToDO: shouldn't it ) at the end
if shelf and not shelf_lib.check_shelf_edit_permissions(shelf): if shelf and not shelf_lib.check_shelf_edit_permissions(shelf):
abort(401, description="User is unauthaurized to edit shelf.") abort(401, description="User is unauthaurized to edit shelf.")
@ -517,6 +519,7 @@ def HandleTagRemoveItem(tag_id):
log.debug("Received malformed v1/library/tags/<tag_id>/items/delete request.") log.debug("Received malformed v1/library/tags/<tag_id>/items/delete request.")
abort(400, description="Malformed tags POST request. Data is missing 'Items' field") abort(400, description="Malformed tags POST request. Data is missing 'Items' field")
# insconsitent to above requests
shelf = ub.session.query(ub.Shelf).filter(ub.Shelf.uuid == tag_id, shelf = ub.session.query(ub.Shelf).filter(ub.Shelf.uuid == tag_id,
ub.Shelf.user_id == current_user.id).one_or_none() ub.Shelf.user_id == current_user.id).one_or_none()
if not shelf: if not shelf:
@ -552,7 +555,8 @@ def HandleTagRemoveItem(tag_id):
def sync_shelves(sync_token, sync_results): def sync_shelves(sync_token, sync_results):
new_tags_last_modified = sync_token.tags_last_modified new_tags_last_modified = sync_token.tags_last_modified
for shelf in ub.session.query(ub.ShelfArchive).filter(func.datetime(ub.ShelfArchive.last_modified) > sync_token.tags_last_modified, ub.ShelfArchive.user_id == current_user.id): for shelf in ub.session.query(ub.ShelfArchive).filter(func.datetime(ub.ShelfArchive.last_modified) > sync_token.tags_last_modified,
ub.ShelfArchive.user_id == current_user.id):
new_tags_last_modified = max(shelf.last_modified, new_tags_last_modified) new_tags_last_modified = max(shelf.last_modified, new_tags_last_modified)
sync_results.append({ sync_results.append({
@ -564,7 +568,8 @@ def sync_shelves(sync_token, sync_results):
} }
}) })
for shelf in ub.session.query(ub.Shelf).filter(func.datetime(ub.Shelf.last_modified) > sync_token.tags_last_modified, ub.Shelf.user_id == current_user.id): for shelf in ub.session.query(ub.Shelf).filter(func.datetime(ub.Shelf.last_modified) > sync_token.tags_last_modified,
ub.Shelf.user_id == current_user.id):
if not shelf_lib.check_shelf_view_permissions(shelf): if not shelf_lib.check_shelf_view_permissions(shelf):
continue continue
@ -600,6 +605,7 @@ def create_kobo_tag(shelf):
book = db.session.query(db.Books).filter(db.Books.id == book_shelf.book_id).one_or_none() book = db.session.query(db.Books).filter(db.Books.id == book_shelf.book_id).one_or_none()
if not book: if not book:
log.info(u"Book (id: %s) in BookShelf (id: %s) not found in book database", book_shelf.book_id, shelf.id) log.info(u"Book (id: %s) in BookShelf (id: %s) not found in book database", book_shelf.book_id, shelf.id)
# ToDo shouldn't it continue?
return None return None
tag["Items"].append( tag["Items"].append(
{ {
@ -769,7 +775,8 @@ def HandleCoverImageRequest(book_uuid, width, height,Quality, isGreyscale):
height=height), 307) height=height), 307)
else: else:
log.debug("Cover for unknown book: %s requested" % book_uuid) log.debug("Cover for unknown book: %s requested" % book_uuid)
return redirect_or_proxy_request() # additional proxy request make no sense, -> direct return
return make_response(jsonify({}))
log.debug("Cover request received for book %s" % book_uuid) log.debug("Cover request received for book %s" % book_uuid)
return book_cover return book_cover

View File

@ -56,7 +56,7 @@ def requires_basic_auth_if_no_ano(f):
return decorated return decorated
class FeedObject(): class FeedObject:
def __init__(self, rating_id, rating_name): def __init__(self, rating_id, rating_name):
self.rating_id = rating_id self.rating_id = rating_id
self.rating_name = rating_name self.rating_name = rating_name
@ -119,7 +119,8 @@ def feed_discover():
def feed_best_rated(): def feed_best_rated():
off = request.args.get("offset") or 0 off = request.args.get("offset") or 0
entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1),
db.Books, db.Books.ratings.any(db.Ratings.rating > 9), [db.Books.timestamp.desc()]) db.Books, db.Books.ratings.any(db.Ratings.rating > 9),
[db.Books.timestamp.desc()])
return render_xml_template('feed.xml', entries=entries, pagination=pagination) return render_xml_template('feed.xml', entries=entries, pagination=pagination)
@ -153,7 +154,8 @@ def feed_hot():
def feed_authorindex(): def feed_authorindex():
off = request.args.get("offset") or 0 off = request.args.get("offset") or 0
entries = db.session.query(db.Authors).join(db.books_authors_link).join(db.Books).filter(common_filters())\ entries = db.session.query(db.Authors).join(db.books_authors_link).join(db.Books).filter(common_filters())\
.group_by(text('books_authors_link.author')).order_by(db.Authors.sort).limit(config.config_books_per_page).offset(off) .group_by(text('books_authors_link.author')).order_by(db.Authors.sort).limit(config.config_books_per_page)\
.offset(off)
pagination = Pagination((int(off) / (int(config.config_books_per_page)) + 1), config.config_books_per_page, pagination = Pagination((int(off) / (int(config.config_books_per_page)) + 1), config.config_books_per_page,
len(db.session.query(db.Authors).all())) len(db.session.query(db.Authors).all()))
return render_xml_template('feed.xml', listelements=entries, folder='opds.feed_author', pagination=pagination) return render_xml_template('feed.xml', listelements=entries, folder='opds.feed_author', pagination=pagination)
@ -164,7 +166,9 @@ def feed_authorindex():
def feed_author(book_id): def feed_author(book_id):
off = request.args.get("offset") or 0 off = request.args.get("offset") or 0
entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1),
db.Books, db.Books.authors.any(db.Authors.id == book_id), [db.Books.timestamp.desc()]) db.Books,
db.Books.authors.any(db.Authors.id == book_id),
[db.Books.timestamp.desc()])
return render_xml_template('feed.xml', entries=entries, pagination=pagination) return render_xml_template('feed.xml', entries=entries, pagination=pagination)
@ -173,7 +177,8 @@ def feed_author(book_id):
def feed_publisherindex(): def feed_publisherindex():
off = request.args.get("offset") or 0 off = request.args.get("offset") or 0
entries = db.session.query(db.Publishers).join(db.books_publishers_link).join(db.Books).filter(common_filters())\ entries = db.session.query(db.Publishers).join(db.books_publishers_link).join(db.Books).filter(common_filters())\
.group_by(text('books_publishers_link.publisher')).order_by(db.Publishers.sort).limit(config.config_books_per_page).offset(off) .group_by(text('books_publishers_link.publisher')).order_by(db.Publishers.sort)\
.limit(config.config_books_per_page).offset(off)
pagination = Pagination((int(off) / (int(config.config_books_per_page)) + 1), config.config_books_per_page, pagination = Pagination((int(off) / (int(config.config_books_per_page)) + 1), config.config_books_per_page,
len(db.session.query(db.Publishers).all())) len(db.session.query(db.Publishers).all()))
return render_xml_template('feed.xml', listelements=entries, folder='opds.feed_publisher', pagination=pagination) return render_xml_template('feed.xml', listelements=entries, folder='opds.feed_publisher', pagination=pagination)
@ -184,7 +189,8 @@ def feed_publisherindex():
def feed_publisher(book_id): def feed_publisher(book_id):
off = request.args.get("offset") or 0 off = request.args.get("offset") or 0
entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1),
db.Books, db.Books.publishers.any(db.Publishers.id == book_id), db.Books,
db.Books.publishers.any(db.Publishers.id == book_id),
[db.Books.timestamp.desc()]) [db.Books.timestamp.desc()])
return render_xml_template('feed.xml', entries=entries, pagination=pagination) return render_xml_template('feed.xml', entries=entries, pagination=pagination)
@ -205,7 +211,9 @@ def feed_categoryindex():
def feed_category(book_id): def feed_category(book_id):
off = request.args.get("offset") or 0 off = request.args.get("offset") or 0
entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1),
db.Books, db.Books.tags.any(db.Tags.id == book_id), [db.Books.timestamp.desc()]) db.Books,
db.Books.tags.any(db.Tags.id == book_id),
[db.Books.timestamp.desc()])
return render_xml_template('feed.xml', entries=entries, pagination=pagination) return render_xml_template('feed.xml', entries=entries, pagination=pagination)
@ -225,9 +233,12 @@ def feed_seriesindex():
def feed_series(book_id): def feed_series(book_id):
off = request.args.get("offset") or 0 off = request.args.get("offset") or 0
entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1),
db.Books, db.Books.series.any(db.Series.id == book_id), [db.Books.series_index]) db.Books,
db.Books.series.any(db.Series.id == book_id),
[db.Books.series_index])
return render_xml_template('feed.xml', entries=entries, pagination=pagination) return render_xml_template('feed.xml', entries=entries, pagination=pagination)
@opds.route("/opds/ratings") @opds.route("/opds/ratings")
@requires_basic_auth_if_no_ano @requires_basic_auth_if_no_ano
def feed_ratingindex(): def feed_ratingindex():
@ -244,16 +255,18 @@ def feed_ratingindex():
element.append(FeedObject(entry[0].id, "{} Stars".format(entry.name))) element.append(FeedObject(entry[0].id, "{} Stars".format(entry.name)))
return render_xml_template('feed.xml', listelements=element, folder='opds.feed_ratings', pagination=pagination) return render_xml_template('feed.xml', listelements=element, folder='opds.feed_ratings', pagination=pagination)
@opds.route("/opds/ratings/<book_id>") @opds.route("/opds/ratings/<book_id>")
@requires_basic_auth_if_no_ano @requires_basic_auth_if_no_ano
def feed_ratings(book_id): def feed_ratings(book_id):
off = request.args.get("offset") or 0 off = request.args.get("offset") or 0
entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1),
db.Books, db.Books.ratings.any(db.Ratings.id == book_id),[db.Books.timestamp.desc()]) db.Books,
db.Books.ratings.any(db.Ratings.id == book_id),
[db.Books.timestamp.desc()])
return render_xml_template('feed.xml', entries=entries, pagination=pagination) return render_xml_template('feed.xml', entries=entries, pagination=pagination)
@opds.route("/opds/formats") @opds.route("/opds/formats")
@requires_basic_auth_if_no_ano @requires_basic_auth_if_no_ano
def feed_formatindex(): def feed_formatindex():
@ -274,7 +287,9 @@ def feed_formatindex():
def feed_format(book_id): def feed_format(book_id):
off = request.args.get("offset") or 0 off = request.args.get("offset") or 0
entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1),
db.Books, db.Books.data.any(db.Data.format == book_id.upper()), [db.Books.timestamp.desc()]) db.Books,
db.Books.data.any(db.Data.format == book_id.upper()),
[db.Books.timestamp.desc()])
return render_xml_template('feed.xml', entries=entries, pagination=pagination) return render_xml_template('feed.xml', entries=entries, pagination=pagination)
@ -306,7 +321,9 @@ def feed_languagesindex():
def feed_languages(book_id): def feed_languages(book_id):
off = request.args.get("offset") or 0 off = request.args.get("offset") or 0
entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1),
db.Books, db.Books.languages.any(db.Languages.id == book_id), [db.Books.timestamp.desc()]) db.Books,
db.Books.languages.any(db.Languages.id == book_id),
[db.Books.timestamp.desc()])
return render_xml_template('feed.xml', entries=entries, pagination=pagination) return render_xml_template('feed.xml', entries=entries, pagination=pagination)
@ -326,7 +343,8 @@ def feed_shelfindex():
def feed_shelf(book_id): def feed_shelf(book_id):
off = request.args.get("offset") or 0 off = request.args.get("offset") or 0
if current_user.is_anonymous: if current_user.is_anonymous:
shelf = ub.session.query(ub.Shelf).filter(ub.Shelf.is_public == 1, ub.Shelf.id == book_id, not ub.Shelf.deleted).first() shelf = ub.session.query(ub.Shelf).filter(ub.Shelf.is_public == 1,
ub.Shelf.id == book_id, not ub.Shelf.deleted).first()
else: else:
shelf = ub.session.query(ub.Shelf).filter(or_(and_(ub.Shelf.user_id == int(current_user.id), shelf = ub.session.query(ub.Shelf).filter(or_(and_(ub.Shelf.user_id == int(current_user.id),
ub.Shelf.id == book_id), ub.Shelf.id == book_id),
@ -376,6 +394,7 @@ def feed_search(term):
else: else:
return render_xml_template('feed.xml', searchterm="") return render_xml_template('feed.xml', searchterm="")
def check_auth(username, password): def check_auth(username, password):
if sys.version_info.major == 3: if sys.version_info.major == 3:
username = username.encode('windows-1252') username = username.encode('windows-1252')
@ -399,6 +418,7 @@ def render_xml_template(*args, **kwargs):
response.headers["Content-Type"] = "application/atom+xml; charset=utf-8" response.headers["Content-Type"] = "application/atom+xml; charset=utf-8"
return response return response
@opds.route("/opds/thumb_240_240/<book_id>") @opds.route("/opds/thumb_240_240/<book_id>")
@opds.route("/opds/cover_240_240/<book_id>") @opds.route("/opds/cover_240_240/<book_id>")
@opds.route("/opds/cover_90_90/<book_id>") @opds.route("/opds/cover_90_90/<book_id>")
@ -407,6 +427,7 @@ def render_xml_template(*args, **kwargs):
def feed_get_cover(book_id): def feed_get_cover(book_id):
return get_book_cover(book_id) return get_book_cover(book_id)
@opds.route("/opds/readbooks") @opds.route("/opds/readbooks")
@requires_basic_auth_if_no_ano @requires_basic_auth_if_no_ano
def feed_read_books(): def feed_read_books():
@ -414,6 +435,7 @@ def feed_read_books():
result, pagination = render_read_books(int(off) / (int(config.config_books_per_page)) + 1, True, True) result, pagination = render_read_books(int(off) / (int(config.config_books_per_page)) + 1, True, True)
return render_xml_template('feed.xml', entries=result, pagination=pagination) return render_xml_template('feed.xml', entries=result, pagination=pagination)
@opds.route("/opds/unreadbooks") @opds.route("/opds/unreadbooks")
@requires_basic_auth_if_no_ano @requires_basic_auth_if_no_ano
def feed_unread_books(): def feed_unread_books():

View File

@ -43,7 +43,6 @@ from . import logger
log = logger.create() log = logger.create()
def _readable_listen_address(address, port): def _readable_listen_address(address, port):
if ':' in address: if ':' in address:
address = "[" + address + "]" address = "[" + address + "]"
@ -84,7 +83,8 @@ class WebServer(object):
if os.path.isfile(certfile_path) and os.path.isfile(keyfile_path): if os.path.isfile(certfile_path) and os.path.isfile(keyfile_path):
self.ssl_args = dict(certfile=certfile_path, keyfile=keyfile_path) self.ssl_args = dict(certfile=certfile_path, keyfile=keyfile_path)
else: else:
log.warning('The specified paths for the ssl certificate file and/or key file seem to be broken. Ignoring ssl.') log.warning('The specified paths for the ssl certificate file and/or key file seem to be broken. '
'Ignoring ssl.')
log.warning('Cert path: %s', certfile_path) log.warning('Cert path: %s', certfile_path)
log.warning('Key path: %s', keyfile_path) log.warning('Key path: %s', keyfile_path)

View File

@ -49,10 +49,11 @@ def get_datetime_from_json(json_object, field_name):
return datetime.min return datetime.min
class SyncToken(): class SyncToken:
""" The SyncToken is used to persist state accross requests. """ The SyncToken is used to persist state accross requests.
When serialized over the response headers, the Kobo device will propagate the token onto following requests to the service. When serialized over the response headers, the Kobo device will propagate the token onto following
As an example use-case, the SyncToken is used to detect books that have been added to the library since the last time the device synced to the server. requests to the service. As an example use-case, the SyncToken is used to detect books that have been added
to the library since the last time the device synced to the server.
Attributes: Attributes:
books_last_created: Datetime representing the newest book that the device knows about. books_last_created: Datetime representing the newest book that the device knows about.
@ -69,7 +70,8 @@ class SyncToken():
"properties": {"version": {"type": "string"}, "data": {"type": "object"}, }, "properties": {"version": {"type": "string"}, "data": {"type": "object"}, },
} }
# This Schema doesn't contain enough information to detect and propagate book deletions from Calibre to the device. # This Schema doesn't contain enough information to detect and propagate book deletions from Calibre to the device.
# A potential solution might be to keep a list of all known book uuids in the token, and look for any missing from the db. # A potential solution might be to keep a list of all known book uuids in the token, and look for any missing
# from the db.
data_schema_v1 = { data_schema_v1 = {
"type": "object", "type": "object",
"properties": { "properties": {

View File

@ -25,7 +25,7 @@ from __future__ import division, print_function, unicode_literals
from flask import Blueprint, request, flash, redirect, url_for from flask import Blueprint, request, flash, redirect, url_for
from flask_babel import gettext as _ from flask_babel import gettext as _
from flask_login import login_required, current_user from flask_login import login_required, current_user
from sqlalchemy.sql.expression import func, or_, and_ from sqlalchemy.sql.expression import func
from . import logger, ub, searched_ids, db from . import logger, ub, searched_ids, db
from .web import render_title_template from .web import render_title_template
@ -35,6 +35,7 @@ from .helper import common_filters
shelf = Blueprint('shelf', __name__) shelf = Blueprint('shelf', __name__)
log = logger.create() log = logger.create()
def check_shelf_edit_permissions(cur_shelf): def check_shelf_edit_permissions(cur_shelf):
if not cur_shelf.is_public and not cur_shelf.user_id == int(current_user.id): if not cur_shelf.is_public and not cur_shelf.user_id == int(current_user.id):
log.error("User %s not allowed to edit shelf %s", current_user, cur_shelf) log.error("User %s not allowed to edit shelf %s", current_user, cur_shelf)
@ -195,7 +196,6 @@ def remove_from_shelf(shelf_id, book_id):
return "Sorry you are not allowed to remove a book from this shelf: %s" % shelf.name, 403 return "Sorry you are not allowed to remove a book from this shelf: %s" % shelf.name, 403
@shelf.route("/shelf/create", methods=["GET", "POST"]) @shelf.route("/shelf/create", methods=["GET", "POST"])
@login_required @login_required
def create_shelf(): def create_shelf():
@ -214,14 +214,17 @@ def create_shelf():
.first() is None .first() is None
if not is_shelf_name_unique: if not is_shelf_name_unique:
flash(_(u"A public shelf with the name '%(title)s' already exists.", title=to_save["title"]), category="error") flash(_(u"A public shelf with the name '%(title)s' already exists.", title=to_save["title"]),
category="error")
else: else:
is_shelf_name_unique = ub.session.query(ub.Shelf) \ is_shelf_name_unique = ub.session.query(ub.Shelf) \
.filter((ub.Shelf.name == to_save["title"]) & (ub.Shelf.is_public == 0) & (ub.Shelf.user_id == int(current_user.id))) \ .filter((ub.Shelf.name == to_save["title"]) & (ub.Shelf.is_public == 0) &
(ub.Shelf.user_id == int(current_user.id)))\
.first() is None .first() is None
if not is_shelf_name_unique: if not is_shelf_name_unique:
flash(_(u"A private shelf with the name '%(title)s' already exists.", title=to_save["title"]), category="error") flash(_(u"A private shelf with the name '%(title)s' already exists.", title=to_save["title"]),
category="error")
if is_shelf_name_unique: if is_shelf_name_unique:
try: try:
@ -251,15 +254,18 @@ def edit_shelf(shelf_id):
.first() is None .first() is None
if not is_shelf_name_unique: if not is_shelf_name_unique:
flash(_(u"A public shelf with the name '%(title)s' already exists.", title=to_save["title"]), category="error") flash(_(u"A public shelf with the name '%(title)s' already exists.", title=to_save["title"]),
category="error")
else: else:
is_shelf_name_unique = ub.session.query(ub.Shelf) \ is_shelf_name_unique = ub.session.query(ub.Shelf) \
.filter((ub.Shelf.name == to_save["title"]) & (ub.Shelf.is_public == 0) & (ub.Shelf.user_id == int(current_user.id))) \ .filter((ub.Shelf.name == to_save["title"]) & (ub.Shelf.is_public == 0) &
(ub.Shelf.user_id == int(current_user.id)))\
.filter(ub.Shelf.id != shelf_id)\ .filter(ub.Shelf.id != shelf_id)\
.first() is None .first() is None
if not is_shelf_name_unique: if not is_shelf_name_unique:
flash(_(u"A private shelf with the name '%(title)s' already exists.", title=to_save["title"]), category="error") flash(_(u"A private shelf with the name '%(title)s' already exists.", title=to_save["title"]),
category="error")
if is_shelf_name_unique: if is_shelf_name_unique:
shelf.name = to_save["title"] shelf.name = to_save["title"]
@ -295,7 +301,7 @@ def delete_shelf(shelf_id):
delete_shelf_helper(cur_shelf) delete_shelf_helper(cur_shelf)
return redirect(url_for('web.index')) return redirect(url_for('web.index'))
# @shelf.route("/shelfdown/<int:shelf_id>")
@shelf.route("/shelf/<int:shelf_id>", defaults={'shelf_type': 1}) @shelf.route("/shelf/<int:shelf_id>", defaults={'shelf_type': 1})
@shelf.route("/shelf/<int:shelf_id>/<int:shelf_type>") @shelf.route("/shelf/<int:shelf_id>/<int:shelf_type>")
def show_shelf(shelf_type, shelf_id): def show_shelf(shelf_type, shelf_id):
@ -325,7 +331,6 @@ def show_shelf(shelf_type, shelf_id):
return redirect(url_for("web.index")) return redirect(url_for("web.index"))
@shelf.route("/shelf/order/<int:shelf_id>", methods=["GET", "POST"]) @shelf.route("/shelf/order/<int:shelf_id>", methods=["GET", "POST"])
@login_required @login_required
def order_shelf(shelf_id): def order_shelf(shelf_id):

View File

@ -45,10 +45,10 @@ def process_open(command, quotes=(), env=None, sout=subprocess.PIPE, serr=subpro
def process_wait(command, serr=subprocess.PIPE): def process_wait(command, serr=subprocess.PIPE):
'''Run command, wait for process to terminate, and return an iterator over lines of its output.''' # Run command, wait for process to terminate, and return an iterator over lines of its output.
p = process_open(command, serr=serr) p = process_open(command, serr=serr)
p.wait() p.wait()
for l in p.stdout.readlines(): for line in p.stdout.readlines():
if isinstance(l, bytes): if isinstance(line, bytes):
l = l.decode('utf-8') line = line.decode('utf-8')
yield l yield line

View File

@ -4,18 +4,18 @@
<div class="filterheader hidden-xs hidden-sm"> <div class="filterheader hidden-xs hidden-sm">
{% if entries.__len__() %} {% if entries.__len__() %}
{% if entries[0][0].sort %} {% if data == 'author' %}
<button id="sort_name" class="btn btn-success"><b>B,A <-> A B</b></button> <button id="sort_name" class="btn btn-primary"><b>B,A <-> A B</b></button>
{% endif %} {% endif %}
{% endif %} {% endif %}
<button id="desc" class="btn btn-success"><span class="glyphicon glyphicon-sort-by-alphabet"></span></button> <button id="desc" class="btn btn-primary"><span class="glyphicon glyphicon-sort-by-alphabet"></span></button>
<button id="asc" class="btn btn-success"><span class="glyphicon glyphicon-sort-by-alphabet-alt"></span></button> <button id="asc" class="btn btn-primary"><span class="glyphicon glyphicon-sort-by-alphabet-alt"></span></button>
{% if charlist|length %} {% if charlist|length %}
<button id="all" class="btn btn-success">{{_('All')}}</button> <button id="all" class="btn btn-primary">{{_('All')}}</button>
{% endif %} {% endif %}
<div class="btn-group character" role="group"> <div class="btn-group character" role="group">
{% for char in charlist%} {% for char in charlist%}
<button class="btn btn-success char">{{char.char}}</button> <button class="btn btn-primary char">{{char.char}}</button>
{% endfor %} {% endfor %}
</div> </div>
</div> </div>

View File

@ -42,11 +42,10 @@ from sqlalchemy import create_engine, exc, exists, event
from sqlalchemy import Column, ForeignKey from sqlalchemy import Column, ForeignKey
from sqlalchemy import String, Integer, SmallInteger, Boolean, DateTime, Float from sqlalchemy import String, Integer, SmallInteger, Boolean, DateTime, Float
from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import backref, foreign, relationship, remote, sessionmaker, Session from sqlalchemy.orm import backref, relationship, sessionmaker, Session
from sqlalchemy.sql.expression import and_
from werkzeug.security import generate_password_hash from werkzeug.security import generate_password_hash
from . import constants # , config from . import constants
session = None session = None
@ -65,8 +64,8 @@ def get_sidebar_config(kwargs=None):
"visibility": constants.SIDEBAR_RECENT, 'public': True, "page": "root", "visibility": constants.SIDEBAR_RECENT, 'public': True, "page": "root",
"show_text": _('Show recent books'), "config_show": True}) "show_text": _('Show recent books'), "config_show": True})
sidebar.append({"glyph": "glyphicon-fire", "text": _('Hot Books'), "link": 'web.books_list', "id": "hot", sidebar.append({"glyph": "glyphicon-fire", "text": _('Hot Books'), "link": 'web.books_list', "id": "hot",
"visibility": constants.SIDEBAR_HOT, 'public': True, "page": "hot", "show_text": _('Show Hot Books'), "visibility": constants.SIDEBAR_HOT, 'public': True, "page": "hot",
"config_show":True}) "show_text": _('Show Hot Books'), "config_show": True})
sidebar.append( sidebar.append(
{"glyph": "glyphicon-star", "text": _('Top Rated Books'), "link": 'web.books_list', "id": "rated", {"glyph": "glyphicon-star", "text": _('Top Rated Books'), "link": 'web.books_list', "id": "rated",
"visibility": constants.SIDEBAR_BEST_RATED, 'public': True, "page": "rated", "visibility": constants.SIDEBAR_BEST_RATED, 'public': True, "page": "rated",
@ -236,7 +235,8 @@ class Anonymous(AnonymousUserMixin, UserBase):
self.loadSettings() self.loadSettings()
def loadSettings(self): def loadSettings(self):
data = session.query(User).filter(User.role.op('&')(constants.ROLE_ANONYMOUS) == constants.ROLE_ANONYMOUS).first() # type: User data = session.query(User).filter(User.role.op('&')(constants.ROLE_ANONYMOUS) == constants.ROLE_ANONYMOUS)\
.first() # type: User
self.nickname = data.nickname self.nickname = data.nickname
self.role = data.role self.role = data.role
self.id=data.id self.id=data.id
@ -259,7 +259,7 @@ class Anonymous(AnonymousUserMixin, UserBase):
@property @property
def is_anonymous(self): def is_anonymous(self):
return True # self.anon_browse return True
@property @property
def is_authenticated(self): def is_authenticated(self):
@ -318,8 +318,12 @@ class ReadBook(Base):
book_id = Column(Integer, unique=False) book_id = Column(Integer, unique=False)
user_id = Column(Integer, ForeignKey('user.id'), unique=False) user_id = Column(Integer, ForeignKey('user.id'), unique=False)
read_status = Column(Integer, unique=False, default=STATUS_UNREAD, nullable=False) read_status = Column(Integer, unique=False, default=STATUS_UNREAD, nullable=False)
kobo_reading_state = relationship("KoboReadingState", uselist=False, primaryjoin="and_(ReadBook.user_id == foreign(KoboReadingState.user_id), " kobo_reading_state = relationship("KoboReadingState", uselist=False,
"ReadBook.book_id == foreign(KoboReadingState.book_id))", cascade="all", backref=backref("book_read_link", uselist=False)) primaryjoin="and_(ReadBook.user_id == foreign(KoboReadingState.user_id), "
"ReadBook.book_id == foreign(KoboReadingState.book_id))",
cascade="all",
backref=backref("book_read_link",
uselist=False))
last_modified = Column(DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow) last_modified = Column(DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow)
last_time_started_reading = Column(DateTime, nullable=True) last_time_started_reading = Column(DateTime, nullable=True)
times_started_reading = Column(Integer, default=0, nullable=False) times_started_reading = Column(Integer, default=0, nullable=False)
@ -334,6 +338,7 @@ class Bookmark(Base):
format = Column(String(collation='NOCASE')) format = Column(String(collation='NOCASE'))
bookmark_key = Column(String) bookmark_key = Column(String)
# Baseclass representing books that are archived on the user's Kobo device. # Baseclass representing books that are archived on the user's Kobo device.
class ArchivedBook(Base): class ArchivedBook(Base):
__tablename__ = 'archived_book' __tablename__ = 'archived_book'
@ -421,7 +426,6 @@ class Registration(Base):
return u"<Registration('{0}')>".format(self.domain) return u"<Registration('{0}')>".format(self.domain)
class RemoteAuthToken(Base): class RemoteAuthToken(Base):
__tablename__ = 'remote_auth_token' __tablename__ = 'remote_auth_token'
@ -532,18 +536,12 @@ def migrate_Database(session):
conn = engine.connect() conn = engine.connect()
conn.execute("UPDATE user SET 'sidebar_view' = (random_books* :side_random + language_books * :side_lang " conn.execute("UPDATE user SET 'sidebar_view' = (random_books* :side_random + language_books * :side_lang "
"+ series_books * :side_series + category_books * :side_category + hot_books * " "+ series_books * :side_series + category_books * :side_category + hot_books * "
":side_hot + :side_autor + :detail_random)" ":side_hot + :side_autor + :detail_random)",
,{'side_random': constants.SIDEBAR_RANDOM, 'side_lang': constants.SIDEBAR_LANGUAGE, {'side_random': constants.SIDEBAR_RANDOM, 'side_lang': constants.SIDEBAR_LANGUAGE,
'side_series': constants.SIDEBAR_SERIES, 'side_series': constants.SIDEBAR_SERIES, 'side_category': constants.SIDEBAR_CATEGORY,
'side_category': constants.SIDEBAR_CATEGORY, 'side_hot': constants.SIDEBAR_HOT, 'side_hot': constants.SIDEBAR_HOT, 'side_autor': constants.SIDEBAR_AUTHOR,
'side_autor': constants.SIDEBAR_AUTHOR,
'detail_random': constants.DETAIL_RANDOM}) 'detail_random': constants.DETAIL_RANDOM})
session.commit() session.commit()
'''try:
session.query(exists().where(User.mature_content)).scalar()
except exc.OperationalError:
conn = engine.connect()
conn.execute("ALTER TABLE user ADD column `mature_content` INTEGER DEFAULT 1")'''
try: try:
session.query(exists().where(User.denied_tags)).scalar() session.query(exists().where(User.denied_tags)).scalar()
except exc.OperationalError: # Database is not compatible, some columns are missing except exc.OperationalError: # Database is not compatible, some columns are missing
@ -552,7 +550,8 @@ def migrate_Database(session):
conn.execute("ALTER TABLE user ADD column `allowed_tags` String DEFAULT ''") conn.execute("ALTER TABLE user ADD column `allowed_tags` String DEFAULT ''")
conn.execute("ALTER TABLE user ADD column `denied_column_value` DEFAULT ''") conn.execute("ALTER TABLE user ADD column `denied_column_value` DEFAULT ''")
conn.execute("ALTER TABLE user ADD column `allowed_column_value` DEFAULT ''") conn.execute("ALTER TABLE user ADD column `allowed_column_value` DEFAULT ''")
if session.query(User).filter(User.role.op('&')(constants.ROLE_ANONYMOUS) == constants.ROLE_ANONYMOUS).first() is None: if session.query(User).filter(User.role.op('&')(constants.ROLE_ANONYMOUS) == constants.ROLE_ANONYMOUS).first() \
is None:
create_anonymous_user(session) create_anonymous_user(session)
try: try:
# check if one table with autoincrement is existing (should be user table) # check if one table with autoincrement is existing (should be user table)
@ -597,19 +596,20 @@ def clean_database(session):
# Save downloaded books per user in calibre-web's own database # Save downloaded books per user in calibre-web's own database
def update_download(book_id, user_id): def update_download(book_id, user_id):
check = session.query(Downloads).filter(Downloads.user_id == user_id).filter(Downloads.book_id == check = session.query(Downloads).filter(Downloads.user_id == user_id).filter(Downloads.book_id == book_id).first()
book_id).first()
if not check: if not check:
new_download = Downloads(user_id=user_id, book_id=book_id) new_download = Downloads(user_id=user_id, book_id=book_id)
session.add(new_download) session.add(new_download)
session.commit() session.commit()
# Delete non exisiting downloaded books in calibre-web's own database # Delete non exisiting downloaded books in calibre-web's own database
def delete_download(book_id): def delete_download(book_id):
session.query(Downloads).filter(book_id == Downloads.book_id).delete() session.query(Downloads).filter(book_id == Downloads.book_id).delete()
session.commit() session.commit()
# Generate user Guest (translated text), as anoymous user, no rights # Generate user Guest (translated text), as anoymous user, no rights
def create_anonymous_user(session): def create_anonymous_user(session):
user = User() user = User()
@ -667,8 +667,12 @@ def dispose():
old_session = session old_session = session
session = None session = None
if old_session: if old_session:
try: old_session.close() try:
except: pass old_session.close()
except Exception:
pass
if old_session.bind: if old_session.bind:
try: old_session.bind.dispose() try:
except: pass old_session.bind.dispose()
except Exception:
pass