Start work on API documentation for the new database API

This commit is contained in:
Kovid Goyal 2014-07-04 22:12:57 +05:30
parent 00d3a5fad2
commit 041f92959c
3 changed files with 66 additions and 20 deletions

24
manual/db_api.rst Normal file
View File

@ -0,0 +1,24 @@
.. include:: global.rst
.. _db_api:
API Documentation for the database interface
======================================================
.. module:: calibre.db.cache
:synopsis: The API accessing and manipulating a calibre library.
This API is thread safe (it uses a multiple reader, single writer locking scheme). You can access this API like this::
from calibre.library import db
db = db('Path to calibre library folder').new_api
If you are in a calibre plugin that is part of the main calibre GUI, you
get access to it like this instead::
db = self.gui.current_db.new_api
.. autoclass:: Cache
:members:

View File

@ -61,8 +61,10 @@ All the |app| python code is in the ``calibre`` package. This package contains t
code for editing is in ``ebooks.oeb.polish`` in particular the
container object is in ``ebooks.oeb.polish.container``.
* db - The database back-end. See ``db.cache`` for the interface to the |app| library. With a DB object you can access this API via ``db.new_api``. The db object itself exposes a legacy API that should not be used in new code. The legacy API is in ``library.database2``.
* db - The database back-end. See :ref:`db_api` for the interface to the |app| library.
* content server: ``library.server`` is the |app| Content Server.
* gui2 - The Graphical User Interface. GUI initialization happens in ``gui2.main`` and ``gui2.ui``. The ebook-viewer is in ``gui2.viewer``. The ebook editor is in ``gui2.tweak_book``.
If you want to locate the entry points for all the various |app| executables,
@ -396,3 +398,8 @@ you can also directly import |app|, as follows::
It is essential that you import the init_calibre module before any other |app| modules/packages as
it sets up the interpreter to run |app| code.
.. toctree::
:hidden:
db_api

View File

@ -301,8 +301,6 @@ class Cache(object):
return mi
# }}}
# Cache Layer API {{{
@api
def init(self):
'''
@ -335,27 +333,25 @@ class Cache(object):
self.update_last_modified(self.all_book_ids())
self.backend.prefs.set('update_all_last_mod_dates_on_start', False)
# Cache Layer API {{{
@read_api
def field_for(self, name, book_id, default_value=None):
'''
Return the value of the field ``name`` for the book identified by
``book_id``. If no such book exists or it has no defined value for the
field ``name`` or no such field exists, then ``default_value`` is returned.
Return the value of the field ``name`` for the book identified
by ``book_id``. If no such book exists or it has no defined
value for the field ``name` or no such field exists, then
``default_value`` is returned.
default_value is not used for title, title_sort, authors, author_sort
``default_value`` is not used for title, title_sort, authors, author_sort
and series_index. This is because these always have values in the db.
default_value is used for all custom columns.
``default_value`` is used for all custom columns.
The returned value for is_multiple fields are always tuples, even when
no values are found (in other words, default_value is ignored). The
exception is identifiers for which the returned value is always a dict.
WARNING: For is_multiple fields this method returns tuples, the old
interface generally returned lists.
WARNING: For is_multiple fields the order of items is always in link
order (order in which they were entered), whereas the old db had them
in random order for fields other than author.
The returned tuples are always in link order, that is, the order in
which they were created.
'''
if self.composites and name in self.composites:
return self.composite_for(name, book_id,
@ -454,6 +450,8 @@ class Cache(object):
@read_api
def get_usage_count_by_id(self, field):
''' Return a mapping of id to usage count for all values of the specified
field, which must be a many-one or many-many field. '''
try:
return {k:len(v) for k, v in self.fields[field].table.col_book_map.iteritems()}
except AttributeError:
@ -461,6 +459,9 @@ class Cache(object):
@read_api
def get_id_map(self, field):
''' Return a mapping of id numbers to values for the specified field.
The field must be a many-one or many-many field, otherwise a ValueError
is raised. '''
try:
return self.fields[field].table.id_map.copy()
except AttributeError:
@ -470,6 +471,8 @@ class Cache(object):
@read_api
def get_item_name(self, field, item_id):
''' Return the item name for the item specified by item_id in the
specified field. See also :meth:`get_id_map`.'''
return self.fields[field].table.id_map[item_id]
@read_api
@ -499,6 +502,8 @@ class Cache(object):
@read_api
def format_hash(self, book_id, fmt):
''' Return the hash of the specified format for the specified book. The
kind of hash is backend dependent, but is usually SHA-256. '''
try:
name = self.fields['formats'].format_fname(book_id, fmt)
path = self._field_for('path', book_id).replace('/', os.sep)
@ -753,7 +758,7 @@ class Cache(object):
:param as_file: If True the ebook format is returned as a file object. Note
that the file object is a SpooledTemporaryFile, so if what you want to
do is copy the format to another file, use :method:`copy_format_to`
do is copy the format to another file, use :meth:`copy_format_to`
instead for performance.
:param as_path: Copies the format file to a temp file and returns the
path to the temp file
@ -1344,6 +1349,9 @@ class Cache(object):
@read_api
def has_book(self, mi):
''' Return True iff the database contains an entry with the same title
as the passed in Metadata object. The comparison is case-insensitive.
'''
title = mi.title
if title:
if isbytestring(title):
@ -1409,6 +1417,10 @@ class Cache(object):
2-tuples, each 2-tuple of the form (mi, format_map) where mi is a
Metadata object and format_map is a dictionary of the form {fmt: path_or_stream},
for example: {'EPUB': '/path/to/file.epub'}.
Returns a pair of lists: ids, duplicates. ``ids`` contains the book ids for all newly created books in the
database. ``duplicates`` contains the (mi, format_map) for all books that already exist in the database
as per the simple duplicate detection heuristic used by :meth:`has_book`.
'''
duplicates, ids = [], []
for mi, format_map in books:
@ -1569,12 +1581,15 @@ class Cache(object):
than the specified time. tag comparison is case insensitive.
:param delta: A timedelta object or None. If None, then all ids with
the tag are returned.
the tag are returned.
:param must_have_tag: If not None the list of matches will be
restricted to books that have this tag
restricted to books that have this tag
:param must_have_authors: A list of authors. If not None the list of
matches will be restricted to books that have these authors (case
insensitive).
matches will be restricted to books that have these authors (case
insensitive).
'''
tag_map = {icu_lower(v):k for k, v in self._get_id_map('tags').iteritems()}
tag = icu_lower(tag.strip())