openlp-core team mailing list archive
-
openlp-core team
-
Mailing list archive
-
Message #09981
[Merge] lp:~googol-hush/openlp/database-version-control into lp:openlp
Andreas Preikschat has proposed merging lp:~googol-hush/openlp/database-version-control into lp:openlp.
Requested reviews:
OpenLP Core (openlp-core)
For more details, see:
https://code.launchpad.net/~googol-hush/openlp/database-version-control/+merge/64223
Hello,
- added database version control API
**WARNING**
This creates a "meatadata" table in your databases!
**NOTE**
Work is still in progress.
--
https://code.launchpad.net/~googol-hush/openlp/database-version-control/+merge/64223
Your team OpenLP Core is requested to review the proposed merge of lp:~googol-hush/openlp/database-version-control into lp:openlp.
=== modified file 'openlp/core/lib/db.py'
--- openlp/core/lib/db.py 2011-05-28 12:50:29 +0000
+++ openlp/core/lib/db.py 2011-06-10 17:19:24 +0000
@@ -31,9 +31,10 @@
import os
from PyQt4 import QtCore
-from sqlalchemy import create_engine, MetaData
+from sqlalchemy import create_engine, MetaData, Column, Table, types
from sqlalchemy.exc import InvalidRequestError
-from sqlalchemy.orm import scoped_session, sessionmaker
+from sqlalchemy.orm import class_mapper, scoped_session, mapper, sessionmaker
+from sqlalchemy.orm.exc import UnmappedClassError
from sqlalchemy.pool import NullPool
from openlp.core.utils import AppLocation, delete_file
@@ -52,11 +53,24 @@
``auto_commit``
Sets the commit behaviour of the session
+
+ The database has a **metadata** table. It has two columns, namely a ``key``
+ and a ``value`` column. This table is the place of any data related to the
+ database but not to its content.
"""
engine = create_engine(url, poolclass=NullPool)
metadata = MetaData(bind=engine)
session = scoped_session(sessionmaker(autoflush=auto_flush,
autocommit=auto_commit, bind=engine))
+ # Create the default metadata table.
+ meta_table = Table(u'metadata', metadata,
+ Column(u'key', types.Unicode(255), primary_key=True, index=True),
+ Column(u'value', types.Unicode(255)),
+ )
+ try:
+ class_mapper(MetaModel)
+ except UnmappedClassError:
+ mapper(MetaModel, meta_table)
return session, metadata
def delete_database(plugin_name, db_file_name=None):
@@ -94,6 +108,13 @@
return instance
+class MetaModel(BaseModel):
+ """
+ Metadata Model
+ """
+ pass
+
+
class Manager(object):
"""
Provide generic object persistence management
@@ -136,6 +157,41 @@
settings.endGroup()
self.session = init_schema(self.db_url)
+ def is_valid_database(self, allowed_version):
+ """
+ Returns ``False`` when the given version is lower than the version found
+ in the database.
+
+ ``allowed_version``
+ The version the database is expected to be.
+ """
+ db_version = self.get_meta(u'dbversion')
+ if db_version is None:
+ return True
+ return allowed_version >= int(db_version.value)
+
+ def get_meta(self, key):
+ """
+ Returns the meta data object for given ``key``.
+ """
+ return self.get_object(MetaModel, key)
+
+ def create_meta(self, key, value):
+ """
+ Utility method to save meta data for this database.
+
+ ``key``
+ The key for this instance.
+
+ ``value``
+ The value for this instance.
+ """
+ if not isinstance(value, unicode):
+ value = unicode(value)
+ obj = MetaModel.populate(key=key, value=value)
+ self.save_object(obj)
+ return obj
+
def save_object(self, object_instance, commit=True):
"""
Save an object to the database
@@ -215,10 +271,10 @@
``filter_clause``
The filter governing selection of objects to return. Defaults to
- None.
+ ``None``.
``order_by_ref``
- Any parameters to order the returned objects by. Defaults to None.
+ Any parameters to order the returned objects by. Defaults to ``None``.
"""
query = self.session.query(object_class)
if filter_clause is not None:
@@ -236,7 +292,7 @@
``filter_clause``
The filter governing selection of objects to return. Defaults to
- None.
+ ``None``.
"""
query = self.session.query(object_class)
if filter_clause is not None:
=== modified file 'openlp/core/lib/plugin.py'
--- openlp/core/lib/plugin.py 2011-06-05 18:46:00 +0000
+++ openlp/core/lib/plugin.py 2011-06-10 17:19:24 +0000
@@ -156,6 +156,7 @@
self.icon = None
self.media_item_class = media_item_class
self.settings_tab_class = settings_tab_class
+ self.settings_tab = None
self.weight = 0
self.status = PluginStatus.Inactive
# Set up logging
=== modified file 'openlp/core/ui/pluginform.py'
--- openlp/core/ui/pluginform.py 2011-06-05 18:46:00 +0000
+++ openlp/core/ui/pluginform.py 2011-06-10 17:19:24 +0000
@@ -102,9 +102,9 @@
self.versionNumberLabel.setText(self.activePlugin.version)
self.aboutTextBrowser.setHtml(self.activePlugin.about())
self.programaticChange = True
- status = 1
+ status = PluginStatus.Active
if self.activePlugin.status == PluginStatus.Active:
- status = 0
+ status = PluginStatus.Inactive
self.statusComboBox.setCurrentIndex(status)
self.statusComboBox.setEnabled(True)
self.programaticChange = False
@@ -128,7 +128,7 @@
def onStatusComboBoxChanged(self, status):
if self.programaticChange:
return
- if status == 0:
+ if status == PluginStatus.Inactive:
Receiver.send_message(u'cursor_busy')
self.activePlugin.toggleStatus(PluginStatus.Active)
Receiver.send_message(u'cursor_normal')
=== modified file 'openlp/plugins/alerts/alertsplugin.py'
--- openlp/plugins/alerts/alertsplugin.py 2011-05-26 17:11:22 +0000
+++ openlp/plugins/alerts/alertsplugin.py 2011-06-10 17:19:24 +0000
@@ -40,7 +40,20 @@
log = logging.getLogger(__name__)
class AlertsPlugin(Plugin):
+ """
+ This plugin allows to display short messages on the display screen without
+ hiding the text (e. g. lyrics).
+
+ ``CURRENT_DB_VERSION``
+ This class constant indicates the database version number which the
+ alerts database is supposed to have.
+
+ ``2``
+ Prior to 1.9.6 the alerts database did not have a version number.
+ After 1.9.6 the version number was set to ``2``.
+ """
log.info(u'Alerts Plugin loaded')
+ CURRENT_DB_VERSION = 2
def __init__(self, plugin_helpers):
Plugin.__init__(self, u'Alerts', plugin_helpers,
@@ -92,6 +105,11 @@
action_list = ActionList.get_instance()
action_list.remove_action(self.toolsAlertItem, u'Tools')
+ def appStartup(self):
+ db_version = self.manager.get_meta(u'dbversion')
+ if db_version is None:
+ db_version = self.manager.create_meta(u'dbversion', u'2')
+
def toggleAlertsState(self):
self.alertsActive = not self.alertsActive
QtCore.QSettings().setValue(self.settingsSection + u'/active',
=== modified file 'openlp/plugins/bibles/bibleplugin.py'
--- openlp/plugins/bibles/bibleplugin.py 2011-05-26 20:41:19 +0000
+++ openlp/plugins/bibles/bibleplugin.py 2011-06-10 17:19:24 +0000
@@ -38,7 +38,20 @@
log = logging.getLogger(__name__)
class BiblePlugin(Plugin):
+ """
+ This plugin allows to display bible verses.
+
+ ``CURRENT_DB_VERSION``
+ This class constant indicates the database version number which the
+ bible databases are supposed to have.
+
+ ``2``
+ The database version was initially set to 2. However, there has been
+ a database change (between 1.9.5 and 1.9.6) without increasing the
+ version number.
+ """
log.info(u'Bible Plugin loaded')
+ CURRENT_DB_VERSION = 2
def __init__(self, plugin_helpers):
Plugin.__init__(self, u'Bibles', plugin_helpers,
@@ -80,12 +93,13 @@
"""
Perform tasks on application starup
"""
- if len(self.manager.old_bible_databases):
- if QtGui.QMessageBox.information(self.formparent,
+ # Upgrade pre 1.9.6 bible databases.
+ if self.manager.old_bible_databases:
+ if QtGui.QMessageBox.information(self.formparent,
translate('OpenLP', 'Information'), translate('OpenLP',
'Bible format has changed.\nYou have to upgrade your '
- 'existing Bibles.\nShould OpenLP upgrade now?'),
- QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes |
+ 'existing Bibles.\nShould OpenLP upgrade now?'),
+ QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes |
QtGui.QMessageBox.No)) == QtGui.QMessageBox.Yes:
self.onToolsUpgradeItemTriggered()
@@ -130,11 +144,11 @@
"""
Upgrade older bible databases.
"""
- if not hasattr(self, u'upgrade_wizard'):
- self.upgrade_wizard = BibleUpgradeForm(self.formparent,
+ if not hasattr(self, u'upgradeWizard'):
+ self.upgradeWizard = BibleUpgradeForm(self.formparent,
self.manager, self)
# If the import was not cancelled then reload.
- if self.upgrade_wizard.exec_():
+ if self.upgradeWizard.exec_():
self.mediaItem.reloadBibles()
def onBibleImportClick(self):
=== modified file 'openlp/plugins/bibles/forms/bibleupgradeform.py'
--- openlp/plugins/bibles/forms/bibleupgradeform.py 2011-06-05 17:39:13 +0000
+++ openlp/plugins/bibles/forms/bibleupgradeform.py 2011-06-10 17:19:24 +0000
@@ -39,7 +39,7 @@
from openlp.core.lib.ui import UiStrings, critical_error_message_box
from openlp.core.ui.wizard import OpenLPWizard, WizardStrings
from openlp.core.utils import AppLocation, delete_file
-from openlp.plugins.bibles.lib.db import BibleDB, BibleMeta, OldBibleDB,\
+from openlp.plugins.bibles.lib.db import BibleDB, OldBibleDB,\
BiblesResourcesDB, clean_filename
from openlp.plugins.bibles.lib.http import BSExtract, BGExtract, CWExtract
@@ -70,8 +70,7 @@
self.mediaItem = bibleplugin.mediaItem
self.suffix = u'.sqlite'
self.settingsSection = u'bibles'
- self.path = AppLocation.get_section_data_path(
- self.settingsSection)
+ self.path = AppLocation.get_section_data_path(self.settingsSection)
self.files = self.manager.old_bible_databases
self.success = {}
self.newbibles = {}
@@ -676,8 +675,7 @@
Receiver.send_message(u'openlp_process_events')
self.newbibles[number].session.commit()
else:
- language_id = self.newbibles[number].get_object(BibleMeta,
- u'language_id')
+ language_id = self.newbibles[number].get_meta(u'language_id')
if not language_id:
language_id = self.newbibles[number].get_language(name)
if not language_id:
=== modified file 'openlp/plugins/bibles/lib/db.py'
--- openlp/plugins/bibles/lib/db.py 2011-06-04 13:52:26 +0000
+++ openlp/plugins/bibles/lib/db.py 2011-06-10 17:19:24 +0000
@@ -43,13 +43,6 @@
log = logging.getLogger(__name__)
-class BibleMeta(BaseModel):
- """
- Bible Meta Data
- """
- pass
-
-
class Book(BaseModel):
"""
Song model
@@ -85,11 +78,6 @@
"""
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)),
- )
-
book_table = Table(u'book', metadata,
Column(u'id', types.Integer, primary_key=True),
Column(u'book_reference_id', types.Integer, index=True),
@@ -106,10 +94,6 @@
)
try:
- class_mapper(BibleMeta)
- except UnmappedClassError:
- mapper(BibleMeta, meta_table)
- try:
class_mapper(Book)
except UnmappedClassError:
mapper(Book, book_table,
@@ -181,7 +165,7 @@
"""
Returns the version name of the Bible.
"""
- version_name = self.get_object(BibleMeta, u'Version')
+ version_name = self.get_meta(u'Version')
self.name = version_name.value if version_name else None
return self.name
@@ -285,21 +269,6 @@
self.session.add(verse)
return verse
- def create_meta(self, key, value):
- """
- Utility method to save BibleMeta objects in a Bible database.
-
- ``key``
- The key for this instance.
-
- ``value``
- The value for this instance.
- """
- if not isinstance(value, unicode):
- value = unicode(value)
- log.debug(u'BibleDB.save_meta("%s/%s")', key, value)
- self.save_object(BibleMeta.populate(key=key, value=value))
-
def get_book(self, book):
"""
Return a book object from the database.
=== modified file 'openlp/plugins/bibles/lib/manager.py'
--- openlp/plugins/bibles/lib/manager.py 2011-05-29 19:32:37 +0000
+++ openlp/plugins/bibles/lib/manager.py 2011-06-10 17:19:24 +0000
@@ -34,7 +34,7 @@
from openlp.core.lib.ui import critical_error_message_box
from openlp.core.utils import AppLocation, delete_file
from openlp.plugins.bibles.lib import parse_reference
-from openlp.plugins.bibles.lib.db import BibleDB, BibleMeta, OldBibleDB
+from openlp.plugins.bibles.lib.db import BibleDB, OldBibleDB
from csvbible import CSVBible
from http import HTTPBible
from opensong import OpenSongBible
@@ -161,13 +161,10 @@
log.debug(u'Bible Name: "%s"', name)
self.db_cache[name] = bible
# Look to see if lazy load bible exists and get create getter.
- source = self.db_cache[name].get_object(BibleMeta,
- u'download source')
+ source = self.get_meta_data(name, u'download source')
if source:
- download_name = self.db_cache[name].get_object(BibleMeta,
- u'download name').value
- meta_proxy = self.db_cache[name].get_object(BibleMeta,
- u'proxy url')
+ download_name = self.get_meta_data(name, u'download name').value
+ meta_proxy = self.get_meta_data(name, u'proxy url')
web_bible = HTTPBible(self.parent, path=self.path,
file=filename, download_source=source.value,
download_name=download_name)
@@ -220,7 +217,7 @@
return [
{
u'name': book.name,
- u'book_reference_id': book.book_reference_id,
+ u'book_reference_id': book.book_reference_id,
u'chapters': self.db_cache[bible].get_chapter_count(book)
}
for book in self.db_cache[bible].get_books()
@@ -229,10 +226,10 @@
def get_chapter_count(self, bible, book):
"""
Returns the number of Chapters for a given book.
-
+
``bible``
Unicode. The Bible to get the list of books from.
-
+
``book``
The book object to get the chapter count for.
"""
@@ -295,7 +292,7 @@
if db_book:
book_id = db_book.book_reference_id
log.debug(u'Book name corrected to "%s"', db_book.name)
- new_reflist.append((book_id, item[1], item[2],
+ new_reflist.append((book_id, item[1], item[2],
item[3]))
else:
log.debug(u'OpenLP failed to find book %s', item[0])
@@ -348,11 +345,10 @@
})
return None
# Check if the bible or second_bible is a web bible.
- webbible = self.db_cache[bible].get_object(BibleMeta,
- u'download source')
+ webbible = self.get_meta_data(bible, u'download source')
second_webbible = u''
if second_bible:
- second_webbible = self.db_cache[second_bible].get_object(BibleMeta,
+ second_webbible = self.get_meta_data(second_bible,
u'download source')
if webbible or second_webbible:
Receiver.send_message(u'openlp_information_message', {
@@ -360,7 +356,7 @@
'Web Bible cannot be used'),
u'message': translate('BiblesPlugin.BibleManager',
'Text Search is not available with Web Bibles.')
- })
+ })
return None
if text:
return self.db_cache[bible].verse_search(text)
@@ -391,7 +387,7 @@
Returns the meta data for a given key.
"""
log.debug(u'get_meta %s,%s', bible, key)
- return self.db_cache[bible].get_object(BibleMeta, key)
+ return self.db_cache[bible].get_meta(key)
def exists(self, name):
"""
=== modified file 'openlp/plugins/custom/customplugin.py'
--- openlp/plugins/custom/customplugin.py 2011-05-27 09:34:14 +0000
+++ openlp/plugins/custom/customplugin.py 2011-06-10 17:19:24 +0000
@@ -42,8 +42,17 @@
Custom shows are designed to replace the use of songs where
the songs plugin has become restrictive. Examples could be
Welcome slides, Bible Reading information, Orders of service.
+
+ ``CURRENT_DB_VERSION``
+ This class constant indicates the database version number which the
+ customs database is supposed to have.
+
+ ``2``
+ Prior to 1.9.6 the customs database did not have a version number.
+ After 1.9.6 the version number was set to ``2``.
"""
log.info(u'Custom Plugin loaded')
+ CURRENT_DB_VERSION = 2
def __init__(self, plugin_helpers):
Plugin.__init__(self, u'Custom', plugin_helpers,
@@ -53,6 +62,11 @@
self.icon_path = u':/plugins/plugin_custom.png'
self.icon = build_icon(self.icon_path)
+ def appStartup(self):
+ db_version = self.manager.get_meta(u'dbversion')
+ if db_version is None:
+ db_version = self.manager.create_meta(u'dbversion', u'2')
+
def about(self):
about_text = translate('CustomPlugin', '<strong>Custom Plugin</strong>'
'<br />The custom plugin provides the ability to set up custom '
=== modified file 'openlp/plugins/presentations/presentationplugin.py'
--- openlp/plugins/presentations/presentationplugin.py 2011-06-03 06:52:16 +0000
+++ openlp/plugins/presentations/presentationplugin.py 2011-06-10 17:19:24 +0000
@@ -87,7 +87,7 @@
to close down their applications and release resources.
"""
log.info(u'Plugin Finalise')
- #Ask each controller to tidy up
+ # Ask each controller to tidy up.
for key in self.controllers:
controller = self.controllers[key]
if controller.enabled():
=== modified file 'openlp/plugins/songs/songsplugin.py'
--- openlp/plugins/songs/songsplugin.py 2011-05-26 17:11:22 +0000
+++ openlp/plugins/songs/songsplugin.py 2011-06-10 17:19:24 +0000
@@ -50,8 +50,17 @@
songs. Songs are divided into verses, and the verse order can be
specified. Authors, topics and song books can be assigned to songs
as well.
+
+ ``CURRENT_DB_VERSION``
+ This class constant indicates the database version number which the song
+ database is supposed to have.
+
+ ``2``
+ Prior to 1.9.6 the song database did not have a version number.
+ After 1.9.6 the version number was set to ``2``.
"""
log.info(u'Song Plugin loaded')
+ CURRENT_DB_VERSION = 2
def __init__(self, plugin_helpers):
"""
@@ -63,6 +72,9 @@
self.icon_path = u':/plugins/plugin_songs.png'
self.icon = build_icon(self.icon_path)
+ def checkPreConditions(self):
+ return self.manager.is_valid_database(SongsPlugin.CURRENT_DB_VERSION)
+
def initialise(self):
log.info(u'Songs Initialising')
Plugin.initialise(self)
@@ -269,3 +281,8 @@
action_list.remove_action(self.songExportItem, UiStrings().Export)
action_list.remove_action(self.toolsReindexItem, UiStrings().Tools)
Plugin.finalise(self)
+
+ def appStartup(self):
+ db_version = self.manager.get_meta(u'dbversion')
+ if db_version is None:
+ db_version = self.manager.create_meta(u'dbversion', u'2')
=== modified file 'openlp/plugins/songusage/songusageplugin.py'
--- openlp/plugins/songusage/songusageplugin.py 2011-05-29 06:23:12 +0000
+++ openlp/plugins/songusage/songusageplugin.py 2011-06-10 17:19:24 +0000
@@ -42,7 +42,19 @@
log = logging.getLogger(__name__)
class SongUsagePlugin(Plugin):
+ """
+ This plugin allows to record the usage of songs.
+
+ ``CURRENT_DB_VERSION``
+ This class constant indicates the database version number which the song
+ usage database is supposed to have.
+
+ ``2``
+ Prior to 1.9.6 the song usage database did not have a version
+ number. After 1.9.6 the version number was set to ``2``.
+ """
log.info(u'SongUsage Plugin loaded')
+ CURRENT_DB_VERSION = 2
def __init__(self, plugin_helpers):
Plugin.__init__(self, u'SongUsage', plugin_helpers)
@@ -140,9 +152,14 @@
translate('SongUsagePlugin', 'Song Usage'))
action_list.remove_action(self.songUsageStatus,
translate('SongUsagePlugin', 'Song Usage'))
- #stop any events being processed
+ # Stop any events being processed.
self.SongUsageActive = False
+ def appStartup(self):
+ db_version = self.manager.get_meta(u'dbversion')
+ if db_version is None:
+ db_version = self.manager.create_meta(u'dbversion', u'2')
+
def toggleSongUsageState(self):
self.SongUsageActive = not self.SongUsageActive
QtCore.QSettings().setValue(self.settingsSection + u'/active',
Follow ups