calibre-web/cps/db.py
OzzieIsaacs edadf84710 Added editing star rating graphically
Added ability to reload database connection (refresh problem)
Bugfix display hot books (no hot books, hot books are deleted books, no longer result in error 500)
2017-03-07 19:10:17 +01:00

350 lines
12 KiB
Python
Executable File

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from sqlalchemy import *
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import *
import os
import re
import ast
from ub import config
import ub
session = None
cc_exceptions = ['datetime', 'int', 'comments', 'float', 'composite', 'series']
cc_classes = None
engine = None
# user defined sort function for calibre databases (Series, etc.)
def title_sort(title):
# calibre sort stuff
title_pat = re.compile(config.config_title_regex, re.IGNORECASE)
match = title_pat.search(title)
if match:
prep = match.group(1)
title = title.replace(prep, '') + ', ' + prep
return title.strip()
Base = declarative_base()
books_authors_link = Table('books_authors_link', Base.metadata,
Column('book', Integer, ForeignKey('books.id'), primary_key=True),
Column('author', Integer, ForeignKey('authors.id'), primary_key=True)
)
books_tags_link = Table('books_tags_link', Base.metadata,
Column('book', Integer, ForeignKey('books.id'), primary_key=True),
Column('tag', Integer, ForeignKey('tags.id'), primary_key=True)
)
books_series_link = Table('books_series_link', Base.metadata,
Column('book', Integer, ForeignKey('books.id'), primary_key=True),
Column('series', Integer, ForeignKey('series.id'), primary_key=True)
)
books_ratings_link = Table('books_ratings_link', Base.metadata,
Column('book', Integer, ForeignKey('books.id'), primary_key=True),
Column('rating', Integer, ForeignKey('ratings.id'), primary_key=True)
)
books_languages_link = Table('books_languages_link', Base.metadata,
Column('book', Integer, ForeignKey('books.id'), primary_key=True),
Column('lang_code', Integer, ForeignKey('languages.id'), primary_key=True)
)
books_publishers_link = Table('books_publishers_link', Base.metadata,
Column('book', Integer, ForeignKey('books.id'), primary_key=True),
Column('publisher', Integer, ForeignKey('publishers.id'), primary_key=True)
)
class Identifiers(Base):
__tablename__ = 'identifiers'
id = Column(Integer, primary_key=True)
type = Column(String)
val = Column(String)
book = Column(Integer, ForeignKey('books.id'))
def __init__(self, val, type, book):
self.val = val
self.type = type
self.book = book
def formatType(self):
if self.type == "amazon":
return u"Amazon"
elif self.type == "isbn":
return u"ISBN"
elif self.type == "doi":
return u"DOI"
elif self.type == "goodreads":
return u"Goodreads"
else:
return self.type
def __repr__(self):
if self.type == "amazon":
return u"https://amzn.com/{0}".format(self.val)
elif self.type == "isbn":
return u"http://www.worldcat.org/isbn/{0}".format(self.val)
elif self.type == "doi":
return u"http://dx.doi.org/{0}".format(self.val)
elif self.type == "goodreads":
return u"http://www.goodreads.com/book/show/{0}".format(self.val)
elif self.type == "douban":
return u"https://book.douban.com/subject/{0}".format(self.val)
else:
return u""
class Comments(Base):
__tablename__ = 'comments'
id = Column(Integer, primary_key=True)
text = Column(String)
book = Column(Integer, ForeignKey('books.id'))
def __init__(self, text, book):
self.text = text
self.book = book
def __repr__(self):
return u"<Comments({0})>".format(self.text)
class Tags(Base):
__tablename__ = 'tags'
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String)
def __init__(self, name):
self.name = name
def __repr__(self):
return u"<Tags('{0})>".format(self.name)
class Authors(Base):
__tablename__ = 'authors'
id = Column(Integer, primary_key=True)
name = Column(String)
sort = Column(String)
link = Column(String)
def __init__(self, name, sort, link):
self.name = name
self.sort = sort
self.link = link
def __repr__(self):
return u"<Authors('{0},{1}{2}')>".format(self.name, self.sort, self.link)
class Series(Base):
__tablename__ = 'series'
id = Column(Integer, primary_key=True)
name = Column(String)
sort = Column(String)
def __init__(self, name, sort):
self.name = name
self.sort = sort
def __repr__(self):
return u"<Series('{0},{1}')>".format(self.name, self.sort)
class Ratings(Base):
__tablename__ = 'ratings'
id = Column(Integer, primary_key=True)
rating = Column(Integer)
def __init__(self, rating):
self.rating = rating
def __repr__(self):
return u"<Ratings('{0}')>".format(self.rating)
class Languages(Base):
__tablename__ = 'languages'
id = Column(Integer, primary_key=True)
lang_code = Column(String)
def __init__(self, lang_code):
self.lang_code = lang_code
def __repr__(self):
return u"<Languages('{0}')>".format(self.lang_code)
class Publishers(Base):
__tablename__ = 'publishers'
id = Column(Integer, primary_key=True)
name = Column(String)
sort = Column(String)
def __init__(self, name,sort):
self.name = name
self.sort = sort
def __repr__(self):
return u"<Publishers('{0},{1}')>".format(self.name, self.sort)
class Data(Base):
__tablename__ = 'data'
id = Column(Integer, primary_key=True)
book = Column(Integer, ForeignKey('books.id'))
format = Column(String)
uncompressed_size = Column(Integer)
name = Column(String)
def __init__(self, book, format, uncompressed_size, name):
self.book = book
self.format = format
self.uncompressed_size = uncompressed_size
self.name = name
def __repr__(self):
return u"<Data('{0},{1}{2}{3}')>".format(self.book, self.format, self.uncompressed_size, self.name)
class Books(Base):
__tablename__ = 'books'
id = Column(Integer, primary_key=True)
title = Column(String)
sort = Column(String)
author_sort = Column(String)
timestamp = Column(String)
pubdate = Column(String)
series_index = Column(String)
last_modified = Column(String)
path = Column(String)
has_cover = Column(Integer)
uuid = Column(String)
authors = relationship('Authors', secondary=books_authors_link, backref='books')
tags = relationship('Tags', secondary=books_tags_link, backref='books')
comments = relationship('Comments', backref='books')
data = relationship('Data', backref='books')
series = relationship('Series', secondary=books_series_link, backref='books')
ratings = relationship('Ratings', secondary=books_ratings_link, backref='books')
languages = relationship('Languages', secondary=books_languages_link, backref='books')
publishers = relationship('Publishers', secondary=books_publishers_link, backref='books')
identifiers = relationship('Identifiers', backref='books')
def __init__(self, title, sort, author_sort, timestamp, pubdate, series_index, last_modified, path, has_cover,
authors, tags, languages = None):
self.title = title
self.sort = sort
self.author_sort = author_sort
self.timestamp = timestamp
self.pubdate = pubdate
self.series_index = series_index
self.last_modified = last_modified
self.path = path
self.has_cover = has_cover
def __repr__(self):
return u"<Books('{0},{1}{2}{3}{4}{5}{6}{7}{8}')>".format(self.title, self.sort, self.author_sort,
self.timestamp, self.pubdate, self.series_index,
self.last_modified, self.path, self.has_cover)
class Custom_Columns(Base):
__tablename__ = 'custom_columns'
id = Column(Integer, primary_key=True)
label = Column(String)
name = Column(String)
datatype = Column(String)
mark_for_delete = Column(Boolean)
editable = Column(Boolean)
display = Column(String)
is_multiple = Column(Boolean)
normalized = Column(Boolean)
def get_display_dict(self):
display_dict = ast.literal_eval(self.display)
return display_dict
def setup_db():
global engine
global session
global cc_classes
if config.config_calibre_dir is None or config.config_calibre_dir == u'':
return False
dbpath = os.path.join(config.config_calibre_dir, "metadata.db")
engine = create_engine('sqlite:///{0}'.format(dbpath.encode('utf-8')), echo=False, isolation_level="SERIALIZABLE")
try:
conn = engine.connect()
except:
content = ub.session.query(ub.Settings).first()
content.config_calibre_dir = None
content.db_configured = False
ub.session.commit()
config.loadSettings()
return False
content = ub.session.query(ub.Settings).first()
content.db_configured = True
ub.session.commit()
config.loadSettings()
conn.connection.create_function('title_sort', 1, title_sort)
if not cc_classes:
cc = conn.execute("SELECT id, datatype FROM custom_columns")
cc_ids = []
books_custom_column_links = {}
cc_classes = {}
for row in cc:
if row.datatype not in cc_exceptions:
books_custom_column_links[row.id] = Table('books_custom_column_' + str(row.id) + '_link', Base.metadata,
Column('book', Integer, ForeignKey('books.id'),
primary_key=True),
Column('value', Integer,
ForeignKey('custom_column_' + str(row.id) + '.id'),
primary_key=True)
)
cc_ids.append([row.id, row.datatype])
if row.datatype == 'bool':
ccdict = {'__tablename__': 'custom_column_' + str(row.id),
'id': Column(Integer, primary_key=True),
'book': Column(Integer, ForeignKey('books.id')),
'value': Column(Boolean)}
else:
ccdict = {'__tablename__': 'custom_column_' + str(row.id),
'id': Column(Integer, primary_key=True),
'value': Column(String)}
cc_classes[row.id] = type('Custom_Column_' + str(row.id), (Base,), ccdict)
for id in cc_ids:
if id[1] == 'bool':
setattr(Books, 'custom_column_' + str(id[0]), relationship(cc_classes[id[0]],
primaryjoin=(
Books.id == cc_classes[id[0]].book),
backref='books'))
else:
setattr(Books, 'custom_column_' + str(id[0]), relationship(cc_classes[id[0]],
secondary=books_custom_column_links[id[0]],
backref='books'))
# Base.metadata.create_all(engine)
Session = sessionmaker()
Session.configure(bind=engine)
session = Session()
return True