RFC PT3: Bibles

This commit is contained in:
Jon Tibble 2010-06-15 01:44:06 +01:00
parent e71caf43e7
commit 554ab59caf
10 changed files with 151 additions and 198 deletions

View File

@ -74,13 +74,20 @@ class Manager(object):
""" """
Provide generic object persistence management Provide generic object persistence management
""" """
def __init__(self, plugin_name, init_schema): def __init__(self, plugin_name, init_schema, db_file_name=None):
""" """
Runs the initialisation process that includes creating the connection Runs the initialisation process that includes creating the connection
to the database and the tables if they don't exist. to the database and the tables if they don't exist.
``plugin_name`` ``plugin_name``
The name to setup paths and settings section names The name to setup paths and settings section names
``init_schema``
The init_schema function for this database
``db_file_name``
The file name to use for this database. Defaults to None resulting
in the plugin_name being used.
""" """
settings = QtCore.QSettings() settings = QtCore.QSettings()
settings.beginGroup(plugin_name) settings.beginGroup(plugin_name)
@ -88,8 +95,13 @@ class Manager(object):
db_type = unicode( db_type = unicode(
settings.value(u'db type', QtCore.QVariant(u'sqlite')).toString()) settings.value(u'db type', QtCore.QVariant(u'sqlite')).toString())
if db_type == u'sqlite': if db_type == u'sqlite':
self.db_url = u'sqlite:///%s/%s.sqlite' % ( if db_file_name:
AppLocation.get_section_data_path(plugin_name), plugin_name) self.db_url = u'sqlite:///%s/%s' % (
AppLocation.get_section_data_path(plugin_name),
db_file_name)
else:
self.db_url = u'sqlite:///%s/%s.sqlite' % (
AppLocation.get_section_data_path(plugin_name), plugin_name)
else: else:
self.db_url = u'%s://%s:%s@%s/%s' % (db_type, self.db_url = u'%s://%s:%s@%s/%s' % (db_type,
unicode(settings.value(u'db username').toString()), unicode(settings.value(u'db username').toString()),
@ -99,6 +111,30 @@ class Manager(object):
settings.endGroup() settings.endGroup()
self.session = init_schema(self.db_url) self.session = init_schema(self.db_url)
def delete_database(self, plugin_name, db_file_name=None):
"""
Remove a database file from the system.
``plugin_name``
The name of the plugin to remove the database for
``db_file_name``
The database file name. Defaults to None resulting in the
plugin_name being used.
"""
db_file_path = None
if db_file_name:
db_file_path = os.path.join(
AppLocation.get_section_data_path(plugin_name), db_file_name)
else:
db_file_path = os.path.join(
AppLocation.get_section_data_path(plugin_name), plugin_name)
try:
os.remove(db_file_path)
return True
except OSError:
return False
def insert_object(self, object_instance): def insert_object(self, object_instance):
""" """
Save an object to the database Save an object to the database

View File

@ -224,7 +224,7 @@ class ImportWizardForm(QtGui.QWizard, Ui_BibleImportWizard):
Show the file open dialog for the OSIS file. Show the file open dialog for the OSIS file.
""" """
self.getFileName( self.getFileName(
translate(u'BiblesPlugin.ImportWizardForm', u'Open OSIS File'), translate(u'BiblesPlugin.ImportWizardForm', u'Open OSIS File'),
self.OSISLocationEdit) self.OSISLocationEdit)
def onBooksFileButtonClicked(self): def onBooksFileButtonClicked(self):
@ -239,10 +239,8 @@ class ImportWizardForm(QtGui.QWizard, Ui_BibleImportWizard):
""" """
Show the file open dialog for the verses CSV file. Show the file open dialog for the verses CSV file.
""" """
self.getFileName( self.getFileName(translate(u'BiblesPlugin.ImportWizardForm',
translate(u'BiblesPlugin.ImportWizardForm', u'Open Verses CSV File'), self.CsvVerseLocationEdit)
u'Open Verses CSV File'),
self.CsvVerseLocationEdit)
def onOpenSongBrowseButtonClicked(self): def onOpenSongBrowseButtonClicked(self):
""" """
@ -450,11 +448,11 @@ class ImportWizardForm(QtGui.QWizard, Ui_BibleImportWizard):
self.ImportProgressLabel.setText( self.ImportProgressLabel.setText(
translate(u'BiblesPlugin.ImportWizardForm', translate(u'BiblesPlugin.ImportWizardForm',
u'Your Bible import failed.')) u'Your Bible import failed.'))
importer.delete() importer.delete_database(self.bibleplugin.settingsSection,
importer.file)
def postImport(self): def postImport(self):
self.ImportProgressBar.setValue(self.ImportProgressBar.maximum()) self.ImportProgressBar.setValue(self.ImportProgressBar.maximum())
self.finishButton.setVisible(True) self.finishButton.setVisible(True)
self.cancelButton.setVisible(False) self.cancelButton.setVisible(False)
Receiver.send_message(u'openlp_process_events') Receiver.send_message(u'openlp_process_events')

View File

@ -97,11 +97,11 @@ class CSVBible(BibleDB):
book_ptr = book.name book_ptr = book.name
self.wizard.incrementProgressBar( self.wizard.incrementProgressBar(
u'Importing %s %s' % (book.name, line[1])) u'Importing %s %s' % (book.name, line[1]))
self.commit() self.session.commit()
self.create_verse(book.id, line[1], line[2], self.create_verse(book.id, line[1], line[2],
unicode(line[3], details['encoding'])) unicode(line[3], details['encoding']))
Receiver.send_message(u'openlp_process_events') Receiver.send_message(u'openlp_process_events')
self.commit() self.session.commit()
except IOError: except IOError:
log.exception(u'Loading verses from file failed') log.exception(u'Loading verses from file failed')
success = False success = False
@ -113,5 +113,3 @@ class CSVBible(BibleDB):
return False return False
else: else:
return success return success

View File

@ -23,19 +23,98 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
import os
import logging import logging
import chardet import chardet
import re import re
from sqlalchemy import or_
from PyQt4 import QtCore from PyQt4 import QtCore
from sqlalchemy import Column, ForeignKey, or_, Table, types
from sqlalchemy.orm import class_mapper, mapper, relation
from sqlalchemy.orm.exc import UnmappedClassError
from openlp.plugins.bibles.lib.models import * from openlp.core.lib.db import BaseModel, init_db, Manager
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class BibleDB(QtCore.QObject): class BibleMeta(BaseModel):
"""
Bible Meta Data
"""
pass
class Testament(BaseModel):
"""
Bible Testaments
"""
pass
class Book(BaseModel):
"""
Song model
"""
pass
class Verse(BaseModel):
"""
Topic model
"""
pass
def init_schema(url):
"""
Setup a bible database connection and initialise the database schema
``url``
The database to setup
"""
session, metadata = init_db(url)
meta_table = Table(u'metadata', metadata,
Column(u'key', types.Unicode(255), primary_key=True, index=True),
Column(u'value', types.Unicode(255)),
)
testament_table = Table(u'testament', metadata,
Column(u'id', types.Integer, primary_key=True),
Column(u'name', types.Unicode(50)),
)
book_table = Table(u'book', metadata,
Column(u'id', types.Integer, primary_key=True),
Column(u'testament_id', types.Integer, ForeignKey(u'testament.id')),
Column(u'name', types.Unicode(50), index=True),
Column(u'abbreviation', types.Unicode(5), index=True),
)
verse_table = Table(u'verse', metadata,
Column(u'id', types.Integer, primary_key=True, index=True),
Column(u'book_id', types.Integer, ForeignKey(u'book.id'), index=True),
Column(u'chapter', types.Integer, index=True),
Column(u'verse', types.Integer, index=True),
Column(u'text', types.UnicodeText, index=True),
)
try:
class_mapper(BibleMeta)
except UnmappedClassError:
mapper(BibleMeta, meta_table)
try:
class_mapper(Testament)
except UnmappedClassError:
mapper(Testament, testament_table,
properties={'books': relation(Book, backref='testament')})
try:
class_mapper(Book)
except UnmappedClassError:
mapper(Book, book_table,
properties={'verses': relation(Verse, backref='book')})
try:
class_mapper(Verse)
except UnmappedClassError:
mapper(Verse, verse_table)
metadata.create_all(checkfirst=True)
return session
class BibleDB(QtCore.QObject, Manager):
""" """
This class represents a database-bound Bible. It is used as a base class This class represents a database-bound Bible. It is used as a base class
for all the custom importers, so that the can implement their own import for all the custom importers, so that the can implement their own import
@ -72,24 +151,7 @@ class BibleDB(QtCore.QObject):
self.file = self.clean_filename(self.name) self.file = self.clean_filename(self.name)
if u'file' in kwargs: if u'file' in kwargs:
self.file = kwargs[u'file'] self.file = kwargs[u'file']
self.db_file = os.path.join(kwargs[u'path'], self.file) Manager.__init__(self, u'bibles', init_schema, self.file)
log.debug(u'Load bible %s on path %s', self.file, self.db_file)
settings = QtCore.QSettings()
settings.beginGroup(u'bibles')
db_type = unicode(
settings.value(u'db type', QtCore.QVariant(u'sqlite')).toString())
db_url = u''
if db_type == u'sqlite':
db_url = u'sqlite:///' + self.db_file
else:
db_url = u'%s://%s:%s@%s/%s' % (db_type,
unicode(settings.value(u'db username').toString()),
unicode(settings.value(u'db password').toString()),
unicode(settings.value(u'db hostname').toString()),
unicode(settings.value(u'db database').toString()))
settings.endGroup()
self.session = init_models(db_url)
metadata.create_all(checkfirst=True)
if u'file' in kwargs: if u'file' in kwargs:
self.get_name() self.get_name()
@ -104,7 +166,7 @@ class BibleDB(QtCore.QObject):
""" """
Returns the version name of the Bible. Returns the version name of the Bible.
""" """
version_name = self.get_meta(u'Version') version_name = self.get_object(BibleMeta, u'Version')
if version_name: if version_name:
self.name = version_name.value self.name = version_name.value
else: else:
@ -124,16 +186,6 @@ class BibleDB(QtCore.QObject):
old_filename = re.sub(r'[^\w]+', u'_', old_filename).strip(u'_') old_filename = re.sub(r'[^\w]+', u'_', old_filename).strip(u'_')
return old_filename + u'.sqlite' return old_filename + u'.sqlite'
def delete(self):
"""
Remove the Bible database file. Used when a Bible import fails.
"""
try:
os.remove(self.db_file)
return True
except OSError:
return False
def register(self, wizard): def register(self, wizard):
""" """
This method basically just initialialises the database. It is called This method basically just initialialises the database. It is called
@ -148,33 +200,15 @@ class BibleDB(QtCore.QObject):
self.create_tables() self.create_tables()
return self.name return self.name
def commit(self):
"""
Perform a database commit.
"""
log.debug('Committing...')
self.session.commit()
def create_tables(self): def create_tables(self):
""" """
Create some initial metadata. Create some initial metadata.
""" """
log.debug(u'createTables') log.debug(u'createTables')
self.create_meta(u'dbversion', u'2') self.create_meta(u'dbversion', u'2')
self.create_testament(u'Old Testament') self.insert_object(Testament.populate(name=u'Old Testament'))
self.create_testament(u'New Testament') self.insert_object(Testament.populate(name=u'New Testament'))
self.create_testament(u'Apocrypha') self.insert_object(Testament.populate(name=u'Apocrypha'))
def create_testament(self, testament):
"""
Add a testament to the database.
``testament``
The testament name.
"""
log.debug(u'BibleDB.create_testament("%s")', testament)
self.session.add(Testament.populate(name=testament))
self.commit()
def create_book(self, name, abbrev, testament=1): def create_book(self, name, abbrev, testament=1):
""" """
@ -192,8 +226,7 @@ class BibleDB(QtCore.QObject):
log.debug(u'create_book %s,%s', name, abbrev) log.debug(u'create_book %s,%s', name, abbrev)
book = Book.populate(name=name, abbreviation=abbrev, book = Book.populate(name=name, abbreviation=abbrev,
testament_id=testament) testament_id=testament)
self.session.add(book) self.insert_object(book)
self.commit()
return book return book
def create_chapter(self, book_id, chapter, textlist): def create_chapter(self, book_id, chapter, textlist):
@ -220,7 +253,7 @@ class BibleDB(QtCore.QObject):
text = verse_text text = verse_text
) )
self.session.add(verse) self.session.add(verse)
self.commit() self.session.commit()
def create_verse(self, book_id, chapter, verse, text): def create_verse(self, book_id, chapter, verse, text):
""" """
@ -252,12 +285,7 @@ class BibleDB(QtCore.QObject):
def create_meta(self, key, value): def create_meta(self, key, value):
log.debug(u'save_meta %s/%s', key, value) log.debug(u'save_meta %s/%s', key, value)
self.session.add(BibleMeta.populate(key=key, value=value)) self.insert_object(BibleMeta.populate(key=key, value=value))
self.commit()
def get_books(self):
log.debug(u'BibleDB.get_books()')
return self.session.query(Book).order_by(Book.id).all()
def get_book(self, book): def get_book(self, book):
log.debug(u'BibleDb.get_book("%s")', book) log.debug(u'BibleDb.get_book("%s")', book)
@ -362,19 +390,6 @@ class BibleDB(QtCore.QObject):
else: else:
return count return count
def get_meta(self, key):
log.debug(u'get meta %s', key)
return self.session.query(BibleMeta).get(key)
def delete_meta(self, metakey):
biblemeta = self.get_meta(metakey)
try:
self.session.delete(biblemeta)
self.commit()
return True
except:
return False
def dump_bible(self): def dump_bible(self):
log.debug(u'.........Dumping Bible Database') log.debug(u'.........Dumping Bible Database')
log.debug('...............................Books ') log.debug('...............................Books ')
@ -383,4 +398,3 @@ class BibleDB(QtCore.QObject):
log.debug(u'...............................Verses ') log.debug(u'...............................Verses ')
verses = self.session.query(Verse).all() verses = self.session.query(Verse).all()
log.debug(verses) log.debug(verses)

View File

@ -35,8 +35,7 @@ from openlp.core.lib import Receiver
from openlp.core.utils import AppLocation from openlp.core.utils import AppLocation
from openlp.plugins.bibles.lib.common import BibleCommon, SearchResults, \ from openlp.plugins.bibles.lib.common import BibleCommon, SearchResults, \
unescape unescape
from openlp.plugins.bibles.lib.db import BibleDB from openlp.plugins.bibles.lib.db import BibleDB, Book
from openlp.plugins.bibles.lib.models import Book
log = logging.getLogger(__name__) log = logging.getLogger(__name__)

View File

@ -29,12 +29,12 @@ from PyQt4 import QtCore
from openlp.core.lib import SettingsManager from openlp.core.lib import SettingsManager
from openlp.core.utils import AppLocation from openlp.core.utils import AppLocation
from openlp.plugins.bibles.lib.db import BibleDB, Book, BibleMeta
from common import parse_reference from common import parse_reference
from opensong import OpenSongBible from opensong import OpenSongBible
from osis import OSISBible from osis import OSISBible
from csvbible import CSVBible from csvbible import CSVBible
from db import BibleDB
from http import HTTPBible from http import HTTPBible
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -137,11 +137,13 @@ class BibleManager(object):
log.debug(u'Bible Name: "%s"', name) log.debug(u'Bible Name: "%s"', name)
self.db_cache[name] = bible self.db_cache[name] = bible
# look to see if lazy load bible exists and get create getter. # look to see if lazy load bible exists and get create getter.
source = self.db_cache[name].get_meta(u'download source') source = self.db_cache[name].get_object(BibleMeta,
u'download source')
if source: if source:
download_name = \ download_name = self.db_cache[name].get_object(BibleMeta,
self.db_cache[name].get_meta(u'download name').value u'download name').value
meta_proxy = self.db_cache[name].get_meta(u'proxy url') meta_proxy = self.db_cache[name].get_object(BibleMeta,
u'proxy url')
web_bible = HTTPBible(self.parent, path=self.path, web_bible = HTTPBible(self.parent, path=self.path,
file=filename, download_source=source.value, file=filename, download_source=source.value,
download_name=download_name) download_name=download_name)
@ -196,7 +198,7 @@ class BibleManager(object):
u'name': book.name, u'name': book.name,
u'chapters': self.db_cache[bible].get_chapter_count(book.name) u'chapters': self.db_cache[bible].get_chapter_count(book.name)
} }
for book in self.db_cache[bible].get_books() for book in self.db_cache[bible].get_all_objects(Book, Book.id)
] ]
def get_chapter_count(self, bible, book): def get_chapter_count(self, bible, book):
@ -249,7 +251,7 @@ class BibleManager(object):
Returns the meta data for a given key Returns the meta data for a given key
""" """
log.debug(u'get_meta %s,%s', bible, key) log.debug(u'get_meta %s,%s', bible, key)
return self.db_cache[bible].get_meta(key) return self.db_cache[bible].get_object(BibleMeta, key)
def exists(self, name): def exists(self, name):
""" """

View File

@ -1,94 +0,0 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2010 Raoul Snyman #
# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael #
# Gorven, Scott Guerrieri, Christian Richter, Maikel Stuivenberg, Martin #
# Thompson, Jon Tibble, Carsten Tinggaard #
# --------------------------------------------------------------------------- #
# 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; version 2 of the License. #
# #
# 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, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
from sqlalchemy import Column, Table, MetaData, ForeignKey, types, \
create_engine
from sqlalchemy.orm import mapper, relation, sessionmaker, scoped_session
from openlp.core.lib.db import BaseModel
class BibleMeta(BaseModel):
"""
Bible Meta Data
"""
pass
class Testament(BaseModel):
"""
Bible Testaments
"""
pass
class Book(BaseModel):
"""
Song model
"""
pass
class Verse(BaseModel):
"""
Topic model
"""
pass
def init_models(db_url):
engine = create_engine(db_url)
metadata.bind = engine
session = scoped_session(sessionmaker(autoflush=True, autocommit=False,
bind=engine))
return session
metadata = MetaData()
meta_table = Table(u'metadata', metadata,
Column(u'key', types.Unicode(255), primary_key=True, index=True),
Column(u'value', types.Unicode(255)),
)
testament_table = Table(u'testament', metadata,
Column(u'id', types.Integer, primary_key=True),
Column(u'name', types.Unicode(50)),
)
book_table = Table(u'book', metadata,
Column(u'id', types.Integer, primary_key=True),
Column(u'testament_id', types.Integer, ForeignKey(u'testament.id')),
Column(u'name', types.Unicode(50), index=True),
Column(u'abbreviation', types.Unicode(5), index=True),
)
verse_table = Table(u'verse', metadata,
Column(u'id', types.Integer, primary_key=True, index=True),
Column(u'book_id', types.Integer, ForeignKey(u'book.id'), index=True),
Column(u'chapter', types.Integer, index=True),
Column(u'verse', types.Integer, index=True),
Column(u'text', types.UnicodeText, index=True),
)
mapper(BibleMeta, meta_table)
mapper(Testament, testament_table,
properties={'books': relation(Book, backref='testament')})
mapper(Book, book_table,
properties={'verses': relation(Verse, backref='book')})
mapper(Verse, verse_table)

View File

@ -90,7 +90,7 @@ class OpenSongBible(BibleDB):
QtCore.QString('%s %s %s' % ( QtCore.QString('%s %s %s' % (
translate(u'BiblesPlugin.Opensong', u'Importing'), \ translate(u'BiblesPlugin.Opensong', u'Importing'), \
db_book.name, chapter.attrib[u'n']))) db_book.name, chapter.attrib[u'n'])))
self.commit() self.session.commit()
except IOError: except IOError:
log.exception(u'Loading bible from OpenSong file failed') log.exception(u'Loading bible from OpenSong file failed')
success = False success = False

View File

@ -136,7 +136,7 @@ class OSISBible(BibleDB):
self.wizard.ImportProgressBar.setMaximum(260) self.wizard.ImportProgressBar.setMaximum(260)
if last_chapter != chapter: if last_chapter != chapter:
if last_chapter != 0: if last_chapter != 0:
self.commit() self.session.commit()
self.wizard.incrementProgressBar( self.wizard.incrementProgressBar(
u'Importing %s %s...' % \ u'Importing %s %s...' % \
(self.books[match.group(1)][0], chapter)) (self.books[match.group(1)][0], chapter))
@ -162,7 +162,7 @@ class OSISBible(BibleDB):
verse_text = self.spaces_regex.sub(u' ', verse_text) verse_text = self.spaces_regex.sub(u' ', verse_text)
self.create_verse(db_book.id, chapter, verse, verse_text) self.create_verse(db_book.id, chapter, verse, verse_text)
Receiver.send_message(u'openlp_process_events') Receiver.send_message(u'openlp_process_events')
self.commit() self.session.commit()
self.wizard.incrementProgressBar(u'Finishing import...') self.wizard.incrementProgressBar(u'Finishing import...')
if match_count == 0: if match_count == 0:
success = False success = False

View File

@ -28,8 +28,8 @@ import string
from PyQt4 import QtGui from PyQt4 import QtGui
from openlp.core.lib import SongXMLBuilder from openlp.core.lib import SongXMLBuilder
from openlp.plugins.songs.lib import VerseType
from openlp.plugins.songs.lib.db import Song, Author, Topic, Book from openlp.plugins.songs.lib.db import Song, Author, Topic, Book
from openlp.plugins.songs.forms import VerseType
class SongImport(object): class SongImport(object):
""" """