diff --git a/openlp/core/lib/db.py b/openlp/core/lib/db.py
index 8acc79541..6b8bb297c 100644
--- a/openlp/core/lib/db.py
+++ b/openlp/core/lib/db.py
@@ -135,16 +135,20 @@ class Manager(object):
settings.endGroup()
self.session = init_schema(self.db_url)
- def save_object(self, object_instance):
+ def save_object(self, object_instance, commit=True):
"""
Save an object to the database
``object_instance``
The object to save
+
+ ``commit``
+ Commit the session with this object
"""
try:
self.session.add(object_instance)
- self.session.commit()
+ if commit:
+ self.session.commit()
return True
except InvalidRequestError:
self.session.rollback()
@@ -178,36 +182,24 @@ class Manager(object):
"""
return self.session.query(object_class).filter(filter_clause).first()
- def get_all_objects(self, object_class, order_by_ref=None):
+ def get_all_objects(self, object_class, filter_clause=None,
+ order_by_ref=None):
"""
Returns all the objects from the database
``object_class``
The type of objects to return
+ ``filter_clause``
+ The filter governing selection of objects to return. Defaults to
+ None.
+
``order_by_ref``
Any parameters to order the returned objects by. Defaults to None.
"""
query = self.session.query(object_class)
- if order_by_ref is not None:
- return query.order_by(order_by_ref).all()
- return query.all()
-
- def get_all_objects_filtered(self, object_class, filter_clause,
- order_by_ref=None):
- """
- Returns a selection of objects from the database
-
- ``object_class``
- The type of objects to return
-
- ``filter_clause``
- The filter governing selection of objects to return
-
- ``order_by_ref``
- Any parameters to order the returned objects by. Defaults to None.
- """
- query = self.session.query(object_class).filter(filter_clause)
+ if filter_clause:
+ query = query.filter(filter_clause)
if order_by_ref is not None:
return query.order_by(order_by_ref).all()
return query.all()
@@ -235,7 +227,7 @@ class Manager(object):
else:
return True
- def delete_all_objects(self, object_class):
+ def delete_all_objects(self, object_class, filter_clause=None):
"""
Delete all object records
@@ -243,11 +235,13 @@ class Manager(object):
The type of object to delete
"""
try:
- self.session.query(object_class).delete(synchronize_session=False)
+ query = self.session.query(object_class)
+ if filter_clause:
+ query = query.filter(filter_clause)
+ query.delete(synchronize_session=False)
self.session.commit()
return True
except InvalidRequestError:
self.session.rollback()
- log.exception(u'Failed to delete all %s records',
- object_class.__name__)
+ log.exception(u'Failed to delete %s records', object_class.__name__)
return False
diff --git a/openlp/core/ui/generaltab.py b/openlp/core/ui/generaltab.py
index 72718bb2c..1392992d3 100644
--- a/openlp/core/ui/generaltab.py
+++ b/openlp/core/ui/generaltab.py
@@ -287,16 +287,16 @@ class GeneralTab(SettingsTab):
Translate the general settings tab to the currently selected language
"""
self.MonitorGroupBox.setTitle(translate('GeneralTab', 'Monitors'))
- self.MonitorLabel.setText(
- translate('OpenLP.GeneralTab', 'Select monitor for output display:'))
+ self.MonitorLabel.setText(translate('OpenLP.GeneralTab',
+ 'Select monitor for output display:'))
self.DisplayOnMonitorCheck.setText(
translate('OpenLP.GeneralTab', 'Display if a single screen'))
self.StartupGroupBox.setTitle(
translate('OpenLP.GeneralTab', 'Application Startup'))
self.WarningCheckBox.setText(
translate('OpenLP.GeneralTab', 'Show blank screen warning'))
- self.AutoOpenCheckBox.setText(
- translate('OpenLP.GeneralTab', 'Automatically open the last service'))
+ self.AutoOpenCheckBox.setText(translate('OpenLP.GeneralTab',
+ 'Automatically open the last service'))
self.ShowSplashCheckBox.setText(
translate('OpenLP.GeneralTab', 'Show the splash screen'))
self.SettingsGroupBox.setTitle(translate('OpenLP.GeneralTab',
@@ -318,7 +318,8 @@ class GeneralTab(SettingsTab):
self.currentXValueLabel.setText(u'0')
self.currentYLabel.setText(translate('OpenLP.GeneralTab', 'Y'))
self.currentYValueLabel.setText(u'0')
- self.currentHeightLabel.setText(translate('OpenLP.GeneralTab', 'Height'))
+ self.currentHeightLabel.setText(
+ translate('OpenLP.GeneralTab', 'Height'))
self.currentHeightValueLabel.setText(u'0')
self.currentWidthLabel.setText(translate('OpenLP.GeneralTab', 'Width'))
self.currentWidthValueLabel.setText(u'0')
@@ -375,10 +376,12 @@ class GeneralTab(SettingsTab):
QtCore.QVariant(self.screens.current[u'size'].x())).toString())
self.customYValueEdit.setText(settings.value(u'y position',
QtCore.QVariant(self.screens.current[u'size'].y())).toString())
- self.customHeightValueEdit.setText(settings.value(u'height',
- QtCore.QVariant(self.screens.current[u'size'].height())).toString())
- self.customWidthValueEdit.setText(settings.value(u'width',
- QtCore.QVariant(self.screens.current[u'size'].width())).toString())
+ self.customHeightValueEdit.setText(
+ settings.value(u'height', QtCore.QVariant(
+ self.screens.current[u'size'].height())).toString())
+ self.customWidthValueEdit.setText(
+ settings.value(u'width', QtCore.QVariant(
+ self.screens.current[u'size'].width())).toString())
else:
self.customXValueEdit.setText(
unicode(self.screens.current[u'size'].x()))
diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py
index cf9afe066..19811b979 100644
--- a/openlp/core/ui/servicemanager.py
+++ b/openlp/core/ui/servicemanager.py
@@ -108,8 +108,8 @@ class ServiceManager(QtGui.QWidget):
self.droppos = 0
#is a new service and has not been saved
self.isNew = True
- self.serviceNoteForm = ServiceNoteForm()
- self.serviceItemEditForm = ServiceItemEditForm()
+ self.serviceNoteForm = ServiceNoteForm(self.parent)
+ self.serviceItemEditForm = ServiceItemEditForm(self.parent)
#start with the layout
self.Layout = QtGui.QVBoxLayout(self)
self.Layout.setSpacing(0)
diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py
index d321630f4..1745e6030 100644
--- a/openlp/core/ui/thememanager.py
+++ b/openlp/core/ui/thememanager.py
@@ -211,6 +211,14 @@ class ThemeManager(QtGui.QWidget):
'You must select a theme to delete.')):
item = self.ThemeListWidget.currentItem()
theme = unicode(item.text())
+ # confirm deletion
+ answer = QtGui.QMessageBox.question(self,
+ translate('ThemeManager', 'Delete Confirmation'),
+ translate('ThemeManager', 'Delete theme?'),
+ QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes |
+ QtGui.QMessageBox.No), QtGui.QMessageBox.No)
+ if answer == QtGui.QMessageBox.No:
+ return
# should be the same unless default
if theme != unicode(item.data(QtCore.Qt.UserRole).toString()):
QtGui.QMessageBox.critical(self,
@@ -245,15 +253,14 @@ class ThemeManager(QtGui.QWidget):
The theme to delete.
"""
self.themelist.remove(theme)
- th = theme + u'.png'
+ thumb = theme + u'.png'
try:
- os.remove(os.path.join(self.path, th))
- os.remove(os.path.join(self.thumbPath, th))
+ os.remove(os.path.join(self.path, thumb))
+ os.remove(os.path.join(self.thumbPath, thumb))
encoding = get_filesystem_encoding()
shutil.rmtree(os.path.join(self.path, theme).encode(encoding))
except OSError:
- #if not present do not worry
- pass
+ log.exception(u'Error deleting theme %s', theme)
# As we do not reload the themes push out the change
# Reaload the list as the internal lists and events need
# to be triggered
@@ -597,19 +604,21 @@ class ThemeManager(QtGui.QWidget):
if newThemeIndex != -1:
self.serviceComboBox.setCurrentIndex(newThemeIndex)
if self.editingDefault:
- newThemeItem = self.ThemeListWidget.findItems(name,
- QtCore.Qt.MatchExactly)[0]
- newThemeIndex = self.ThemeListWidget.indexFromItem(
- newThemeItem).row()
- self.global_theme = unicode(
- self.ThemeListWidget.item(newThemeIndex).text())
- newName = unicode(translate('ThemeManager', '%s (default)')) % \
- self.global_theme
- self.ThemeListWidget.item(newThemeIndex).setText(newName)
- QtCore.QSettings().setValue(
- self.settingsSection + u'/global theme',
- QtCore.QVariant(self.global_theme))
- Receiver.send_message(u'theme_update_global', self.global_theme)
+ if self.saveThemeName != name:
+ newThemeItem = self.ThemeListWidget.findItems(name,
+ QtCore.Qt.MatchExactly)[0]
+ newThemeIndex = self.ThemeListWidget.indexFromItem(
+ newThemeItem).row()
+ self.global_theme = unicode(
+ self.ThemeListWidget.item(newThemeIndex).text())
+ newName = unicode(translate('ThemeManager',
+ '%s (default)')) % self.global_theme
+ self.ThemeListWidget.item(newThemeIndex).setText(newName)
+ QtCore.QSettings().setValue(
+ self.settingsSection + u'/global theme',
+ QtCore.QVariant(self.global_theme))
+ Receiver.send_message(u'theme_update_global',
+ self.global_theme)
self.editingDefault = False
self.pushThemes()
else:
diff --git a/openlp/plugins/alerts/forms/alertform.py b/openlp/plugins/alerts/forms/alertform.py
index 388d35751..71edd8e9f 100644
--- a/openlp/plugins/alerts/forms/alertform.py
+++ b/openlp/plugins/alerts/forms/alertform.py
@@ -62,7 +62,8 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog):
def loadList(self):
self.AlertListWidget.clear()
- alerts = self.manager.get_all_objects(AlertItem, AlertItem.text)
+ alerts = self.manager.get_all_objects(AlertItem,
+ order_by_ref=AlertItem.text)
for alert in alerts:
item_name = QtGui.QListWidgetItem(alert.text)
item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(alert.id))
diff --git a/openlp/plugins/bibles/bibleplugin.py b/openlp/plugins/bibles/bibleplugin.py
index 3a7baf48b..5ea870da6 100644
--- a/openlp/plugins/bibles/bibleplugin.py
+++ b/openlp/plugins/bibles/bibleplugin.py
@@ -40,7 +40,7 @@ class BiblePlugin(Plugin):
self.weight = -9
self.icon_path = u':/plugins/plugin_bibles.png'
self.icon = build_icon(self.icon_path)
- #Register the bible Manager
+ # Register the bible Manager.
self.status = PluginStatus.Active
self.manager = None
@@ -62,7 +62,7 @@ class BiblePlugin(Plugin):
return BiblesTab(self.name)
def getMediaManagerItem(self):
- # Create the BibleManagerItem object
+ # Create the BibleManagerItem object.
return BibleMediaItem(self, self.icon, self.name)
def addImportMenuItem(self, import_menu):
@@ -71,7 +71,7 @@ class BiblePlugin(Plugin):
import_menu.addAction(self.ImportBibleItem)
self.ImportBibleItem.setText(
translate('BiblePlugin', '&Bible'))
- # Signals and slots
+ # signals and slots
QtCore.QObject.connect(self.ImportBibleItem,
QtCore.SIGNAL(u'triggered()'), self.onBibleImportClick)
self.ImportBibleItem.setVisible(False)
diff --git a/openlp/plugins/bibles/lib/manager.py b/openlp/plugins/bibles/lib/manager.py
index 39f3c255b..f862e1d1c 100644
--- a/openlp/plugins/bibles/lib/manager.py
+++ b/openlp/plugins/bibles/lib/manager.py
@@ -198,7 +198,8 @@ class BibleManager(object):
u'name': book.name,
u'chapters': self.db_cache[bible].get_chapter_count(book.name)
}
- for book in self.db_cache[bible].get_all_objects(Book, Book.id)
+ for book in self.db_cache[bible].get_all_objects(Book,
+ order_by_ref=Book.id)
]
def get_chapter_count(self, bible, book):
diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py
index 9324e74ce..96538a7eb 100644
--- a/openlp/plugins/bibles/lib/mediaitem.py
+++ b/openlp/plugins/bibles/lib/mediaitem.py
@@ -129,8 +129,7 @@ class BibleMediaItem(MediaManagerItem):
self.QuickClearLabel.setObjectName(u'QuickSearchLabel')
self.QuickLayout.addWidget(self.QuickClearLabel, 4, 0, 1, 1)
self.ClearQuickSearchComboBox = QtGui.QComboBox(self.QuickTab)
- self.ClearQuickSearchComboBox.setObjectName(
- u'ClearQuickSearchComboBox')
+ self.ClearQuickSearchComboBox.setObjectName(u'ClearQuickSearchComboBox')
self.QuickLayout.addWidget(self.ClearQuickSearchComboBox, 4, 1, 1, 2)
self.QuickSearchButtonLayout = QtGui.QHBoxLayout()
self.QuickSearchButtonLayout.setMargin(0)
@@ -168,8 +167,7 @@ class BibleMediaItem(MediaManagerItem):
self.AdvancedVersionComboBox.setObjectName(u'AdvancedVersionComboBox')
self.AdvancedLayout.addWidget(self.AdvancedVersionComboBox, 0, 1, 1, 2)
self.AdvancedSecondBibleLabel = QtGui.QLabel(self.AdvancedTab)
- self.AdvancedSecondBibleLabel.setObjectName(
- u'AdvancedSecondBibleLabel')
+ self.AdvancedSecondBibleLabel.setObjectName(u'AdvancedSecondBibleLabel')
self.AdvancedLayout.addWidget(self.AdvancedSecondBibleLabel, 1, 0, 1, 1)
self.AdvancedSecondBibleComboBox = QtGui.QComboBox(self.AdvancedTab)
self.AdvancedSecondBibleComboBox.setSizeAdjustPolicy(
@@ -223,8 +221,7 @@ class BibleMediaItem(MediaManagerItem):
u'AdvancedSearchButtonLayout')
self.AdvancedSearchButtonSpacer = QtGui.QSpacerItem(40, 20,
QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
- self.AdvancedSearchButtonLayout.addItem(
- self.AdvancedSearchButtonSpacer)
+ self.AdvancedSearchButtonLayout.addItem(self.AdvancedSearchButtonSpacer)
self.AdvancedSearchButton = QtGui.QPushButton(self.AdvancedTab)
self.AdvancedSearchButton.setObjectName(u'AdvancedSearchButton')
self.AdvancedSearchButtonLayout.addWidget(self.AdvancedSearchButton)
@@ -511,14 +508,14 @@ class BibleMediaItem(MediaManagerItem):
reference = bitem.data(QtCore.Qt.UserRole)
if isinstance(reference, QtCore.QVariant):
reference = reference.toPyObject()
- bible = self._decodeQtObject(reference, 'bible')
+ #bible = self._decodeQtObject(reference, 'bible')
book = self._decodeQtObject(reference, 'book')
chapter = self._decodeQtObject(reference, 'chapter')
verse = self._decodeQtObject(reference, 'verse')
text = self._decodeQtObject(reference, 'text')
version = self._decodeQtObject(reference, 'version')
copyright = self._decodeQtObject(reference, 'copyright')
- permission = self._decodeQtObject(reference, 'permission')
+ #permission = self._decodeQtObject(reference, 'permission')
if self.parent.settings_tab.display_style == 1:
verse_text = self.formatVerse(old_chapter, chapter, verse,
u'(u', u')')
@@ -618,8 +615,7 @@ class BibleMediaItem(MediaManagerItem):
else:
self.AdvancedSearchButton.setEnabled(True)
self.AdvancedMessage.setText(u'')
- self.adjustComboBox(1, self.chapters_from,
- self.AdvancedFromChapter)
+ self.adjustComboBox(1, self.chapters_from, self.AdvancedFromChapter)
self.adjustComboBox(1, self.chapters_from, self.AdvancedToChapter)
self.adjustComboBox(1, self.verses, self.AdvancedFromVerse)
self.adjustComboBox(1, self.verses, self.AdvancedToVerse)
diff --git a/openlp/plugins/custom/customplugin.py b/openlp/plugins/custom/customplugin.py
index 2cdbac362..79ccb1f9b 100644
--- a/openlp/plugins/custom/customplugin.py
+++ b/openlp/plugins/custom/customplugin.py
@@ -75,7 +75,7 @@ class CustomPlugin(Plugin):
Returns True if the theme is being used, otherwise returns False.
"""
- if self.custommanager.get_all_objects_filtered(CustomSlide,
+ if self.custommanager.get_all_objects(CustomSlide,
CustomSlide.theme_name == theme):
return True
return False
@@ -91,8 +91,8 @@ class CustomPlugin(Plugin):
``newTheme``
The new name the plugin should now use.
"""
- customsUsingTheme = self.custommanager.get_all_objects_filtered(
- CustomSlide, CustomSlide.theme_name == oldTheme)
+ customsUsingTheme = self.custommanager.get_all_objects(CustomSlide,
+ CustomSlide.theme_name == oldTheme)
for custom in customsUsingTheme:
custom.theme_name = newTheme
self.custommanager.save_object(custom)
diff --git a/openlp/plugins/custom/lib/mediaitem.py b/openlp/plugins/custom/lib/mediaitem.py
index 34ffeeac4..cdea8cb48 100644
--- a/openlp/plugins/custom/lib/mediaitem.py
+++ b/openlp/plugins/custom/lib/mediaitem.py
@@ -73,7 +73,7 @@ class CustomMediaItem(MediaManagerItem):
def initialise(self):
self.loadCustomListView(self.parent.custommanager.get_all_objects(
- CustomSlide, CustomSlide.title))
+ CustomSlide, order_by_ref=CustomSlide.title))
#Called to redisplay the song list screen edith from a search
#or from the exit of the Song edit dialog. If remote editing is active
#Trigger it and clean up so it will not update again.
diff --git a/openlp/plugins/presentations/lib/mediaitem.py b/openlp/plugins/presentations/lib/mediaitem.py
index b9ee2b79f..9d83d4c57 100644
--- a/openlp/plugins/presentations/lib/mediaitem.py
+++ b/openlp/plugins/presentations/lib/mediaitem.py
@@ -29,7 +29,7 @@ import os
from PyQt4 import QtCore, QtGui
from openlp.core.lib import MediaManagerItem, BaseListWithDnD, build_icon, \
- SettingsManager, translate, check_item_selected
+ SettingsManager, translate, check_item_selected, Receiver
from openlp.plugins.presentations.lib import MessageListener
log = logging.getLogger(__name__)
@@ -67,7 +67,9 @@ class PresentationMediaItem(MediaManagerItem):
self.ListViewWithDnD_class = PresentationListView
MediaManagerItem.__init__(self, parent, icon, title)
self.message_listener = MessageListener(self)
-
+ QtCore.QObject.connect(Receiver.get_receiver(),
+ QtCore.SIGNAL(u'mediaitem_presentation_rebuild'), self.rebuild)
+
def retranslateUi(self):
"""
The name of the plugin media displayed in UI
@@ -76,9 +78,12 @@ class PresentationMediaItem(MediaManagerItem):
'Select Presentation(s)')
self.Automatic = translate('PresentationPlugin.MediaItem',
'Automatic')
+ self.buildFileMaskString()
+
+ def buildFileMaskString(self):
fileType = u''
for controller in self.controllers:
- if self.controllers[controller].enabled:
+ if self.controllers[controller].enabled():
types = self.controllers[controller].supports + \
self.controllers[controller].alsosupports
for type in types:
@@ -131,13 +136,26 @@ class PresentationMediaItem(MediaManagerItem):
list = SettingsManager.load_list(
self.settingsSection, u'presentations')
self.loadList(list, True)
+ self.populateDisplayTypes()
+
+ def rebuild(self):
+ self.populateDisplayTypes()
+ self.buildFileMaskString()
+
+ def populateDisplayTypes(self):
+ self.DisplayTypeComboBox.clear()
for item in self.controllers:
#load the drop down selection
- if self.controllers[item].enabled:
+ if self.controllers[item].enabled():
self.DisplayTypeComboBox.addItem(item)
if self.DisplayTypeComboBox.count() > 1:
self.DisplayTypeComboBox.insertItem(0, self.Automatic)
self.DisplayTypeComboBox.setCurrentIndex(0)
+ if QtCore.QSettings().value(self.settingsSection + u'/override app',
+ QtCore.QVariant(QtCore.Qt.Unchecked)) == QtCore.Qt.Checked:
+ self.PresentationWidget.show()
+ else:
+ self.PresentationWidget.hide()
def loadList(self, list, initialLoad=False):
"""
@@ -262,11 +280,11 @@ class PresentationMediaItem(MediaManagerItem):
if not filetype:
return None
for controller in self.controllers:
- if self.controllers[controller].enabled:
+ if self.controllers[controller].enabled():
if filetype in self.controllers[controller].supports:
return controller
for controller in self.controllers:
- if self.controllers[controller].enabled:
+ if self.controllers[controller].enabled():
if filetype in self.controllers[controller].alsosupports:
return controller
return None
diff --git a/openlp/plugins/presentations/lib/presentationcontroller.py b/openlp/plugins/presentations/lib/presentationcontroller.py
index ed4081ed6..15d58c206 100644
--- a/openlp/plugins/presentations/lib/presentationcontroller.py
+++ b/openlp/plugins/presentations/lib/presentationcontroller.py
@@ -109,13 +109,6 @@ class PresentationController(object):
self.name = name
self.settings_section = self.plugin.settingsSection
self.available = self.check_available()
- if self.available:
- self.enabled = QtCore.QSettings().value(
- self.settings_section + u'/' + name,
- QtCore.QVariant(QtCore.Qt.Unchecked)).toInt()[0] == \
- QtCore.Qt.Checked
- else:
- self.enabled = False
self.temp_folder = os.path.join(
AppLocation.get_section_data_path(self.settings_section), name)
self.thumbnail_folder = os.path.join(
@@ -127,6 +120,18 @@ class PresentationController(object):
if not os.path.isdir(self.temp_folder):
os.makedirs(self.temp_folder)
+ def enabled(self):
+ """
+ Return whether the controller is currently enabled
+ """
+ if self.available:
+ return QtCore.QSettings().value(
+ self.settings_section + u'/' + self.name,
+ QtCore.QVariant(QtCore.Qt.Checked)).toInt()[0] == \
+ QtCore.Qt.Checked
+ else:
+ return False
+
def check_available(self):
"""
Presentation app is able to run on this machine
diff --git a/openlp/plugins/presentations/lib/presentationtab.py b/openlp/plugins/presentations/lib/presentationtab.py
index 980c62f47..1b9b3bb55 100644
--- a/openlp/plugins/presentations/lib/presentationtab.py
+++ b/openlp/plugins/presentations/lib/presentationtab.py
@@ -25,7 +25,7 @@
from PyQt4 import QtCore, QtGui
-from openlp.core.lib import SettingsTab, translate
+from openlp.core.lib import Receiver, SettingsTab, translate
class PresentationTab(SettingsTab):
"""
@@ -77,7 +77,17 @@ class PresentationTab(SettingsTab):
self.PresentationThemeLayout.setSpacing(8)
self.PresentationThemeLayout.setMargin(0)
self.PresentationThemeLayout.setObjectName(u'PresentationThemeLayout')
+ self.AdvancedGroupBox = QtGui.QGroupBox(self)
+ self.AdvancedGroupBox.setObjectName(u'AdvancedGroupBox')
+ self.AdvancedLayout = QtGui.QVBoxLayout(self.AdvancedGroupBox)
+ self.AdvancedLayout.setSpacing(8)
+ self.AdvancedLayout.setMargin(8)
+ self.AdvancedLayout.setObjectName(u'AdvancedLayout')
+ self.OverrideAppCheckBox = QtGui.QCheckBox(self.AdvancedGroupBox)
+ self.OverrideAppCheckBox.setObjectName(u'OverrideAppCheckBox')
+ self.AdvancedLayout.addWidget(self.OverrideAppCheckBox)
self.PresentationLeftLayout.addWidget(self.VerseDisplayGroupBox)
+ self.PresentationLeftLayout.addWidget(self.AdvancedGroupBox)
self.PresentationLeftSpacer = QtGui.QSpacerItem(40, 20,
QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
self.PresentationLeftLayout.addItem(self.PresentationLeftSpacer)
@@ -107,6 +117,12 @@ class PresentationTab(SettingsTab):
checkbox.setText(
u'%s %s' % (controller.name,
translate('PresentationPlugin.PresentationTab', 'available')))
+ self.AdvancedGroupBox.setTitle(
+ translate('PresentationPlugin.PresentationTab',
+ 'Advanced'))
+ self.OverrideAppCheckBox.setText(
+ translate('PresentationPlugin.PresentationTab',
+ 'Allow presentation application to be overriden'))
def load(self):
"""
@@ -118,15 +134,33 @@ class PresentationTab(SettingsTab):
checkbox = self.PresenterCheckboxes[controller.name]
checkbox.setChecked(QtCore.QSettings().value(
self.settingsSection + u'/' + controller.name,
- QtCore.QVariant(0)).toInt()[0])
+ QtCore.QVariant(QtCore.Qt.Checked)).toInt()[0])
+ self.OverrideAppCheckBox.setChecked(QtCore.QSettings().value(
+ self.settingsSection + u'/override app',
+ QtCore.QVariant(QtCore.Qt.Unchecked)).toInt()[0])
def save(self):
"""
Save the settings.
"""
+ changed = False
for key in self.controllers:
controller = self.controllers[key]
checkbox = self.PresenterCheckboxes[controller.name]
- QtCore.QSettings().setValue(
- self.settingsSection + u'/' + controller.name,
- QtCore.QVariant(checkbox.checkState()))
+ setting_key = self.settingsSection + u'/' + controller.name
+ if QtCore.QSettings().value(setting_key) != checkbox.checkState():
+ changed = True
+ QtCore.QSettings().setValue(setting_key,
+ QtCore.QVariant(checkbox.checkState()))
+ if checkbox.checkState() == QtCore.Qt.Checked:
+ controller.start_process()
+ else:
+ controller.kill()
+ setting_key = self.settingsSection + u'/override app'
+ if QtCore.QSettings().value(setting_key) != \
+ self.OverrideAppCheckBox.checkState():
+ QtCore.QSettings().setValue(setting_key,
+ QtCore.QVariant(self.OverrideAppCheckBox.checkState()))
+ changed = True
+ if changed:
+ Receiver.send_message(u'mediaitem_presentation_rebuild')
diff --git a/openlp/plugins/presentations/presentationplugin.py b/openlp/plugins/presentations/presentationplugin.py
index 50784ed51..dabe5becd 100644
--- a/openlp/plugins/presentations/presentationplugin.py
+++ b/openlp/plugins/presentations/presentationplugin.py
@@ -67,7 +67,7 @@ class PresentationPlugin(Plugin):
Plugin.initialise(self)
self.insertToolboxItem()
for controller in self.controllers:
- if self.controllers[controller].enabled:
+ if self.controllers[controller].enabled():
self.controllers[controller].start_process()
def finalise(self):
@@ -79,7 +79,7 @@ class PresentationPlugin(Plugin):
#Ask each controller to tidy up
for key in self.controllers:
controller = self.controllers[key]
- if controller.enabled:
+ if controller.enabled():
controller.kill()
Plugin.finalise(self)
diff --git a/openlp/plugins/songs/forms/authorsform.py b/openlp/plugins/songs/forms/authorsform.py
index c219b12ac..fdc704294 100644
--- a/openlp/plugins/songs/forms/authorsform.py
+++ b/openlp/plugins/songs/forms/authorsform.py
@@ -97,7 +97,7 @@ class AuthorsForm(QtGui.QDialog, Ui_AuthorsDialog):
if QtGui.QMessageBox.critical(
self, translate('SongsPlugin.AuthorsForm', 'Error'),
translate('SongsPlugin.AuthorsForm',
- 'You haven\'t set a display name for the '
+ 'You have not set a display name for the '
'author, would you like me to combine the first and '
'last names for you?'),
QtGui.QMessageBox.StandardButtons(
diff --git a/openlp/plugins/songs/forms/editsongdialog.py b/openlp/plugins/songs/forms/editsongdialog.py
index 43684c1a9..dccc6d6ba 100644
--- a/openlp/plugins/songs/forms/editsongdialog.py
+++ b/openlp/plugins/songs/forms/editsongdialog.py
@@ -430,7 +430,7 @@ class Ui_EditSongDialog(object):
self.AuthorRemoveButton.setText(
translate('SongsPlugin.EditSongForm', '&Remove'))
self.MaintenanceButton.setText(translate('SongsPlugin.EditSongForm',
- '&Manage Authors, Topics, Books'))
+ '&Manage Authors, Topics, Song Books'))
self.TopicGroupBox.setTitle(
translate('SongsPlugin.EditSongForm', 'Topic'))
self.TopicAddButton.setText(
@@ -441,7 +441,8 @@ class Ui_EditSongDialog(object):
translate('SongsPlugin.EditSongForm', 'Song Book'))
self.SongTabWidget.setTabText(
self.SongTabWidget.indexOf(self.AuthorsTab),
- translate('SongsPlugin.EditSongForm', 'Authors, Topics && Book'))
+ translate('SongsPlugin.EditSongForm',
+ 'Authors, Topics && Song Book'))
self.ThemeGroupBox.setTitle(
translate('SongsPlugin.EditSongForm', 'Theme'))
self.ThemeAddButton.setText(
diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py
index c0db7b741..440371c83 100644
--- a/openlp/plugins/songs/forms/editsongform.py
+++ b/openlp/plugins/songs/forms/editsongform.py
@@ -48,7 +48,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
"""
QtGui.QDialog.__init__(self, parent)
self.parent = parent
- #can this be automated?
+ # can this be automated?
self.width = 400
self.setupUi(self)
# Connecting signals and slots
@@ -118,7 +118,8 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
self.TopicRemoveButton.setEnabled(False)
def loadAuthors(self):
- authors = self.songmanager.get_all_objects(Author, Author.display_name)
+ authors = self.songmanager.get_all_objects(Author,
+ order_by_ref=Author.display_name)
self.AuthorsSelectionComboItem.clear()
self.AuthorsSelectionComboItem.addItem(u'')
for author in authors:
@@ -128,7 +129,8 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
row, QtCore.QVariant(author.id))
def loadTopics(self):
- topics = self.songmanager.get_all_objects(Topic, Topic.name)
+ topics = self.songmanager.get_all_objects(Topic,
+ order_by_ref=Topic.name)
self.SongTopicCombo.clear()
self.SongTopicCombo.addItem(u'')
for topic in topics:
@@ -137,7 +139,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
self.SongTopicCombo.setItemData(row, QtCore.QVariant(topic.id))
def loadBooks(self):
- books = self.songmanager.get_all_objects(Book, Book.name)
+ books = self.songmanager.get_all_objects(Book, order_by_ref=Book.name)
self.SongbookCombo.clear()
self.SongbookCombo.addItem(u'')
for book in books:
@@ -169,7 +171,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
self.loadAuthors()
self.loadTopics()
self.loadBooks()
- #it's a new song to preview is not possible
+ # it's a new song to preview is not possible
self.previewButton.setVisible(False)
def loadSong(self, id, preview):
@@ -180,7 +182,10 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
self.loadBooks()
self.song = self.songmanager.get_object(Song, id)
self.TitleEditItem.setText(self.song.title)
- title = self.song.search_title.split(u'@')
+ if self.song.alternate_title:
+ self.AlternativeEdit.setText(self.song.alternate_title)
+ else:
+ self.AlternativeEdit.setText(u'')
if self.song.song_book_id != 0:
book_name = self.songmanager.get_object(Book,
self.song.song_book_id)
@@ -198,8 +203,6 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
id = 0
self.song.theme_name = None
self.ThemeSelectionComboItem.setCurrentIndex(id)
- if len(title) > 1:
- self.AlternativeEdit.setText(title[1])
if self.song.copyright:
self.CopyrightEditItem.setText(self.song.copyright)
else:
@@ -218,7 +221,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
self.CCLNumberEdit.setText(self.song.ccli_number)
else:
self.CCLNumberEdit.setText(u'')
- #lazy xml migration for now
+ # lazy xml migration for now
self.VerseListWidget.clear()
self.VerseListWidget.setRowCount(0)
self.VerseListWidget.setColumnWidth(0, self.width)
@@ -260,7 +263,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
topic_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(topic.id))
self.TopicsListView.addItem(topic_name)
self.TitleEditItem.setFocus(QtCore.Qt.OtherFocusReason)
- #if not preview hide the preview button
+ # if not preview hide the preview button
self.previewButton.setVisible(False)
if preview:
self.previewButton.setVisible(True)
@@ -285,12 +288,16 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
if QtGui.QMessageBox.question(self,
translate('SongsPlugin.EditSongForm', 'Add Author'),
translate('SongsPlugin.EditSongForm', 'This author does not '
- 'exist, do you want to add them?'),
+ 'exist, do you want to add them?'),
QtGui.QMessageBox.Yes | QtGui.QMessageBox.No,
QtGui.QMessageBox.Yes) == QtGui.QMessageBox.Yes:
- author = Author.populate(first_name=text.rsplit(u' ', 1)[0],
- last_name=text.rsplit(u' ', 1)[1], display_name=text)
- self.songmanager.save_object(author)
+ if text.find(u' ') == -1:
+ author = Author.populate(first_name=u'', last_name=u'',
+ display_name=text)
+ else:
+ author = Author.populate(first_name=text.rsplit(u' ', 1)[0],
+ last_name=text.rsplit(u' ', 1)[1], display_name=text)
+ self.songmanager.save_object(author, False)
self.song.authors.append(author)
author_item = QtGui.QListWidgetItem(
unicode(author.display_name))
@@ -304,18 +311,26 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
elif item > 0:
item_id = (self.AuthorsSelectionComboItem.itemData(item)).toInt()[0]
author = self.songmanager.get_object(Author, item_id)
- self.song.authors.append(author)
- author_item = QtGui.QListWidgetItem(unicode(author.display_name))
- author_item.setData(QtCore.Qt.UserRole, QtCore.QVariant(author.id))
- self.AuthorsListView.addItem(author_item)
+ if author in self.song.authors:
+ QtGui.QMessageBox.warning(self,
+ translate('SongsPlugin.EditSongForm', 'Error'),
+ translate('SongsPlugin.EditSongForm', 'This author is '
+ 'already in the list.'))
+ else:
+ self.song.authors.append(author)
+ author_item = QtGui.QListWidgetItem(unicode(
+ author.display_name))
+ author_item.setData(QtCore.Qt.UserRole,
+ QtCore.QVariant(author.id))
+ self.AuthorsListView.addItem(author_item)
self.AuthorsSelectionComboItem.setCurrentIndex(0)
else:
QtGui.QMessageBox.warning(self,
translate('SongsPlugin.EditSongForm', 'No Author Selected'),
translate('SongsPlugin.EditSongForm', 'You have not selected '
- 'a valid author. Either select an author from the list, '
- 'or type in a new author and click the "Add Author to '
- 'Song" button to add the new author.'),
+ 'a valid author. Either select an author from the list, '
+ 'or type in a new author and click the "Add Author to '
+ 'Song" button to add the new author.'),
QtGui.QMessageBox.Ok, QtGui.QMessageBox.Ok)
def onAuthorsListViewPressed(self):
@@ -338,11 +353,11 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
if QtGui.QMessageBox.question(self,
translate('SongsPlugin.EditSongForm', 'Add Topic'),
translate('SongsPlugin.EditSongForm', 'This topic does not '
- 'exist, do you want to add it?'),
+ 'exist, do you want to add it?'),
QtGui.QMessageBox.Yes | QtGui.QMessageBox.No,
QtGui.QMessageBox.Yes) == QtGui.QMessageBox.Yes:
topic = Topic.populate(name=text)
- self.songmanager.save_object(topic)
+ self.songmanager.save_object(topic, False)
self.song.topics.append(topic)
topic_item = QtGui.QListWidgetItem(unicode(topic.name))
topic_item.setData(QtCore.Qt.UserRole,
@@ -355,18 +370,25 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
elif item > 0:
item_id = (self.SongTopicCombo.itemData(item)).toInt()[0]
topic = self.songmanager.get_object(Topic, item_id)
- self.song.topics.append(topic)
- topic_item = QtGui.QListWidgetItem(unicode(topic.name))
- topic_item.setData(QtCore.Qt.UserRole, QtCore.QVariant(topic.id))
- self.TopicsListView.addItem(topic_item)
+ if topic in self.song.topics:
+ QtGui.QMessageBox.warning(self,
+ translate('SongsPlugin.EditSongForm', 'Error'),
+ translate('SongsPlugin.EditSongForm', 'This topic is '
+ 'already in the list.'))
+ else:
+ self.song.topics.append(topic)
+ topic_item = QtGui.QListWidgetItem(unicode(topic.name))
+ topic_item.setData(QtCore.Qt.UserRole,
+ QtCore.QVariant(topic.id))
+ self.TopicsListView.addItem(topic_item)
self.SongTopicCombo.setCurrentIndex(0)
else:
QtGui.QMessageBox.warning(self,
translate('SongsPlugin.EditSongForm', 'No Topic Selected'),
translate('SongsPlugin.EditSongForm', 'You have not selected '
- 'a valid topic. Either select a topic from the list, or '
- 'type in a new topic and click the "Add Topic to Song" '
- 'button to add the new topic.'),
+ 'a valid topic. Either select a topic from the list, or '
+ 'type in a new topic and click the "Add Topic to Song" '
+ 'button to add the new topic.'),
QtGui.QMessageBox.Ok, QtGui.QMessageBox.Ok)
def onTopicListViewPressed(self):
@@ -382,23 +404,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
self.TopicsListView.takeItem(row)
def onSongBookComboChanged(self, item):
- item = int(self.SongbookCombo.currentIndex())
- text = unicode(self.SongbookCombo.currentText())
- if item == 0 and text:
- if QtGui.QMessageBox.question(self,
- translate('SongsPlugin.EditSongForm', 'Add Book'),
- translate('SongsPlugin.EditSongForm', 'This song book does '
- 'not exist, do you want to add it?'),
- QtGui.QMessageBox.Yes | QtGui.QMessageBox.No,
- QtGui.QMessageBox.Yes) == QtGui.QMessageBox.Yes:
- book = Book.populate(name=text)
- self.songmanager.save_object(book)
- self.song.book = book
- self.loadBooks()
- else:
- return
- elif item >= 1:
- item = int(self.SongbookCombo.currentIndex())
+ if item >= 1:
self.song.song_book_id = \
(self.SongbookCombo.itemData(item)).toInt()[0]
else:
@@ -406,7 +412,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
def onThemeComboChanged(self, item):
if item == 0:
- #None means no Theme
+ # None means no Theme
self.song.theme_name = None
else:
them_name = unicode(self.ThemeSelectionComboItem.itemText(item))
@@ -443,7 +449,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
data = u'%s:%s' % (verse, subVerse)
item.setData(QtCore.Qt.UserRole, QtCore.QVariant(data))
item.setText(afterText)
- #number of lines has change so repaint the list moving the data
+ # number of lines has change so repaint the list moving the data
if len(tempText.split(u'\n')) != len(afterText.split(u'\n')):
tempList = {}
tempId = {}
@@ -482,7 +488,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
for count, parts in enumerate(match.split(u']---\n')):
if len(parts) > 1:
if count == 0:
- #make sure the tag is correctly cased
+ # make sure the tag is correctly cased
variant = u'%s%s' % \
(parts[0:1].upper(), parts[1:].lower())
else:
@@ -519,7 +525,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
QtGui.QMessageBox.critical(self,
translate('SongsPlugin.EditSongForm', 'Error'),
translate('SongsPlugin.EditSongForm',
- 'You need to type in a song title.'))
+ 'You need to type in a song title.'))
return False
if self.VerseListWidget.rowCount() == 0:
self.SongTabWidget.setCurrentIndex(0)
@@ -527,7 +533,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
QtGui.QMessageBox.critical(self,
translate('SongsPlugin.EditSongForm', 'Error'),
translate('SongsPlugin.EditSongForm',
- 'You need to type in at least one verse.'))
+ 'You need to type in at least one verse.'))
return False
if self.AuthorsListView.count() == 0:
self.SongTabWidget.setCurrentIndex(1)
@@ -535,8 +541,8 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
answer = QtGui.QMessageBox.warning(self,
translate('SongsPlugin.EditSongForm', 'Warning'),
translate('SongsPlugin.EditSongForm',
- 'You have not added any authors for this song. Do you '
- 'want to add an author now?'),
+ 'You have not added any authors for this song. Do you '
+ 'want to add an author now?'),
QtGui.QMessageBox.Yes | QtGui.QMessageBox.No)
if answer == QtGui.QMessageBox.Yes:
return False
@@ -567,9 +573,9 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
QtGui.QMessageBox.critical(self,
translate('SongsPlugin.EditSongForm', 'Error'),
unicode(translate('SongsPlugin.EditSongForm',
- 'The verse order is invalid. There is no verse '
- 'corresponding to %s. Valid entries are %s.')) % \
- (order_names[count], valid))
+ 'The verse order is invalid. There is no verse '
+ 'corresponding to %s. Valid entries are %s.')) % \
+ (order_names[count], valid))
return False
for count, verse in enumerate(verses):
if verse not in order:
@@ -578,10 +584,9 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
answer = QtGui.QMessageBox.warning(self,
translate('SongsPlugin.EditSongForm', 'Warning'),
unicode(translate('SongsPlugin.EditSongForm',
- 'You have not used %s anywhere in the verse '
- 'order. Are you sure you want to save the song '
- 'like this?')) % \
- verse_names[count].replace(u':', u' '),
+ 'You have not used %s anywhere in the verse '
+ 'order. Are you sure you want to save the song '
+ 'like this?')) % verse_names[count].replace(u':', u' '),
QtGui.QMessageBox.Yes | QtGui.QMessageBox.No)
if answer == QtGui.QMessageBox.No:
return False
@@ -616,12 +621,28 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
def accept(self):
log.debug(u'accept')
+ item = int(self.SongbookCombo.currentIndex())
+ text = unicode(self.SongbookCombo.currentText())
+ if item == 0 and text:
+ if QtGui.QMessageBox.question(self,
+ translate('SongsPlugin.EditSongForm', 'Add Book'),
+ translate('SongsPlugin.EditSongForm', 'This song book does '
+ 'not exist, do you want to add it?'),
+ QtGui.QMessageBox.Yes | QtGui.QMessageBox.No,
+ QtGui.QMessageBox.Yes) == QtGui.QMessageBox.Yes:
+ book = Book.populate(name=text, publisher=u'')
+ self.songmanager.save_object(book)
+ self.song.book = book
+ self.loadBooks()
+ else:
+ return
if self.saveSong():
Receiver.send_message(u'songs_load_list')
self.close()
def saveSong(self):
self.song.title = unicode(self.TitleEditItem.text())
+ self.song.alternate_title = unicode(self.AlternativeEdit.text())
self.song.copyright = unicode(self.CopyrightEditItem.text())
self.song.search_title = self.song.title + u'@' + \
unicode(self.AlternativeEdit.text())
diff --git a/openlp/plugins/songs/forms/songbookdialog.py b/openlp/plugins/songs/forms/songbookdialog.py
index 2ab223b34..a2e5658cd 100644
--- a/openlp/plugins/songs/forms/songbookdialog.py
+++ b/openlp/plugins/songs/forms/songbookdialog.py
@@ -41,6 +41,7 @@ class Ui_SongBookDialog(object):
QtGui.QFormLayout.LabelRole, self.NameLabel)
self.NameEdit = QtGui.QLineEdit(SongBookDialog)
self.NameEdit.setObjectName(u'NameEdit')
+ self.NameLabel.setBuddy(self.NameEdit)
self.SongBookLayout.setWidget(0,
QtGui.QFormLayout.FieldRole, self.NameEdit)
self.PublisherLabel = QtGui.QLabel(SongBookDialog)
@@ -49,6 +50,7 @@ class Ui_SongBookDialog(object):
QtGui.QFormLayout.LabelRole, self.PublisherLabel)
self.PublisherEdit = QtGui.QLineEdit(SongBookDialog)
self.PublisherEdit.setObjectName(u'PublisherEdit')
+ self.PublisherLabel.setBuddy(self.PublisherEdit)
self.SongBookLayout.setWidget(1,
QtGui.QFormLayout.FieldRole, self.PublisherEdit)
self.ButtonBox = QtGui.QDialogButtonBox(SongBookDialog)
@@ -68,7 +70,7 @@ class Ui_SongBookDialog(object):
def retranslateUi(self, SongBookDialog):
SongBookDialog.setWindowTitle(
- translate('SongsPlugin.SongBookForm', 'Edit Book'))
+ translate('SongsPlugin.SongBookForm', 'Song Book Maintenance'))
self.NameLabel.setText(translate('SongsPlugin.SongBookForm', '&Name:'))
self.PublisherLabel.setText(
translate('SongsPlugin.SongBookForm', '&Publisher:'))
diff --git a/openlp/plugins/songs/forms/songimportform.py b/openlp/plugins/songs/forms/songimportform.py
index 09db3dcb8..db66be4df 100644
--- a/openlp/plugins/songs/forms/songimportform.py
+++ b/openlp/plugins/songs/forms/songimportform.py
@@ -178,7 +178,8 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
SettingsManager.get_last_dir(self.songsplugin.settingsSection, 1))
if filename:
editbox.setText(filename)
- self.config.set_last_dir(filename, 1)
+ SettingsManager.set_last_dir(self.songsplugin.settingsSection,
+ filename, 1)
def incrementProgressBar(self, status_text):
log.debug(u'IncrementBar %s', status_text)
@@ -253,4 +254,4 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
self.ImportProgressBar.setValue(self.ImportProgressBar.maximum())
self.finishButton.setVisible(True)
self.cancelButton.setVisible(False)
- Receiver.send_message(u'process_events')
\ No newline at end of file
+ Receiver.send_message(u'process_events')
diff --git a/openlp/plugins/songs/forms/songimportwizard.py b/openlp/plugins/songs/forms/songimportwizard.py
index d740d42a5..57ad3d9cf 100644
--- a/openlp/plugins/songs/forms/songimportwizard.py
+++ b/openlp/plugins/songs/forms/songimportwizard.py
@@ -109,16 +109,16 @@ class Ui_SongImportWizard(object):
self.OpenLyricsButtonLayout.setSpacing(8)
self.OpenLyricsButtonLayout.setObjectName(u'OpenLyricsButtonLayout')
self.OpenLyricsAddButton = QtGui.QPushButton(self.OpenLyricsPage)
- self.OpenLyricsAddButton.setIcon(
- build_icon(u':/general/general_open.png'))
+ openIcon = build_icon(u':/general/general_open.png')
+ self.OpenLyricsAddButton.setIcon(openIcon)
self.OpenLyricsAddButton.setObjectName(u'OpenLyricsAddButton')
self.OpenLyricsButtonLayout.addWidget(self.OpenLyricsAddButton)
self.OpenLyricsButtonSpacer = QtGui.QSpacerItem(40, 20,
QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.OpenLyricsButtonLayout.addItem(self.OpenLyricsButtonSpacer)
self.OpenLyricsRemoveButton = QtGui.QPushButton(self.OpenLyricsPage)
- self.OpenLyricsRemoveButton.setIcon(
- build_icon(u':/general/general_delete.png'))
+ deleteIcon = build_icon(u':/general/general_delete.png')
+ self.OpenLyricsRemoveButton.setIcon(deleteIcon)
self.OpenLyricsRemoveButton.setObjectName(u'OpenLyricsRemoveButton')
self.OpenLyricsButtonLayout.addWidget(self.OpenLyricsRemoveButton)
self.OpenLyricsLayout.addLayout(self.OpenLyricsButtonLayout)
@@ -136,14 +136,14 @@ class Ui_SongImportWizard(object):
self.OpenSongButtonLayout.setSpacing(8)
self.OpenSongButtonLayout.setObjectName(u'OpenSongButtonLayout')
self.OpenSongAddButton = QtGui.QPushButton(self.OpenSongPage)
- self.OpenSongAddButton.setIcon(self.OpenIcon)
+ self.OpenSongAddButton.setIcon(openIcon)
self.OpenSongAddButton.setObjectName(u'OpenSongAddButton')
self.OpenSongButtonLayout.addWidget(self.OpenSongAddButton)
self.OpenSongButtonSpacer = QtGui.QSpacerItem(40, 20,
QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.OpenSongButtonLayout.addItem(self.OpenSongButtonSpacer)
self.OpenSongRemoveButton = QtGui.QPushButton(self.OpenSongPage)
- self.OpenSongRemoveButton.setIcon(self.DeleteIcon)
+ self.OpenSongRemoveButton.setIcon(deleteIcon)
self.OpenSongRemoveButton.setObjectName(u'OpenSongRemoveButton')
self.OpenSongButtonLayout.addWidget(self.OpenSongRemoveButton)
self.OpenSongLayout.addLayout(self.OpenSongButtonLayout)
@@ -161,14 +161,14 @@ class Ui_SongImportWizard(object):
self.CCLIButtonLayout.setSpacing(8)
self.CCLIButtonLayout.setObjectName(u'CCLIButtonLayout')
self.CCLIAddButton = QtGui.QPushButton(self.CCLIPage)
- self.CCLIAddButton.setIcon(self.OpenIcon)
+ self.CCLIAddButton.setIcon(openIcon)
self.CCLIAddButton.setObjectName(u'CCLIAddButton')
self.CCLIButtonLayout.addWidget(self.CCLIAddButton)
self.CCLIButtonSpacer = QtGui.QSpacerItem(40, 20,
QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.CCLIButtonLayout.addItem(self.CCLIButtonSpacer)
self.CCLIRemoveButton = QtGui.QPushButton(self.CCLIPage)
- self.CCLIRemoveButton.setIcon(self.DeleteIcon)
+ self.CCLIRemoveButton.setIcon(deleteIcon)
self.CCLIRemoveButton.setObjectName(u'CCLIRemoveButton')
self.CCLIButtonLayout.addWidget(self.CCLIRemoveButton)
self.CCLILayout.addLayout(self.CCLIButtonLayout)
@@ -190,7 +190,7 @@ class Ui_SongImportWizard(object):
self.CSVFilenameEdit.setObjectName(u'CSVFilenameEdit')
self.CSVFileLayout.addWidget(self.CSVFilenameEdit)
self.CSVBrowseButton = QtGui.QToolButton(self.CSVPage)
- self.CSVBrowseButton.setIcon(self.OpenIcon)
+ self.CSVBrowseButton.setIcon(openIcon)
self.CSVBrowseButton.setObjectName(u'CSVBrowseButton')
self.CSVFileLayout.addWidget(self.CSVBrowseButton)
self.CSVLayout.setLayout(0, QtGui.QFormLayout.FieldRole,
@@ -213,14 +213,11 @@ class Ui_SongImportWizard(object):
self.ImportProgressBar.setObjectName(u'ImportProgressBar')
self.ImportLayout.addWidget(self.ImportProgressBar)
SongImportWizard.addPage(self.ImportPage)
-
self.retranslateUi(SongImportWizard)
self.FormatStackedWidget.setCurrentIndex(0)
- QtCore.QObject.connect(
- self.FormatComboBox,
+ QtCore.QObject.connect(self.FormatComboBox,
QtCore.SIGNAL(u'currentIndexChanged(int)'),
- self.FormatStackedWidget.setCurrentIndex
- )
+ self.FormatStackedWidget.setCurrentIndex)
QtCore.QMetaObject.connectSlotsByName(SongImportWizard)
def retranslateUi(self, SongImportWizard):
@@ -275,4 +272,3 @@ class Ui_SongImportWizard(object):
translate('SongsPlugin.ImportWizardForm', 'Ready.'))
self.ImportProgressBar.setFormat(
translate('SongsPlugin.ImportWizardForm', '%p%'))
-
diff --git a/openlp/plugins/songs/forms/songmaintenancedialog.py b/openlp/plugins/songs/forms/songmaintenancedialog.py
index f86754e0c..3e2f37dd3 100644
--- a/openlp/plugins/songs/forms/songmaintenancedialog.py
+++ b/openlp/plugins/songs/forms/songmaintenancedialog.py
@@ -217,7 +217,7 @@ class Ui_SongMaintenanceDialog(object):
self.TypeListWidget.item(1).setText(
translate('SongsPlugin.SongMaintenanceForm', 'Topics'))
self.TypeListWidget.item(2).setText(
- translate('SongsPlugin.SongMaintenanceForm', 'Books/Hymnals'))
+ translate('SongsPlugin.SongMaintenanceForm', 'Song Books'))
self.AuthorAddButton.setText(
translate('SongsPlugin.SongMaintenanceForm', '&Add'))
self.AuthorEditButton.setText(
diff --git a/openlp/plugins/songs/forms/songmaintenanceform.py b/openlp/plugins/songs/forms/songmaintenanceform.py
index 2a848ad98..e0506e5bc 100644
--- a/openlp/plugins/songs/forms/songmaintenanceform.py
+++ b/openlp/plugins/songs/forms/songmaintenanceform.py
@@ -26,9 +26,9 @@
from PyQt4 import QtGui, QtCore
from sqlalchemy.sql import and_
-from openlp.core.lib import translate
+from openlp.core.lib import Receiver, translate
from openlp.plugins.songs.forms import AuthorsForm, TopicsForm, SongBookForm
-from openlp.plugins.songs.lib.db import Author, Book, Topic
+from openlp.plugins.songs.lib.db import Author, Book, Topic, Song
from songmaintenancedialog import Ui_SongMaintenanceDialog
class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
@@ -102,7 +102,8 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
Reloads the Authors list.
"""
self.AuthorsListWidget.clear()
- authors = self.songmanager.get_all_objects(Author, Author.display_name)
+ authors = self.songmanager.get_all_objects(Author,
+ order_by_ref=Author.display_name)
for author in authors:
if author.display_name:
author_name = QtGui.QListWidgetItem(author.display_name)
@@ -117,7 +118,8 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
Reloads the Topics list.
"""
self.TopicsListWidget.clear()
- topics = self.songmanager.get_all_objects(Topic, Topic.name)
+ topics = self.songmanager.get_all_objects(Topic,
+ order_by_ref=Topic.name)
for topic in topics:
topic_name = QtGui.QListWidgetItem(topic.name)
topic_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(topic.id))
@@ -128,7 +130,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
Reloads the Books list.
"""
self.BooksListWidget.clear()
- books = self.songmanager.get_all_objects(Book, Book.name)
+ books = self.songmanager.get_all_objects(Book, order_by_ref=Book.name)
for book in books:
book_name = QtGui.QListWidgetItem(u'%s (%s)' % (book.name,
book.publisher))
@@ -140,13 +142,10 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
Returns False if the given Author is already in the list otherwise
True.
"""
- authors = self.songmanager.get_all_objects_filtered(Author,
- and_(
- Author.first_name == new_author.first_name,
- Author.last_name == new_author.last_name,
- Author.display_name == new_author.display_name
- )
- )
+ authors = self.songmanager.get_all_objects(Author,
+ and_(Author.first_name == new_author.first_name,
+ Author.last_name == new_author.last_name,
+ Author.display_name == new_author.display_name))
if len(authors) > 0:
# If we edit an existing Author, we need to make sure that we do
# not return False when nothing has changed (because this would
@@ -165,7 +164,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
"""
Returns False if the given Topic is already in the list otherwise True.
"""
- topics = self.songmanager.get_all_objects_filtered(Topic,
+ topics = self.songmanager.get_all_objects(Topic,
Topic.name == new_topic.name)
if len(topics) > 0:
# If we edit an existing Topic, we need to make sure that we do
@@ -185,8 +184,8 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
"""
Returns False if the given Book is already in the list otherwise True.
"""
- books = self.songmanager.get_all_objects_filtered(Book,
- and_(Book.name == new_book.name,
+ books = self.songmanager.get_all_objects(Book,
+ and_(Book.name == new_book.name,
Book.publisher == new_book.publisher))
if len(books) > 0:
# If we edit an existing Book, we need to make sure that we do
@@ -212,11 +211,16 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
if self.checkAuthor(author):
if self.songmanager.save_object(author):
self.resetAuthors()
+ else:
+ QtGui.QMessageBox.critical(self,
+ translate('SongsPlugin.SongMaintenanceForm', 'Error'),
+ translate('SongsPlugin.SongMaintenanceForm',
+ 'Could not add your author.'))
else:
QtGui.QMessageBox.critical(self,
translate('SongsPlugin.SongMaintenanceForm', 'Error'),
translate('SongsPlugin.SongMaintenanceForm',
- 'Could not add your author.'))
+ 'This author already exists.'))
def onTopicAddButtonClick(self):
if self.topicform.exec_():
@@ -224,25 +228,34 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
if self.checkTopic(topic):
if self.songmanager.save_object(topic):
self.resetTopics()
+ else:
+ QtGui.QMessageBox.critical(self,
+ translate('SongsPlugin.SongMaintenanceForm', 'Error'),
+ translate('SongsPlugin.SongMaintenanceForm',
+ 'Could not add your topic.'))
else:
QtGui.QMessageBox.critical(self,
translate('SongsPlugin.SongMaintenanceForm', 'Error'),
translate('SongsPlugin.SongMaintenanceForm',
- 'Could not add your topic.'))
+ 'This topic already exists.'))
def onBookAddButtonClick(self):
if self.bookform.exec_():
- book = Book.populate(
- name=unicode(self.bookform.NameEdit.text()),
+ book = Book.populate(name=unicode(self.bookform.NameEdit.text()),
publisher=unicode(self.bookform.PublisherEdit.text()))
if self.checkBook(book):
if self.songmanager.save_object(book):
self.resetBooks()
+ else:
+ QtGui.QMessageBox.critical(self,
+ translate('SongsPlugin.SongMaintenanceForm', 'Error'),
+ translate('SongsPlugin.SongMaintenanceForm',
+ 'Could not add your book.'))
else:
QtGui.QMessageBox.critical(self,
translate('SongsPlugin.SongMaintenanceForm', 'Error'),
translate('SongsPlugin.SongMaintenanceForm',
- 'Could not add your book.'))
+ 'This book already exists.'))
def onAuthorEditButtonClick(self):
author_id = self._getCurrentItemId(self.AuthorsListWidget)
@@ -266,6 +279,24 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
if self.checkAuthor(author, True):
if self.songmanager.save_object(author):
self.resetAuthors()
+ Receiver.send_message(u'songs_load_list')
+ else:
+ QtGui.QMessageBox.critical(self,
+ translate('SongsPlugin.SongMaintenanceForm',
+ 'Error'),
+ translate('SongsPlugin.SongMaintenanceForm',
+ 'Could not save your changes.'))
+ elif QtGui.QMessageBox.critical(self,
+ translate('SongsPlugin.SongMaintenanceForm', 'Error'),
+ translate('SongsPlugin.SongMaintenanceForm', 'The author %s'
+ ' already exists. Would you like to make songs with author '
+ '%s use the existing author %s?' % (author.display_name,
+ temp_display_name, author.display_name)),
+ QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.No |
+ QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.Yes:
+ self.mergeAuthors(author)
+ self.resetAuthors()
+ Receiver.send_message(u'songs_load_list')
else:
# We restore the author's old first and last name as well as
# his display name.
@@ -275,7 +306,8 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
QtGui.QMessageBox.critical(self,
translate('SongsPlugin.SongMaintenanceForm', 'Error'),
translate('SongsPlugin.SongMaintenanceForm',
- 'Could not save your author.'))
+ 'Could not save your modified author, because he '
+ 'already exists.'))
def onTopicEditButtonClick(self):
topic_id = self._getCurrentItemId(self.TopicsListWidget)
@@ -289,18 +321,37 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
if self.checkTopic(topic, True):
if self.songmanager.save_object(topic):
self.resetTopics()
+ else:
+ QtGui.QMessageBox.critical(self,
+ translate('SongsPlugin.SongMaintenanceForm',
+ 'Error'),
+ translate('SongsPlugin.SongMaintenanceForm',
+ 'Could not save your changes.'))
+ elif QtGui.QMessageBox.critical(self,
+ translate('SongsPlugin.SongMaintenanceForm', 'Error'),
+ translate('SongsPlugin.SongMaintenanceForm', 'The topic %s '
+ 'already exists. Would you like to make songs with topic %s'
+ ' use the existing topic %s?' % (topic.name, temp_name,
+ topic.name)),
+ QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.No |
+ QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.Yes:
+ self.mergeTopics(topic)
+ self.resetTopics()
else:
# We restore the topics's old name.
topic.name = temp_name
QtGui.QMessageBox.critical(self,
translate('SongsPlugin.SongMaintenanceForm', 'Error'),
translate('SongsPlugin.SongMaintenanceForm',
- 'Could not save your topic.'))
+ 'Could not save your modified topic, because it '
+ 'already exists.'))
def onBookEditButtonClick(self):
book_id = self._getCurrentItemId(self.BooksListWidget)
if book_id != -1:
book = self.songmanager.get_object(Book, book_id)
+ if book.publisher is None:
+ book.publisher = u''
self.bookform.NameEdit.setText(book.name)
self.bookform.PublisherEdit.setText(book.publisher)
# Save the book's name and publisher for the case that they have to
@@ -313,18 +364,89 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
if self.checkBook(book, True):
if self.songmanager.save_object(book):
self.resetBooks()
+ else:
+ QtGui.QMessageBox.critical(self,
+ translate('SongsPlugin.SongMaintenanceForm',
+ 'Error'),
+ translate('SongsPlugin.SongMaintenanceForm',
+ 'Could not save your changes.'))
+ elif QtGui.QMessageBox.critical(self,
+ translate('SongsPlugin.SongMaintenanceForm', 'Error'),
+ translate('SongsPlugin.SongMaintenanceForm', 'The book %s '
+ 'already exists. Would you like to make songs with book %s '
+ 'use the existing book %s?' % (book.name, temp_name,
+ book.name)),
+ QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.No |
+ QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.Yes:
+ self.mergeBooks(book)
+ self.resetBooks()
else:
# We restore the book's old name and publisher.
book.name = temp_name
book.publisher = temp_publisher
- QtGui.QMessageBox.critical(self,
- translate('SongsPlugin.SongMaintenanceForm', 'Error'),
- translate('SongsPlugin.SongMaintenanceForm',
- 'Could not save your book.'))
+
+ def mergeAuthors(self, old_author):
+ '''
+ Merges two authors into one author.
+
+ ``old_author``
+ The author which will be deleted afterwards.
+ '''
+ existing_author = self.songmanager.get_object_filtered(Author,
+ and_(Author.first_name == old_author.first_name,
+ Author.last_name == old_author.last_name,
+ Author.display_name == old_author.display_name))
+ songs = self.songmanager.get_all_objects(Song,
+ Song.authors.contains(old_author))
+ for song in songs:
+ # We check if the song has already existing_author as author. If
+ # that is not the case we add it.
+ if existing_author not in song.authors:
+ song.authors.append(existing_author)
+ song.authors.remove(old_author)
+ self.songmanager.save_object(song)
+ self.songmanager.delete_object(Author, old_author.id)
+
+ def mergeTopics(self, old_topic):
+ '''
+ Merges two topics into one topic.
+
+ ``old_topic``
+ The topic which will be deleted afterwards.
+ '''
+ existing_topic = self.songmanager.get_object_filtered(Topic,
+ Topic.name == old_topic.name)
+ songs = self.songmanager.get_all_objects(Song,
+ Song.topics.contains(old_topic))
+ for song in songs:
+ # We check if the song has already existing_topic as topic. If that
+ # is not the case we add it.
+ if existing_topic not in song.topics:
+ song.topics.append(existing_topic)
+ song.topics.remove(old_topic)
+ self.songmanager.save_object(song)
+ self.songmanager.delete_object(Topic, old_topic.id)
+
+ def mergeBooks(self, old_book):
+ '''
+ Merges two books into one book.
+
+ ``old_book``
+ The book which will be deleted afterwards.
+ '''
+ existing_book = self.songmanager.get_object_filtered(Book,
+ and_(Book.name == old_book.name,
+ Book.publisher == old_book.publisher))
+ songs = self.songmanager.get_all_objects(Song,
+ Song.song_book_id == old_book.id)
+ for song in songs:
+ song.song_book_id = existing_book.id
+ self.songmanager.save_object(song)
+ self.songmanager.delete_object(Book, old_book.id)
def onAuthorDeleteButtonClick(self):
"""
- Delete the author if the author is not attached to any songs
+ Delete the author if the author is not attached to any songs.
"""
self._deleteItem(Author, self.AuthorsListWidget, self.resetAuthors,
translate('SongsPlugin.SongMaintenanceForm', 'Delete Author'),
@@ -337,7 +459,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
def onTopicDeleteButtonClick(self):
"""
- Delete the Book is the Book is not attached to any songs
+ Delete the Book is the Book is not attached to any songs.
"""
self._deleteItem(Topic, self.TopicsListWidget, self.resetTopics,
translate('SongsPlugin.SongMaintenanceForm', 'Delete Topic'),
@@ -350,7 +472,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
def onBookDeleteButtonClick(self):
"""
- Delete the Book is the Book is not attached to any songs
+ Delete the Book is the Book is not attached to any songs.
"""
self._deleteItem(Book, self.BooksListWidget, self.resetBooks,
translate('SongsPlugin.SongMaintenanceForm', 'Delete Book'),
diff --git a/openlp/plugins/songs/lib/__init__.py b/openlp/plugins/songs/lib/__init__.py
index ef7b8b7d3..0e2b93bd6 100644
--- a/openlp/plugins/songs/lib/__init__.py
+++ b/openlp/plugins/songs/lib/__init__.py
@@ -141,6 +141,8 @@ from xml import LyricsXML, SongXMLBuilder, SongXMLParser
from songstab import SongsTab
from mediaitem import SongMediaItem
from songimport import SongImport
+from opensongimport import OpenSongImport
+from olpimport import OpenLPSongImport
try:
from sofimport import SofImport
from oooimport import OooImport
diff --git a/openlp/plugins/songs/lib/db.py b/openlp/plugins/songs/lib/db.py
index 655043144..156a5e383 100644
--- a/openlp/plugins/songs/lib/db.py
+++ b/openlp/plugins/songs/lib/db.py
@@ -46,6 +46,12 @@ class Book(BaseModel):
return u'' % (
str(self.id), self.name, self.publisher)
+class MediaFile(BaseModel):
+ """
+ MediaFile model
+ """
+ pass
+
class Song(BaseModel):
"""
Song model
@@ -75,6 +81,13 @@ def init_schema(url):
Column(u'display_name', types.Unicode(255), nullable=False)
)
+ # Definition of the "media_files" table
+ media_files_table = Table(u'media_files', metadata,
+ Column(u'id', types.Integer, primary_key=True),
+ Column(u'file_name', types.Unicode(255), nullable=False),
+ Column(u'type', types.Unicode(64), nullable=False, default=u'audio')
+ )
+
# Definition of the "song_books" table
song_books_table = Table(u'song_books', metadata,
Column(u'id', types.Integer, primary_key=True),
@@ -88,6 +101,7 @@ def init_schema(url):
Column(u'song_book_id', types.Integer,
ForeignKey(u'song_books.id'), default=0),
Column(u'title', types.Unicode(255), nullable=False),
+ Column(u'alternate_title', types.Unicode(255)),
Column(u'lyrics', types.UnicodeText, nullable=False),
Column(u'verse_order', types.Unicode(128)),
Column(u'copyright', types.Unicode(255)),
@@ -113,6 +127,14 @@ def init_schema(url):
ForeignKey(u'songs.id'), primary_key=True)
)
+ # Definition of the "media_files_songs" table
+ media_files_songs_table = Table(u'media_files_songs', metadata,
+ Column(u'media_file_id', types.Integer,
+ ForeignKey(u'media_files.id'), primary_key=True),
+ Column(u'song_id', types.Integer,
+ ForeignKey(u'songs.id'), primary_key=True)
+ )
+
# Definition of the "songs_topics" table
songs_topics_table = Table(u'songs_topics', metadata,
Column(u'song_id', types.Integer,
@@ -125,6 +147,7 @@ def init_schema(url):
Index(u'authors_id', authors_table.c.id)
Index(u'authors_display_name_id', authors_table.c.display_name,
authors_table.c.id)
+ Index(u'media_files_id', media_files_table.c.id)
Index(u'song_books_id', song_books_table.c.id)
Index(u'songs_id', songs_table.c.id)
Index(u'topics_id', topics_table.c.id)
@@ -132,6 +155,10 @@ def init_schema(url):
authors_songs_table.c.song_id)
Index(u'authors_songs_song', authors_songs_table.c.song_id,
authors_songs_table.c.author_id)
+ Index(u'media_files_songs_file', media_files_songs_table.c.media_file_id,
+ media_files_songs_table.c.song_id)
+ Index(u'media_files_songs_song', media_files_songs_table.c.song_id,
+ media_files_songs_table.c.media_file_id)
Index(u'topics_song_topic', songs_topics_table.c.topic_id,
songs_topics_table.c.song_id)
Index(u'topics_song_song', songs_topics_table.c.song_id,
@@ -139,12 +166,17 @@ def init_schema(url):
mapper(Author, authors_table)
mapper(Book, song_books_table)
+ mapper(MediaFile, media_files_table)
mapper(Song, songs_table,
- properties={'authors': relation(Author, backref='songs',
- secondary=authors_songs_table),
+ properties={
+ 'authors': relation(Author, backref='songs',
+ secondary=authors_songs_table),
'book': relation(Book, backref='songs'),
+ 'media_files': relation(MediaFile, backref='songs',
+ secondary=media_files_songs_table),
'topics': relation(Topic, backref='songs',
- secondary=songs_topics_table)})
+ secondary=songs_topics_table)
+ })
mapper(Topic, topics_table)
metadata.create_all(checkfirst=True)
diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py
index 93f376d1f..0b9ef789d 100644
--- a/openlp/plugins/songs/lib/mediaitem.py
+++ b/openlp/plugins/songs/lib/mediaitem.py
@@ -164,20 +164,20 @@ class SongMediaItem(MediaManagerItem):
search_type = self.SearchTypeComboBox.currentIndex()
if search_type == 0:
log.debug(u'Titles Search')
- search_results = self.parent.manager.get_all_objects_filtered(Song,
+ search_results = self.parent.manager.get_all_objects(Song,
Song.search_title.like(u'%' + search_keywords + u'%'),
Song.search_title.asc())
self.displayResultsSong(search_results)
elif search_type == 1:
log.debug(u'Lyrics Search')
- search_results = self.parent.manager.get_all_objects_filtered(Song,
+ search_results = self.parent.manager.get_all_objects(Song,
Song.search_lyrics.like(u'%' + search_keywords + u'%'),
Song.search_lyrics.asc())
self.displayResultsSong(search_results)
elif search_type == 2:
log.debug(u'Authors Search')
- search_results = self.parent.manager.get_all_objects_filtered(
- Author, Author.display_name.like(u'%' + search_keywords + u'%'),
+ search_results = self.parent.manager.get_all_objects(Author,
+ Author.display_name.like(u'%' + search_keywords + u'%'),
Author.display_name.asc())
self.displayResultsAuthor(search_results)
#Called to redisplay the song list screen edith from a search
diff --git a/openlp/plugins/songs/lib/olpimport.py b/openlp/plugins/songs/lib/olpimport.py
new file mode 100644
index 000000000..e4a58277c
--- /dev/null
+++ b/openlp/plugins/songs/lib/olpimport.py
@@ -0,0 +1,204 @@
+# -*- 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 #
+###############################################################################
+"""
+The :mod:`olpimport` module provides the functionality for importing OpenLP
+song databases into the current installation database.
+"""
+import logging
+
+from sqlalchemy import create_engine, MetaData
+from sqlalchemy.orm import class_mapper, mapper, relation, scoped_session, \
+ sessionmaker
+from sqlalchemy.orm.exc import UnmappedClassError
+
+from openlp.core.lib.db import BaseModel
+from openlp.plugins.songs.lib.db import Author, Book, Song, Topic #, MediaFile
+
+log = logging.getLogger(__name__)
+
+class OldAuthor(BaseModel):
+ """
+ Author model
+ """
+ pass
+
+class OldBook(BaseModel):
+ """
+ Book model
+ """
+ pass
+
+class OldMediaFile(BaseModel):
+ """
+ MediaFile model
+ """
+ pass
+
+class OldSong(BaseModel):
+ """
+ Song model
+ """
+ pass
+
+class OldTopic(BaseModel):
+ """
+ Topic model
+ """
+ pass
+
+class OpenLPSongImport(object):
+ """
+
+ """
+ def __init__(self, master_manager, source_db):
+ """
+
+ """
+ self.master_manager = master_manager
+ self.import_source = source_db
+ self.source_session = None
+
+ def import_source_v2_db(self):
+ """
+
+ """
+ engine = create_engine(self.import_source)
+ source_meta = MetaData()
+ source_meta.reflect(engine)
+ self.source_session = scoped_session(sessionmaker(bind=engine))
+ if u'media_files' in source_meta.tables.keys():
+ has_media_files = True
+ else:
+ has_media_files = False
+ source_authors_table = source_meta.tables[u'authors']
+ source_song_books_table = source_meta.tables[u'song_books']
+ source_songs_table = source_meta.tables[u'songs']
+ source_topics_table = source_meta.tables[u'topics']
+ source_authors_songs_table = source_meta.tables[u'authors_songs']
+ source_songs_topics_table = source_meta.tables[u'songs_topics']
+ if has_media_files:
+ source_media_files_table = source_meta.tables[u'media_files']
+ source_media_files_songs_table = \
+ source_meta.tables[u'media_files_songs']
+ try:
+ class_mapper(OldMediaFile)
+ except UnmappedClassError:
+ mapper(OldMediaFile, source_media_files_table)
+ song_props = {
+ 'authors': relation(OldAuthor, backref='songs',
+ secondary=source_authors_songs_table),
+ 'book': relation(OldBook, backref='songs'),
+ 'topics': relation(OldTopic, backref='songs',
+ secondary=source_songs_topics_table)
+ }
+ if has_media_files:
+ song_props['media_files'] = relation(OldMediaFile, backref='songs',
+ secondary=source_media_files_songs_table)
+ try:
+ class_mapper(OldAuthor)
+ except UnmappedClassError:
+ mapper(OldAuthor, source_authors_table)
+ try:
+ class_mapper(OldBook)
+ except UnmappedClassError:
+ mapper(OldBook, source_song_books_table)
+ try:
+ class_mapper(OldSong)
+ except UnmappedClassError:
+ mapper(OldSong, source_songs_table, properties=song_props)
+ try:
+ class_mapper(OldTopic)
+ except UnmappedClassError:
+ mapper(OldTopic, source_topics_table)
+
+ source_songs = self.source_session.query(OldSong).all()
+ for song in source_songs:
+ new_song = Song()
+ new_song.title = song.title
+ if has_media_files:
+ new_song.alternate_title = song.alternate_title
+ else:
+ old_titles = song.search_title.split(u'@')
+ if len(old_titles) > 1:
+ new_song.alternate_title = old_titles[1]
+ else:
+ new_song.alternate_title = u''
+ new_song.search_title = song.search_title
+ new_song.song_number = song.song_number
+ new_song.lyrics = song.lyrics
+ new_song.search_lyrics = song.search_lyrics
+ new_song.verse_order = song.verse_order
+ new_song.copyright = song.copyright
+ new_song.comments = song.comments
+ new_song.theme_name = song.theme_name
+ new_song.ccli_number = song.ccli_number
+ if song.authors:
+ for author in song.authors:
+ existing_author = self.master_manager.get_object_filtered(
+ Author, Author.display_name == author.display_name)
+ if existing_author:
+ new_song.authors.append(existing_author)
+ else:
+ new_song.authors.append(Author.populate(
+ first_name=author.first_name,
+ last_name=author.last_name,
+ display_name=author.display_name))
+ else:
+ au = self.master_manager.get_object_filtered(Author,
+ Author.display_name == u'Author Unknown')
+ if au:
+ new_song.authors.append(au)
+ else:
+ new_song.authors.append(Author.populate(
+ display_name=u'Author Unknown'))
+ if song.book:
+ existing_song_book = self.master_manager.get_object_filtered(
+ Book, Book.name == song.book.name)
+ if existing_song_book:
+ new_song.book = existing_song_book
+ else:
+ new_song.book = Book.populate(name=song.book.name,
+ publisher=song.book.publisher)
+ if song.topics:
+ for topic in song.topics:
+ existing_topic = self.master_manager.get_object_filtered(
+ Topic, Topic.name == topic.name)
+ if existing_topic:
+ new_song.topics.append(existing_topic)
+ else:
+ new_song.topics.append(Topic.populate(name=topic.name))
+# if has_media_files:
+# if song.media_files:
+# for media_file in song.media_files:
+# existing_media_file = \
+# self.master_manager.get_object_filtered(MediaFile,
+# MediaFile.file_name == media_file.file_name)
+# if existing_media_file:
+# new_song.media_files.append(existing_media_file)
+# else:
+# new_song.media_files.append(MediaFile.populate(
+# file_name=media_file.file_name))
+ self.master_manager.save_object(new_song)
+ engine.dispose()
diff --git a/openlp/plugins/songs/lib/opensongimport.py b/openlp/plugins/songs/lib/opensongimport.py
new file mode 100644
index 000000000..345b0922f
--- /dev/null
+++ b/openlp/plugins/songs/lib/opensongimport.py
@@ -0,0 +1,242 @@
+# -*- 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 #
+###############################################################################
+
+import logging
+import os
+from zipfile import ZipFile
+from lxml import objectify
+
+from openlp.plugins.songs.lib.songimport import SongImport
+
+log = logging.getLogger(__name__)
+
+class OpenSongImportError(Exception):
+ pass
+
+class OpenSongImport(object):
+ """
+ Import songs exported from OpenSong - the format is described loosly here:
+ http://www.opensong.org/d/manual/song_file_format_specification
+
+ However, it doesn't describe the section, so here's an attempt:
+
+ Verses can be expressed in one of 2 ways:
+
+ [v1]List of words
+ Another Line
+
+ [v2]Some words for the 2nd verse
+ etc...
+
+
+ The 'v' can be left out - it is implied
+ or:
+
+ [V]
+ 1List of words
+ 2Some words for the 2nd Verse
+
+ 1Another Line
+ 2etc...
+
+
+ Either or both forms can be used in one song. The Number does not
+ necessarily appear at the start of the line
+
+ The [v1] labels can have either upper or lower case Vs
+ Other labels can be used also:
+ C - Chorus
+ B - Bridge
+
+ Guitar chords can be provided 'above' the lyrics (the line is
+ preceeded by a'.') and _s can be used to signify long-drawn-out
+ words:
+
+ . A7 Bm
+ 1 Some____ Words
+
+ Chords and _s are removed by this importer.
+
+ The verses etc. are imported and tagged appropriately.
+
+ The tag is used to populate the OpenLP verse
+ display order field. The Author and Copyright tags are also
+ imported to the appropriate places.
+
+ """
+ def __init__(self, songmanager):
+ """
+ Initialise the class. Requires a songmanager class which
+ is passed to SongImport for writing song to disk
+ """
+ self.songmanager = songmanager
+ self.song = None
+
+ def do_import(self, filename, commit=True):
+ """
+ Import either a single opensong file, or a zipfile
+ containing multiple opensong files If the commit parameter is
+ set False, the import will not be committed to the database
+ (useful for test scripts)
+ """
+ ext = os.path.splitext(filename)[1]
+ if ext.lower() == ".zip":
+ log.info('Zipfile found %s', filename)
+ z = ZipFile(filename, u'r')
+ for song in z.infolist():
+ parts = os.path.split(song.filename)
+ if parts[-1] == u'':
+ #No final part => directory
+ continue
+ songfile = z.open(song)
+ self.do_import_file(songfile)
+ if commit:
+ self.finish()
+ else:
+ log.info('Direct import %s', filename)
+ file = open(filename)
+ self.do_import_file(file)
+ if commit:
+ self.finish()
+
+
+ def do_import_file(self, file):
+ """
+ Process the OpenSong file - pass in a file-like object,
+ not a filename
+ """
+ self.song_import = SongImport(self.songmanager)
+ tree = objectify.parse(file)
+ root = tree.getroot()
+ fields = dir(root)
+ decode = {u'copyright':self.song_import.add_copyright,
+ u'ccli':u'ccli_number',
+ u'author':self.song_import.parse_author,
+ u'title':u'title',
+ u'aka':u'alternate_title',
+ u'hymn_number':u'song_number'}
+ for (attr, fn_or_string) in decode.items():
+ if attr in fields:
+ ustring = unicode(root.__getattr__(attr))
+ if type(fn_or_string) == type(u''):
+ self.song_import.__setattr__(fn_or_string, ustring)
+ else:
+ fn_or_string(ustring)
+ if u'theme' in fields:
+ self.song_import.topics.append(unicode(root.theme))
+ if u'alttheme' in fields:
+ self.song_import.topics.append(unicode(root.alttheme))
+ # data storage while importing
+ verses = {}
+ lyrics = unicode(root.lyrics)
+ # keep track of a "default" verse order, in case none is specified
+ our_verse_order = []
+ verses_seen = {}
+ # in the absence of any other indication, verses are the default,
+ # erm, versetype!
+ versetype = u'V'
+ for thisline in lyrics.split(u'\n'):
+ # remove comments
+ semicolon = thisline.find(u';')
+ if semicolon >= 0:
+ thisline = thisline[:semicolon]
+ thisline = thisline.strip()
+ if len(thisline) == 0:
+ continue
+ # skip inthisline guitar chords and page and column breaks
+ if thisline[0] == u'.' or thisline.startswith(u'---') \
+ or thisline.startswith(u'-!!'):
+ continue
+
+ # verse/chorus/etc. marker
+ if thisline[0] == u'[':
+ versetype = thisline[1].upper()
+ if versetype.isdigit():
+ versenum = versetype
+ versetype = u'V'
+ elif thisline[2] != u']':
+ # there's a number to go with it - extract that as well
+ right_bracket = thisline.find(u']')
+ versenum = thisline[2:right_bracket]
+ else:
+ # if there's no number, assume it's no.1
+ versenum = u'1'
+ continue
+ words = None
+
+ # number at start of line.. it's verse number
+ if thisline[0].isdigit():
+ versenum = thisline[0]
+ words = thisline[1:].strip()
+ if words is None and \
+ versenum is not None and \
+ versetype is not None:
+ words = thisline
+ if versenum is not None:
+ versetag = u'%s%s' % (versetype, versenum)
+ if not verses.has_key(versetype):
+ verses[versetype] = {}
+ if not verses[versetype].has_key(versenum):
+ # storage for lines in this verse
+ verses[versetype][versenum] = []
+ if not verses_seen.has_key(versetag):
+ verses_seen[versetag] = 1
+ our_verse_order.append(versetag)
+ if words:
+ # Tidy text and remove the ____s from extended words
+ words = self.song_import.tidy_text(words)
+ words = words.replace('_', '')
+ verses[versetype][versenum].append(words)
+ # done parsing
+ versetypes = verses.keys()
+ versetypes.sort()
+ versetags = {}
+ for versetype in versetypes:
+ versenums = verses[versetype].keys()
+ versenums.sort()
+ for num in versenums:
+ versetag = u'%s%s' % (versetype, num)
+ lines = u'\n'.join(verses[versetype][num])
+ self.song_import.verses.append([versetag, lines])
+ # Keep track of what we have for error checking later
+ versetags[versetag] = 1
+ # now figure out the presentation order
+ if u'presentation' in fields and root.presentation != u'':
+ order = unicode(root.presentation)
+ order = order.split()
+ else:
+ assert len(our_verse_order)>0
+ order = our_verse_order
+ for tag in order:
+ if len(tag) == 1:
+ tag = tag + u'1' # Assume it's no.1 if it's not there
+ if not versetags.has_key(tag):
+ log.warn(u'Got order %s but not in versetags, skipping', tag)
+ else:
+ self.song_import.verse_order_list.append(tag)
+
+ def finish(self):
+ """ Separate function, allows test suite to not pollute database"""
+ self.song_import.finish()
diff --git a/openlp/plugins/songs/lib/sofimport.py b/openlp/plugins/songs/lib/sofimport.py
index da56580aa..52fd38634 100644
--- a/openlp/plugins/songs/lib/sofimport.py
+++ b/openlp/plugins/songs/lib/sofimport.py
@@ -142,7 +142,7 @@ class SofImport(OooImport):
self.blanklines += 1
if self.blanklines > 1:
return
- if self.song.get_title() != u'':
+ if self.song.title != u'':
self.finish_verse()
return
self.blanklines = 0
@@ -166,8 +166,8 @@ class SofImport(OooImport):
self.finish_verse()
self.song.repeat_verse()
return
- if self.song.get_title() == u'':
- if self.song.get_copyright() == u'':
+ if self.song.title == u'':
+ if self.song.copyright == u'':
self.add_author(text)
else:
self.song.add_copyright(text)
@@ -187,10 +187,10 @@ class SofImport(OooImport):
return text
if textportion.CharWeight == BOLD:
boldtext = text.strip()
- if boldtext.isdigit() and self.song.get_song_number() == '':
+ if boldtext.isdigit() and self.song.song_number == '':
self.add_songnumber(boldtext)
return u''
- if self.song.get_title() == u'':
+ if self.song.title == u'':
text = self.uncap_text(text)
self.add_title(text)
return text
@@ -220,20 +220,17 @@ class SofImport(OooImport):
Add a song number, store as alternate title. Also use the song
number to work out which songbook we're in
"""
- self.song.set_song_number(song_no)
- self.song.set_alternate_title(song_no + u'.')
+ self.song.song_number = song_no
+ self.song.alternate_title = song_no + u'.'
+ self.song.song_book_pub = u'Kingsway Publications'
if int(song_no) <= 640:
- self.song.set_song_book(u'Songs of Fellowship 1',
- u'Kingsway Publications')
+ self.song.song_book = u'Songs of Fellowship 1'
elif int(song_no) <= 1150:
- self.song.set_song_book(u'Songs of Fellowship 2',
- u'Kingsway Publications')
+ self.song.song_book = u'Songs of Fellowship 2'
elif int(song_no) <= 1690:
- self.song.set_song_book(u'Songs of Fellowship 3',
- u'Kingsway Publications')
+ self.song.song_book = u'Songs of Fellowship 3'
else:
- self.song.set_song_book(u'Songs of Fellowship 4',
- u'Kingsway Publications')
+ self.song.song_book = u'Songs of Fellowship 4'
def add_title(self, text):
"""
@@ -245,7 +242,7 @@ class SofImport(OooImport):
title = title[1:]
if title.endswith(u','):
title = title[:-1]
- self.song.set_title(title)
+ self.song.title = title
def add_author(self, text):
"""
@@ -283,7 +280,7 @@ class SofImport(OooImport):
splitat = None
else:
versetag = u'V'
- splitat = self.verse_splits(self.song.get_song_number())
+ splitat = self.verse_splits(self.song.song_number)
if splitat:
ln = 0
verse = u''
@@ -538,4 +535,3 @@ class SofImport(OooImport):
if song_number == 1119:
return 7
return None
-
diff --git a/openlp/plugins/songs/lib/songimport.py b/openlp/plugins/songs/lib/songimport.py
index bf3b404cd..222bbeedd 100644
--- a/openlp/plugins/songs/lib/songimport.py
+++ b/openlp/plugins/songs/lib/songimport.py
@@ -50,7 +50,7 @@ class SongImport(object):
self.song_number = u''
self.alternate_title = u''
self.copyright = u''
- self.comment = u''
+ self.comments = u''
self.theme_name = u''
self.ccli_number = u''
self.authors = []
@@ -123,53 +123,10 @@ class SongImport(object):
if len(lines) == 1:
self.parse_author(lines[0])
return
- if not self.get_title():
- self.set_title(lines[0])
+ if not self.title:
+ self.title = lines[0]
self.add_verse(text)
- def get_title(self):
- """
- Return the title
- """
- return self.title
-
- def get_copyright(self):
- """
- Return the copyright
- """
- return self.copyright
-
- def get_song_number(self):
- """
- Return the song number
- """
- return self.song_number
-
- def set_title(self, title):
- """
- Set the title
- """
- self.title = title
-
- def set_alternate_title(self, title):
- """
- Set the alternate title
- """
- self.alternate_title = title
-
- def set_song_number(self, song_number):
- """
- Set the song number
- """
- self.song_number = song_number
-
- def set_song_book(self, song_book, publisher):
- """
- Set the song book name and publisher
- """
- self.song_book_name = song_book
- self.song_book_pub = publisher
-
def add_copyright(self, copyright):
"""
Build the copyright field
@@ -184,8 +141,8 @@ class SongImport(object):
"""
Add the author. OpenLP stores them individually so split by 'and', '&'
and comma.
- However need to check for "Mr and Mrs Smith" and turn it to
- "Mr Smith" and "Mrs Smith".
+ However need to check for 'Mr and Mrs Smith' and turn it to
+ 'Mr Smith' and 'Mrs Smith'.
"""
for author in text.split(u','):
authors = author.split(u'&')
@@ -267,7 +224,7 @@ class SongImport(object):
def commit_song(self):
"""
- Write the song and it's fields to disk
+ Write the song and its fields to disk
"""
song = Song()
song.title = self.title
@@ -296,7 +253,7 @@ class SongImport(object):
song.lyrics = unicode(sxml.extract_xml(), u'utf-8')
song.verse_order = u' '.join(self.verse_order_list)
song.copyright = self.copyright
- song.comment = self.comment
+ song.comments = self.comments
song.theme_name = self.theme_name
song.ccli_number = self.ccli_number
for authortext in self.authors:
@@ -315,11 +272,13 @@ class SongImport(object):
publisher=self.song_book_pub)
song.book = song_book
for topictext in self.topics:
+ if len(topictext) == 0:
+ continue
topic = self.manager.get_object_filtered(Topic,
Topic.name == topictext)
if topic is None:
topic = Topic.populate(name=topictext)
- song.topics.append(topictext)
+ song.topics.append(topic)
self.manager.save_object(song)
def print_song(self):
@@ -345,8 +304,8 @@ class SongImport(object):
print u'NUMBER: ' + self.song_number
for topictext in self.topics:
print u'TOPIC: ' + topictext
- if self.comment:
- print u'COMMENT: ' + self.comment
+ if self.comments:
+ print u'COMMENTS: ' + self.comments
if self.theme_name:
print u'THEME: ' + self.theme_name
if self.ccli_number:
diff --git a/openlp/plugins/songs/lib/songxml.py b/openlp/plugins/songs/lib/songxml.py
index 2965c579b..85aa5dcc6 100644
--- a/openlp/plugins/songs/lib/songxml.py
+++ b/openlp/plugins/songs/lib/songxml.py
@@ -28,7 +28,6 @@ import logging
#import os
from types import ListType
-from xml.etree.ElementTree import ElementTree, XML
# Do we need these two lines?
#sys.path.append(os.path.abspath(u'./../../../..'))
@@ -60,175 +59,6 @@ class SongFeatureError(SongException):
# TODO: Song: Import ChangingSong
# TODO: Song: Export ChangingSong
-_BLANK_OPENSONG_XML = \
-'''
-
-
-
-
-
-
-
-
-
-
-'''
-
-class _OpenSong(object):
- """
- Class for import of OpenSong
- """
- def __init__(self, xmlContent = None):
- """
- Initialize from given xml content
- """
- self._set_from_xml(_BLANK_OPENSONG_XML, 'song')
- if xmlContent:
- self._set_from_xml(xmlContent, 'song')
-
- def _set_from_xml(self, xml, root_tag):
- """
- Set song properties from given xml content.
-
- ``xml``
- Formatted xml tags and values.
- ``root_tag``
- The root tag of the xml.
- """
- root = ElementTree(element=XML(xml))
- xml_iter = root.getiterator()
- for element in xml_iter:
- if element.tag != root_tag:
- text = element.text
- if text is None:
- val = text
- elif isinstance(text, basestring):
- # Strings need special handling to sort the colours out
- if text[0] == u'$':
- # This might be a hex number, let's try to convert it.
- try:
- val = int(text[1:], 16)
- except ValueError:
- pass
- else:
- # Let's just see if it's a integer.
- try:
- val = int(text)
- except ValueError:
- # Ok, it seems to be a string.
- val = text
- if hasattr(self, u'post_tag_hook'):
- (element.tag, val) = \
- self.post_tag_hook(element.tag, val)
- setattr(self, element.tag, val)
-
- def __str__(self):
- """
- Return string with all public attributes
-
- The string is formatted with one attribute per line
- If the string is split on newline then the length of the
- list is equal to the number of attributes
- """
- attributes = []
- for attrib in dir(self):
- if not attrib.startswith(u'_'):
- attributes.append(
- u'%30s : %s' % (attrib, getattr(self, attrib)))
- return u'\n'.join(attributes)
-
- def _get_as_string(self):
- """
- Return one string with all public attributes
- """
- result = u''
- for attrib in dir(self):
- if not attrib.startswith(u'_'):
- result += u'_%s_' % getattr(self, attrib)
- return result
-
- def get_author_list(self):
- """Convert author field to an authorlist
-
- in OpenSong an author list may be separated by '/'
- return as a string
- """
- if self.author:
- list = self.author.split(u' and ')
- res = [item.strip() for item in list]
- return u', '.join(res)
-
- def get_category_array(self):
- """Convert theme and alttheme into category_array
-
- return as a string
- """
- res = []
- if self.theme:
- res.append(self.theme)
- if self.alttheme:
- res.append(self.alttheme)
- return u', u'.join(res)
-
- def _reorder_verse(self, tag, tmpVerse):
- """
- Reorder the verse in case of first char is a number
- tag -- the tag of this verse / verse group
- tmpVerse -- list of strings
- """
- res = []
- for digit in '1234567890 ':
- tagPending = True
- for line in tmpVerse:
- if line.startswith(digit):
- if tagPending:
- tagPending = False
- tagChar = tag.strip(u'[]').lower()
- if 'v' == tagChar:
- newtag = "Verse"
- elif 'c' == tagChar:
- newtag = "Chorus"
- elif 'b' == tagChar:
- newtag = "Bridge"
- elif 'p' == tagChar:
- newtag = "Pre-chorus"
- else:
- newtag = tagChar
- tagString = (u'# %s %s' % (newtag, digit)).rstrip()
- res.append(tagString)
- res.append(line[1:])
- if (len(line) == 0) and (not tagPending):
- res.append(line)
- return res
-
- def get_lyrics(self):
- """
- Convert the lyrics to openlp lyrics format
- return as list of strings
- """
- lyrics = self.lyrics.split(u'\n')
- tmpVerse = []
- finalLyrics = []
- tag = ""
- for lyric in lyrics:
- line = lyric.rstrip()
- if not line.startswith(u'.'):
- # drop all chords
- tmpVerse.append(line)
- if line:
- if line.startswith(u'['):
- tag = line
- else:
- reorderedVerse = self._reorder_verse(tag, tmpVerse)
- finalLyrics.extend(reorderedVerse)
- tag = ""
- tmpVerse = []
- # catch up final verse
- reorderedVerse = self._reorder_verse(tag, tmpVerse)
- finalLyrics.extend(reorderedVerse)
- return finalLyrics
-
-
class Song(object):
"""Handling song properties and methods
@@ -275,7 +105,7 @@ class Song(object):
show_author_list -- 0: no show, 1: show
show_copyright -- 0: no show, 1: show
show_song_cclino -- 0: no show, 1: show
- theme -- name of theme or blank
+ theme_name -- name of theme or blank
category_array -- list of user defined properties (hymn, gospel)
song_book -- name of originating book
song_number -- number of the song, related to a songbook
@@ -298,7 +128,7 @@ class Song(object):
self.show_copyright = 1
self.show_song_cclino = 1
self.show_title = 1
- self.theme = ""
+ self.theme_name = ""
self.category_array = None
self.song_book = ""
self.song_number = ""
@@ -307,40 +137,6 @@ class Song(object):
self.set_lyrics(u'')
return
- def from_opensong_buffer(self, xmlcontent):
- """Initialize from buffer(string) of xml lines in opensong format"""
- self._reset()
- opensong = _OpenSong(xmlcontent)
- if opensong.title:
- self.set_title(opensong.title)
- if opensong.copyright:
- self.set_copyright(opensong.copyright)
- if opensong.presentation:
- self.set_verse_order(opensong.presentation)
- if opensong.ccli:
- self.set_song_cclino(opensong.ccli)
- self.set_author_list(opensong.get_author_list())
- self.set_category_array(opensong.get_category_array())
- self.set_lyrics(opensong.get_lyrics())
-
- def from_opensong_file(self, xmlfilename):
- """
- Initialize from file containing xml
- xmlfilename -- path to xml file
- """
- osfile = None
- try:
- osfile = open(xmlfilename, 'r')
- list = [line for line in osfile]
- osfile.close()
- xml = "".join(list)
- self.from_opensong_buffer(xml)
- except IOError:
- log.exception(u'Failed to load opensong xml file')
- finally:
- if osfile:
- osfile.close()
-
def _remove_punctuation(self, title):
"""Remove the puntuation chars from title
@@ -366,10 +162,6 @@ class Song(object):
if len(self.search_title) < 1:
raise SongTitleError(u'The searchable title is empty')
- def get_title(self):
- """Return title value"""
- return self.title
-
def from_ccli_text_buffer(self, textList):
"""
Create song from a list of texts (strings) - CCLI text format expected
@@ -424,7 +216,7 @@ class Song(object):
self.set_title(sName)
self.set_author_list(author_list)
self.set_copyright(sCopyright)
- self.set_song_cclino(sCcli)
+ self.set_ccli_number(sCcli)
self.set_lyrics(lyrics)
def from_ccli_text_file(self, textFileName):
@@ -479,21 +271,21 @@ class Song(object):
"""Set the copyright string"""
self.copyright = copyright
- def get_song_cclino(self):
+ def get_ccli_number(self):
"""Return the songCclino"""
- return self._assure_string(self.song_cclino)
+ return self._assure_string(self.ccli_number)
- def set_song_cclino(self, song_cclino):
- """Set the song_cclino"""
- self.song_cclino = song_cclino
+ def set_ccli_number(self, ccli_number):
+ """Set the ccli_number"""
+ self.ccli_number = ccli_number
- def get_theme(self):
+ def get_theme_name(self):
"""Return the theme name for the song"""
- return self._assure_string(self.theme)
+ return self._assure_string(self.theme_name)
- def set_theme(self, theme):
+ def set_theme_name(self, theme_name):
"""Set the theme name (string)"""
- self.theme = theme
+ self.theme_name = theme_name
def get_song_book(self):
"""Return the song_book (string)"""
@@ -532,9 +324,9 @@ class Song(object):
asOneString
True -- string:
- "John Newton, A Parker"
+ 'John Newton, A Parker'
False -- list of strings
- ["John Newton", u'A Parker"]
+ ['John Newton', u'A Parker']
"""
if asOneString:
res = self._assure_string(self.author_list)
@@ -557,9 +349,9 @@ class Song(object):
asOneString
True -- string:
- "Hymn, Gospel"
+ 'Hymn, Gospel'
False -- list of strings
- ["Hymn", u'Gospel"]
+ ['Hymn', u'Gospel']
"""
if asOneString:
res = self._assure_string(self.category_array)
@@ -601,13 +393,13 @@ class Song(object):
"""Set the show_copyright flag (bool)"""
self.show_copyright = show_copyright
- def get_show_song_cclino(self):
+ def get_show_ccli_number(self):
"""Return the showSongCclino (string)"""
- return self.show_song_cclino
+ return self.show_ccli_number
- def set_show_song_cclino(self, show_song_cclino):
- """Set the show_song_cclino flag (bool)"""
- self.show_song_cclino = show_song_cclino
+ def set_show_ccli_number(self, show_ccli_number):
+ """Set the show_ccli_number flag (bool)"""
+ self.show_ccli_number = show_ccli_number
def get_lyrics(self):
"""Return the lyrics as a list of strings
@@ -674,7 +466,7 @@ class Song(object):
slideNumber -- 1 .. numberOfSlides
Returns a list as:
- [theme (string),
+ [theme_name (string),
title (string),
authorlist (string),
copyright (string),
@@ -688,7 +480,7 @@ class Song(object):
raise SongSlideError(u'Slide number too high')
res = []
if self.show_title:
- title = self.get_title()
+ title = self.title
else:
title = ""
if self.show_author_list:
@@ -699,13 +491,13 @@ class Song(object):
cpright = self.get_copyright()
else:
cpright = ""
- if self.show_song_cclino:
- ccli = self.get_song_cclino()
+ if self.show_ccli_number:
+ ccli = self.get_ccli_number()
else:
ccli = ""
- theme = self.get_theme()
+ theme_name = self.get_theme_name()
# examine the slide for a theme
- res.append(theme)
+ res.append(theme_name)
res.append(title)
res.append(author)
res.append(cpright)
diff --git a/openlp/plugins/songs/lib/test/test.opensong b/openlp/plugins/songs/lib/test/test.opensong
new file mode 100644
index 000000000..1ce60cf3b
--- /dev/null
+++ b/openlp/plugins/songs/lib/test/test.opensong
@@ -0,0 +1,54 @@
+
+
+ Martins Test
+ MartiÑ Thómpson
+ 2010 Martin Thompson
+ 1
+ V1 C V2 C2 V3 B1 V1
+ Blah
+
+
+
+
+
+
+
+ TestTheme
+ TestAltTheme
+
+
+ ;Comment
+. A B C
+1 v1 Line 1___
+2 v2 Line 1___
+. A B C7
+1 V1 Line 2
+2 V2 Line 2
+
+[3]
+ V3 Line 1
+ V3 Line 2
+
+[b1]
+ Bridge 1
+---
+-!!
+ Bridge 1 line 2
+
+[C]
+. A B
+ Chorus 1
+
+[C2]
+. A B
+ Chorus 2
+
+
diff --git a/openlp/plugins/songs/lib/test/test.opensong.zip b/openlp/plugins/songs/lib/test/test.opensong.zip
new file mode 100644
index 000000000..62588c890
Binary files /dev/null and b/openlp/plugins/songs/lib/test/test.opensong.zip differ
diff --git a/openlp/plugins/songs/lib/test/test2.opensong b/openlp/plugins/songs/lib/test/test2.opensong
new file mode 100644
index 000000000..e6a3b4913
--- /dev/null
+++ b/openlp/plugins/songs/lib/test/test2.opensong
@@ -0,0 +1,45 @@
+
+
+ Martins 2nd Test
+ Martin Thompson
+ 2010 Martin Thompson
+ 2
+
+ Blah
+
+
+
+
+
+
+
+
+
+
+ ;Comment
+[V]
+. A B C
+1 v1 Line 1___
+2 v2 Line 1___
+. A B C7
+1 V1 Line 2
+2 V2 Line 2
+
+[b1]
+ Bridge 1
+ Bridge 1 line 2
+[C1]
+ Chorus 1
+
+[C2]
+ Chorus 2
+
+
diff --git a/openlp/plugins/songs/lib/test/test_importing_lots.py b/openlp/plugins/songs/lib/test/test_importing_lots.py
new file mode 100644
index 000000000..9f1908bca
--- /dev/null
+++ b/openlp/plugins/songs/lib/test/test_importing_lots.py
@@ -0,0 +1,57 @@
+from openlp.plugins.songs.lib.opensongimport import OpenSongImport
+from openlp.plugins.songs.lib.db import init_schema
+from openlp.core.lib.db import Manager
+from glob import glob
+from zipfile import ZipFile
+import os
+from traceback import print_exc
+import sys
+import codecs
+
+def opensong_import_lots():
+ ziploc = u'/home/mjt/openlp/OpenSong_Data/'
+ files = []
+ #files = [u'test.opensong.zip', ziploc+u'ADond.zip']
+ files.extend(glob(ziploc+u'Songs.zip'))
+ #files.extend(glob(ziploc+u'SOF.zip'))
+ #files.extend(glob(ziploc+u'spanish_songs_for_opensong.zip'))
+# files.extend(glob(ziploc+u'opensong_*.zip'))
+ errfile = codecs.open(u'import_lots_errors.txt', u'w', u'utf8')
+ manager = Manager(u'songs', init_schema)
+ for file in files:
+ print u'Importing', file
+ z = ZipFile(file, u'r')
+ for song in z.infolist():
+ # need to handle unicode filenames (CP437 - Winzip does this)
+ filename = song.filename#.decode('cp852')
+ parts = os.path.split(filename)
+ if parts[-1] == u'':
+ #No final part => directory
+ continue
+ print " ", file, ":",filename,
+ songfile = z.open(song)
+ #z.extract(song)
+ #songfile=open(filename, u'r')
+ o = OpenSongImport(manager)
+ try:
+ o.do_import_file(songfile)
+ # o.song_import.print_song()
+ except:
+ print "Failure",
+
+ errfile.write(u'Failure: %s:%s\n' %(file, filename.decode('cp437')))
+ songfile = z.open(song)
+ for l in songfile.readlines():
+ l = l.decode('utf8')
+ print(u' |%s\n' % l.strip())
+ errfile.write(u' |%s\n'%l.strip())
+ print_exc(3, file = errfile)
+ print_exc(3)
+ sys.exit(1)
+ # continue
+ #o.finish()
+ print "OK"
+ #os.unlink(filename)
+ # o.song_import.print_song()
+if __name__ == "__main__":
+ opensong_import_lots()
diff --git a/openlp/plugins/songs/lib/test/test_opensongimport.py b/openlp/plugins/songs/lib/test/test_opensongimport.py
new file mode 100644
index 000000000..bcd74ba17
--- /dev/null
+++ b/openlp/plugins/songs/lib/test/test_opensongimport.py
@@ -0,0 +1,88 @@
+# -*- 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 openlp.plugins.songs.lib.opensongimport import OpenSongImport
+from openlp.core.lib.db import Manager
+from openlp.plugins.songs.lib.db import init_schema
+
+def test():
+ manager = Manager(u'songs', init_schema)
+ o = OpenSongImport(manager)
+ o.do_import(u'test.opensong', commit=False)
+ o.song_import.print_song()
+ assert o.song_import.copyright == u'2010 Martin Thompson'
+ assert o.song_import.authors == [u'MartiÑ Thómpson']
+ assert o.song_import.title == u'Martins Test'
+ assert o.song_import.alternate_title == u''
+ assert o.song_import.song_number == u'1'
+ assert [u'C1', u'Chorus 1'] in o.song_import.verses
+ assert [u'C2', u'Chorus 2'] in o.song_import.verses
+ assert not [u'C3', u'Chorus 3'] in o.song_import.verses
+ assert [u'B1', u'Bridge 1\nBridge 1 line 2'] in o.song_import.verses
+ assert [u'V1', u'v1 Line 1\nV1 Line 2'] in o.song_import.verses
+ assert [u'V2', u'v2 Line 1\nV2 Line 2'] in o.song_import.verses
+ assert o.song_import.verse_order_list == [u'V1', u'C1', u'V2', u'C2', u'V3', u'B1', u'V1']
+ assert o.song_import.ccli_number == u'Blah'
+ assert o.song_import.topics == [u'TestTheme', u'TestAltTheme']
+ o.do_import(u'test.opensong.zip', commit=False)
+ o.song_import.print_song()
+ o.finish()
+ assert o.song_import.copyright == u'2010 Martin Thompson'
+ assert o.song_import.authors == [u'MartiÑ Thómpson']
+ assert o.song_import.title == u'Martins Test'
+ assert o.song_import.alternate_title == u''
+ assert o.song_import.song_number == u'1'
+ assert [u'B1', u'Bridge 1\nBridge 1 line 2'] in o.song_import.verses
+ assert [u'C1', u'Chorus 1'] in o.song_import.verses
+ assert [u'C2', u'Chorus 2'] in o.song_import.verses
+ assert not [u'C3', u'Chorus 3'] in o.song_import.verses
+ assert [u'V1', u'v1 Line 1\nV1 Line 2'] in o.song_import.verses
+ assert [u'V2', u'v2 Line 1\nV2 Line 2'] in o.song_import.verses
+ assert o.song_import.verse_order_list == [u'V1', u'C1', u'V2', u'C2', u'V3', u'B1', u'V1']
+
+ o = OpenSongImport(manager)
+ o.do_import(u'test2.opensong', commit=False)
+ # o.finish()
+ o.song_import.print_song()
+ assert o.song_import.copyright == u'2010 Martin Thompson'
+ assert o.song_import.authors == [u'Martin Thompson']
+ assert o.song_import.title == u'Martins 2nd Test'
+ assert o.song_import.alternate_title == u''
+ assert o.song_import.song_number == u'2'
+ print o.song_import.verses
+ assert [u'B1', u'Bridge 1\nBridge 1 line 2'] in o.song_import.verses
+ assert [u'C1', u'Chorus 1'] in o.song_import.verses
+ assert [u'C2', u'Chorus 2'] in o.song_import.verses
+ assert not [u'C3', u'Chorus 3'] in o.song_import.verses
+ assert [u'V1', u'v1 Line 1\nV1 Line 2'] in o.song_import.verses
+ assert [u'V2', u'v2 Line 1\nV2 Line 2'] in o.song_import.verses
+ print o.song_import.verse_order_list
+ assert o.song_import.verse_order_list == [u'V1', u'V2', u'B1', u'C1', u'C2']
+
+ print "Tests passed"
+ pass
+
+if __name__ == "__main__":
+ test()
diff --git a/openlp/plugins/songs/lib/xml.py b/openlp/plugins/songs/lib/xml.py
index 359c28eda..336c1ebf1 100644
--- a/openlp/plugins/songs/lib/xml.py
+++ b/openlp/plugins/songs/lib/xml.py
@@ -76,8 +76,9 @@ class SongXMLBuilder(object):
``content``
The actual text of the verse to be stored.
"""
- #log.debug(u'add_verse_to_lyrics %s, %s\n%s' % (type, number, content))
- verse = etree.Element(u'verse', type=type, label=number)
+ # log.debug(u'add_verse_to_lyrics %s, %s\n%s' % (type, number, content))
+ verse = etree.Element(u'verse', type = unicode(type),
+ label = unicode(number))
verse.text = etree.CDATA(content)
self.lyrics.append(verse)
diff --git a/openlp/plugins/songs/songsplugin.py b/openlp/plugins/songs/songsplugin.py
index e7be7cae3..bf1c0cb23 100644
--- a/openlp/plugins/songs/songsplugin.py
+++ b/openlp/plugins/songs/songsplugin.py
@@ -30,7 +30,7 @@ from PyQt4 import QtCore, QtGui
from openlp.core.lib import Plugin, build_icon, PluginStatus, Receiver, \
translate
from openlp.core.lib.db import Manager
-from openlp.plugins.songs.lib import SongMediaItem, SongsTab
+from openlp.plugins.songs.lib import OpenLPSongImport, SongMediaItem, SongsTab
from openlp.plugins.songs.lib.db import init_schema, Song
try:
@@ -39,6 +39,8 @@ try:
except ImportError:
OOo_available = False
+from openlp.plugins.songs.lib import OpenSongImport
+
log = logging.getLogger(__name__)
class SongsPlugin(Plugin):
@@ -69,7 +71,7 @@ class SongsPlugin(Plugin):
log.info(u'Songs Initialising')
Plugin.initialise(self)
self.mediaItem.displayResultsSong(
- self.manager.get_all_objects(Song, Song.title))
+ self.manager.get_all_objects(Song, order_by_ref=Song.title))
def getMediaManagerItem(self):
"""
@@ -137,6 +139,37 @@ class SongsPlugin(Plugin):
QtCore.SIGNAL(u'triggered()'), self.onImportSofItemClick)
QtCore.QObject.connect(self.ImportOooItem,
QtCore.SIGNAL(u'triggered()'), self.onImportOooItemClick)
+ # OpenSong import menu item - will be removed and the
+ # functionality will be contained within the import wizard
+ self.ImportOpenSongItem = QtGui.QAction(import_menu)
+ self.ImportOpenSongItem.setObjectName(u'ImportOpenSongItem')
+ self.ImportOpenSongItem.setText(
+ translate('SongsPlugin',
+ 'OpenSong (temp menu item)'))
+ self.ImportOpenSongItem.setToolTip(
+ translate('SongsPlugin',
+ 'Import songs from OpenSong files' +
+ '(either raw text or ZIPfiles)'))
+ self.ImportOpenSongItem.setStatusTip(
+ translate('SongsPlugin',
+ 'Import songs from OpenSong files' +
+ '(either raw text or ZIPfiles)'))
+ import_menu.addAction(self.ImportOpenSongItem)
+ QtCore.QObject.connect(self.ImportOpenSongItem,
+ QtCore.SIGNAL(u'triggered()'), self.onImportOpenSongItemClick)
+ # OpenLP v2 import menu item - ditto above regarding refactoring into
+ # an import wizard
+ self.ImportOpenLPSongItem = QtGui.QAction(import_menu)
+ self.ImportOpenLPSongItem.setObjectName(u'ImportOpenLPSongItem')
+ self.ImportOpenLPSongItem.setText(translate('SongsPlugin',
+ 'OpenLP v2 Songs (temporary)'))
+ self.ImportOpenLPSongItem.setToolTip(translate('SongsPlugin',
+ 'Import an OpenLP v2 song database'))
+ self.ImportOpenLPSongItem.setStatusTip(translate('SongsPlugin',
+ 'Import an OpenLP v2 song database'))
+ import_menu.addAction(self.ImportOpenLPSongItem)
+ QtCore.QObject.connect(self.ImportOpenLPSongItem,
+ QtCore.SIGNAL(u'triggered()'), self.onImportOpenLPSongItemClick)
def addExportMenuItem(self, export_menu):
"""
@@ -177,6 +210,45 @@ class SongsPlugin(Plugin):
QtGui.QMessageBox.Ok)
Receiver.send_message(u'songs_load_list')
+ def onImportOpenSongItemClick(self):
+ filenames = QtGui.QFileDialog.getOpenFileNames(
+ None, translate('SongsPlugin',
+ 'Open OpenSong file'),
+ u'', u'All files (*.*)')
+ try:
+ for filename in filenames:
+ importer = OpenSongImport(self.manager)
+ importer.do_import(unicode(filename))
+ except:
+ log.exception('Could not import OpenSong file')
+ QtGui.QMessageBox.critical(None,
+ translate('SongsPlugin',
+ 'Import Error'),
+ translate('SongsPlugin',
+ 'Error importing OpenSong file'),
+ QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok),
+ QtGui.QMessageBox.Ok)
+ Receiver.send_message(u'songs_load_list')
+
+ def onImportOpenLPSongItemClick(self):
+ filenames = QtGui.QFileDialog.getOpenFileNames(None,
+ translate('SongsPlugin', 'Select OpenLP database(s) to import...'),
+ u'', u'OpenLP databases (*.sqlite);;All Files (*)')
+ try:
+ for filename in filenames:
+ db_url = u'sqlite:///%s' % filename
+ importer = OpenLPSongImport(self.manager, db_url)
+ importer.import_source_v2_db()
+ QtGui.QMessageBox.information(None, translate('SongsPlugin',
+ 'Database(s) imported'), translate('SongsPlugin', 'Your '
+ 'OpenLP v2 song databases have been successfully imported'))
+ except:
+ log.exception(u'Failed to import OpenLP v2 database(s)')
+ QtGui.QMessageBox.critical(None, translate('SongsPlugin',
+ 'Import Error'), translate('SongsPlugin',
+ 'Error importing OpenLP v2 database(s)'))
+ Receiver.send_message(u'songs_load_list')
+
def onImportOooItemClick(self):
filenames = QtGui.QFileDialog.getOpenFileNames(
None, translate('SongsPlugin',
@@ -198,8 +270,7 @@ class SongsPlugin(Plugin):
Returns True if the theme is being used, otherwise returns False.
"""
- if self.manager.get_all_objects_filtered(Song,
- Song.theme_name == theme):
+ if self.manager.get_all_objects(Song, Song.theme_name == theme):
return True
return False
@@ -214,7 +285,7 @@ class SongsPlugin(Plugin):
``newTheme``
The new name the plugin should now use.
"""
- songsUsingTheme = self.manager.get_all_objects_filtered(Song,
+ songsUsingTheme = self.manager.get_all_objects(Song,
Song.theme_name == oldTheme)
for song in songsUsingTheme:
song.theme_name = newTheme
diff --git a/openlp/plugins/songusage/forms/songusagedeleteform.py b/openlp/plugins/songusage/forms/songusagedeleteform.py
index 97e032413..4ded872f0 100644
--- a/openlp/plugins/songusage/forms/songusagedeleteform.py
+++ b/openlp/plugins/songusage/forms/songusagedeleteform.py
@@ -25,8 +25,9 @@
from PyQt4 import QtGui
-from songusagedeletedialog import Ui_SongUsageDeleteDialog
from openlp.core.lib import translate
+from openlp.plugins.songusage.lib.db import SongUsageItem
+from songusagedeletedialog import Ui_SongUsageDeleteDialog
class SongUsageDeleteForm(QtGui.QDialog, Ui_SongUsageDeleteDialog):
"""
@@ -52,6 +53,6 @@ class SongUsageDeleteForm(QtGui.QDialog, Ui_SongUsageDeleteDialog):
QtGui.QMessageBox.Cancel)
if ret == QtGui.QMessageBox.Ok:
deleteDate = self.DeleteCalendar.selectedDate().toPyDate()
- self.songusagemanager.delete_to_date(deleteDate)
+ self.songusagemanager.delete_all_objects(SongUsageItem,
+ SongUsageItem.usagedate <= deleteDate)
self.close()
-
diff --git a/openlp/plugins/songusage/forms/songusagedetailform.py b/openlp/plugins/songusage/forms/songusagedetailform.py
index be1b8221c..ac65ce857 100644
--- a/openlp/plugins/songusage/forms/songusagedetailform.py
+++ b/openlp/plugins/songusage/forms/songusagedetailform.py
@@ -27,9 +27,10 @@ import logging
import os
from PyQt4 import QtCore, QtGui
+from sqlalchemy.sql import and_
from openlp.core.lib import SettingsManager, translate
-
+from openlp.plugins.songusage.lib.db import SongUsageItem
from songusagedetaildialog import Ui_SongUsageDetailDialog
log = logging.getLogger(__name__)
@@ -74,8 +75,11 @@ class SongUsageDetailForm(QtGui.QDialog, Ui_SongUsageDetailDialog):
filename = u'usage_detail_%s_%s.txt' % (
self.FromDate.selectedDate().toString(u'ddMMyyyy'),
self.ToDate.selectedDate().toString(u'ddMMyyyy'))
- usage = self.parent.songusagemanager.get_songusage_for_period(
- self.FromDate.selectedDate(), self.ToDate.selectedDate())
+ usage = self.parent.songusagemanager.get_all_objects(
+ SongUsageItem, and_(
+ SongUsageItem.usagedate >= self.FromDate.selectedDate().toPyDate(),
+ SongUsageItem.usagedate < self.ToDate.selectedDate().toPyDate()),
+ [SongUsageItem.usagedate, SongUsageItem.usagetime])
outname = os.path.join(unicode(self.FileLineEdit.text()), filename)
file = None
try:
diff --git a/openlp/plugins/songusage/lib/__init__.py b/openlp/plugins/songusage/lib/__init__.py
index ae8425317..bd83e0532 100644
--- a/openlp/plugins/songusage/lib/__init__.py
+++ b/openlp/plugins/songusage/lib/__init__.py
@@ -25,4 +25,3 @@
"""
The :mod:`lib` module contains the library functions for the songusage plugin.
"""
-from openlp.plugins.songusage.lib.manager import SongUsageManager
diff --git a/openlp/plugins/songusage/lib/manager.py b/openlp/plugins/songusage/lib/manager.py
deleted file mode 100644
index 2c34f3a54..000000000
--- a/openlp/plugins/songusage/lib/manager.py
+++ /dev/null
@@ -1,81 +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 #
-###############################################################################
-"""
-The :mod:`manager` module provides song usage specific database query code
-"""
-import logging
-
-from sqlalchemy.exceptions import InvalidRequestError
-
-from openlp.core.lib.db import Manager
-from openlp.plugins.songusage.lib.db import init_schema, SongUsageItem
-
-log = logging.getLogger(__name__)
-
-class SongUsageManager(Manager):
- """
- The Song Manager provides a central location for all database code. This
- class takes care of connecting to the database and running all the queries.
- """
- log.info(u'SongUsage manager loaded')
-
- def __init__(self):
- """
- Creates the connection to the database, and creates the tables if they
- don't exist.
- """
- log.debug(u'SongUsage Initialising')
- Manager.__init__(self, u'songusage', init_schema)
- log.debug(u'SongUsage Initialised')
-
- def get_songusage_for_period(self, start_date, end_date):
- """
- Returns the details of SongUsage for a designated time period
-
- ``start_date``
- The start of the period to return
-
- ``end_date``
- The end of the period to return
- """
- return self.session.query(SongUsageItem) \
- .filter(SongUsageItem.usagedate >= start_date.toPyDate()) \
- .filter(SongUsageItem.usagedate < end_date.toPyDate()) \
- .order_by(SongUsageItem.usagedate, SongUsageItem.usagetime).all()
-
- def delete_to_date(self, date):
- """
- Delete SongUsage records before given date
- """
- try:
- self.session.query(SongUsageItem) \
- .filter(SongUsageItem.usagedate <= date) \
- .delete(synchronize_session=False)
- self.session.commit()
- return True
- except InvalidRequestError:
- self.session.rollback()
- log.exception(u'Failed to delete all Song Usage items to %s' % date)
- return False
diff --git a/openlp/plugins/songusage/songusageplugin.py b/openlp/plugins/songusage/songusageplugin.py
index 07e7271a1..c7a8a30aa 100644
--- a/openlp/plugins/songusage/songusageplugin.py
+++ b/openlp/plugins/songusage/songusageplugin.py
@@ -24,15 +24,15 @@
###############################################################################
import logging
-
from datetime import datetime
+
from PyQt4 import QtCore, QtGui
from openlp.core.lib import Plugin, Receiver, build_icon, translate
-from openlp.plugins.songusage.lib import SongUsageManager
+from openlp.core.lib.db import Manager
from openlp.plugins.songusage.forms import SongUsageDetailForm, \
SongUsageDeleteForm
-from openlp.plugins.songusage.lib.db import SongUsageItem
+from openlp.plugins.songusage.lib.db import init_schema, SongUsageItem
log = logging.getLogger(__name__)
@@ -117,7 +117,7 @@ class SongUsagePlugin(Plugin):
QtCore.QVariant(False)).toBool()
self.SongUsageStatus.setChecked(self.SongUsageActive)
if self.songusagemanager is None:
- self.songusagemanager = SongUsageManager()
+ self.songusagemanager = Manager(u'songusage', init_schema)
self.SongUsagedeleteform = SongUsageDeleteForm(self.songusagemanager)
self.SongUsagedetailform = SongUsageDetailForm(self)
self.SongUsageMenu.menuAction().setVisible(True)