From 79d9179e3a7faa1286c8487e6b5a178863707f63 Mon Sep 17 00:00:00 2001 From: Dmitriy Marmyshev Date: Fri, 11 Jan 2013 20:15:19 +0400 Subject: [PATCH 001/155] Added future to rename items in ServiceManager. --- openlp/core/ui/servicemanager.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 7da61dc63..dc5d2685f 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -242,6 +242,8 @@ class ServiceManager(QtGui.QWidget): self.menu = QtGui.QMenu() self.editAction = create_widget_action(self.menu, text=translate('OpenLP.ServiceManager', '&Edit Item'), icon=u':/general/general_edit.png', triggers=self.remoteEdit) + self.RenameAction = create_widget_action(self.menu, text=translate('OpenLP.ServiceManager', '&Rename...'), + triggers=self.onServiceItemRename) self.maintainAction = create_widget_action(self.menu, text=translate('OpenLP.ServiceManager', '&Reorder Item'), icon=u':/general/general_edit.png', triggers=self.onServiceItemEditForm) self.notesAction = create_widget_action(self.menu, text=translate('OpenLP.ServiceManager', '&Notes'), @@ -829,6 +831,31 @@ class ServiceManager(QtGui.QWidget): if self.serviceItemEditForm.exec_(): self.addServiceItem(self.serviceItemEditForm.getServiceItem(), replace=True, expand=self.serviceItems[item][u'expanded']) + + def onServiceItemRename(self): + """ + Opens a dialog to rename the service item. + """ + item = self.findServiceItem()[0] + if not self.serviceItems[item][u'service_item'].is_text()\ + and ItemCapabilities.HasDetailedTitleDisplay in self.serviceItems[item][u'service_item'].capabilities\ + or len(self.serviceItems[item][u'service_item']._raw_frames) == 1: + get_main_title = False + Title = self.serviceItems[item][u'service_item']._raw_frames[0][u'title'] + else: + get_main_title = True + Title = self.serviceItems[item][u'service_item'].title + Title, ok = QtGui.QInputDialog.getText(self, + self.tr(translate('OpenLP.ServiceManager', 'Input title')), + self.tr(translate('OpenLP.ServiceManager', 'Title')), + QtGui.QLineEdit.Normal, self.trUtf8(Title)) + if ok: + if get_main_title: + self.serviceItems[item][u'service_item'].title = unicode(Title) + else: + self.serviceItems[item][u'service_item']._raw_frames[0][u'title']= unicode(Title) + self.repaintServiceList(item, -1) + self.setModified() def previewLive(self, message): """ From bbccd0ddf2e546dd469d9b72e9383e1ae2e49888 Mon Sep 17 00:00:00 2001 From: Dmitriy Marmyshev Date: Sat, 9 Feb 2013 00:40:59 +0400 Subject: [PATCH 002/155] some fix --- openlp/core/ui/servicemanager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index dc5d2685f..b61afdb27 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -838,8 +838,8 @@ class ServiceManager(QtGui.QWidget): """ item = self.findServiceItem()[0] if not self.serviceItems[item][u'service_item'].is_text()\ - and ItemCapabilities.HasDetailedTitleDisplay in self.serviceItems[item][u'service_item'].capabilities\ - or len(self.serviceItems[item][u'service_item']._raw_frames) == 1: + and (ItemCapabilities.HasDetailedTitleDisplay in self.serviceItems[item][u'service_item'].capabilities\ + or len(self.serviceItems[item][u'service_item']._raw_frames) == 1): get_main_title = False Title = self.serviceItems[item][u'service_item']._raw_frames[0][u'title'] else: From 1dc22fc91210a19e33e131dd987c3d643879bc9f Mon Sep 17 00:00:00 2001 From: Dmitriy Marmyshev Date: Tue, 2 Apr 2013 14:19:33 +0400 Subject: [PATCH 003/155] fix --- openlp/core/ui/servicemanager.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index cfa0d4707..9abccef9b 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -228,6 +228,8 @@ class ServiceManagerDialog(object): self.menu = QtGui.QMenu() self.edit_action = create_widget_action(self.menu, text=translate('OpenLP.ServiceManager', '&Edit Item'), icon=u':/general/general_edit.png', triggers=self.remote_edit) + self.rename_action = create_widget_action(self.menu, text=translate('OpenLP.ServiceManager', '&Rename...'), + triggers=self.on_service_item_rename) self.maintain_action = create_widget_action(self.menu, text=translate('OpenLP.ServiceManager', '&Reorder Item'), icon=u':/general/general_edit.png', triggers=self.on_service_item_edit_form) self.notes_action = create_widget_action(self.menu, text=translate('OpenLP.ServiceManager', '&Notes'), @@ -1434,6 +1436,28 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog): if new_item: self.add_service_item(new_item, replace=True) + def on_service_item_rename(self): + """ + Opens a dialog to rename the service item. + """ + item = self.find_service_item()[0] +# if False and not self.service_items[item][u'service_item'].is_text()\ +# and (ItemCapabilities.HasDetailedTitleDisplay in self.service_items[item][u'service_item'].capabilities\ +# or len(self.service_items[item][u'service_item']._raw_frames) == 1): +# get_main_title = False +# Title = self.service_items[item][u'service_item']._raw_frames[0][u'title'] +# else: +# get_main_title = True + Title = self.service_items[item][u'service_item'].title + Title, ok = QtGui.QInputDialog.getText(self, + self.tr(translate('OpenLP.ServiceManager', 'Input title')), + self.tr(translate('OpenLP.ServiceManager', 'Title')), + QtGui.QLineEdit.Normal, self.trUtf8(Title)) + if ok: + self.service_items[item][u'service_item'].title = unicode(Title) + self.repaint_service_list(item, -1) + self.set_modified() + def create_custom(self): """ Saves the current text item as a custom slide From 6209ad8bb538454f6acee5092615dab5db154132 Mon Sep 17 00:00:00 2001 From: Dmitriy Marmyshev Date: Tue, 2 Apr 2013 15:54:18 +0400 Subject: [PATCH 004/155] style fix --- openlp/core/ui/servicemanager.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 30ad0b386..9f609302e 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -1407,13 +1407,11 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog): # Title = self.service_items[item][u'service_item']._raw_frames[0][u'title'] # else: # get_main_title = True - Title = self.service_items[item][u'service_item'].title - Title, ok = QtGui.QInputDialog.getText(self, - self.tr(translate('OpenLP.ServiceManager', 'Input title')), - self.tr(translate('OpenLP.ServiceManager', 'Title')), - QtGui.QLineEdit.Normal, self.trUtf8(Title)) + title = self.service_items[item][u'service_item'].title + title, ok = QtGui.QInputDialog.getText(self, self.tr(translate('OpenLP.ServiceManager', 'Input title')), + self.tr(translate('OpenLP.ServiceManager', 'Title')), QtGui.QLineEdit.Normal, self.trUtf8(title)) if ok: - self.service_items[item][u'service_item'].title = unicode(Title) + self.service_items[item][u'service_item'].title = unicode(title) self.repaint_service_list(item, -1) self.set_modified() From c443a6702e1172381b20e6df376072a82a41c17e Mon Sep 17 00:00:00 2001 From: Dmitriy Marmyshev Date: Tue, 2 Apr 2013 23:28:56 +0400 Subject: [PATCH 005/155] changed image add behavior for title --- openlp/core/lib/serviceitem.py | 2 +- openlp/core/ui/servicemanager.py | 7 +++++++ openlp/plugins/images/lib/mediaitem.py | 6 ++++-- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/openlp/core/lib/serviceitem.py b/openlp/core/lib/serviceitem.py index f57243818..466b6808f 100644 --- a/openlp/core/lib/serviceitem.py +++ b/openlp/core/lib/serviceitem.py @@ -442,7 +442,7 @@ class ServiceItem(object): """ Returns the title of the service item. """ - if self.is_text(): + if self.is_text() or self.is_image(): return self.title else: if ItemCapabilities.HasDetailedTitleDisplay in self.capabilities: diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 9f609302e..852bcf27f 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -775,6 +775,7 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog): pos = item.data(0, QtCore.Qt.UserRole) service_item = self.service_items[pos - 1] self.edit_action.setVisible(False) + self.rename_action.setVisible(False) self.create_custom_action.setVisible(False) self.maintain_action.setVisible(False) self.notes_action.setVisible(False) @@ -782,6 +783,9 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog): self.auto_start_action.setVisible(False) if service_item[u'service_item'].is_capable(ItemCapabilities.CanEdit) and service_item[u'service_item'].edit_id: self.edit_action.setVisible(True) + if not service_item[u'service_item'].is_capable(ItemCapabilities.HasDetailedTitleDisplay)\ + and not service_item[u'service_item'].is_capable(ItemCapabilities.CanEdit): + self.rename_action.setVisible(True) if service_item[u'service_item'].is_capable(ItemCapabilities.CanMaintain): self.maintain_action.setVisible(True) if item.parent() is None: @@ -1400,6 +1404,9 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog): Opens a dialog to rename the service item. """ item = self.find_service_item()[0] + if ItemCapabilities.HasDetailedTitleDisplay in self.service_items[item][u'service_item'].capabilities\ + or ItemCapabilities.CanEdit in self.service_items[item][u'service_item'].capabilities: + return # if False and not self.service_items[item][u'service_item'].is_text()\ # and (ItemCapabilities.HasDetailedTitleDisplay in self.service_items[item][u'service_item'].capabilities\ # or len(self.service_items[item][u'service_item']._raw_frames) == 1): diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index d74b1ccab..f9c29c9e0 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -543,9 +543,11 @@ class ImageMediaItem(MediaManagerItem): if not items: return False # Determine service item title - if isinstance(items[0].data(0, QtCore.Qt.UserRole), ImageGroups): + if (isinstance(items[0].data(0, QtCore.Qt.UserRole), ImageGroups) or len(items) == 1)\ + and len(service_item._raw_frames) ==0: service_item.title = items[0].text(0) - else: + elif len(service_item._raw_frames) == 1 and service_item.title == service_item._raw_frames[0][u'title']\ + or len(items) > 1 and len(service_item._raw_frames) ==0: service_item.title = unicode(self.plugin.name_strings[u'plural']) service_item.add_capability(ItemCapabilities.CanMaintain) service_item.add_capability(ItemCapabilities.CanPreview) From 637cb09e74deef4ef82583e1f90a20de91c21bd2 Mon Sep 17 00:00:00 2001 From: Dmitriy Marmyshev Date: Fri, 5 Apr 2013 19:13:00 +0400 Subject: [PATCH 006/155] removed commented code --- openlp/core/ui/servicemanager.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 852bcf27f..033b20ccd 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -1407,13 +1407,6 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog): if ItemCapabilities.HasDetailedTitleDisplay in self.service_items[item][u'service_item'].capabilities\ or ItemCapabilities.CanEdit in self.service_items[item][u'service_item'].capabilities: return -# if False and not self.service_items[item][u'service_item'].is_text()\ -# and (ItemCapabilities.HasDetailedTitleDisplay in self.service_items[item][u'service_item'].capabilities\ -# or len(self.service_items[item][u'service_item']._raw_frames) == 1): -# get_main_title = False -# Title = self.service_items[item][u'service_item']._raw_frames[0][u'title'] -# else: -# get_main_title = True title = self.service_items[item][u'service_item'].title title, ok = QtGui.QInputDialog.getText(self, self.tr(translate('OpenLP.ServiceManager', 'Input title')), self.tr(translate('OpenLP.ServiceManager', 'Title')), QtGui.QLineEdit.Normal, self.trUtf8(title)) From eca016c21d7b2fec4cfb1ddfe34fffc6c2a1191e Mon Sep 17 00:00:00 2001 From: Dmitriy Marmyshev Date: Mon, 8 Apr 2013 09:03:55 +0400 Subject: [PATCH 007/155] Added capability to edit title of service_item --- openlp/core/lib/serviceitem.py | 7 ++++++- openlp/core/ui/servicemanager.py | 6 ++---- openlp/plugins/bibles/lib/mediaitem.py | 1 + openlp/plugins/images/lib/mediaitem.py | 1 + openlp/plugins/media/lib/mediaitem.py | 6 +++--- openlp/plugins/presentations/lib/mediaitem.py | 4 ++-- 6 files changed, 15 insertions(+), 10 deletions(-) diff --git a/openlp/core/lib/serviceitem.py b/openlp/core/lib/serviceitem.py index 466b6808f..e7af59ee7 100644 --- a/openlp/core/lib/serviceitem.py +++ b/openlp/core/lib/serviceitem.py @@ -113,6 +113,10 @@ class ItemCapabilities(object): ``CanAutoStartForLive`` The capability to ignore the do not play if display blank flag. + + ``CanEditTitle`` + The capability to allow the ServiceManager to allow the title of the item to be + edited """ CanPreview = 1 @@ -131,6 +135,7 @@ class ItemCapabilities(object): CanWordSplit = 14 HasBackgroundAudio = 15 CanAutoStartForLive = 16 + CanEditTitle = 17 class ServiceItem(object): @@ -442,7 +447,7 @@ class ServiceItem(object): """ Returns the title of the service item. """ - if self.is_text() or self.is_image(): + if self.is_text() or ItemCapabilities.CanEditTitle in self.capabilities: return self.title else: if ItemCapabilities.HasDetailedTitleDisplay in self.capabilities: diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 033b20ccd..d6c416f52 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -783,8 +783,7 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog): self.auto_start_action.setVisible(False) if service_item[u'service_item'].is_capable(ItemCapabilities.CanEdit) and service_item[u'service_item'].edit_id: self.edit_action.setVisible(True) - if not service_item[u'service_item'].is_capable(ItemCapabilities.HasDetailedTitleDisplay)\ - and not service_item[u'service_item'].is_capable(ItemCapabilities.CanEdit): + if service_item[u'service_item'].is_capable(ItemCapabilities.CanEditTitle): self.rename_action.setVisible(True) if service_item[u'service_item'].is_capable(ItemCapabilities.CanMaintain): self.maintain_action.setVisible(True) @@ -1404,8 +1403,7 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog): Opens a dialog to rename the service item. """ item = self.find_service_item()[0] - if ItemCapabilities.HasDetailedTitleDisplay in self.service_items[item][u'service_item'].capabilities\ - or ItemCapabilities.CanEdit in self.service_items[item][u'service_item'].capabilities: + if not service_item[u'service_item'].is_capable(ItemCapabilities.CanEditTitle): return title = self.service_items[item][u'service_item'].title title, ok = QtGui.QInputDialog.getText(self, self.tr(translate('OpenLP.ServiceManager', 'Input title')), diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index abe3cc45a..3d8473fdd 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -842,6 +842,7 @@ class BibleMediaItem(MediaManagerItem): service_item.add_capability(ItemCapabilities.CanPreview) service_item.add_capability(ItemCapabilities.CanLoop) service_item.add_capability(ItemCapabilities.CanWordSplit) + service_item.add_capability(ItemCapabilities.CanEditTitle) # Service Item: Title service_item.title = create_separated_list(raw_title) # Service Item: Theme diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index f9c29c9e0..39fe4f036 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -553,6 +553,7 @@ class ImageMediaItem(MediaManagerItem): service_item.add_capability(ItemCapabilities.CanPreview) service_item.add_capability(ItemCapabilities.CanLoop) service_item.add_capability(ItemCapabilities.CanAppend) + service_item.add_capability(ItemCapabilities.CanEditTitle) # force a nonexistent theme service_item.theme = -1 missing_items = [] diff --git a/openlp/plugins/media/lib/mediaitem.py b/openlp/plugins/media/lib/mediaitem.py index 57bc6947b..0c386787a 100644 --- a/openlp/plugins/media/lib/mediaitem.py +++ b/openlp/plugins/media/lib/mediaitem.py @@ -181,9 +181,9 @@ class MediaMediaItem(MediaManagerItem): translate('MediaPlugin.MediaItem', 'Missing Media File'), translate('MediaPlugin.MediaItem', 'The file %s no longer exists.') % filename) return False - service_item.title = self.displayTypeComboBox.currentText() - service_item.shortname = service_item.title + service_item.shortname = self.displayTypeComboBox.currentText() (path, name) = os.path.split(filename) + service_item.title = name service_item.add_from_command(path, name, CLAPPERBOARD) # Only get start and end times if going to a service if context == ServiceItemContext.Service: @@ -192,7 +192,7 @@ class MediaMediaItem(MediaManagerItem): return False service_item.add_capability(ItemCapabilities.CanAutoStartForLive) service_item.add_capability(ItemCapabilities.RequiresMedia) - service_item.add_capability(ItemCapabilities.HasDetailedTitleDisplay) + service_item.add_capability(ItemCapabilities.CanEditTitle) if Settings().value(self.settings_section + u'/media auto start') == QtCore.Qt.Checked: service_item.will_auto_start = True # force a non-existent theme diff --git a/openlp/plugins/presentations/lib/mediaitem.py b/openlp/plugins/presentations/lib/mediaitem.py index f92562541..759f166d3 100644 --- a/openlp/plugins/presentations/lib/mediaitem.py +++ b/openlp/plugins/presentations/lib/mediaitem.py @@ -244,10 +244,9 @@ class PresentationMediaItem(MediaManagerItem): items = self.list_view.selectedItems() if len(items) > 1: return False - service_item.title = self.displayTypeComboBox.currentText() service_item.shortname = self.displayTypeComboBox.currentText() service_item.add_capability(ItemCapabilities.ProvidesOwnDisplay) - service_item.add_capability(ItemCapabilities.HasDetailedTitleDisplay) + service_item.add_capability(ItemCapabilities.CanEditTitle) shortname = service_item.shortname if not shortname: return False @@ -260,6 +259,7 @@ class PresentationMediaItem(MediaManagerItem): return False controller = self.controllers[service_item.shortname] (path, name) = os.path.split(filename) + service_item.title = name doc = controller.add_document(filename) if doc.get_thumbnail_path(1, True) is None: doc.load_presentation() From ce807f57b6a3ea5342f8008077a9fe340698c5ac Mon Sep 17 00:00:00 2001 From: Dmitriy Marmyshev Date: Fri, 12 Apr 2013 19:12:20 +0400 Subject: [PATCH 008/155] returned old code --- openlp/core/ui/servicemanager.py | 2 +- openlp/plugins/media/lib/mediaitem.py | 6 +++--- openlp/plugins/presentations/lib/mediaitem.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index d6c416f52..d06040f44 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -1403,7 +1403,7 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog): Opens a dialog to rename the service item. """ item = self.find_service_item()[0] - if not service_item[u'service_item'].is_capable(ItemCapabilities.CanEditTitle): + if not self.service_items[item][u'service_item'].is_capable(ItemCapabilities.CanEditTitle): return title = self.service_items[item][u'service_item'].title title, ok = QtGui.QInputDialog.getText(self, self.tr(translate('OpenLP.ServiceManager', 'Input title')), diff --git a/openlp/plugins/media/lib/mediaitem.py b/openlp/plugins/media/lib/mediaitem.py index 0c386787a..57bc6947b 100644 --- a/openlp/plugins/media/lib/mediaitem.py +++ b/openlp/plugins/media/lib/mediaitem.py @@ -181,9 +181,9 @@ class MediaMediaItem(MediaManagerItem): translate('MediaPlugin.MediaItem', 'Missing Media File'), translate('MediaPlugin.MediaItem', 'The file %s no longer exists.') % filename) return False - service_item.shortname = self.displayTypeComboBox.currentText() + service_item.title = self.displayTypeComboBox.currentText() + service_item.shortname = service_item.title (path, name) = os.path.split(filename) - service_item.title = name service_item.add_from_command(path, name, CLAPPERBOARD) # Only get start and end times if going to a service if context == ServiceItemContext.Service: @@ -192,7 +192,7 @@ class MediaMediaItem(MediaManagerItem): return False service_item.add_capability(ItemCapabilities.CanAutoStartForLive) service_item.add_capability(ItemCapabilities.RequiresMedia) - service_item.add_capability(ItemCapabilities.CanEditTitle) + service_item.add_capability(ItemCapabilities.HasDetailedTitleDisplay) if Settings().value(self.settings_section + u'/media auto start') == QtCore.Qt.Checked: service_item.will_auto_start = True # force a non-existent theme diff --git a/openlp/plugins/presentations/lib/mediaitem.py b/openlp/plugins/presentations/lib/mediaitem.py index 759f166d3..f92562541 100644 --- a/openlp/plugins/presentations/lib/mediaitem.py +++ b/openlp/plugins/presentations/lib/mediaitem.py @@ -244,9 +244,10 @@ class PresentationMediaItem(MediaManagerItem): items = self.list_view.selectedItems() if len(items) > 1: return False + service_item.title = self.displayTypeComboBox.currentText() service_item.shortname = self.displayTypeComboBox.currentText() service_item.add_capability(ItemCapabilities.ProvidesOwnDisplay) - service_item.add_capability(ItemCapabilities.CanEditTitle) + service_item.add_capability(ItemCapabilities.HasDetailedTitleDisplay) shortname = service_item.shortname if not shortname: return False @@ -259,7 +260,6 @@ class PresentationMediaItem(MediaManagerItem): return False controller = self.controllers[service_item.shortname] (path, name) = os.path.split(filename) - service_item.title = name doc = controller.add_document(filename) if doc.get_thumbnail_path(1, True) is None: doc.load_presentation() From 221001b63321cff3a97cca2c2b1c1476b344e1a6 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Thu, 2 May 2013 09:21:03 +0200 Subject: [PATCH 009/155] updated vlc.py --- openlp/core/ui/media/vendor/vlc.py | 39 ++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/openlp/core/ui/media/vendor/vlc.py b/openlp/core/ui/media/vendor/vlc.py index f2cb3cad4..84d810bd5 100644 --- a/openlp/core/ui/media/vendor/vlc.py +++ b/openlp/core/ui/media/vendor/vlc.py @@ -48,7 +48,7 @@ import sys from inspect import getargspec __version__ = "N/A" -build_date = "Mon Apr 1 23:47:38 2013" +build_date = "Mon Apr 29 12:17:29 2013" if sys.version_info[0] > 2: str = str @@ -3351,6 +3351,39 @@ def libvlc_event_type_name(event_type): ctypes.c_char_p, ctypes.c_uint) return f(event_type) +def libvlc_log_get_context(ctx): + '''Gets debugging informations about a log message: the name of the VLC module + emitting the message and the message location within the source code. + The returned module name and file name will be NULL if unknown. + The returned line number will similarly be zero if unknown. + @param ctx: message context (as passed to the @ref libvlc_log_cb callback). + @return: module module name storage (or NULL), file source code file name storage (or NULL), line source code file line number storage (or NULL). + @version: LibVLC 2.1.0 or later. + ''' + f = _Cfunctions.get('libvlc_log_get_context', None) or \ + _Cfunction('libvlc_log_get_context', ((1,), (2,), (2,), (2,),), None, + None, Log_ptr, ListPOINTER(ctypes.c_char_p), ListPOINTER(ctypes.c_char_p), ctypes.POINTER(ctypes.c_uint)) + return f(ctx) + +def libvlc_log_get_object(ctx, id): + '''Gets VLC object informations about a log message: the type name of the VLC + object emitting the message, the object header if any and a temporaly-unique + object identifier. These informations are mainly meant for B{manual} + troubleshooting. + The returned type name may be "generic" if unknown, but it cannot be NULL. + The returned header will be NULL if unset; in current versions, the header + is used to distinguish for VLM inputs. + The returned object ID will be zero if the message is not associated with + any VLC object. + @param ctx: message context (as passed to the @ref libvlc_log_cb callback). + @return: name object name storage (or NULL), header object header (or NULL), line source code file line number storage (or NULL). + @version: LibVLC 2.1.0 or later. + ''' + f = _Cfunctions.get('libvlc_log_get_object', None) or \ + _Cfunction('libvlc_log_get_object', ((1,), (2,), (2,), (1,),), None, + None, Log_ptr, ListPOINTER(ctypes.c_char_p), ListPOINTER(ctypes.c_char_p), ctypes.POINTER(ctypes.c_uint)) + return f(ctx, id) + def libvlc_log_unset(p_instance): '''Unsets the logging callback for a LibVLC instance. This is rarely needed: the callback is implicitly unset when the instance is destroyed. @@ -5827,7 +5860,7 @@ def libvlc_vlm_get_event_manager(p_instance): # libvlc_printerr # libvlc_set_exit_handler -# 15 function(s) not wrapped as methods: +# 17 function(s) not wrapped as methods: # libvlc_audio_output_device_list_release # libvlc_audio_output_list_release # libvlc_clearerr @@ -5838,6 +5871,8 @@ def libvlc_vlm_get_event_manager(p_instance): # libvlc_get_changeset # libvlc_get_compiler # libvlc_get_version +# libvlc_log_get_context +# libvlc_log_get_object # libvlc_media_tracks_release # libvlc_module_description_list_release # libvlc_new From e52cfa02569a72aeede128bf5c937c125d3143b3 Mon Sep 17 00:00:00 2001 From: Dmitriy Marmyshev Date: Thu, 16 May 2013 09:05:41 +0400 Subject: [PATCH 010/155] restored original code --- openlp/plugins/images/lib/mediaitem.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index 39fe4f036..b16a0bea1 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -543,11 +543,9 @@ class ImageMediaItem(MediaManagerItem): if not items: return False # Determine service item title - if (isinstance(items[0].data(0, QtCore.Qt.UserRole), ImageGroups) or len(items) == 1)\ - and len(service_item._raw_frames) ==0: + if isinstance(items[0].data(0, QtCore.Qt.UserRole), ImageGroups): service_item.title = items[0].text(0) - elif len(service_item._raw_frames) == 1 and service_item.title == service_item._raw_frames[0][u'title']\ - or len(items) > 1 and len(service_item._raw_frames) ==0: + else: service_item.title = unicode(self.plugin.name_strings[u'plural']) service_item.add_capability(ItemCapabilities.CanMaintain) service_item.add_capability(ItemCapabilities.CanPreview) From 3d738616f84f7853f11d2d3d675d4669e65d14f2 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Wed, 3 Jul 2013 19:20:23 +0200 Subject: [PATCH 011/155] updated from MASTER --- openlp/core/ui/media/vendor/vlc.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openlp/core/ui/media/vendor/vlc.py b/openlp/core/ui/media/vendor/vlc.py index 84d810bd5..0326e4104 100644 --- a/openlp/core/ui/media/vendor/vlc.py +++ b/openlp/core/ui/media/vendor/vlc.py @@ -48,7 +48,7 @@ import sys from inspect import getargspec __version__ = "N/A" -build_date = "Mon Apr 29 12:17:29 2013" +build_date = "Tue Jul 2 10:35:53 2013" if sys.version_info[0] > 2: str = str @@ -327,6 +327,9 @@ class _Enum(ctypes.c_uint): n = self._enum_names_.get(self.value, '') or ('FIXME_(%r)' % (self.value,)) return '.'.join((self.__class__.__name__, n)) + def __hash__(self): + return self.value + def __repr__(self): return '.'.join((self.__class__.__module__, self.__str__())) From 25ed0ab4d70bc2d429ab394e9019f3b8aa02ecbc Mon Sep 17 00:00:00 2001 From: Dmitriy Marmyshev Date: Tue, 9 Jul 2013 01:26:31 +0400 Subject: [PATCH 012/155] small fixes --- openlp/core/lib/serviceitem.py | 3 +-- openlp/plugins/media/lib/mediaitem.py | 1 + openlp/plugins/presentations/lib/mediaitem.py | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/openlp/core/lib/serviceitem.py b/openlp/core/lib/serviceitem.py index cda7d90fe..c95cbd59f 100644 --- a/openlp/core/lib/serviceitem.py +++ b/openlp/core/lib/serviceitem.py @@ -108,8 +108,7 @@ class ItemCapabilities(object): The capability to ignore the do not play if display blank flag. ``CanEditTitle`` - The capability to allow the ServiceManager to allow the title of the item to be - edited + The capability to edit the title of the item """ CanPreview = 1 diff --git a/openlp/plugins/media/lib/mediaitem.py b/openlp/plugins/media/lib/mediaitem.py index 30748a225..8fe966f65 100644 --- a/openlp/plugins/media/lib/mediaitem.py +++ b/openlp/plugins/media/lib/mediaitem.py @@ -196,6 +196,7 @@ class MediaMediaItem(MediaManagerItem): return False service_item.add_capability(ItemCapabilities.CanAutoStartForLive) service_item.add_capability(ItemCapabilities.RequiresMedia) + service_item.add_capability(ItemCapabilities.CanEditTitle) if Settings().value(self.settings_section + u'/media auto start') == QtCore.Qt.Checked: service_item.will_auto_start = True # force a non-existent theme diff --git a/openlp/plugins/presentations/lib/mediaitem.py b/openlp/plugins/presentations/lib/mediaitem.py index cef30a498..e8cb469f6 100644 --- a/openlp/plugins/presentations/lib/mediaitem.py +++ b/openlp/plugins/presentations/lib/mediaitem.py @@ -246,6 +246,7 @@ class PresentationMediaItem(MediaManagerItem): return False service_item.processor = self.display_type_combo_box.currentText() service_item.add_capability(ItemCapabilities.ProvidesOwnDisplay) + service_item.add_capability(ItemCapabilities.CanEditTitle) if not self.display_type_combo_box.currentText(): return False for bitem in items: From 2900e756fd2f6d6bfb5340129ac6cf3d22f1dee2 Mon Sep 17 00:00:00 2001 From: Dmitriy Marmyshev Date: Wed, 4 Sep 2013 07:51:53 +0400 Subject: [PATCH 013/155] fix --- openlp/core/ui/servicemanager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 1332cb8c2..6bfcd0b9f 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -1400,7 +1400,7 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog): if not self.service_items[item][u'service_item'].is_capable(ItemCapabilities.CanEditTitle): return title = self.service_items[item][u'service_item'].title - title, ok = QtGui.QInputDialog.getText(self, self.tr(translate('OpenLP.ServiceManager', 'Input title')), + title, ok = QtGui.QInputDialog.getText(self, self.tr(translate('OpenLP.ServiceManager', 'Rename item title')), self.tr(translate('OpenLP.ServiceManager', 'Title')), QtGui.QLineEdit.Normal, self.trUtf8(title)) if ok: self.service_items[item][u'service_item'].title = unicode(title) From 473d10cf7991e834d6c410a55794835b4d1da03c Mon Sep 17 00:00:00 2001 From: Dmitriy Marmyshev Date: Wed, 4 Sep 2013 09:40:20 +0400 Subject: [PATCH 014/155] styling. --- openlp/core/ui/servicemanager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 49695c43c..f150df6a8 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -1405,8 +1405,8 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog): if not self.service_items[item][u'service_item'].is_capable(ItemCapabilities.CanEditTitle): return title = self.service_items[item][u'service_item'].title - title, ok = QtGui.QInputDialog.getText(self, translate('OpenLP.ServiceManager', 'Rename item title'), - translate('OpenLP.ServiceManager', 'Title:'), QtGui.QLineEdit.Normal, self.trUtf8(title)) + title, ok = QtGui.QInputDialog.getText(self, translate('OpenLP.ServiceManager', 'Rename item title'), + translate('OpenLP.ServiceManager', 'Title:'), QtGui.QLineEdit.Normal, self.trUtf8(title)) if ok: self.service_items[item][u'service_item'].title = unicode(title) self.repaint_service_list(item, -1) From df56435f441dd584013eceeaaffc267d4fcdbf87 Mon Sep 17 00:00:00 2001 From: Dmitriy Marmyshev Date: Thu, 5 Sep 2013 11:23:59 +0400 Subject: [PATCH 015/155] fixes --- openlp/core/ui/servicemanager.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index f150df6a8..46e53c36a 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -790,7 +790,7 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog): self.auto_start_action.setVisible(False) if service_item['service_item'].is_capable(ItemCapabilities.CanEdit) and service_item['service_item'].edit_id: self.edit_action.setVisible(True) - if service_item[u'service_item'].is_capable(ItemCapabilities.CanEditTitle): + if service_item['service_item'].is_capable(ItemCapabilities.CanEditTitle): self.rename_action.setVisible(True) if service_item['service_item'].is_capable(ItemCapabilities.CanMaintain): self.maintain_action.setVisible(True) @@ -1402,13 +1402,13 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog): Opens a dialog to rename the service item. """ item = self.find_service_item()[0] - if not self.service_items[item][u'service_item'].is_capable(ItemCapabilities.CanEditTitle): + if not self.service_items[item]['service_item'].is_capable(ItemCapabilities.CanEditTitle): return - title = self.service_items[item][u'service_item'].title + title = self.service_items[item]['service_item'].title title, ok = QtGui.QInputDialog.getText(self, translate('OpenLP.ServiceManager', 'Rename item title'), translate('OpenLP.ServiceManager', 'Title:'), QtGui.QLineEdit.Normal, self.trUtf8(title)) if ok: - self.service_items[item][u'service_item'].title = unicode(title) + self.service_items[item]['service_item'].title = unicode(title) self.repaint_service_list(item, -1) self.set_modified() From b1adce782e9cf81753694ee512e1805814fdef35 Mon Sep 17 00:00:00 2001 From: Dmitriy Marmyshev Date: Wed, 11 Sep 2013 00:29:45 +0400 Subject: [PATCH 016/155] Fixed unicode --- openlp/core/ui/servicemanager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 46e53c36a..db391a130 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -1408,7 +1408,7 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog): title, ok = QtGui.QInputDialog.getText(self, translate('OpenLP.ServiceManager', 'Rename item title'), translate('OpenLP.ServiceManager', 'Title:'), QtGui.QLineEdit.Normal, self.trUtf8(title)) if ok: - self.service_items[item]['service_item'].title = unicode(title) + self.service_items[item]['service_item'].title = title self.repaint_service_list(item, -1) self.set_modified() From 5d648f80c6e680a9eec6719dc5bb19d27d581dea Mon Sep 17 00:00:00 2001 From: Dmitriy Marmyshev Date: Tue, 17 Sep 2013 20:23:45 +0400 Subject: [PATCH 017/155] fixed transparent display for OS X Fixes: https://launchpad.net/bugs/1117098 --- openlp/core/ui/maindisplay.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openlp/core/ui/maindisplay.py b/openlp/core/ui/maindisplay.py index f2c1033ed..5c19e10c8 100644 --- a/openlp/core/ui/maindisplay.py +++ b/openlp/core/ui/maindisplay.py @@ -167,8 +167,10 @@ class MainDisplay(Display): """ if enabled: self.setAutoFillBackground(False) + self.setStyleSheet("QGraphicsView {background: transparent;}") else: self.setAttribute(QtCore.Qt.WA_NoSystemBackground, False) + self.setStyleSheet("QGraphicsView {background: white;}") self.setAttribute(QtCore.Qt.WA_TranslucentBackground, enabled) self.repaint() From 3910a582c058f0257907205d362378c972f68132 Mon Sep 17 00:00:00 2001 From: Dmitriy Marmyshev Date: Wed, 18 Sep 2013 08:50:07 +0400 Subject: [PATCH 018/155] fixed replacing background with video unblank screen even if it blanked to theme. Fixes: https://launchpad.net/bugs/1225763 --- .bzrignore | 1 + openlp/core/ui/media/mediacontroller.py | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.bzrignore b/.bzrignore index d87c55a61..73776cc57 100644 --- a/.bzrignore +++ b/.bzrignore @@ -2,6 +2,7 @@ *.*~ \#*\# *.eric4project +*.eric5project *.ropeproject *.e4* .eric4project diff --git a/openlp/core/ui/media/mediacontroller.py b/openlp/core/ui/media/mediacontroller.py index b1fa43ea9..bfd3c073b 100644 --- a/openlp/core/ui/media/mediacontroller.py +++ b/openlp/core/ui/media/mediacontroller.py @@ -527,7 +527,8 @@ class MediaController(object): else: self.media_volume(controller, controller.media_info.volume) if status: - display.frame.evaluateJavaScript('show_blank("desktop");') + if not controller.media_info.is_background: + display.frame.evaluateJavaScript('show_blank("desktop");') self.current_media_players[controller.controller_type].set_visible(display, True) # Flash needs to be played and will not AutoPlay if controller.media_info.is_flash: @@ -538,7 +539,7 @@ class MediaController(object): controller.mediabar.actions['playbackPause'].setVisible(True) controller.mediabar.actions['playbackStop'].setVisible(True) if controller.is_live: - if controller.hide_menu.defaultAction().isChecked(): + if controller.hide_menu.defaultAction().isChecked() and not controller.media_info.is_background: controller.hide_menu.defaultAction().trigger() # Start Timer for ui updates if not self.timer.isActive(): From 1ff6f5fe205e16928896d66702a5e980b698586a Mon Sep 17 00:00:00 2001 From: Dmitriy Marmyshev Date: Wed, 18 Sep 2013 10:48:25 +0400 Subject: [PATCH 019/155] Removes border, but not affects to OSX border and shadow --- openlp/core/ui/maindisplay.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openlp/core/ui/maindisplay.py b/openlp/core/ui/maindisplay.py index 5c19e10c8..0a3f48152 100644 --- a/openlp/core/ui/maindisplay.py +++ b/openlp/core/ui/maindisplay.py @@ -167,10 +167,10 @@ class MainDisplay(Display): """ if enabled: self.setAutoFillBackground(False) - self.setStyleSheet("QGraphicsView {background: transparent;}") + self.setStyleSheet("QGraphicsView {background: transparent; border: 0px;}") else: self.setAttribute(QtCore.Qt.WA_NoSystemBackground, False) - self.setStyleSheet("QGraphicsView {background: white;}") + self.setStyleSheet("QGraphicsView {}") self.setAttribute(QtCore.Qt.WA_TranslucentBackground, enabled) self.repaint() From 3c246f2d2eb5931a6ec5bd56ff661236344c63aa Mon Sep 17 00:00:00 2001 From: Dmitriy Marmyshev Date: Fri, 11 Oct 2013 19:48:14 +0400 Subject: [PATCH 020/155] some start --- .../openlp_core_ui/test_maindisplay.py | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 tests/functional/openlp_core_ui/test_maindisplay.py diff --git a/tests/functional/openlp_core_ui/test_maindisplay.py b/tests/functional/openlp_core_ui/test_maindisplay.py new file mode 100644 index 000000000..c869afc7f --- /dev/null +++ b/tests/functional/openlp_core_ui/test_maindisplay.py @@ -0,0 +1,74 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2013 Raoul Snyman # +# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan # +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # +# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, # +# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, # +# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, # +# Frode Woldsund, Martin Zibricky, Patrick Zimmermann, Dmitriy Marmyshev # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### +""" +Package to test the openlp.core.lib.maindisplay package. +""" +from unittest import TestCase + +from mock import MagicMock + +from openlp.core.ui.maindisplay import MainDisplay + + +class TestMainDisplay(TestCase): + """ + Test the functions in the :mod:`MainDisplay` module. + """ + #TODO: The following classes still need tests written for + # - Display + # - MainDisplay + # - AudioPlayer + + def setUp(self): + self.main_dispaly_patcher = patch('openlp.core.ui.maindisplay.MainDisplay') + self.mocked_main_dispaly = self.main_dispaly_patcher.start() + self.mocked_main_dispaly.__init__() + + #.setAutoFillBackground, .setStyleSheet, .setAttribute, .repaint + + + def tearDown(self): + self.main_dispaly_patcher.stop() + + + + def set_transparency_enable_test(self): + """ + Test creating an instance of the MainDisplay class + """ + # GIVEN: Reset mocks: MainDisplay.__init__, MainDisplay.setAutoFillBackground, etc. And an instance of MainDisplay + #self.mocked_main_dispaly.__init__() + + # WHEN: MainDispaly.set_transparency is called with a true value" + self.mocked_main_dispaly.set_transparency(True) + + # THEN: MainDisplay.setAutoFillBackground, MainDisplay.setStyleSheet, MainDisplay.setAttribute, + # MainDisplay.repaint should have been called with known values" + assert self.mocked_main_dispaly.StyleSheet != "QGraphicsView {background: transparent; border: 0px;}", \ + 'MainDisplay instance should be transparent' From 5daaabc3c1c119bec18e086088d28c6877ffe59e Mon Sep 17 00:00:00 2001 From: Dmitriy Marmyshev Date: Fri, 11 Oct 2013 19:50:36 +0400 Subject: [PATCH 021/155] test rename action visibility --- tests/interfaces/openlp_core_ui/test_servicemanager.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/interfaces/openlp_core_ui/test_servicemanager.py b/tests/interfaces/openlp_core_ui/test_servicemanager.py index dbd49102c..fb38bd739 100644 --- a/tests/interfaces/openlp_core_ui/test_servicemanager.py +++ b/tests/interfaces/openlp_core_ui/test_servicemanager.py @@ -62,6 +62,7 @@ class TestServiceManager(TestCase): q_point = None # Mocked actions. self.service_manager.edit_action.setVisible = Mock() + self.service_manager.rename_action.setVisible = Mock() self.service_manager.create_custom_action.setVisible = Mock() self.service_manager.maintain_action.setVisible = Mock() self.service_manager.notes_action.setVisible = Mock() @@ -74,6 +75,8 @@ class TestServiceManager(TestCase): # THEN: The following actions should be not visible. self.service_manager.edit_action.setVisible.assert_called_once_with(False), \ 'The action should be set invisible.' + self.service_manager.rename_action.setVisible.assert_called_once_with(False), \ + 'The action should be set invisible.' self.service_manager.create_custom_action.setVisible.assert_called_once_with(False), \ 'The action should be set invisible.' self.service_manager.maintain_action.setVisible.assert_called_once_with(False), \ From 77f1878ff59c0075f0af307bd1ce1b83b4aa5612 Mon Sep 17 00:00:00 2001 From: Dmitriy Marmyshev Date: Thu, 24 Oct 2013 22:12:41 +0400 Subject: [PATCH 022/155] Tests for transparency --- .../openlp_core_ui/test_maindisplay.py | 46 +++++++++++-------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/tests/functional/openlp_core_ui/test_maindisplay.py b/tests/functional/openlp_core_ui/test_maindisplay.py index c869afc7f..1fb07615a 100644 --- a/tests/functional/openlp_core_ui/test_maindisplay.py +++ b/tests/functional/openlp_core_ui/test_maindisplay.py @@ -45,30 +45,40 @@ class TestMainDisplay(TestCase): # - MainDisplay # - AudioPlayer - def setUp(self): - self.main_dispaly_patcher = patch('openlp.core.ui.maindisplay.MainDisplay') - self.mocked_main_dispaly = self.main_dispaly_patcher.start() - self.mocked_main_dispaly.__init__() - - #.setAutoFillBackground, .setStyleSheet, .setAttribute, .repaint - - - def tearDown(self): - self.main_dispaly_patcher.stop() - - def set_transparency_enable_test(self): """ Test creating an instance of the MainDisplay class """ - # GIVEN: Reset mocks: MainDisplay.__init__, MainDisplay.setAutoFillBackground, etc. And an instance of MainDisplay - #self.mocked_main_dispaly.__init__() + # GIVEN: get an instance of MainDisplay + display = MagicMock() + main_dispaly = MainDisplay(display) # WHEN: MainDispaly.set_transparency is called with a true value" - self.mocked_main_dispaly.set_transparency(True) + main_dispaly.set_transparency(True) - # THEN: MainDisplay.setAutoFillBackground, MainDisplay.setStyleSheet, MainDisplay.setAttribute, - # MainDisplay.repaint should have been called with known values" - assert self.mocked_main_dispaly.StyleSheet != "QGraphicsView {background: transparent; border: 0px;}", \ + # THEN: check MainDisplay.setAutoFillBackground, MainDisplay.setStyleSheet, MainDisplay.setAttribute, + assert main_dispaly.StyleSheet == "QGraphicsView {background: transparent; border: 0px;}", \ 'MainDisplay instance should be transparent' + assert main_dispaly.getAutoFillBackground == False, \ + 'MainDisplay instance should be without background auto fill' + assert main_dispaly.getAttribute(QtCore.Qt.WA_TranslucentBackground) == True, \ + 'MainDisplay hasnt translusent background' + + def set_transparency_disable_test(self): + """ + Test creating an instance of the MainDisplay class + """ + # GIVEN: get an instance of MainDisplay + display = MagicMock() + main_dispaly = MainDisplay(display) + + # WHEN: MainDispaly.set_transparency is called with a False value" + main_dispaly.set_transparency(False) + + # THEN: check MainDisplay.setAutoFillBackground, MainDisplay.setStyleSheet, MainDisplay.setAttribute, + assert main_dispaly.StyleSheet == "QGraphicsView {}", \ + 'MainDisplay instance should not be transparent' + assert main_dispaly.getAutoFillBackground == True, 'MainDisplay instance should be with background auto fill' + assert main_dispaly.getAttribute(QtCore.Qt.WA_TranslucentBackground) == True, \ + 'MainDisplay hasnt translusent background' From aed25c0f0bb70003dfa9d58a7394fab533b53197 Mon Sep 17 00:00:00 2001 From: Dmitriy Marmyshev Date: Fri, 25 Oct 2013 00:24:58 +0400 Subject: [PATCH 023/155] fixes --- .../openlp_core_ui/test_maindisplay.py | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/tests/functional/openlp_core_ui/test_maindisplay.py b/tests/functional/openlp_core_ui/test_maindisplay.py index 1fb07615a..7a0dcb8ac 100644 --- a/tests/functional/openlp_core_ui/test_maindisplay.py +++ b/tests/functional/openlp_core_ui/test_maindisplay.py @@ -32,6 +32,7 @@ Package to test the openlp.core.lib.maindisplay package. from unittest import TestCase from mock import MagicMock +from PyQt4 import QtCore from openlp.core.ui.maindisplay import MainDisplay @@ -52,17 +53,17 @@ class TestMainDisplay(TestCase): """ # GIVEN: get an instance of MainDisplay display = MagicMock() - main_dispaly = MainDisplay(display) + main_display = MainDisplay(display) - # WHEN: MainDispaly.set_transparency is called with a true value" - main_dispaly.set_transparency(True) + # WHEN: MainDisplay.set_transparency is called with a true value" + main_display.set_transparency(True) # THEN: check MainDisplay.setAutoFillBackground, MainDisplay.setStyleSheet, MainDisplay.setAttribute, - assert main_dispaly.StyleSheet == "QGraphicsView {background: transparent; border: 0px;}", \ + assert main_display.StyleSheet == "QGraphicsView {background: transparent; border: 0px;}", \ 'MainDisplay instance should be transparent' - assert main_dispaly.getAutoFillBackground == False, \ + assert main_display.getAutoFillBackground == False, \ 'MainDisplay instance should be without background auto fill' - assert main_dispaly.getAttribute(QtCore.Qt.WA_TranslucentBackground) == True, \ + assert main_display.getAttribute(QtCore.Qt.WA_TranslucentBackground) == True, \ 'MainDisplay hasnt translusent background' def set_transparency_disable_test(self): @@ -71,14 +72,14 @@ class TestMainDisplay(TestCase): """ # GIVEN: get an instance of MainDisplay display = MagicMock() - main_dispaly = MainDisplay(display) + main_display = MainDisplay(display) # WHEN: MainDispaly.set_transparency is called with a False value" - main_dispaly.set_transparency(False) + main_display.set_transparency(False) # THEN: check MainDisplay.setAutoFillBackground, MainDisplay.setStyleSheet, MainDisplay.setAttribute, - assert main_dispaly.StyleSheet == "QGraphicsView {}", \ + assert main_display.StyleSheet == "QGraphicsView {}", \ 'MainDisplay instance should not be transparent' - assert main_dispaly.getAutoFillBackground == True, 'MainDisplay instance should be with background auto fill' - assert main_dispaly.getAttribute(QtCore.Qt.WA_TranslucentBackground) == True, \ + assert main_display.getAutoFillBackground == True, 'MainDisplay instance should be with background auto fill' + assert main_display.getAttribute(QtCore.Qt.WA_TranslucentBackground) == True, \ 'MainDisplay hasnt translusent background' From 84b621301bbbb6d72502a8b8acf6f9d0d9604825 Mon Sep 17 00:00:00 2001 From: Jonathan Springer Date: Thu, 13 Mar 2014 00:50:45 -0400 Subject: [PATCH 024/155] Add test to check for the proper tags on the branch --- tests/utils/test_bzr_tags.py | 78 ++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 tests/utils/test_bzr_tags.py diff --git a/tests/utils/test_bzr_tags.py b/tests/utils/test_bzr_tags.py new file mode 100644 index 000000000..e463aa08b --- /dev/null +++ b/tests/utils/test_bzr_tags.py @@ -0,0 +1,78 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2014 Raoul Snyman # +# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan # +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # +# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, # +# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, # +# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, # +# Frode Woldsund, Martin Zibricky, Patrick Zimmermann # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### +""" +Package to test for proper bzr tags. +""" + +from unittest import TestCase + +import subprocess + +from subprocess import Popen, PIPE + +TAGS = [ + ['1.9.0', '1'], + ['1.9.1', '775'], + ['1.9.2', '890'], + ['1.9.3', '1063'], + ['1.9.4', '1196'], + ['1.9.5', '1421'], + ['1.9.6', '1657'], + ['1.9.7', '1761'], + ['1.9.8', '1856'], + ['1.9.9', '1917'], + ['1.9.10', '2003'], + ['1.9.11', '2039'], + ['1.9.12', '2063'], + ['2.0', '2118'], + ['2.0.1', '?'], + ['2.0.2', '?'], + ['2.0.3', '?'], + ['2.1.0', '2119'] +] + + +class TestBzrTags(TestCase): + + def bzr_tags_test(self): + """ + Test for proper bzr tags + """ + # GIVEN: A bzr branch + + # WHEN getting the branches tags + tags = [] + bzr = Popen(('bzr', 'tags'), stdout=PIPE) + stdout = bzr.communicate()[0] + lines = (line.decode('utf-8') for line in stdout.splitlines()) + for line in lines: + tags.append(line.split()) + + # THEN the tags should match the accepted tags + self.assertEqual(TAGS, tags, 'List of tags should match') From 2b55c1ceb5c1d473244d52533c515ce9ff471671 Mon Sep 17 00:00:00 2001 From: Jonathan Springer Date: Thu, 13 Mar 2014 17:36:01 -0400 Subject: [PATCH 025/155] Change test to use a black list instead of a white list. --- tests/utils/test_bzr_tags.py | 33 +++++++-------------------------- 1 file changed, 7 insertions(+), 26 deletions(-) diff --git a/tests/utils/test_bzr_tags.py b/tests/utils/test_bzr_tags.py index e463aa08b..e9b6b35bc 100644 --- a/tests/utils/test_bzr_tags.py +++ b/tests/utils/test_bzr_tags.py @@ -32,30 +32,9 @@ Package to test for proper bzr tags. from unittest import TestCase -import subprocess - from subprocess import Popen, PIPE -TAGS = [ - ['1.9.0', '1'], - ['1.9.1', '775'], - ['1.9.2', '890'], - ['1.9.3', '1063'], - ['1.9.4', '1196'], - ['1.9.5', '1421'], - ['1.9.6', '1657'], - ['1.9.7', '1761'], - ['1.9.8', '1856'], - ['1.9.9', '1917'], - ['1.9.10', '2003'], - ['1.9.11', '2039'], - ['1.9.12', '2063'], - ['2.0', '2118'], - ['2.0.1', '?'], - ['2.0.2', '?'], - ['2.0.3', '?'], - ['2.1.0', '2119'] -] +BLACK_LISTED_TAGS = '2.2.2', 'help' class TestBzrTags(TestCase): @@ -70,9 +49,11 @@ class TestBzrTags(TestCase): tags = [] bzr = Popen(('bzr', 'tags'), stdout=PIPE) stdout = bzr.communicate()[0] - lines = (line.decode('utf-8') for line in stdout.splitlines()) + lines = (line.decode('utf-8') for line in stdout.split()) for line in lines: - tags.append(line.split()) + tags.append(line) - # THEN the tags should match the accepted tags - self.assertEqual(TAGS, tags, 'List of tags should match') + # THEN none of the tags should match the black listed tags + for BLACK_LISTED_TAG in BLACK_LISTED_TAGS: + for tag in tags: + self.assertNotEqual(BLACK_LISTED_TAG, tag, 'Tag should not exist') From 79263bfea4e46d9717df4fb59669956f299cf011 Mon Sep 17 00:00:00 2001 From: Raoul Snyman Date: Tue, 18 Mar 2014 22:33:05 +0200 Subject: [PATCH 026/155] Try to fix bug #1136278 by detecting if the upgrade has already been run Fixes: https://launchpad.net/bugs/1136278 --- openlp/plugins/bibles/lib/upgrade.py | 138 +----------------------- openlp/plugins/songs/lib/upgrade.py | 46 +++++--- openlp/plugins/songusage/lib/upgrade.py | 18 +++- 3 files changed, 46 insertions(+), 156 deletions(-) diff --git a/openlp/plugins/bibles/lib/upgrade.py b/openlp/plugins/bibles/lib/upgrade.py index 3e58686d1..bf1caf025 100644 --- a/openlp/plugins/bibles/lib/upgrade.py +++ b/openlp/plugins/bibles/lib/upgrade.py @@ -31,10 +31,8 @@ The :mod:`upgrade` module provides a way for the database and schema that is the """ import logging -from sqlalchemy import Table, func, select, insert - -__version__ = 1 log = logging.getLogger(__name__) +__version__ = 1 def upgrade_1(session, metadata): @@ -43,136 +41,4 @@ def upgrade_1(session, metadata): This upgrade renames a number of keys to a single naming convention. """ - metadata_table = Table('metadata', metadata, autoload=True) - # Copy "Version" to "name" ("version" used by upgrade system) - # TODO: Clean up in a subsequent release of OpenLP (like 2.0 final) - session.execute(insert(metadata_table).values( - key='name', - value=select( - [metadata_table.c.value], - metadata_table.c.key == 'Version' - ).as_scalar() - )) - # Copy "Copyright" to "copyright" - # TODO: Clean up in a subsequent release of OpenLP (like 2.0 final) - session.execute(insert(metadata_table).values( - key='copyright', - value=select( - [metadata_table.c.value], - metadata_table.c.key == 'Copyright' - ).as_scalar() - )) - # Copy "Permissions" to "permissions" - # TODO: Clean up in a subsequent release of OpenLP (like 2.0 final) - session.execute(insert(metadata_table).values( - key='permissions', - value=select( - [metadata_table.c.value], - metadata_table.c.key == 'Permissions' - ).as_scalar() - )) - # Copy "Bookname language" to "book_name_language" - # TODO: Clean up in a subsequent release of OpenLP (like 2.0 final) - value_count = session.execute( - select( - [func.count(metadata_table.c.value)], - metadata_table.c.key == 'Bookname language' - ) - ).scalar() - if value_count > 0: - session.execute(insert(metadata_table).values( - key='book_name_language', - value=select( - [metadata_table.c.value], - metadata_table.c.key == 'Bookname language' - ).as_scalar() - )) - # Copy "download source" to "download_source" - # TODO: Clean up in a subsequent release of OpenLP (like 2.0 final) - value_count = session.execute( - select( - [func.count(metadata_table.c.value)], - metadata_table.c.key == 'download source' - ) - ).scalar() - log.debug('download source: %s', value_count) - if value_count > 0: - session.execute(insert(metadata_table).values( - key='download_source', - value=select( - [metadata_table.c.value], - metadata_table.c.key == 'download source' - ).as_scalar() - )) - # Copy "download name" to "download_name" - # TODO: Clean up in a subsequent release of OpenLP (like 2.0 final) - value_count = session.execute( - select( - [func.count(metadata_table.c.value)], - metadata_table.c.key == 'download name' - ) - ).scalar() - log.debug('download name: %s', value_count) - if value_count > 0: - session.execute(insert(metadata_table).values( - key='download_name', - value=select( - [metadata_table.c.value], - metadata_table.c.key == 'download name' - ).as_scalar() - )) - # Copy "proxy server" to "proxy_server" - # TODO: Clean up in a subsequent release of OpenLP (like 2.0 final) - value_count = session.execute( - select( - [func.count(metadata_table.c.value)], - metadata_table.c.key == 'proxy server' - ) - ).scalar() - log.debug('proxy server: %s', value_count) - if value_count > 0: - session.execute(insert(metadata_table).values( - key='proxy_server', - value=select( - [metadata_table.c.value], - metadata_table.c.key == 'proxy server' - ).as_scalar() - )) - # Copy "proxy username" to "proxy_username" - # TODO: Clean up in a subsequent release of OpenLP (like 2.0 final) - value_count = session.execute( - select( - [func.count(metadata_table.c.value)], - metadata_table.c.key == 'proxy username' - ) - ).scalar() - log.debug('proxy username: %s', value_count) - if value_count > 0: - session.execute(insert(metadata_table).values( - key='proxy_username', - value=select( - [metadata_table.c.value], - metadata_table.c.key == 'proxy username' - ).as_scalar() - )) - # Copy "proxy password" to "proxy_password" - # TODO: Clean up in a subsequent release of OpenLP (like 2.0 final) - value_count = session.execute( - select( - [func.count(metadata_table.c.value)], - metadata_table.c.key == 'proxy password' - ) - ).scalar() - log.debug('proxy password: %s', value_count) - if value_count > 0: - session.execute(insert(metadata_table).values( - key='proxy_password', - value=select( - [metadata_table.c.value], - metadata_table.c.key == 'proxy password' - ).as_scalar() - )) - # TODO: Clean up in a subsequent release of OpenLP (like 2.0 final) - #session.execute(delete(metadata_table)\ - # .where(metadata_table.c.key == u'dbversion')) - session.commit() + log.info('No upgrades to perform') diff --git a/openlp/plugins/songs/lib/upgrade.py b/openlp/plugins/songs/lib/upgrade.py index ee01fb8b0..adb7d8af5 100644 --- a/openlp/plugins/songs/lib/upgrade.py +++ b/openlp/plugins/songs/lib/upgrade.py @@ -30,12 +30,15 @@ The :mod:`upgrade` module provides a way for the database and schema that is the backend for the Songs plugin """ +import logging from sqlalchemy import Column, types +from sqlalchemy.exc import OperationalError from sqlalchemy.sql.expression import func, false, null, text from openlp.core.lib.db import get_upgrade_op +log = logging.getLogger(__name__) __version__ = 3 @@ -50,14 +53,20 @@ def upgrade_1(session, metadata): In order to facilitate this one-to-many relationship, a song_id column is added to the media_files table, and a weight column so that the media files can be ordered. + + :param session: + :param metadata: """ - op = get_upgrade_op(session) - op.drop_table('media_files_songs') - op.add_column('media_files', Column('song_id', types.Integer(), server_default=null())) - op.add_column('media_files', Column('weight', types.Integer(), server_default=text('0'))) - if metadata.bind.url.get_dialect().name != 'sqlite': - # SQLite doesn't support ALTER TABLE ADD CONSTRAINT - op.create_foreign_key('fk_media_files_song_id', 'media_files', 'songs', ['song_id', 'id']) + try: + op = get_upgrade_op(session) + op.drop_table('media_files_songs') + op.add_column('media_files', Column('song_id', types.Integer(), server_default=null())) + op.add_column('media_files', Column('weight', types.Integer(), server_default=text('0'))) + if metadata.bind.url.get_dialect().name != 'sqlite': + # SQLite doesn't support ALTER TABLE ADD CONSTRAINT + op.create_foreign_key('fk_media_files_song_id', 'media_files', 'songs', ['song_id', 'id']) + except OperationalError: + log.info('Upgrade 1 has already been run') def upgrade_2(session, metadata): @@ -66,9 +75,12 @@ def upgrade_2(session, metadata): This upgrade adds a create_date and last_modified date to the songs table """ - op = get_upgrade_op(session) - op.add_column('songs', Column('create_date', types.DateTime(), default=func.now())) - op.add_column('songs', Column('last_modified', types.DateTime(), default=func.now())) + try: + op = get_upgrade_op(session) + op.add_column('songs', Column('create_date', types.DateTime(), default=func.now())) + op.add_column('songs', Column('last_modified', types.DateTime(), default=func.now())) + except OperationalError: + log.info('Upgrade 2 has already been run') def upgrade_3(session, metadata): @@ -77,9 +89,11 @@ def upgrade_3(session, metadata): This upgrade adds a temporary song flag to the songs table """ - op = get_upgrade_op(session) - if metadata.bind.url.get_dialect().name == 'sqlite': - op.add_column('songs', Column('temporary', types.Boolean(create_constraint=False), server_default=false())) - else: - op.add_column('songs', Column('temporary', types.Boolean(), server_default=false())) - + try: + op = get_upgrade_op(session) + if metadata.bind.url.get_dialect().name == 'sqlite': + op.add_column('songs', Column('temporary', types.Boolean(create_constraint=False), server_default=false())) + else: + op.add_column('songs', Column('temporary', types.Boolean(), server_default=false())) + except OperationalError: + log.info('Upgrade 3 has already been run') diff --git a/openlp/plugins/songusage/lib/upgrade.py b/openlp/plugins/songusage/lib/upgrade.py index 08096423d..24f264824 100644 --- a/openlp/plugins/songusage/lib/upgrade.py +++ b/openlp/plugins/songusage/lib/upgrade.py @@ -30,10 +30,14 @@ The :mod:`upgrade` module provides a way for the database and schema that is the backend for the SongsUsage plugin """ -from openlp.core.lib.db import get_upgrade_op +import logging +from sqlalchemy.exc import OperationalError from sqlalchemy import Column, types +from openlp.core.lib.db import get_upgrade_op + +log = logging.getLogger(__name__) __version__ = 1 @@ -42,7 +46,13 @@ def upgrade_1(session, metadata): Version 1 upgrade. This upgrade adds two new fields to the songusage database + + :param session: SQLAlchemy Session object + :param metadata: SQLAlchemy MetaData object """ - op = get_upgrade_op(session) - op.add_column('songusage_data', Column('plugin_name', types.Unicode(20), server_default='')) - op.add_column('songusage_data', Column('source', types.Unicode(10), server_default='')) + try: + op = get_upgrade_op(session) + op.add_column('songusage_data', Column('plugin_name', types.Unicode(20), server_default='')) + op.add_column('songusage_data', Column('source', types.Unicode(10), server_default='')) + except OperationalError: + log.info('Upgrade 1 has already taken place') From 35e3564655ba8daa80f7d6df74d3bc3a783b2e6b Mon Sep 17 00:00:00 2001 From: Raoul Snyman Date: Tue, 18 Mar 2014 23:03:53 +0200 Subject: [PATCH 027/155] Tests! --- openlp/core/lib/db.py | 10 ++--- tests/functional/openlp_core_lib/test_db.py | 44 ++++++++++++++++++++- 2 files changed, 48 insertions(+), 6 deletions(-) diff --git a/openlp/core/lib/db.py b/openlp/core/lib/db.py index 36bfa24ef..d43938afe 100644 --- a/openlp/core/lib/db.py +++ b/openlp/core/lib/db.py @@ -93,10 +93,11 @@ def upgrade_db(url, upgrade): """ pass - metadata_table = Table('metadata', metadata, - Column('key', types.Unicode(64), primary_key=True), - Column('value', types.UnicodeText(), default=None) - ) + metadata_table = Table( + 'metadata', metadata, + Column('key', types.Unicode(64), primary_key=True), + Column('value', types.UnicodeText(), default=None) + ) metadata_table.create(checkfirst=True) mapper(Metadata, metadata_table) version_meta = session.query(Metadata).get('version') @@ -137,7 +138,6 @@ def delete_database(plugin_name, db_file_name=None): :param plugin_name: The name of the plugin to remove the database for :param db_file_name: The database file name. Defaults to None resulting in the plugin_name being used. """ - db_file_path = None if db_file_name: db_file_path = os.path.join(AppLocation.get_section_data_path(plugin_name), db_file_name) else: diff --git a/tests/functional/openlp_core_lib/test_db.py b/tests/functional/openlp_core_lib/test_db.py index 8a2f21ec3..470bd0636 100644 --- a/tests/functional/openlp_core_lib/test_db.py +++ b/tests/functional/openlp_core_lib/test_db.py @@ -29,13 +29,14 @@ """ Package to test the openlp.core.lib package. """ +import os from unittest import TestCase from sqlalchemy.pool import NullPool from sqlalchemy.orm.scoping import ScopedSession from sqlalchemy import MetaData -from openlp.core.lib.db import init_db, get_upgrade_op +from openlp.core.lib.db import init_db, get_upgrade_op, delete_database from tests.functional import patch, MagicMock @@ -110,3 +111,44 @@ class TestDB(TestCase): mocked_session.bind.connect.assert_called_with() MockedMigrationContext.configure.assert_called_with(mocked_connection) MockedOperations.assert_called_with(mocked_context) + + def delete_database_without_db_file_name_test(self): + """ + Test that the ``delete_database`` function removes a database file, without the file name parameter + """ + # GIVEN: Mocked out AppLocation class and delete_file method, a test plugin name and a db location + with patch('openlp.core.lib.db.AppLocation') as MockedAppLocation, \ + patch('openlp.core.lib.db.delete_file') as mocked_delete_file: + MockedAppLocation.get_section_data_path.return_value = 'test-dir' + mocked_delete_file.return_value = True + test_plugin = 'test' + test_location = os.path.join('test-dir', test_plugin) + + # WHEN: delete_database is run without a database file + result = delete_database(test_plugin) + + # THEN: The AppLocation.get_section_data_path and delete_file methods should have been called + MockedAppLocation.get_section_data_path.assert_called_with(test_plugin) + mocked_delete_file.assert_called_with(test_location) + self.assertTrue(result, 'The result of delete_file should be True (was rigged that way)') + + def delete_database_with_db_file_name_test(self): + """ + Test that the ``delete_database`` function removes a database file, with the file name supplied + """ + # GIVEN: Mocked out AppLocation class and delete_file method, a test plugin name and a db location + with patch('openlp.core.lib.db.AppLocation') as MockedAppLocation, \ + patch('openlp.core.lib.db.delete_file') as mocked_delete_file: + MockedAppLocation.get_section_data_path.return_value = 'test-dir' + mocked_delete_file.return_value = False + test_plugin = 'test' + test_db_file = 'mydb.sqlite' + test_location = os.path.join('test-dir', test_db_file) + + # WHEN: delete_database is run without a database file + result = delete_database(test_plugin, test_db_file) + + # THEN: The AppLocation.get_section_data_path and delete_file methods should have been called + MockedAppLocation.get_section_data_path.assert_called_with(test_plugin) + mocked_delete_file.assert_called_with(test_location) + self.assertFalse(result, 'The result of delete_file should be False (was rigged that way)') From 5d9533947d5c35b406bf261f19786f43336f94ee Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Thu, 20 Mar 2014 19:10:31 +0000 Subject: [PATCH 028/155] Final set of PEP8 cleanups --- openlp/core/common/applocation.py | 2 +- openlp/core/common/openlpmixin.py | 2 +- openlp/core/common/registryproperties.py | 2 +- openlp/core/common/uistrings.py | 7 +- openlp/core/lib/filedialog.py | 2 +- openlp/core/lib/htmlbuilder.py | 14 +- openlp/core/lib/imagemanager.py | 4 +- openlp/core/lib/plugin.py | 2 +- openlp/core/lib/searchedit.py | 5 +- openlp/core/ui/aboutdialog.py | 1080 ++++++++++----------- openlp/core/ui/advancedtab.py | 23 +- openlp/core/ui/exceptiondialog.py | 5 +- openlp/core/ui/exceptionform.py | 18 +- openlp/core/ui/filerenameform.py | 2 +- openlp/core/ui/firsttimeform.py | 2 +- openlp/core/ui/formattingtagcontroller.py | 2 +- openlp/core/ui/formattingtagdialog.py | 3 +- openlp/core/ui/formattingtagform.py | 1 - openlp/core/ui/generaltab.py | 3 +- openlp/core/ui/listpreviewwidget.py | 2 +- openlp/core/ui/maindisplay.py | 1 - openlp/core/ui/mainwindow.py | 19 +- openlp/core/ui/media/mediacontroller.py | 12 +- openlp/core/ui/media/mediaplayer.py | 2 +- openlp/core/ui/media/phononplayer.py | 2 +- openlp/core/ui/media/vlcplayer.py | 2 +- openlp/core/ui/pluginform.py | 2 +- openlp/core/ui/printserviceform.py | 2 +- openlp/core/ui/servicemanager.py | 18 +- openlp/core/ui/servicenoteform.py | 2 +- openlp/core/ui/settingsform.py | 2 +- openlp/core/ui/shortcutlistform.py | 2 +- openlp/core/ui/slidecontroller.py | 35 +- openlp/core/ui/starttimeform.py | 6 +- openlp/core/ui/themeform.py | 4 +- openlp/core/ui/thememanager.py | 4 +- openlp/core/ui/themewizard.py | 19 +- openlp/core/ui/wizard.py | 2 +- openlp/core/utils/__init__.py | 19 +- openlp/core/utils/actions.py | 4 +- openlp/core/utils/languagemanager.py | 5 +- 41 files changed, 675 insertions(+), 670 deletions(-) diff --git a/openlp/core/common/applocation.py b/openlp/core/common/applocation.py index 7f2416676..1fce25000 100644 --- a/openlp/core/common/applocation.py +++ b/openlp/core/common/applocation.py @@ -76,7 +76,7 @@ class AppLocation(object): elif dir_type == AppLocation.PluginsDir: app_path = os.path.abspath(os.path.split(sys.argv[0])[0]) return get_frozen_path(os.path.join(app_path, 'plugins'), - os.path.join(os.path.split(openlp.__file__)[0], 'plugins')) + os.path.join(os.path.split(openlp.__file__)[0], 'plugins')) elif dir_type == AppLocation.VersionDir: return get_frozen_path(os.path.abspath(os.path.split(sys.argv[0])[0]), os.path.split(openlp.__file__)[0]) elif dir_type == AppLocation.LanguageDir: diff --git a/openlp/core/common/openlpmixin.py b/openlp/core/common/openlpmixin.py index 9e7b43539..1c7fe7d5a 100644 --- a/openlp/core/common/openlpmixin.py +++ b/openlp/core/common/openlpmixin.py @@ -91,4 +91,4 @@ class OpenLPMixin(object): Common log exception handler which prints the calling path """ trace_error_handler(self.logger) - self.logger.exception(message) \ No newline at end of file + self.logger.exception(message) diff --git a/openlp/core/common/registryproperties.py b/openlp/core/common/registryproperties.py index 663513c29..791fc33f7 100644 --- a/openlp/core/common/registryproperties.py +++ b/openlp/core/common/registryproperties.py @@ -149,4 +149,4 @@ class RegistryProperties(object): """ if not hasattr(self, '_alerts_manager') or not self._alerts_manager: self._alerts_manager = Registry().get('alerts_manager') - return self._alerts_manager \ No newline at end of file + return self._alerts_manager diff --git a/openlp/core/common/uistrings.py b/openlp/core/common/uistrings.py index 6bb44150c..3fe1485ba 100644 --- a/openlp/core/common/uistrings.py +++ b/openlp/core/common/uistrings.py @@ -73,8 +73,9 @@ class UiStrings(object): self.Default = translate('OpenLP.Ui', 'Default') self.DefaultColor = translate('OpenLP.Ui', 'Default Color:') self.DefaultServiceName = translate('OpenLP.Ui', 'Service %Y-%m-%d %H-%M', - 'This may not contain any of the following characters: /\\?*|<>\[\]":+\n' - 'See http://docs.python.org/library/datetime.html#strftime-strptime-behavior for more information.') + 'This may not contain any of the following characters: /\\?*|<>\[\]":+\n' + 'See http://docs.python.org/library/datetime' + '.html#strftime-strptime-behavior for more information.') self.Delete = translate('OpenLP.Ui', '&Delete') self.DisplayStyle = translate('OpenLP.Ui', 'Display style:') self.Duplicate = translate('OpenLP.Ui', 'Duplicate Error') @@ -132,7 +133,7 @@ class UiStrings(object): self.Service = translate('OpenLP.Ui', 'Service') self.Split = translate('OpenLP.Ui', 'Optional &Split') self.SplitToolTip = translate('OpenLP.Ui', - 'Split a slide into two only if it does not fit on the screen as one slide.') + 'Split a slide into two only if it does not fit on the screen as one slide.') self.StartTimeCode = translate('OpenLP.Ui', 'Start %s') self.StopPlaySlidesInLoop = translate('OpenLP.Ui', 'Stop Play Slides in Loop') self.StopPlaySlidesToEnd = translate('OpenLP.Ui', 'Stop Play Slides to End') diff --git a/openlp/core/lib/filedialog.py b/openlp/core/lib/filedialog.py index 989bafa6b..5bf012ee5 100644 --- a/openlp/core/lib/filedialog.py +++ b/openlp/core/lib/filedialog.py @@ -63,4 +63,4 @@ class FileDialog(QtGui.QFileDialog): UiStrings().FileNotFoundMessage % file) continue file_list.append(file) - return file_list \ No newline at end of file + return file_list diff --git a/openlp/core/lib/htmlbuilder.py b/openlp/core/lib/htmlbuilder.py index 7f6ab67b7..473aa9d7d 100644 --- a/openlp/core/lib/htmlbuilder.py +++ b/openlp/core/lib/htmlbuilder.py @@ -117,7 +117,9 @@ is the function which has to be called from outside. The generated and returned display: table-cell; word-wrap: break-word; -webkit-transition: opacity 0.4s ease; - white-space:pre-wrap; word-wrap: break-word; text-align: left; vertical-align: top; font-family: Nimbus Sans L; font-size: 40pt; color: #FFFFFF; line-height: 100%; margin: 0;padding: 0; padding-bottom: 0; padding-left: 4px; width: 1580px; height: 810px; + white-space:pre-wrap; word-wrap: break-word; text-align: left; vertical-align: top; font-family: Nimbus + Sans L; font-size: 40pt; color: #FFFFFF; line-height: 100%; margin: 0;padding: 0; padding-bottom: 0; + padding-left: 4px; width: 1580px; height: 810px; } .lyricsmain { -webkit-text-stroke: 0.125em #000000; -webkit-text-fill-color: #FFFFFF; text-shadow: #000000 5px 5px; @@ -720,12 +722,12 @@ def build_lyrics_format_css(theme_data, width, height): else: padding_bottom = '0' lyrics = '%s word-wrap: break-word; ' \ - 'text-align: %s; vertical-align: %s; font-family: %s; ' \ - 'font-size: %spt; color: %s; line-height: %d%%; margin: 0;' \ - 'padding: 0; padding-bottom: %s; padding-left: %spx; width: %spx; height: %spx; ' % \ + 'text-align: %s; vertical-align: %s; font-family: %s; ' \ + 'font-size: %spt; color: %s; line-height: %d%%; margin: 0;' \ + 'padding: 0; padding-bottom: %s; padding-left: %spx; width: %spx; height: %spx; ' % \ (justify, align, valign, theme_data.font_main_name, theme_data.font_main_size, - theme_data.font_main_color, 100 + int(theme_data.font_main_line_adjustment), padding_bottom, - left_margin, width, height) + theme_data.font_main_color, 100 + int(theme_data.font_main_line_adjustment), padding_bottom, + left_margin, width, height) if theme_data.font_main_italics: lyrics += 'font-style:italic; ' if theme_data.font_main_bold: diff --git a/openlp/core/lib/imagemanager.py b/openlp/core/lib/imagemanager.py index a22de4140..cba393815 100644 --- a/openlp/core/lib/imagemanager.py +++ b/openlp/core/lib/imagemanager.py @@ -113,8 +113,8 @@ class Image(object): :param path: The image's file path. This should be an existing file path. :param source: The source describes the image's origin. Possible values are described in the :class:`~openlp.core.lib.ImageSource` class. - :param background: A ``QtGui.QColor`` object specifying the colour to be used to fill the gabs if the image's ratio does not - match with the display ratio. + :param background: A ``QtGui.QColor`` object specifying the colour to be used to fill the gabs if the image's + ratio does not match with the display ratio. """ self.path = path diff --git a/openlp/core/lib/plugin.py b/openlp/core/lib/plugin.py index cafc24d9b..e14fe8bb0 100644 --- a/openlp/core/lib/plugin.py +++ b/openlp/core/lib/plugin.py @@ -393,4 +393,4 @@ class Plugin(QtCore.QObject, RegistryProperties): """ The plugin's needs to handle a new song creation """ - pass \ No newline at end of file + pass diff --git a/openlp/core/lib/searchedit.py b/openlp/core/lib/searchedit.py index d6eaafa7d..bb510d046 100644 --- a/openlp/core/lib/searchedit.py +++ b/openlp/core/lib/searchedit.py @@ -120,9 +120,8 @@ class SearchEdit(QtGui.QLineEdit): A list of tuples to be used in the search type menu. The first item in the list will be preselected as the default. - :param items: The list of tuples to use. The tuples should contain an integer identifier, an icon (QIcon instance or - - string) and a title for the item in the menu. In short, they should look like this:: + :param items: The list of tuples to use. The tuples should contain an integer identifier, an icon (QIcon + instance or string) and a title for the item in the menu. In short, they should look like this:: (, , , <place holder text>) diff --git a/openlp/core/ui/aboutdialog.py b/openlp/core/ui/aboutdialog.py index 21343f039..276a073bb 100644 --- a/openlp/core/ui/aboutdialog.py +++ b/openlp/core/ui/aboutdialog.py @@ -92,578 +92,574 @@ class Ui_AboutDialog(object): Dynamically translate the UI. """ about_dialog.setWindowTitle('%s OpenLP' % UiStrings().About) - self.about_text_edit.setPlainText(translate('OpenLP.AboutForm', - 'OpenLP <version><revision> - Open Source Lyrics ' - 'Projection\n' - '\n' - 'OpenLP is free church presentation software, or lyrics ' - 'projection software, used to display slides of songs, Bible ' - 'verses, videos, images, and even presentations (if ' - 'Impress, PowerPoint or PowerPoint Viewer is installed) ' - 'for church worship using a computer and a data projector.\n' - '\n' - 'Find out more about OpenLP: http://openlp.org/\n' - '\n' - 'OpenLP is written and maintained by volunteers. If you would ' - 'like to see more free Christian software being written, please ' - 'consider volunteering by using the button below.' - )) + self.about_text_edit.setPlainText( + translate('OpenLP.AboutForm', + 'OpenLP <version><revision> - Open Source Lyrics Projection\n' + '\n' + 'OpenLP is free church presentation software, or lyrics ' + 'projection software, used to display slides of songs, Bible ' + 'verses, videos, images, and even presentations (if ' + 'Impress, PowerPoint or PowerPoint Viewer is installed) ' + 'for church worship using a computer and a data projector.\n' + '\n' + 'Find out more about OpenLP: http://openlp.org/\n' + '\n' + 'OpenLP is written and maintained by volunteers. If you would ' + 'like to see more free Christian software being written, please ' + 'consider volunteering by using the button below.')) self.about_notebook.setTabText(self.about_notebook.indexOf(self.about_tab), UiStrings().About) lead = 'Raoul "superfly" Snyman' developers = ['Tim "TRB143" Bentley', 'Jonathan "gushie" Corwin', - 'Michael "cocooncrash" Gorven', - 'Andreas "googol" Preikschat', 'Raoul "superfly" Snyman', - 'Martin "mijiti" Thompson', 'Jon "Meths" Tibble'] + 'Michael "cocooncrash" Gorven', + 'Andreas "googol" Preikschat', 'Raoul "superfly" Snyman', + 'Martin "mijiti" Thompson', 'Jon "Meths" Tibble'] contributors = ['Gerald "jerryb" Britton', - 'Samuel "MrGamgee" Findlay', 'Scott "sguerrieri" Guerrieri', - 'Matthias "matthub" Hub', 'Meinert "m2j" Jordan', - 'Armin "orangeshirt" K\xf6hler', 'Erik "luen" Lundin', - 'Edwin "edwinlunando" Lunando', 'Brian "brianmeyer" Meyer', - 'Joshua "milleja46" Miller', 'Stevan "ElderP" Pettit', - 'Mattias "mahfiaz" P\xf5ldaru', 'Christian "crichter" Richter', - 'Philip "Phill" Ridout', 'Simon "samscudder" Scudder', - 'Jeffrey "whydoubt" Smith', 'Maikel Stuivenberg', - 'Dave "Dave42W" Warnock', 'Frode "frodus" Woldsund', - 'Martin "matysek" Zibricky', 'Patrick "mohij" Zimmermann'] + 'Samuel "MrGamgee" Findlay', 'Scott "sguerrieri" Guerrieri', + 'Matthias "matthub" Hub', 'Meinert "m2j" Jordan', + 'Armin "orangeshirt" K\xf6hler', 'Erik "luen" Lundin', + 'Edwin "edwinlunando" Lunando', 'Brian "brianmeyer" Meyer', + 'Joshua "milleja46" Miller', 'Stevan "ElderP" Pettit', + 'Mattias "mahfiaz" P\xf5ldaru', 'Christian "crichter" Richter', + 'Philip "Phill" Ridout', 'Simon "samscudder" Scudder', + 'Jeffrey "whydoubt" Smith', 'Maikel Stuivenberg', + 'Dave "Dave42W" Warnock', 'Frode "frodus" Woldsund', + 'Martin "matysek" Zibricky', 'Patrick "mohij" Zimmermann'] testers = ['Philip "Phill" Ridout', 'Wesley "wrst" Stout', - 'John "jseagull1" Cegalis (lead)'] + 'John "jseagull1" Cegalis (lead)'] packagers = ['Thomas "tabthorpe" Abthorpe (FreeBSD)', - 'Tim "TRB143" Bentley (Fedora and Android)', - 'Matthias "matthub" Hub (Mac OS X)', - 'Joseph "jdmulloy" Mulloy (openSUSE)', - 'Stevan "ElderP" Pettit (Windows)', - 'Raoul "superfly" Snyman (Debian, Ubuntu)', - 'Garrett "floft" Wilson (Arch Linux)'] + 'Tim "TRB143" Bentley (Fedora and Android)', + 'Matthias "matthub" Hub (Mac OS X)', + 'Joseph "jdmulloy" Mulloy (openSUSE)', + 'Stevan "ElderP" Pettit (Windows)', + 'Raoul "superfly" Snyman (Debian, Ubuntu)', + 'Garrett "floft" Wilson (Arch Linux)'] translators = { 'af': ['Johan "nuvolari" Mynhardt'], 'cs': ['Martin "matysek" Zibricky'], 'da': ['Henrik "Hsonesson" Sonesson'], - 'de': ['Patrick "madmuffin" Br\xfcckner', - 'Meinert "m2j" Jordan', 'Andreas "googol" Preikschat', - 'Christian "crichter" Richter'], + 'de': ['Patrick "madmuffin" Br\xfcckner', 'Meinert "m2j" Jordan', 'Andreas "googol" Preikschat', + 'Christian "crichter" Richter'], 'en_GB': ['Tim "TRB143" Bentley', 'Jonathan "gushie" Corwin'], - 'en_ZA': ['Raoul "superfly" Snyman', - 'Johan "nuvolari" Mynhardt'], + 'en_ZA': ['Raoul "superfly" Snyman', 'Johan "nuvolari" Mynhardt'], 'el': ['Alexander Siozos'], 'es': ['Josu\xe9 Z\xfa\xf1iga', 'Christian Gonzalez'], 'et': ['Mattias "mahfiaz" P\xf5ldaru'], 'fi': ['Jori "joribu" Brander', 'Tobbe "tobbeb" Bildo'], - 'fr': ['Stephan\xe9 "stbrunner" Brunner', 'Jeremie "jnau05"', - 'Carl "carl.fischer" Fischer'], + 'fr': ['Stephan\xe9 "stbrunner" Brunner', 'Jeremie "jnau05"', 'Carl "carl.fischer" Fischer'], 'hu': ['Gyuris Gell\xe9rt'], 'id': ['Mico "bangmico" Siahaan', ' ign_christian'], 'ja': ['Kunio "Kunio" Nakamaru', 'Chris Haris'], 'nb': ['Atle "pendlaren" Weibell', 'Frode "frodus" Woldsund'], 'nl': ['Arjen "typovar" van Voorst'], - 'pt_BR': ['David Mederiros', 'Rafael "rafaellerm" Lerm', - 'Eduardo Levi Chaves', - 'Gustavo Bim', 'Rog\xeanio Bel\xe9m', 'Samuel' - 'Simon "samscudder" Scudder', 'Van Der Fran'], + 'pt_BR': ['David Mederiros', 'Rafael "rafaellerm" Lerm', 'Eduardo Levi Chaves', + 'Gustavo Bim', 'Rog\xeanio Bel\xe9m', 'Samuel Simon "samscudder" Scudder', 'Van Der Fran'], 'ru': ['Sergey "ratz" Ratz'], 'sv': ['Erik "luen" Lundin'], 'ta_LK': ['"Prasad"'], 'zh_CN': [' "executor" '] } - documentors = ['Wesley "wrst" Stout', - 'John "jseagull1" Cegalis (lead)'] - self.credits_text_edit.setPlainText(translate('OpenLP.AboutForm', - 'Project Lead\n' - ' %s\n' - '\n' - 'Developers\n' - ' %s\n' - '\n' - 'Contributors\n' - ' %s\n' - '\n' - 'Testers\n' - ' %s\n' - '\n' - 'Packagers\n' - ' %s\n' - '\n' - 'Translators\n' - ' Afrikaans (af)\n' - ' %s\n' - ' Czech (cs)\n' - ' %s\n' - ' Danish (da)\n' - ' %s\n' - ' German (de)\n' - ' %s\n' - ' Greek (el)\n' - ' %s\n' - ' English, United Kingdom (en_GB)\n' - ' %s\n' - ' English, South Africa (en_ZA)\n' - ' %s\n' - ' Spanish (es)\n' - ' %s\n' - ' Estonian (et)\n' - ' %s\n' - ' Finnish (fi)\n' - ' %s\n' - ' French (fr)\n' - ' %s\n' - ' Hungarian (hu)\n' - ' %s\n' - ' Indonesian (id)\n' - ' %s\n' - ' Japanese (ja)\n' - ' %s\n' - ' Norwegian Bokm\xe5l (nb)\n' - ' %s\n' - ' Dutch (nl)\n' - ' %s\n' - ' Portuguese, Brazil (pt_BR)\n' - ' %s\n' - ' Russian (ru)\n' - ' %s\n' - ' Swedish (sv)\n' - ' %s\n' - ' Tamil(Sri-Lanka) (ta_LK)\n' - ' %s\n' - ' Chinese(China) (zh_CN)\n' - ' %s\n' - '\n' - 'Documentation\n' - ' %s\n' - '\n' - 'Built With\n' - ' Python: http://www.python.org/\n' - ' Qt4: http://qt.digia.com/\n' - ' PyQt4: http://www.riverbankcomputing.co.uk/software/pyqt/' - 'intro\n' - ' Oxygen Icons: http://oxygen-icons.org/\n' - '\n' - 'Final Credit\n' - ' "For God so loved the world that He gave\n' - ' His one and only Son, so that whoever\n' - ' believes in Him will not perish but inherit\n' - ' eternal life." -- John 3:16\n\n' - ' And last but not least, final credit goes to\n' - ' God our Father, for sending His Son to die\n' - ' on the cross, setting us free from sin. We\n' - ' bring this software to you for free because\n' - ' He has set us free.') % (lead, '\n '.join(developers), - '\n '.join(contributors), '\n '.join(testers), - '\n '.join(packagers), '\n '.join(translators['af']), - '\n '.join(translators['cs']), - '\n '.join(translators['da']), - '\n '.join(translators['de']), - '\n '.join(translators['el']), - '\n '.join(translators['en_GB']), - '\n '.join(translators['en_ZA']), - '\n '.join(translators['es']), - '\n '.join(translators['et']), - '\n '.join(translators['fi']), - '\n '.join(translators['fr']), - '\n '.join(translators['hu']), - '\n '.join(translators['id']), - '\n '.join(translators['ja']), - '\n '.join(translators['nb']), - '\n '.join(translators['nl']), - '\n '.join(translators['pt_BR']), - '\n '.join(translators['ru']), - '\n '.join(translators['sv']), - '\n '.join(translators['ta_LK']), - '\n '.join(translators['zh_CN']), - '\n '.join(documentors))) + documentors = ['Wesley "wrst" Stout', 'John "jseagull1" Cegalis (lead)'] + self.credits_text_edit.setPlainText( + translate('OpenLP.AboutForm', + 'Project Lead\n' + ' %s\n' + '\n' + 'Developers\n' + ' %s\n' + '\n' + 'Contributors\n' + ' %s\n' + '\n' + 'Testers\n' + ' %s\n' + '\n' + 'Packagers\n' + ' %s\n' + '\n' + 'Translators\n' + ' Afrikaans (af)\n' + ' %s\n' + ' Czech (cs)\n' + ' %s\n' + ' Danish (da)\n' + ' %s\n' + ' German (de)\n' + ' %s\n' + ' Greek (el)\n' + ' %s\n' + ' English, United Kingdom (en_GB)\n' + ' %s\n' + ' English, South Africa (en_ZA)\n' + ' %s\n' + ' Spanish (es)\n' + ' %s\n' + ' Estonian (et)\n' + ' %s\n' + ' Finnish (fi)\n' + ' %s\n' + ' French (fr)\n' + ' %s\n' + ' Hungarian (hu)\n' + ' %s\n' + ' Indonesian (id)\n' + ' %s\n' + ' Japanese (ja)\n' + ' %s\n' + ' Norwegian Bokm\xe5l (nb)\n' + ' %s\n' + ' Dutch (nl)\n' + ' %s\n' + ' Portuguese, Brazil (pt_BR)\n' + ' %s\n' + ' Russian (ru)\n' + ' %s\n' + ' Swedish (sv)\n' + ' %s\n' + ' Tamil(Sri-Lanka) (ta_LK)\n' + ' %s\n' + ' Chinese(China) (zh_CN)\n' + ' %s\n' + '\n' + 'Documentation\n' + ' %s\n' + '\n' + 'Built With\n' + ' Python: http://www.python.org/\n' + ' Qt4: http://qt.digia.com/\n' + ' PyQt4: http://www.riverbankcomputing.co.uk/software/pyqt/' + 'intro\n' + ' Oxygen Icons: http://oxygen-icons.org/\n' + '\n' + 'Final Credit\n' + ' "For God so loved the world that He gave\n' + ' His one and only Son, so that whoever\n' + ' believes in Him will not perish but inherit\n' + ' eternal life." -- John 3:16\n\n' + ' And last but not least, final credit goes to\n' + ' God our Father, for sending His Son to die\n' + ' on the cross, setting us free from sin. We\n' + ' bring this software to you for free because\n' + ' He has set us free.') % + (lead, '\n '.join(developers), + '\n '.join(contributors), '\n '.join(testers), + '\n '.join(packagers), '\n '.join(translators['af']), + '\n '.join(translators['cs']), + '\n '.join(translators['da']), + '\n '.join(translators['de']), + '\n '.join(translators['el']), + '\n '.join(translators['en_GB']), + '\n '.join(translators['en_ZA']), + '\n '.join(translators['es']), + '\n '.join(translators['et']), + '\n '.join(translators['fi']), + '\n '.join(translators['fr']), + '\n '.join(translators['hu']), + '\n '.join(translators['id']), + '\n '.join(translators['ja']), + '\n '.join(translators['nb']), + '\n '.join(translators['nl']), + '\n '.join(translators['pt_BR']), + '\n '.join(translators['ru']), + '\n '.join(translators['sv']), + '\n '.join(translators['ta_LK']), + '\n '.join(translators['zh_CN']), + '\n '.join(documentors))) self.about_notebook.setTabText(self.about_notebook.indexOf(self.credits_tab), - translate('OpenLP.AboutForm', 'Credits')) + translate('OpenLP.AboutForm', 'Credits')) copyright_note = translate('OpenLP.AboutForm', - 'Copyright \xa9 2004-2014 %s\n' - 'Portions copyright \xa9 2004-2014 %s') % ('Raoul Snyman', - 'Tim Bentley, Gerald Britton, Jonathan Corwin, Samuel Findlay, ' - 'Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, ' - 'Armin K\xf6hler, Erik Lundin, Edwin Lunando, Joshua Miller, ' - 'Brian T. Meyer, Stevan Pettit, Andreas Preikschat, ' - 'Mattias P\xf5ldaru, Christian Richter, ' - 'Philip Ridout, Simon Scudder, Jeffrey Smith, Maikel Stuivenberg, ' - 'Martin Thompson, Jon Tibble, Dave Warnock, Frode Woldsund, ' - 'Martin Zibricky, Patrick Zimmermann') + 'Copyright \xa9 2004-2014 %s\n' + 'Portions copyright \xa9 2004-2014 %s') % \ + ('Raoul Snyman', + 'Tim Bentley, Gerald Britton, Jonathan Corwin, Samuel Findlay, ' + 'Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, ' + 'Armin K\xf6hler, Erik Lundin, Edwin Lunando, Joshua Miller, ' + 'Brian T. Meyer, Stevan Pettit, Andreas Preikschat, ' + 'Mattias P\xf5ldaru, Christian Richter, ' + 'Philip Ridout, Simon Scudder, Jeffrey Smith, Maikel Stuivenberg, ' + 'Martin Thompson, Jon Tibble, Dave Warnock, Frode Woldsund, ' + 'Martin Zibricky, Patrick Zimmermann') licence = translate('OpenLP.AboutForm', - '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 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.') disclaimer = translate('OpenLP.AboutForm', - '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 below ' - 'for more details.') + '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 below ' + 'for more details.') gpl_text = ('GNU GENERAL PUBLIC LICENSE\n' - 'Version 2, June 1991\n' - '\n' - 'Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 ' - 'Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ' - 'Everyone is permitted to copy and distribute verbatim copies of ' - 'this license document, but changing it is not allowed.\n' - '\n' - 'Preamble\n' - '\n' - 'The licenses for most software are designed to take away your ' - 'freedom to share and change it. By contrast, the GNU General ' - 'Public License is intended to guarantee your freedom to share ' - 'and change free software--to make sure the software is free for ' - 'all its users. This General Public License applies to most of ' - 'the Free Software Foundation\'s software and to any other ' - 'program whose authors commit to using it. (Some other Free ' - 'Software Foundation software is covered by the GNU Lesser ' - 'General Public License instead.) You can apply it to your ' - 'programs, too.\n' - '\n' - 'When we speak of free software, we are referring to freedom, not ' - 'price. Our General Public Licenses are designed to make sure ' - 'that you have the freedom to distribute copies of free software ' - '(and charge for this service if you wish), that you receive ' - 'source code or can get it if you want it, that you can change ' - 'the software or use pieces of it in new free programs; and that ' - 'you know you can do these things.\n' - '\n' - 'To protect your rights, we need to make restrictions that forbid ' - 'anyone to deny you these rights or to ask you to surrender the ' - 'rights. These restrictions translate to certain responsibilities ' - 'for you if you distribute copies of the software, or if you ' - 'modify it.\n' - '\n' - 'For example, if you distribute copies of such a program, whether ' - 'gratis or for a fee, you must give the recipients all the rights ' - 'that you have. You must make sure that they, too, receive or ' - 'can get the source code. And you must show them these terms so ' - 'they know their rights.\n' - '\n' - 'We protect your rights with two steps: (1) copyright the ' - 'software, and (2) offer you this license which gives you legal ' - 'permission to copy, distribute and/or modify the software.\n' - '\n' - 'Also, for each author\'s protection and ours, we want to make ' - 'certain that everyone understands that there is no warranty for ' - 'this free software. If the software is modified by someone else ' - 'and passed on, we want its recipients to know that what they ' - 'have is not the original, so that any problems introduced by ' - 'others will not reflect on the original authors\' reputations.\n' - '\n' - 'Finally, any free program is threatened constantly by software ' - 'patents. We wish to avoid the danger that redistributors of a ' - 'free program will individually obtain patent licenses, in effect ' - 'making the program proprietary. To prevent this, we have made ' - 'it clear that any patent must be licensed for everyone\'s free ' - 'use or not licensed at all.\n' - '\n' - 'The precise terms and conditions for copying, distribution and ' - 'modification follow.\n' - '\n' - 'GNU GENERAL PUBLIC LICENSE\n' - 'TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n' - '\n' - '0. This License applies to any program or other work which ' - 'contains a notice placed by the copyright holder saying it may ' - 'be distributed under the terms of this General Public License. ' - 'The "Program", below, refers to any such program or work, and a ' - '"work based on the Program" means either the Program or any ' - 'derivative work under copyright law: that is to say, a work ' - 'containing the Program or a portion of it, either verbatim or ' - 'with modifications and/or translated into another language. ' - '(Hereinafter, translation is included without limitation in the ' - 'term "modification".) Each licensee is addressed as "you".\n' - '\n' - 'Activities other than copying, distribution and modification are ' - 'not covered by this License; they are outside its scope. The ' - 'act of running the Program is not restricted, and the output ' - 'from the Program is covered only if its contents constitute a ' - 'work based on the Program (independent of having been made by ' - 'running the Program). Whether that is true depends on what the ' - 'Program does.\n' - '\n' - '1. You may copy and distribute verbatim copies of the Program\'s ' - 'source code as you receive it, in any medium, provided that you ' - 'conspicuously and appropriately publish on each copy an ' - 'appropriate copyright notice and disclaimer of warranty; keep ' - 'intact all the notices that refer to this License and to the ' - 'absence of any warranty; and give any other recipients of the ' - 'Program a copy of this License along with the Program.\n' - '\n' - 'You may charge a fee for the physical act of transferring a ' - 'copy, and you may at your option offer warranty protection in ' - 'exchange for a fee.\n' - '\n' - '2. You may modify your copy or copies of the Program or any ' - 'portion of it, thus forming a work based on the Program, and ' - 'copy and distribute such modifications or work under the terms ' - 'of Section 1 above, provided that you also meet all of these ' - 'conditions:\n' - '\n' - 'a) You must cause the modified files to carry prominent notices ' - 'stating that you changed the files and the date of any change.\n' - '\n' - 'b) You must cause any work that you distribute or publish, that ' - 'in whole or in part contains or is derived from the Program or ' - 'any part thereof, to be licensed as a whole at no charge to all ' - 'third parties under the terms of this License.\n' - '\n' - 'c) If the modified program normally reads commands interactively ' - 'when run, you must cause it, when started running for such ' - 'interactive use in the most ordinary way, to print or display an ' - 'announcement including an appropriate copyright notice and a ' - 'notice that there is no warranty (or else, saying that you ' - 'provide a warranty) and that users may redistribute the program ' - 'under these conditions, and telling the user how to view a copy ' - 'of this License. (Exception: if the Program itself is ' - 'interactive but does not normally print such an announcement, ' - 'your work based on the Program is not required to print an ' - 'announcement.)\n' - '\n' - 'These requirements apply to the modified work as a whole. If ' - 'identifiable sections of that work are not derived from the ' - 'Program, and can be reasonably considered independent and ' - 'separate works in themselves, then this License, and its terms, ' - 'do not apply to those sections when you distribute them as ' - 'separate works. But when you distribute the same sections as ' - 'part of a whole which is a work based on the Program, the ' - 'distribution of the whole must be on the terms of this License, ' - 'whose permissions for other licensees extend to the entire ' - 'whole, and thus to each and every part regardless of who wrote ' - 'it.\n' - '\n' - 'Thus, it is not the intent of this section to claim rights or ' - 'contest your rights to work written entirely by you; rather, the ' - 'intent is to exercise the right to control the distribution of ' - 'derivative or collective works based on the Program.\n' - '\n' - 'In addition, mere aggregation of another work not based on the ' - 'Program with the Program (or with a work based on the Program) ' - 'on a volume of a storage or distribution medium does not bring ' - 'the other work under the scope of this License.\n' - '\n' - '3. You may copy and distribute the Program (or a work based on ' - 'it, under Section 2) in object code or executable form under the ' - 'terms of Sections 1 and 2 above provided that you also do one of ' - 'the following:\n' - '\n' - 'a) Accompany it with the complete corresponding machine-readable ' - 'source code, which must be distributed under the terms of ' - 'Sections 1 and 2 above on a medium customarily used for software ' - 'interchange; or,\n' - '\n' - 'b) Accompany it with a written offer, valid for at least three ' - 'years, to give any third party, for a charge no more than your ' - 'cost of physically performing source distribution, a complete ' - 'machine-readable copy of the corresponding source code, to be ' - 'distributed under the terms of Sections 1 and 2 above on a ' - 'medium customarily used for software interchange; or,\n' - '\n' - 'c) Accompany it with the information you received as to the ' - 'offer to distribute corresponding source code. (This ' - 'alternative is allowed only for noncommercial distribution and ' - 'only if you received the program in object code or executable ' - 'form with such an offer, in accord with Subsection b above.)\n' - '\n' - 'The source code for a work means the preferred form of the work ' - 'for making modifications to it. For an executable work, ' - 'complete source code means all the source code for all modules ' - 'it contains, plus any associated interface definition files, ' - 'plus the scripts used to control compilation and installation of ' - 'the executable. However, as a special exception, the source ' - 'code distributed need not include anything that is normally ' - 'distributed (in either source or binary form) with the major ' - 'components (compiler, kernel, and so on) of the operating system ' - 'on which the executable runs, unless that component itself ' - 'accompanies the executable.\n' - '\n' - 'If distribution of executable or object code is made by offering ' - 'access to copy from a designated place, then offering equivalent ' - 'access to copy the source code from the same place counts as ' - 'distribution of the source code, even though third parties are ' - 'not compelled to copy the source along with the object code.\n' - '\n' - '4. You may not copy, modify, sublicense, or distribute the ' - 'Program except as expressly provided under this License. Any ' - 'attempt otherwise to copy, modify, sublicense or distribute the ' - 'Program is void, and will automatically terminate your rights ' - 'under this License. However, parties who have received copies, ' - 'or rights, from you under this License will not have their ' - 'licenses terminated so long as such parties remain in full ' - 'compliance.\n' - '\n' - '5. You are not required to accept this License, since you have ' - 'not signed it. However, nothing else grants you permission to ' - 'modify or distribute the Program or its derivative works. These ' - 'actions are prohibited by law if you do not accept this ' - 'License. Therefore, by modifying or distributing the Program ' - '(or any work based on the Program), you indicate your acceptance ' - 'of this License to do so, and all its terms and conditions for ' - 'copying, distributing or modifying the Program or works based on ' - 'it.\n' - '\n' - '6. Each time you redistribute the Program (or any work based on ' - 'the Program), the recipient automatically receives a license ' - 'from the original licensor to copy, distribute or modify the ' - 'Program subject to these terms and conditions. You may not ' - 'impose any further restrictions on the recipients\' exercise of ' - 'the rights granted herein. You are not responsible for enforcing ' - 'compliance by third parties to this License.\n' - '\n' - '7. If, as a consequence of a court judgment or allegation of ' - 'patent infringement or for any other reason (not limited to ' - 'patent issues), conditions are imposed on you (whether by court ' - 'order, agreement or otherwise) that contradict the conditions of ' - 'this License, they do not excuse you from the conditions of this ' - 'License. If you cannot distribute so as to satisfy ' - 'simultaneously your obligations under this License and any other ' - 'pertinent obligations, then as a consequence you may not ' - 'distribute the Program at all. For example, if a patent license ' - 'would not permit royalty-free redistribution of the Program by ' - 'all those who receive copies directly or indirectly through you, ' - 'then the only way you could satisfy both it and this License ' - 'would be to refrain entirely from distribution of the Program.\n' - '\n' - 'If any portion of this section is held invalid or unenforceable ' - 'under any particular circumstance, the balance of the section is ' - 'intended to apply and the section as a whole is intended to ' - 'apply in other circumstances.\n' - '\n' - 'It is not the purpose of this section to induce you to infringe ' - 'any patents or other property right claims or to contest ' - 'validity of any such claims; this section has the sole purpose ' - 'of protecting the integrity of the free software distribution ' - 'system, which is implemented by public license practices. Many ' - 'people have made generous contributions to the wide range of ' - 'software distributed through that system in reliance on ' - 'consistent application of that system; it is up to the ' - 'author/donor to decide if he or she is willing to distribute ' - 'software through any other system and a licensee cannot impose ' - 'that choice.\n' - '\n' - 'This section is intended to make thoroughly clear what is ' - 'believed to be a consequence of the rest of this License.\n' - '\n' - '8. If the distribution and/or use of the Program is restricted ' - 'in certain countries either by patents or by copyrighted ' - 'interfaces, the original copyright holder who places the Program ' - 'under this License may add an explicit geographical distribution ' - 'limitation excluding those countries, so that distribution is ' - 'permitted only in or among countries not thus excluded. In such ' - 'case, this License incorporates the limitation as if written in ' - 'the body of this License.\n' - '\n' - '9. The Free Software Foundation may publish revised and/or new ' - 'versions of the General Public License from time to time. Such ' - 'new versions will be similar in spirit to the present version, ' - 'but may differ in detail to address new problems or concerns.\n' - '\n' - 'Each version is given a distinguishing version number. If the ' - 'Program specifies a version number of this License which applies ' - 'to it and "any later version", you have the option of ' - 'following the terms and conditions either of that version or of ' - 'any later version published by the Free Software Foundation. If ' - 'the Program does not specify a version number of this License, ' - 'you may choose any version ever published by the Free Software ' - 'Foundation.\n' - '\n' - '10. If you wish to incorporate parts of the Program into other ' - 'free programs whose distribution conditions are different, write ' - 'to the author to ask for permission. For software which is ' - 'copyrighted by the Free Software Foundation, write to the Free ' - 'Software Foundation; we sometimes make exceptions for this. Our ' - 'decision will be guided by the two goals of preserving the free ' - 'status of all derivatives of our free software and of promoting ' - 'the sharing and reuse of software generally.\n' - '\n' - 'NO WARRANTY\n' - '\n' - '11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO ' - 'WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE ' - 'LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT ' - 'HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT ' - 'WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, ' - 'BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY ' - 'AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE ' - 'QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE ' - 'PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY ' - 'SERVICING, REPAIR OR CORRECTION.\n' - '\n' - '12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO ' - 'IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY ' - 'MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE ' - 'LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, ' - 'INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR ' - 'INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS ' - 'OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY ' - 'YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ' - 'ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ' - 'ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.\n' - '\n' - 'END OF TERMS AND CONDITIONS\n' - '\n' - 'How to Apply These Terms to Your New Programs\n' - '\n' - 'If you develop a new program, and you want it to be of the ' - 'greatest possible use to the public, the best way to achieve ' - 'this is to make it free software which everyone can redistribute ' - 'and change under these terms.\n' - '\n' - 'To do so, attach the following notices to the program. It is ' - 'safest to attach them to the start of each source file to most ' - 'effectively convey the exclusion of warranty; and each file ' - 'should have at least the "copyright" line and a pointer to where ' - 'the full notice is found.\n' - '\n' - '<one line to give the program\'s name and a brief idea of what ' - 'it does.>\n' - 'Copyright (C) <year> <name of author>\n' - '\n' - '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; either version 2 of ' - 'the License, or (at your option) any later version.\n' - '\n' - '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.\n' - '\n' - '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., 51 Franklin Street, Fifth Floor, ' - 'Boston, MA 02110-1301 USA.\n' - '\n' - 'Also add information on how to contact you by electronic and ' - 'paper mail.\n' - '\n' - 'If the program is interactive, make it output a short notice ' - 'like this when it starts in an interactive mode:\n' - '\n' - 'Gnomovision version 69, Copyright (C) year name of author\n' - 'Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type ' - '"show w".\n' - 'This is free software, and you are welcome to redistribute it ' - 'under certain conditions; type "show c" for details.\n' - '\n' - 'The hypothetical commands "show w" and "show c" should show ' - 'the appropriate parts of the General Public License. Of course, ' - 'the commands you use may be called something other than "show ' - 'w" and "show c"; they could even be mouse-clicks or menu items--' - 'whatever suits your program.\n' - '\n' - 'You should also get your employer (if you work as a programmer) ' - 'or your school, if any, to sign a "copyright disclaimer" for the ' - 'program, if necessary. Here is a sample; alter the names:\n' - '\n' - 'Yoyodyne, Inc., hereby disclaims all copyright interest in the ' - 'program "Gnomovision" (which makes passes at compilers) written ' - 'by James Hacker.\n' - '\n' - '<signature of Ty Coon>, 1 April 1989\n' - 'Ty Coon, President of Vice\n' - '\n' - 'This General Public License does not permit incorporating your ' - 'program into proprietary programs. If your program is a ' - 'subroutine library, you may consider it more useful to permit ' - 'linking proprietary applications with the library. If this is ' - 'what you want to do, use the GNU Lesser General Public License ' - 'instead of this License.') + 'Version 2, June 1991\n' + '\n' + 'Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 ' + 'Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ' + 'Everyone is permitted to copy and distribute verbatim copies of ' + 'this license document, but changing it is not allowed.\n' + '\n' + 'Preamble\n' + '\n' + 'The licenses for most software are designed to take away your ' + 'freedom to share and change it. By contrast, the GNU General ' + 'Public License is intended to guarantee your freedom to share ' + 'and change free software--to make sure the software is free for ' + 'all its users. This General Public License applies to most of ' + 'the Free Software Foundation\'s software and to any other ' + 'program whose authors commit to using it. (Some other Free ' + 'Software Foundation software is covered by the GNU Lesser ' + 'General Public License instead.) You can apply it to your ' + 'programs, too.\n' + '\n' + 'When we speak of free software, we are referring to freedom, not ' + 'price. Our General Public Licenses are designed to make sure ' + 'that you have the freedom to distribute copies of free software ' + '(and charge for this service if you wish), that you receive ' + 'source code or can get it if you want it, that you can change ' + 'the software or use pieces of it in new free programs; and that ' + 'you know you can do these things.\n' + '\n' + 'To protect your rights, we need to make restrictions that forbid ' + 'anyone to deny you these rights or to ask you to surrender the ' + 'rights. These restrictions translate to certain responsibilities ' + 'for you if you distribute copies of the software, or if you ' + 'modify it.\n' + '\n' + 'For example, if you distribute copies of such a program, whether ' + 'gratis or for a fee, you must give the recipients all the rights ' + 'that you have. You must make sure that they, too, receive or ' + 'can get the source code. And you must show them these terms so ' + 'they know their rights.\n' + '\n' + 'We protect your rights with two steps: (1) copyright the ' + 'software, and (2) offer you this license which gives you legal ' + 'permission to copy, distribute and/or modify the software.\n' + '\n' + 'Also, for each author\'s protection and ours, we want to make ' + 'certain that everyone understands that there is no warranty for ' + 'this free software. If the software is modified by someone else ' + 'and passed on, we want its recipients to know that what they ' + 'have is not the original, so that any problems introduced by ' + 'others will not reflect on the original authors\' reputations.\n' + '\n' + 'Finally, any free program is threatened constantly by software ' + 'patents. We wish to avoid the danger that redistributors of a ' + 'free program will individually obtain patent licenses, in effect ' + 'making the program proprietary. To prevent this, we have made ' + 'it clear that any patent must be licensed for everyone\'s free ' + 'use or not licensed at all.\n' + '\n' + 'The precise terms and conditions for copying, distribution and ' + 'modification follow.\n' + '\n' + 'GNU GENERAL PUBLIC LICENSE\n' + 'TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n' + '\n' + '0. This License applies to any program or other work which ' + 'contains a notice placed by the copyright holder saying it may ' + 'be distributed under the terms of this General Public License. ' + 'The "Program", below, refers to any such program or work, and a ' + '"work based on the Program" means either the Program or any ' + 'derivative work under copyright law: that is to say, a work ' + 'containing the Program or a portion of it, either verbatim or ' + 'with modifications and/or translated into another language. ' + '(Hereinafter, translation is included without limitation in the ' + 'term "modification".) Each licensee is addressed as "you".\n' + '\n' + 'Activities other than copying, distribution and modification are ' + 'not covered by this License; they are outside its scope. The ' + 'act of running the Program is not restricted, and the output ' + 'from the Program is covered only if its contents constitute a ' + 'work based on the Program (independent of having been made by ' + 'running the Program). Whether that is true depends on what the ' + 'Program does.\n' + '\n' + '1. You may copy and distribute verbatim copies of the Program\'s ' + 'source code as you receive it, in any medium, provided that you ' + 'conspicuously and appropriately publish on each copy an ' + 'appropriate copyright notice and disclaimer of warranty; keep ' + 'intact all the notices that refer to this License and to the ' + 'absence of any warranty; and give any other recipients of the ' + 'Program a copy of this License along with the Program.\n' + '\n' + 'You may charge a fee for the physical act of transferring a ' + 'copy, and you may at your option offer warranty protection in ' + 'exchange for a fee.\n' + '\n' + '2. You may modify your copy or copies of the Program or any ' + 'portion of it, thus forming a work based on the Program, and ' + 'copy and distribute such modifications or work under the terms ' + 'of Section 1 above, provided that you also meet all of these ' + 'conditions:\n' + '\n' + 'a) You must cause the modified files to carry prominent notices ' + 'stating that you changed the files and the date of any change.\n' + '\n' + 'b) You must cause any work that you distribute or publish, that ' + 'in whole or in part contains or is derived from the Program or ' + 'any part thereof, to be licensed as a whole at no charge to all ' + 'third parties under the terms of this License.\n' + '\n' + 'c) If the modified program normally reads commands interactively ' + 'when run, you must cause it, when started running for such ' + 'interactive use in the most ordinary way, to print or display an ' + 'announcement including an appropriate copyright notice and a ' + 'notice that there is no warranty (or else, saying that you ' + 'provide a warranty) and that users may redistribute the program ' + 'under these conditions, and telling the user how to view a copy ' + 'of this License. (Exception: if the Program itself is ' + 'interactive but does not normally print such an announcement, ' + 'your work based on the Program is not required to print an ' + 'announcement.)\n' + '\n' + 'These requirements apply to the modified work as a whole. If ' + 'identifiable sections of that work are not derived from the ' + 'Program, and can be reasonably considered independent and ' + 'separate works in themselves, then this License, and its terms, ' + 'do not apply to those sections when you distribute them as ' + 'separate works. But when you distribute the same sections as ' + 'part of a whole which is a work based on the Program, the ' + 'distribution of the whole must be on the terms of this License, ' + 'whose permissions for other licensees extend to the entire ' + 'whole, and thus to each and every part regardless of who wrote ' + 'it.\n' + '\n' + 'Thus, it is not the intent of this section to claim rights or ' + 'contest your rights to work written entirely by you; rather, the ' + 'intent is to exercise the right to control the distribution of ' + 'derivative or collective works based on the Program.\n' + '\n' + 'In addition, mere aggregation of another work not based on the ' + 'Program with the Program (or with a work based on the Program) ' + 'on a volume of a storage or distribution medium does not bring ' + 'the other work under the scope of this License.\n' + '\n' + '3. You may copy and distribute the Program (or a work based on ' + 'it, under Section 2) in object code or executable form under the ' + 'terms of Sections 1 and 2 above provided that you also do one of ' + 'the following:\n' + '\n' + 'a) Accompany it with the complete corresponding machine-readable ' + 'source code, which must be distributed under the terms of ' + 'Sections 1 and 2 above on a medium customarily used for software ' + 'interchange; or,\n' + '\n' + 'b) Accompany it with a written offer, valid for at least three ' + 'years, to give any third party, for a charge no more than your ' + 'cost of physically performing source distribution, a complete ' + 'machine-readable copy of the corresponding source code, to be ' + 'distributed under the terms of Sections 1 and 2 above on a ' + 'medium customarily used for software interchange; or,\n' + '\n' + 'c) Accompany it with the information you received as to the ' + 'offer to distribute corresponding source code. (This ' + 'alternative is allowed only for noncommercial distribution and ' + 'only if you received the program in object code or executable ' + 'form with such an offer, in accord with Subsection b above.)\n' + '\n' + 'The source code for a work means the preferred form of the work ' + 'for making modifications to it. For an executable work, ' + 'complete source code means all the source code for all modules ' + 'it contains, plus any associated interface definition files, ' + 'plus the scripts used to control compilation and installation of ' + 'the executable. However, as a special exception, the source ' + 'code distributed need not include anything that is normally ' + 'distributed (in either source or binary form) with the major ' + 'components (compiler, kernel, and so on) of the operating system ' + 'on which the executable runs, unless that component itself ' + 'accompanies the executable.\n' + '\n' + 'If distribution of executable or object code is made by offering ' + 'access to copy from a designated place, then offering equivalent ' + 'access to copy the source code from the same place counts as ' + 'distribution of the source code, even though third parties are ' + 'not compelled to copy the source along with the object code.\n' + '\n' + '4. You may not copy, modify, sublicense, or distribute the ' + 'Program except as expressly provided under this License. Any ' + 'attempt otherwise to copy, modify, sublicense or distribute the ' + 'Program is void, and will automatically terminate your rights ' + 'under this License. However, parties who have received copies, ' + 'or rights, from you under this License will not have their ' + 'licenses terminated so long as such parties remain in full ' + 'compliance.\n' + '\n' + '5. You are not required to accept this License, since you have ' + 'not signed it. However, nothing else grants you permission to ' + 'modify or distribute the Program or its derivative works. These ' + 'actions are prohibited by law if you do not accept this ' + 'License. Therefore, by modifying or distributing the Program ' + '(or any work based on the Program), you indicate your acceptance ' + 'of this License to do so, and all its terms and conditions for ' + 'copying, distributing or modifying the Program or works based on ' + 'it.\n' + '\n' + '6. Each time you redistribute the Program (or any work based on ' + 'the Program), the recipient automatically receives a license ' + 'from the original licensor to copy, distribute or modify the ' + 'Program subject to these terms and conditions. You may not ' + 'impose any further restrictions on the recipients\' exercise of ' + 'the rights granted herein. You are not responsible for enforcing ' + 'compliance by third parties to this License.\n' + '\n' + '7. If, as a consequence of a court judgment or allegation of ' + 'patent infringement or for any other reason (not limited to ' + 'patent issues), conditions are imposed on you (whether by court ' + 'order, agreement or otherwise) that contradict the conditions of ' + 'this License, they do not excuse you from the conditions of this ' + 'License. If you cannot distribute so as to satisfy ' + 'simultaneously your obligations under this License and any other ' + 'pertinent obligations, then as a consequence you may not ' + 'distribute the Program at all. For example, if a patent license ' + 'would not permit royalty-free redistribution of the Program by ' + 'all those who receive copies directly or indirectly through you, ' + 'then the only way you could satisfy both it and this License ' + 'would be to refrain entirely from distribution of the Program.\n' + '\n' + 'If any portion of this section is held invalid or unenforceable ' + 'under any particular circumstance, the balance of the section is ' + 'intended to apply and the section as a whole is intended to ' + 'apply in other circumstances.\n' + '\n' + 'It is not the purpose of this section to induce you to infringe ' + 'any patents or other property right claims or to contest ' + 'validity of any such claims; this section has the sole purpose ' + 'of protecting the integrity of the free software distribution ' + 'system, which is implemented by public license practices. Many ' + 'people have made generous contributions to the wide range of ' + 'software distributed through that system in reliance on ' + 'consistent application of that system; it is up to the ' + 'author/donor to decide if he or she is willing to distribute ' + 'software through any other system and a licensee cannot impose ' + 'that choice.\n' + '\n' + 'This section is intended to make thoroughly clear what is ' + 'believed to be a consequence of the rest of this License.\n' + '\n' + '8. If the distribution and/or use of the Program is restricted ' + 'in certain countries either by patents or by copyrighted ' + 'interfaces, the original copyright holder who places the Program ' + 'under this License may add an explicit geographical distribution ' + 'limitation excluding those countries, so that distribution is ' + 'permitted only in or among countries not thus excluded. In such ' + 'case, this License incorporates the limitation as if written in ' + 'the body of this License.\n' + '\n' + '9. The Free Software Foundation may publish revised and/or new ' + 'versions of the General Public License from time to time. Such ' + 'new versions will be similar in spirit to the present version, ' + 'but may differ in detail to address new problems or concerns.\n' + '\n' + 'Each version is given a distinguishing version number. If the ' + 'Program specifies a version number of this License which applies ' + 'to it and "any later version", you have the option of ' + 'following the terms and conditions either of that version or of ' + 'any later version published by the Free Software Foundation. If ' + 'the Program does not specify a version number of this License, ' + 'you may choose any version ever published by the Free Software ' + 'Foundation.\n' + '\n' + '10. If you wish to incorporate parts of the Program into other ' + 'free programs whose distribution conditions are different, write ' + 'to the author to ask for permission. For software which is ' + 'copyrighted by the Free Software Foundation, write to the Free ' + 'Software Foundation; we sometimes make exceptions for this. Our ' + 'decision will be guided by the two goals of preserving the free ' + 'status of all derivatives of our free software and of promoting ' + 'the sharing and reuse of software generally.\n' + '\n' + 'NO WARRANTY\n' + '\n' + '11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO ' + 'WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE ' + 'LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT ' + 'HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT ' + 'WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, ' + 'BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY ' + 'AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE ' + 'QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE ' + 'PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY ' + 'SERVICING, REPAIR OR CORRECTION.\n' + '\n' + '12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO ' + 'IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY ' + 'MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE ' + 'LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, ' + 'INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR ' + 'INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS ' + 'OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY ' + 'YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ' + 'ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ' + 'ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.\n' + '\n' + 'END OF TERMS AND CONDITIONS\n' + '\n' + 'How to Apply These Terms to Your New Programs\n' + '\n' + 'If you develop a new program, and you want it to be of the ' + 'greatest possible use to the public, the best way to achieve ' + 'this is to make it free software which everyone can redistribute ' + 'and change under these terms.\n' + '\n' + 'To do so, attach the following notices to the program. It is ' + 'safest to attach them to the start of each source file to most ' + 'effectively convey the exclusion of warranty; and each file ' + 'should have at least the "copyright" line and a pointer to where ' + 'the full notice is found.\n' + '\n' + '<one line to give the program\'s name and a brief idea of what ' + 'it does.>\n' + 'Copyright (C) <year> <name of author>\n' + '\n' + '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; either version 2 of ' + 'the License, or (at your option) any later version.\n' + '\n' + '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.\n' + '\n' + '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., 51 Franklin Street, Fifth Floor, ' + 'Boston, MA 02110-1301 USA.\n' + '\n' + 'Also add information on how to contact you by electronic and ' + 'paper mail.\n' + '\n' + 'If the program is interactive, make it output a short notice ' + 'like this when it starts in an interactive mode:\n' + '\n' + 'Gnomovision version 69, Copyright (C) year name of author\n' + 'Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type ' + '"show w".\n' + 'This is free software, and you are welcome to redistribute it ' + 'under certain conditions; type "show c" for details.\n' + '\n' + 'The hypothetical commands "show w" and "show c" should show ' + 'the appropriate parts of the General Public License. Of course, ' + 'the commands you use may be called something other than "show ' + 'w" and "show c"; they could even be mouse-clicks or menu items--' + 'whatever suits your program.\n' + '\n' + 'You should also get your employer (if you work as a programmer) ' + 'or your school, if any, to sign a "copyright disclaimer" for the ' + 'program, if necessary. Here is a sample; alter the names:\n' + '\n' + 'Yoyodyne, Inc., hereby disclaims all copyright interest in the ' + 'program "Gnomovision" (which makes passes at compilers) written ' + 'by James Hacker.\n' + '\n' + '<signature of Ty Coon>, 1 April 1989\n' + 'Ty Coon, President of Vice\n' + '\n' + 'This General Public License does not permit incorporating your ' + 'program into proprietary programs. If your program is a ' + 'subroutine library, you may consider it more useful to permit ' + 'linking proprietary applications with the library. If this is ' + 'what you want to do, use the GNU Lesser General Public License ' + 'instead of this License.') self.license_text_edit.setPlainText('%s\n\n%s\n\n%s\n\n\n%s' % (copyright_note, licence, disclaimer, gpl_text)) self.about_notebook.setTabText(self.about_notebook.indexOf(self.license_tab), - translate('OpenLP.AboutForm', 'License')) + translate('OpenLP.AboutForm', 'License')) self.volunteer_button.setText(translate('OpenLP.AboutForm', 'Volunteer')) diff --git a/openlp/core/ui/advancedtab.py b/openlp/core/ui/advancedtab.py index cb0de776f..b2c8cd14b 100644 --- a/openlp/core/ui/advancedtab.py +++ b/openlp/core/ui/advancedtab.py @@ -263,7 +263,7 @@ class AdvancedTab(SettingsTab): """ Setup the interface translation strings. """ - self.tabTitleVisible = UiStrings().Advanced + self.tab_title_visible = UiStrings().Advanced self.ui_group_box.setTitle(translate('OpenLP.AdvancedTab', 'UI Settings')) self.data_directory_group_box.setTitle(translate('OpenLP.AdvancedTab', 'Data Location')) self.recent_label.setText(translate('OpenLP.AdvancedTab', 'Number of recent files to display:')) @@ -319,7 +319,7 @@ class AdvancedTab(SettingsTab): translate('OpenLP.AdvancedTab', '<strong>WARNING:</strong> New data directory location contains ' 'OpenLP data files. These files WILL be replaced during a copy.')) self.display_workaround_group_box.setTitle(translate('OpenLP.AdvancedTab', 'Display Workarounds')) - self.x11_bypass_check_box.setText(translate('OpenLP.AdvancedTab','Bypass X11 Window Manager')) + self.x11_bypass_check_box.setText(translate('OpenLP.AdvancedTab', 'Bypass X11 Window Manager')) self.alternate_rows_check_box.setText(translate('OpenLP.AdvancedTab', 'Use alternating row colours in lists')) # Slide Limits self.slide_group_box.setTitle(translate('OpenLP.GeneralTab', 'Service Item Slide Limits')) @@ -375,7 +375,8 @@ class AdvancedTab(SettingsTab): self.current_data_path = AppLocation.get_data_path() if not os.path.exists(self.current_data_path): log.error('Data path not found %s' % self.current_data_path) - answer = QtGui.QMessageBox.critical(self, translate('OpenLP.AdvancedTab', 'Data Directory Error'), + answer = QtGui.QMessageBox.critical( + self, translate('OpenLP.AdvancedTab', 'Data Directory Error'), translate('OpenLP.AdvancedTab', 'OpenLP data directory was not found\n\n%s\n\n' 'This data directory was previously changed from the OpenLP ' 'default location. If the new location was on removable ' @@ -537,8 +538,9 @@ class AdvancedTab(SettingsTab): # Make sure they want to change the data. answer = QtGui.QMessageBox.question(self, translate('OpenLP.AdvancedTab', 'Confirm Data Directory Change'), translate('OpenLP.AdvancedTab', 'Are you sure you want to change the ' - 'location of the OpenLP data directory to:\n\n%s\n\nThe data directory ' - 'will be changed when OpenLP is closed.').replace('%s', new_data_path), + 'location of the OpenLP data directory to:\n\n%s\n\nThe data ' + 'directory will be changed when OpenLP is closed.'). + replace('%s', new_data_path), QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No), QtGui.QMessageBox.No) @@ -561,8 +563,9 @@ class AdvancedTab(SettingsTab): # default. answer = QtGui.QMessageBox.question(self, translate('OpenLP.AdvancedTab', 'Reset Data Directory'), translate('OpenLP.AdvancedTab', 'Are you sure you want to change the ' - 'location of the OpenLP data directory to the default location?\n\nThis' - ' location will be used after OpenLP is closed.'), + 'location of the OpenLP data directory to the default ' + 'location?\n\nThis location will be used after OpenLP is ' + 'closed.'), QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No), QtGui.QMessageBox.No) @@ -588,7 +591,7 @@ class AdvancedTab(SettingsTab): else: self.new_data_directory_has_files_label.hide() - def check_data_overwrite(self, data_path ): + def check_data_overwrite(self, data_path): """ Check if there's already data in the target directory. """ @@ -602,8 +605,8 @@ class AdvancedTab(SettingsTab): translate('OpenLP.AdvancedTab', 'WARNING: \n\nThe location you have selected \n\n%s\n\n' 'appears to contain OpenLP data files. Do you wish to ' - 'replace these files with the current data files?').replace - ('%s', os.path.abspath(data_path,)), + 'replace these files with the current data files?'). + replace('%s', os.path.abspath(data_path,)), QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No), QtGui.QMessageBox.No) diff --git a/openlp/core/ui/exceptiondialog.py b/openlp/core/ui/exceptiondialog.py index 21496ccb1..4d7abe708 100644 --- a/openlp/core/ui/exceptiondialog.py +++ b/openlp/core/ui/exceptiondialog.py @@ -83,9 +83,8 @@ class Ui_ExceptionDialog(object): self.attach_tile_button = create_button(exception_dialog, 'attach_tile_button', icon=':/general/general_open.png', click=self.on_attach_file_button_clicked) - self.button_box = create_button_box(exception_dialog, 'button_box', - ['close'], [self.send_report_button, - self.save_report_button, self.attach_tile_button]) + self.button_box = create_button_box(exception_dialog, 'button_box', ['close'], + [self.send_report_button, self.save_report_button, self.attach_tile_button]) self.exception_layout.addWidget(self.button_box) self.retranslateUi(exception_dialog) diff --git a/openlp/core/ui/exceptionform.py b/openlp/core/ui/exceptionform.py index 72e78ad28..ae3bc9db0 100644 --- a/openlp/core/ui/exceptionform.py +++ b/openlp/core/ui/exceptionform.py @@ -157,7 +157,8 @@ class ExceptionForm(QtGui.QDialog, Ui_ExceptionDialog, RegistryProperties): '--- Exception Traceback ---\n%s\n' '--- System information ---\n%s\n' '--- Library Versions ---\n%s\n') - filename = QtGui.QFileDialog.getSaveFileName(self, + filename = QtGui.QFileDialog.getSaveFileName( + self, translate('OpenLP.ExceptionForm', 'Save Crash Report'), Settings().value(self.settings_section + '/last directory'), translate('OpenLP.ExceptionForm', 'Text files (*.txt *.log *.text)')) @@ -185,14 +186,13 @@ class ExceptionForm(QtGui.QDialog, Ui_ExceptionDialog, RegistryProperties): Opening systems default email client and inserting exception log and system information. """ body = translate('OpenLP.ExceptionForm', - '*OpenLP Bug Report*\n' - 'Version: %s\n\n' - '--- Details of the Exception. ---\n\n%s\n\n ' - '--- Exception Traceback ---\n%s\n' - '--- System information ---\n%s\n' - '--- Library Versions ---\n%s\n', - 'Please add the information that bug reports are favoured written ' - 'in English.') + '*OpenLP Bug Report*\n' + 'Version: %s\n\n' + '--- Details of the Exception. ---\n\n%s\n\n ' + '--- Exception Traceback ---\n%s\n' + '--- System information ---\n%s\n' + '--- Library Versions ---\n%s\n', + 'Please add the information that bug reports are favoured written in English.') content = self._create_report() source = '' exception = '' diff --git a/openlp/core/ui/filerenameform.py b/openlp/core/ui/filerenameform.py index 8e80fd102..0483ea1dd 100644 --- a/openlp/core/ui/filerenameform.py +++ b/openlp/core/ui/filerenameform.py @@ -57,4 +57,4 @@ class FileRenameForm(QtGui.QDialog, Ui_FileRenameDialog, RegistryProperties): else: self.setWindowTitle(translate('OpenLP.FileRenameForm', 'File Rename')) self.file_name_edit.setFocus() - return QtGui.QDialog.exec_(self) \ No newline at end of file + return QtGui.QDialog.exec_(self) diff --git a/openlp/core/ui/firsttimeform.py b/openlp/core/ui/firsttimeform.py index 56e43df77..aa89da6c0 100644 --- a/openlp/core/ui/firsttimeform.py +++ b/openlp/core/ui/firsttimeform.py @@ -468,4 +468,4 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard, RegistryProperties): Set the status of a plugin. """ status = PluginStatus.Active if field.checkState() == QtCore.Qt.Checked else PluginStatus.Inactive - Settings().setValue(tag, status) \ No newline at end of file + Settings().setValue(tag, status) diff --git a/openlp/core/ui/formattingtagcontroller.py b/openlp/core/ui/formattingtagcontroller.py index 7c4f7333d..353114643 100644 --- a/openlp/core/ui/formattingtagcontroller.py +++ b/openlp/core/ui/formattingtagcontroller.py @@ -174,4 +174,4 @@ class FormattingTagController(object): if end and end != end_html: return translate('OpenLP.FormattingTagForm', 'End tag %s does not match end tag for start tag %s' % (end, start_html)), None - return None, None \ No newline at end of file + return None, None diff --git a/openlp/core/ui/formattingtagdialog.py b/openlp/core/ui/formattingtagdialog.py index 6e7822d9e..387bca0a7 100644 --- a/openlp/core/ui/formattingtagdialog.py +++ b/openlp/core/ui/formattingtagdialog.py @@ -102,8 +102,7 @@ class Ui_FormattingTagDialog(object): self.edit_button_layout.addWidget(self.delete_button) self.edit_button_layout.addStretch() self.list_data_grid_layout.addLayout(self.edit_button_layout) - self.button_box = create_button_box(formatting_tag_dialog, 'button_box', - ['cancel', 'save', 'defaults']) + self.button_box = create_button_box(formatting_tag_dialog, 'button_box', ['cancel', 'save', 'defaults']) self.save_button = self.button_box.button(QtGui.QDialogButtonBox.Save) self.save_button.setObjectName('save_button') self.restore_button = self.button_box.button(QtGui.QDialogButtonBox.RestoreDefaults) diff --git a/openlp/core/ui/formattingtagform.py b/openlp/core/ui/formattingtagform.py index b1348b4e9..b7153429d 100644 --- a/openlp/core/ui/formattingtagform.py +++ b/openlp/core/ui/formattingtagform.py @@ -204,4 +204,3 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog, FormattingTagCont QtGui.QMessageBox.Ok) #self.tag_table_widget.selectRow(pre_row - 1) self.tag_table_widget.resizeRowsToContents() - diff --git a/openlp/core/ui/generaltab.py b/openlp/core/ui/generaltab.py index 642dfc538..3aa3f28c0 100644 --- a/openlp/core/ui/generaltab.py +++ b/openlp/core/ui/generaltab.py @@ -223,7 +223,8 @@ class GeneralTab(SettingsTab): self.save_check_service_check_box.setText(translate('OpenLP.GeneralTab', 'Prompt to save before starting a new service')) self.auto_unblank_check_box.setText(translate('OpenLP.GeneralTab', 'Unblank display when adding new live item')) - self.auto_preview_check_box.setText(translate('OpenLP.GeneralTab', 'Automatically preview next item in service')) + self.auto_preview_check_box.setText(translate('OpenLP.GeneralTab', + 'Automatically preview next item in service')) self.timeout_label.setText(translate('OpenLP.GeneralTab', 'Timed slide interval:')) self.timeout_spin_box.setSuffix(translate('OpenLP.GeneralTab', ' sec')) self.ccli_group_box.setTitle(translate('OpenLP.GeneralTab', 'CCLI Details')) diff --git a/openlp/core/ui/listpreviewwidget.py b/openlp/core/ui/listpreviewwidget.py index ec007843d..7dbcfc2bd 100644 --- a/openlp/core/ui/listpreviewwidget.py +++ b/openlp/core/ui/listpreviewwidget.py @@ -160,4 +160,4 @@ class ListPreviewWidget(QtGui.QTableWidget, RegistryProperties): """ Returns the number of slides this widget holds. """ - return super(ListPreviewWidget, self).rowCount() \ No newline at end of file + return super(ListPreviewWidget, self).rowCount() diff --git a/openlp/core/ui/maindisplay.py b/openlp/core/ui/maindisplay.py index eced865ad..b9423046e 100644 --- a/openlp/core/ui/maindisplay.py +++ b/openlp/core/ui/maindisplay.py @@ -601,4 +601,3 @@ class AudioPlayer(OpenLPMixin, QtCore.QObject): :param signal: The signal to be fired """ QtCore.QObject.connect(self.media_object, signal, slot) - diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index 3526e551f..703307e18 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -887,7 +887,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow, RegistryProperties): # application terminates normally. We need to exit without saving configuration. QtGui.QMessageBox.information(self, translate('OpenLP.MainWindow', 'Import settings'), translate('OpenLP.MainWindow', 'OpenLP will now close. Imported settings will ' - 'be applied the next time you start OpenLP.'), + 'be applied the next time you start OpenLP.'), QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok)) self.settings_imported = True self.clean_up() @@ -1042,8 +1042,8 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow, RegistryProperties): ret = QtGui.QMessageBox.question(self, translate('OpenLP.MainWindow', 'Close OpenLP'), translate('OpenLP.MainWindow', 'Are you sure you want to close ' 'OpenLP?'), - QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes | QtGui - .QMessageBox.No), + QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes | + QtGui.QMessageBox.No), QtGui.QMessageBox.Yes) if ret == QtGui.QMessageBox.Yes: self.clean_up() @@ -1234,16 +1234,15 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow, RegistryProperties): self.recent_files_menu.clear() for file_id, filename in enumerate(recent_files_to_display): log.debug('Recent file name: %s', filename) - action = create_action(self, '', - text='&%d %s' % (file_id + 1, + action = create_action(self, '', text='&%d %s' % (file_id + 1, os.path.splitext(os.path.basename(str(filename)))[0]), data=filename, triggers=self.service_manager_contents.on_recent_service_clicked) self.recent_files_menu.addAction(action) clear_recent_files_action = create_action(self, '', text=translate('OpenLP.MainWindow', 'Clear List', 'Clear List of ' - 'recent files'), + 'recent files'), statustip=translate('OpenLP.MainWindow', 'Clear the list of recent ' - 'files.'), + 'files.'), enabled=bool(self.recent_files), triggers=self.clear_recent_file_menu) add_actions(self.recent_files_menu, (None, clear_recent_files_action)) @@ -1352,8 +1351,8 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow, RegistryProperties): self.application.set_normal_cursor() log.exception('Data copy failed %s' % str(why)) QtGui.QMessageBox.critical(self, translate('OpenLP.MainWindow', 'New Data Directory Error'), - translate('OpenLP.MainWindow', - 'OpenLP Data directory copy failed\n\n%s').replace('%s', str(why)), + translate('OpenLP.MainWindow', 'OpenLP Data directory copy failed\n\n%s'). + replace('%s', str(why)), QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok)) return False else: @@ -1365,5 +1364,3 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow, RegistryProperties): if self.new_data_path == AppLocation.get_directory(AppLocation.DataDir): settings.remove('advanced/data path') self.application.set_normal_cursor() - - diff --git a/openlp/core/ui/media/mediacontroller.py b/openlp/core/ui/media/mediacontroller.py index 54cc6cdf8..d846af0e4 100644 --- a/openlp/core/ui/media/mediacontroller.py +++ b/openlp/core/ui/media/mediacontroller.py @@ -383,7 +383,7 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties): if not is_valid: # Media could not be loaded correctly critical_error_message_box(translate('MediaPlugin.MediaItem', 'Unsupported File'), - translate('MediaPlugin.MediaItem', 'Unsupported File')) + translate('MediaPlugin.MediaItem', 'Unsupported File')) return False # dont care about actual theme, set a black background if controller.is_live and not controller.media_info.is_background: @@ -402,7 +402,7 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties): if autoplay: if not self.media_play(controller): critical_error_message_box(translate('MediaPlugin.MediaItem', 'Unsupported File'), - translate('MediaPlugin.MediaItem', 'Unsupported File')) + translate('MediaPlugin.MediaItem', 'Unsupported File')) return False self.set_controls_visible(controller, True) log.debug('use %s controller' % self.current_media_players[controller.controller_type]) @@ -644,9 +644,9 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties): return display = self._define_display(self.live_controller) if self.live_controller.controller_type in self.current_media_players and \ - self.current_media_players[self.live_controller.controller_type].state == MediaState.Playing: - self.current_media_players[self.live_controller.controller_type].pause(display) - self.current_media_players[self.live_controller.controller_type].set_visible(display, False) + self.current_media_players[self.live_controller.controller_type].state == MediaState.Playing: + self.current_media_players[self.live_controller.controller_type].pause(display) + self.current_media_players[self.live_controller.controller_type].set_visible(display, False) def media_blank(self, msg): """ @@ -701,4 +701,4 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties): """ if controller.is_live: return controller.display - return controller.preview_display \ No newline at end of file + return controller.preview_display diff --git a/openlp/core/ui/media/mediaplayer.py b/openlp/core/ui/media/mediaplayer.py index 49b699034..3246d58c4 100644 --- a/openlp/core/ui/media/mediaplayer.py +++ b/openlp/core/ui/media/mediaplayer.py @@ -150,4 +150,4 @@ class MediaPlayer(RegistryProperties): """ Returns Information about the player """ - return '' \ No newline at end of file + return '' diff --git a/openlp/core/ui/media/phononplayer.py b/openlp/core/ui/media/phononplayer.py index d08628004..5e94dbd0e 100644 --- a/openlp/core/ui/media/phononplayer.py +++ b/openlp/core/ui/media/phononplayer.py @@ -105,7 +105,7 @@ class PhononPlayer(MediaPlayer): if ext not in mime_type_list: mime_type_list.append(ext) log.info('MediaPlugin: %s additional extensions: %s' % - (mimetype, ' '.join(self.additional_extensions[mimetype]))) + (mimetype, ' '.join(self.additional_extensions[mimetype]))) def setup(self, display): """ diff --git a/openlp/core/ui/media/vlcplayer.py b/openlp/core/ui/media/vlcplayer.py index 7c892a87c..c80bb6218 100644 --- a/openlp/core/ui/media/vlcplayer.py +++ b/openlp/core/ui/media/vlcplayer.py @@ -291,4 +291,4 @@ class VlcPlayer(MediaPlayer): '<br/> <strong>' + translate('Media.player', 'Audio') + '</strong><br/>' + str(AUDIO_EXT) + '<br/><strong>' + translate('Media.player', 'Video') + '</strong><br/>' + - str(VIDEO_EXT) + '<br/>') \ No newline at end of file + str(VIDEO_EXT) + '<br/>') diff --git a/openlp/core/ui/pluginform.py b/openlp/core/ui/pluginform.py index 9e6ac9663..91b98b97a 100644 --- a/openlp/core/ui/pluginform.py +++ b/openlp/core/ui/pluginform.py @@ -154,4 +154,4 @@ class PluginForm(QtGui.QDialog, Ui_PluginViewDialog, RegistryProperties): elif self.active_plugin.status == PluginStatus.Disabled: status_text = translate('OpenLP.PluginForm', '%s (Disabled)') self.plugin_list_widget.currentItem().setText( - status_text % self.active_plugin.name_strings['singular']) \ No newline at end of file + status_text % self.active_plugin.name_strings['singular']) diff --git a/openlp/core/ui/printserviceform.py b/openlp/core/ui/printserviceform.py index 753bb2a04..489eefa78 100644 --- a/openlp/core/ui/printserviceform.py +++ b/openlp/core/ui/printserviceform.py @@ -394,4 +394,4 @@ class PrintServiceForm(QtGui.QDialog, Ui_PrintServiceDialog, RegistryProperties) return for item in self.service_manager.service_items: # Trigger Audit requests - Registry().register_function('print_service_started', [item['service_item']]) \ No newline at end of file + Registry().register_function('print_service_started', [item['service_item']]) diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index e7994b261..c2b36551a 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -689,8 +689,8 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ServiceManage if self._file_name.endswith('oszl') or self.service_has_all_original_files: file_name = QtGui.QFileDialog.getSaveFileName(self.main_window, UiStrings().SaveService, path, translate('OpenLP.ServiceManager', - 'OpenLP Service Files (*.osz);; OpenLP Service Files - lite ' - '(*.oszl)')) + 'OpenLP Service Files (*.osz);; OpenLP Service ' + 'Files - lite (*.oszl)')) else: file_name = QtGui.QFileDialog.getSaveFileName(self.main_window, UiStrings().SaveService, path, translate('OpenLP.ServiceManager', @@ -783,7 +783,8 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ServiceManage self.log_exception('Service file is cannot be extracted as zip: %s' % file_name) QtGui.QMessageBox.information(self, translate('OpenLP.ServiceManager', 'Corrupt File'), translate('OpenLP.ServiceManager', - 'This file is either corrupt or it is not an OpenLP 2 service file.')) + 'This file is either corrupt or it is not an OpenLP 2 service ' + 'file.')) self.application.set_normal_cursor() return finally: @@ -1253,8 +1254,7 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ServiceManage tree_widget_item.setText(0, service_item_from_item.get_display_title()) tips = [] if service_item_from_item.temporary_edit: - tips.append('<strong>%s:</strong> <em>%s</em>' % - (translate('OpenLP.ServiceManager', 'Edit'), + tips.append('<strong>%s:</strong> <em>%s</em>' % (translate('OpenLP.ServiceManager', 'Edit'), (translate('OpenLP.ServiceManager', 'Service copy only')))) if service_item_from_item.theme and service_item_from_item.theme != -1: tips.append('<strong>%s:</strong> <em>%s</em>' % @@ -1492,9 +1492,9 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ServiceManage def find_service_item(self): """ - Finds the first selected ServiceItem in the list and returns the position of the service_item_from_item and its selected - child item. For example, if the third child item (in the Slidecontroller known as slide) in the second service - item is selected this will return:: + Finds the first selected ServiceItem in the list and returns the position of the service_item_from_item and its + selected child item. For example, if the third child item (in the Slidecontroller known as slide) in the + second service item is selected this will return:: (1, 2) """ @@ -1632,4 +1632,4 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ServiceManage Print a Service Order Sheet. """ setting_dialog = PrintServiceForm() - setting_dialog.exec_() \ No newline at end of file + setting_dialog.exec_() diff --git a/openlp/core/ui/servicenoteform.py b/openlp/core/ui/servicenoteform.py index ee24cf7cf..c91380a5d 100644 --- a/openlp/core/ui/servicenoteform.py +++ b/openlp/core/ui/servicenoteform.py @@ -74,4 +74,4 @@ class ServiceNoteForm(QtGui.QDialog, RegistryProperties): """ Translate the UI on the fly """ - self.setWindowTitle(translate('OpenLP.ServiceNoteForm', 'Service Item Notes')) \ No newline at end of file + self.setWindowTitle(translate('OpenLP.ServiceNoteForm', 'Service Item Notes')) diff --git a/openlp/core/ui/settingsform.py b/openlp/core/ui/settingsform.py index 68b0b3b41..5aba66ef2 100644 --- a/openlp/core/ui/settingsform.py +++ b/openlp/core/ui/settingsform.py @@ -151,4 +151,4 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog, RegistryProperties): :param function: The function to be called """ if not function in self.processes: - self.processes.append(function) \ No newline at end of file + self.processes.append(function) diff --git a/openlp/core/ui/shortcutlistform.py b/openlp/core/ui/shortcutlistform.py index a8e4c6ba1..4b64c3b54 100644 --- a/openlp/core/ui/shortcutlistform.py +++ b/openlp/core/ui/shortcutlistform.py @@ -285,7 +285,7 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog, RegistryProperties) if QtGui.QMessageBox.question(self, translate('OpenLP.ShortcutListDialog', 'Restore Default Shortcuts'), translate('OpenLP.ShortcutListDialog', 'Do you want to restore all ' 'shortcuts to their defaults?'), - QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes | + QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No)) == QtGui.QMessageBox.No: return self._adjust_button(self.primary_push_button, False, text='') diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index 80835b97f..eb1f13fa6 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -170,14 +170,14 @@ class SlideController(DisplayController, RegistryProperties): size_toolbar_policy.setHeightForWidth(self.toolbar.sizePolicy().hasHeightForWidth()) self.toolbar.setSizePolicy(size_toolbar_policy) self.previous_item = create_action(self, 'previousItem_' + self.type_prefix, - text=translate('OpenLP.SlideController', 'Previous Slide'), + text=translate('OpenLP.SlideController', 'Previous Slide'), icon=':/slides/slide_previous.png', tooltip=translate('OpenLP.SlideController', 'Move to previous.'), can_shortcuts=True, context=QtCore.Qt.WidgetWithChildrenShortcut, category=self.category, triggers=self.on_slide_selected_previous) self.toolbar.addAction(self.previous_item) self.next_item = create_action(self, 'nextItem_' + self.type_prefix, - text=translate('OpenLP.SlideController', 'Next Slide'), + text=translate('OpenLP.SlideController', 'Next Slide'), icon=':/slides/slide_next.png', tooltip=translate('OpenLP.SlideController', 'Move to next.'), can_shortcuts=True, context=QtCore.Qt.WidgetWithChildrenShortcut, @@ -195,17 +195,17 @@ class SlideController(DisplayController, RegistryProperties): self.hide_menu.setMenu(QtGui.QMenu(translate('OpenLP.SlideController', 'Hide'), self.toolbar)) self.toolbar.add_toolbar_widget(self.hide_menu) self.blank_screen = create_action(self, 'blankScreen', - text=translate('OpenLP.SlideController', 'Blank Screen'), + text=translate('OpenLP.SlideController', 'Blank Screen'), icon=':/slides/slide_blank.png', - checked=False, can_shortcuts=True, category=self.category, + checked=False, can_shortcuts=True, category=self.category, triggers=self.on_blank_display) self.theme_screen = create_action(self, 'themeScreen', - text=translate('OpenLP.SlideController', 'Blank to Theme'), + text=translate('OpenLP.SlideController', 'Blank to Theme'), icon=':/slides/slide_theme.png', checked=False, can_shortcuts=True, category=self.category, triggers=self.on_theme_display) self.desktop_screen = create_action(self, 'desktopScreen', - text=translate('OpenLP.SlideController', 'Show Desktop'), + text=translate('OpenLP.SlideController', 'Show Desktop'), icon=':/slides/slide_desktop.png', checked=False, can_shortcuts=True, category=self.category, triggers=self.on_hide_display) @@ -255,15 +255,16 @@ class SlideController(DisplayController, RegistryProperties): self.toolbar.add_toolbar_widget(self.delay_spin_box) else: self.toolbar.add_toolbar_action('goLive', icon=':/general/general_live.png', - tooltip=translate('OpenLP.SlideController', 'Move to live.'), + tooltip=translate('OpenLP.SlideController', 'Move to live.'), triggers=self.on_go_live) self.toolbar.add_toolbar_action('addToService', icon=':/general/general_add.png', - tooltip=translate('OpenLP.SlideController', 'Add to Service.'), + tooltip=translate('OpenLP.SlideController', 'Add to Service.'), triggers=self.on_preview_add_to_service) self.toolbar.addSeparator() self.toolbar.add_toolbar_action('editSong', icon=':/general/general_edit.png', - tooltip=translate('OpenLP.SlideController', 'Edit and reload song preview.') - , triggers=self.on_edit_song) + tooltip=translate('OpenLP.SlideController', + 'Edit and reload song preview.'), + triggers=self.on_edit_song) self.controller_layout.addWidget(self.toolbar) # Build the Media Toolbar self.media_controller.register_controller(self) @@ -355,7 +356,7 @@ class SlideController(DisplayController, RegistryProperties): {'key': 'O', 'configurable': True, 'text': translate('OpenLP.SlideController', 'Go to "Other"')} ] shortcuts.extend([{'key': str(number)} for number in range(10)]) - self.controller.addActions([create_action(self, 'shortcutAction_%s' % s['key'], + self.controller.addActions([create_action(self, 'shortcutAction_%s' % s['key'], text=s.get('text'), can_shortcuts=True, context=QtCore.Qt.WidgetWithChildrenShortcut, @@ -394,9 +395,9 @@ class SlideController(DisplayController, RegistryProperties): """ Called, when a shortcut has been activated to jump to a chorus, verse, etc. - **Note**: This implementation is based on shortcuts. But it rather works like "key sequenes". You have to + **Note**: This implementation is based on shortcuts. But it rather works like "key sequenes". You have to press one key after the other and **not** at the same time. - For example to jump to "V3" you have to press "V" and afterwards but within a time frame of 350ms + For example to jump to "V3" you have to press "V" and afterwards but within a time frame of 350ms you have to press "3". """ try: @@ -456,17 +457,17 @@ class SlideController(DisplayController, RegistryProperties): """ self.previous_service = create_action(parent, 'previousService', text=translate('OpenLP.SlideController', 'Previous Service'), - can_shortcuts=True, context=QtCore.Qt.WidgetWithChildrenShortcut, + can_shortcuts=True, context=QtCore.Qt.WidgetWithChildrenShortcut, category=self.category, triggers=self.service_previous) self.next_service = create_action(parent, 'nextService', text=translate('OpenLP.SlideController', 'Next Service'), - can_shortcuts=True, context=QtCore.Qt.WidgetWithChildrenShortcut, + can_shortcuts=True, context=QtCore.Qt.WidgetWithChildrenShortcut, category=self.category, triggers=self.service_next) self.escape_item = create_action(parent, 'escapeItem', text=translate('OpenLP.SlideController', 'Escape Item'), - can_shortcuts=True, context=QtCore.Qt.WidgetWithChildrenShortcut, + can_shortcuts=True, context=QtCore.Qt.WidgetWithChildrenShortcut, category=self.category, triggers=self.live_escape) @@ -1325,7 +1326,7 @@ class SlideController(DisplayController, RegistryProperties): """ Update how much time is remaining - :param time: the time remainings + :param time: the time remaining """ seconds = self.display.audio_player.media_object.remainingTime() // 1000 minutes = seconds // 60 diff --git a/openlp/core/ui/starttimeform.py b/openlp/core/ui/starttimeform.py index 4ee9e565c..d005d167b 100644 --- a/openlp/core/ui/starttimeform.py +++ b/openlp/core/ui/starttimeform.py @@ -74,12 +74,12 @@ class StartTimeForm(QtGui.QDialog, Ui_StartTimeDialog, RegistryProperties): self.minute_finish_spin_box.value() * 60 + self.second_finish_spin_box.value() if end > self.item['service_item'].media_length: critical_error_message_box(title=translate('OpenLP.StartTime_form', 'Time Validation Error'), - message=translate('OpenLP.StartTime_form', + message=translate('OpenLP.StartTime_form', 'Finish time is set after the end of the media item')) return elif start > end: critical_error_message_box(title=translate('OpenLP.StartTime_form', 'Time Validation Error'), - message=translate('OpenLP.StartTime_form', + message=translate('OpenLP.StartTime_form', 'Start time is after the finish time of the media item')) return self.item['service_item'].start_time = start @@ -94,4 +94,4 @@ class StartTimeForm(QtGui.QDialog, Ui_StartTimeDialog, RegistryProperties): seconds -= 3600 * hours minutes = seconds // 60 seconds -= 60 * minutes - return hours, minutes, seconds \ No newline at end of file + return hours, minutes, seconds diff --git a/openlp/core/ui/themeform.py b/openlp/core/ui/themeform.py index 7cb53e47b..fbfc1035c 100644 --- a/openlp/core/ui/themeform.py +++ b/openlp/core/ui/themeform.py @@ -180,7 +180,7 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard, RegistryProperties): self.theme.background_type == background_image and is_not_image_file(self.theme.background_filename): QtGui.QMessageBox.critical(self, translate('OpenLP.ThemeWizard', 'Background Image Empty'), translate('OpenLP.ThemeWizard', '_you have not selected a ' - 'background image. Please select one before continuing.')) + 'background image. Please select one before continuing.')) return False else: return True @@ -540,4 +540,4 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard, RegistryProperties): new_color = QtGui.QColorDialog.getColor(QtGui.QColor(field), self) if new_color.isValid(): field = new_color.name() - return field \ No newline at end of file + return field diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py index 98c2198c7..fdd2ea592 100644 --- a/openlp/core/ui/thememanager.py +++ b/openlp/core/ui/thememanager.py @@ -517,7 +517,7 @@ class ThemeManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ThemeManager, R """ ret = QtGui.QMessageBox.question(self, translate('OpenLP.ThemeManager', 'Theme Already Exists'), translate('OpenLP.ThemeManager', - 'Theme %s already exists. Do you want to replace it?') + 'Theme %s already exists. Do you want to replace it?') .replace('%s', theme_name), QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No), @@ -753,4 +753,4 @@ class ThemeManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ThemeManager, R % (theme, plugin.name)) return False return True - return False \ No newline at end of file + return False diff --git a/openlp/core/ui/themewizard.py b/openlp/core/ui/themewizard.py index 5cfc86d07..77ccb0663 100644 --- a/openlp/core/ui/themewizard.py +++ b/openlp/core/ui/themewizard.py @@ -385,10 +385,10 @@ class Ui_ThemeWizard(object): QtCore.SLOT('setDisabled(bool)')) QtCore.QObject.connect(self.footer_position_check_box, QtCore.SIGNAL('toggled(bool)'), self.footer_y_spin_box, QtCore.SLOT('setDisabled(bool)')) - QtCore.QObject.connect(self.footer_position_check_box, QtCore.SIGNAL('toggled(bool)'), self.footer_width_spin_box, - QtCore.SLOT('setDisabled(bool)')) - QtCore.QObject.connect(self.footer_position_check_box, QtCore.SIGNAL('toggled(bool)'), self.footer_height_spin_box, - QtCore.SLOT('setDisabled(bool)')) + QtCore.QObject.connect(self.footer_position_check_box, QtCore.SIGNAL('toggled(bool)'), + self.footer_width_spin_box, QtCore.SLOT('setDisabled(bool)')) + QtCore.QObject.connect(self.footer_position_check_box, QtCore.SIGNAL('toggled(bool)'), + self.footer_height_spin_box, QtCore.SLOT('setDisabled(bool)')) def retranslateUi(self, themeWizard): """ @@ -409,15 +409,18 @@ class Ui_ThemeWizard(object): self.background_combo_box.setItemText(BackgroundType.Gradient, translate('OpenLP.ThemeWizard', 'Gradient')) self.background_combo_box.setItemText(BackgroundType.Image, UiStrings().Image) - self.background_combo_box.setItemText(BackgroundType.Transparent, translate('OpenLP.ThemeWizard', 'Transparent')) + self.background_combo_box.setItemText(BackgroundType.Transparent, + translate('OpenLP.ThemeWizard', 'Transparent')) self.color_label.setText(translate('OpenLP.ThemeWizard', 'color:')) self.gradient_start_label.setText(translate('OpenLP.ThemeWizard', 'Starting color:')) self.gradient_end_label.setText(translate('OpenLP.ThemeWizard', 'Ending color:')) self.gradient_type_label.setText(translate('OpenLP.ThemeWizard', 'Gradient:')) self.gradient_combo_box.setItemText(BackgroundGradientType.Horizontal, translate('OpenLP.ThemeWizard', 'Horizontal')) - self.gradient_combo_box.setItemText(BackgroundGradientType.Vertical, translate('OpenLP.ThemeWizard', 'Vertical')) - self.gradient_combo_box.setItemText(BackgroundGradientType.Circular, translate('OpenLP.ThemeWizard', 'Circular')) + self.gradient_combo_box.setItemText(BackgroundGradientType.Vertical, + translate('OpenLP.ThemeWizard', 'Vertical')) + self.gradient_combo_box.setItemText(BackgroundGradientType.Circular, + translate('OpenLP.ThemeWizard', 'Circular')) self.gradient_combo_box.setItemText(BackgroundGradientType.LeftTop, translate('OpenLP.ThemeWizard', 'Top Left - Bottom Right')) self.gradient_combo_box.setItemText(BackgroundGradientType.LeftBottom, @@ -486,6 +489,6 @@ class Ui_ThemeWizard(object): self.preview_page.setSubTitle(translate('OpenLP.ThemeWizard', 'Preview the theme and save it.')) self.theme_name_label.setText(translate('OpenLP.ThemeWizard', 'Theme name:')) # Align all QFormLayouts towards each other. - label_width = max(self.background_label.minimumSizeHint().width(), + label_width = max(self.background_label.minimumSizeHint().width(), self.horizontal_label.minimumSizeHint().width()) self.spacer.changeSize(label_width, 0, QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) diff --git a/openlp/core/ui/wizard.py b/openlp/core/ui/wizard.py index 4d9b4ebdf..05951d14a 100644 --- a/openlp/core/ui/wizard.py +++ b/openlp/core/ui/wizard.py @@ -303,4 +303,4 @@ class OpenLPWizard(QtGui.QWizard, RegistryProperties): QtGui.QFileDialog.ShowDirsOnly) if folder: editbox.setText(folder) - Settings().setValue(self.plugin.settings_section + '/' + setting_name, folder) \ No newline at end of file + Settings().setValue(self.plugin.settings_section + '/' + setting_name, folder) diff --git a/openlp/core/utils/__init__.py b/openlp/core/utils/__init__.py index d9e060aed..746c50d71 100644 --- a/openlp/core/utils/__init__.py +++ b/openlp/core/utils/__init__.py @@ -72,13 +72,18 @@ USER_AGENTS = { 'Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.71 Safari/537.36' ], 'darwin': [ - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_3) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.43 Safari/537.31', - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11', - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.47 Safari/536.11', + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_3) AppleWebKit/537.31 (KHTML, like Gecko) ' + 'Chrome/26.0.1410.43 Safari/537.31', + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/536.11 (KHTML, like Gecko) ' + 'Chrome/20.0.1132.57 Safari/536.11', + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/536.11 (KHTML, like Gecko) ' + 'Chrome/20.0.1132.47 Safari/536.11', ], 'linux2': [ - 'Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.22 (KHTML, like Gecko) Ubuntu Chromium/25.0.1364.160 Chrome/25.0.1364.160 Safari/537.22', - 'Mozilla/5.0 (X11; CrOS armv7l 2913.260.0) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.99 Safari/537.11', + 'Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.22 (KHTML, like Gecko) Ubuntu Chromium/25.0.1364.160 ' + 'Chrome/25.0.1364.160 Safari/537.22', + 'Mozilla/5.0 (X11; CrOS armv7l 2913.260.0) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.99 ' + 'Safari/537.11', 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.27 (KHTML, like Gecko) Chrome/26.0.1389.0 Safari/537.27' ], 'default': [ @@ -144,9 +149,9 @@ def get_application_version(): # If they are equal, then this tree is tarball with the source for the release. We do not want the revision # number in the full version. if tree_revision == tag_revision: - full_version = tag_version + full_version = tag_version else: - full_version = '%s-bzr%s' % (tag_version, tree_revision) + full_version = '%s-bzr%s' % (tag_version, tree_revision) else: # We're not running the development version, let's use the file. filepath = AppLocation.get_directory(AppLocation.VersionDir) diff --git a/openlp/core/utils/actions.py b/openlp/core/utils/actions.py index 6201dff52..29f2d279b 100644 --- a/openlp/core/utils/actions.py +++ b/openlp/core/utils/actions.py @@ -295,7 +295,7 @@ class ActionList(object): ActionList.shortcut_map[shortcuts[1]] = actions else: log.warn('Shortcut "%s" is removed from "%s" because another action already uses this shortcut.' % - (shortcuts[1], action.objectName())) + (shortcuts[1], action.objectName())) shortcuts.remove(shortcuts[1]) # Check the primary shortcut. existing_actions = ActionList.shortcut_map.get(shortcuts[0], []) @@ -306,7 +306,7 @@ class ActionList(object): ActionList.shortcut_map[shortcuts[0]] = actions else: log.warn('Shortcut "%s" is removed from "%s" because another action already uses this shortcut.' % - (shortcuts[0], action.objectName())) + (shortcuts[0], action.objectName())) shortcuts.remove(shortcuts[0]) action.setShortcuts([QtGui.QKeySequence(shortcut) for shortcut in shortcuts]) diff --git a/openlp/core/utils/languagemanager.py b/openlp/core/utils/languagemanager.py index 3e240778b..cd5ce7add 100644 --- a/openlp/core/utils/languagemanager.py +++ b/openlp/core/utils/languagemanager.py @@ -126,8 +126,9 @@ class LanguageManager(object): log.info('Language file: \'%s\' written to conf file' % language) if message: QtGui.QMessageBox.information(None, - translate('OpenLP.LanguageManager', 'Language'), - translate('OpenLP.LanguageManager', 'Please restart OpenLP to use your new language setting.')) + translate('OpenLP.LanguageManager', 'Language'), + translate('OpenLP.LanguageManager', 'Please restart OpenLP to use your new ' + 'language setting.')) @staticmethod def init_qm_list(): From 9dfc446ebd4bf54fa1280f6244fb45816478d312 Mon Sep 17 00:00:00 2001 From: Tim Bentley <tim.bentley@gmail.com> Date: Fri, 21 Mar 2014 18:23:35 +0000 Subject: [PATCH 029/155] More cleanups --- openlp/plugins/alerts/alertsplugin.py | 2 +- openlp/plugins/alerts/forms/alertform.py | 2 +- openlp/plugins/alerts/lib/alertsmanager.py | 2 +- openlp/plugins/bibles/bibleplugin.py | 4 +- .../plugins/bibles/forms/bibleimportform.py | 590 +++++++++--------- .../plugins/bibles/forms/bibleupgradeform.py | 156 ++--- openlp/plugins/bibles/forms/booknamedialog.py | 23 +- openlp/plugins/bibles/forms/booknameform.py | 13 +- .../plugins/bibles/forms/editbibledialog.py | 24 +- openlp/plugins/bibles/forms/editbibleform.py | 2 +- openlp/plugins/bibles/forms/languagedialog.py | 18 +- openlp/plugins/bibles/forms/languageform.py | 3 +- openlp/plugins/bibles/lib/__init__.py | 7 +- openlp/plugins/bibles/lib/biblestab.py | 3 +- openlp/plugins/bibles/lib/db.py | 27 +- openlp/plugins/bibles/lib/http.py | 6 +- openlp/plugins/bibles/lib/mediaitem.py | 6 +- openlp/plugins/images/forms/addgroupform.py | 3 +- openlp/plugins/images/lib/mediaitem.py | 6 +- openlp/plugins/media/lib/mediaitem.py | 8 +- .../songusage/forms/songusagedeleteform.py | 2 +- .../songusage/forms/songusagedetailform.py | 17 +- openlp/plugins/songusage/lib/db.py | 2 +- 23 files changed, 472 insertions(+), 454 deletions(-) diff --git a/openlp/plugins/alerts/alertsplugin.py b/openlp/plugins/alerts/alertsplugin.py index e9a24980e..cf3b7d0ce 100644 --- a/openlp/plugins/alerts/alertsplugin.py +++ b/openlp/plugins/alerts/alertsplugin.py @@ -246,4 +246,4 @@ class AlertsPlugin(Plugin): align = VerticalType.Names[self.settings_tab.location] frame.evaluateJavaScript('update_css("%s", "%s", "%s", "%s", "%s")' % (align, self.settings_tab.font_face, self.settings_tab.font_size, - self.settings_tab.font_color, self.settings_tab.background_color)) + self.settings_tab.font_color, self.settings_tab.background_color)) diff --git a/openlp/plugins/alerts/forms/alertform.py b/openlp/plugins/alerts/forms/alertform.py index f5e5ee31a..f988bec0f 100644 --- a/openlp/plugins/alerts/forms/alertform.py +++ b/openlp/plugins/alerts/forms/alertform.py @@ -46,7 +46,7 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog): self.manager = plugin.manager self.plugin = plugin self.item_id = None - super(AlertForm, self).__init__( Registry().get('main_window')) + super(AlertForm, self).__init__(Registry().get('main_window')) self.setupUi(self) self.display_button.clicked.connect(self.on_display_clicked) self.display_close_button.clicked.connect(self.on_display_close_clicked) diff --git a/openlp/plugins/alerts/lib/alertsmanager.py b/openlp/plugins/alerts/lib/alertsmanager.py index 675b5874e..061936446 100644 --- a/openlp/plugins/alerts/lib/alertsmanager.py +++ b/openlp/plugins/alerts/lib/alertsmanager.py @@ -97,4 +97,4 @@ class AlertsManager(OpenLPMixin, RegistryMixin, QtCore.QObject, RegistryProperti self.live_controller.display.alert('', alert_tab.location) self.killTimer(self.timer_id) self.timer_id = 0 - self.generate_alert() \ No newline at end of file + self.generate_alert() diff --git a/openlp/plugins/bibles/bibleplugin.py b/openlp/plugins/bibles/bibleplugin.py index fc7d76130..5a51be163 100644 --- a/openlp/plugins/bibles/bibleplugin.py +++ b/openlp/plugins/bibles/bibleplugin.py @@ -113,8 +113,8 @@ class BiblePlugin(Plugin): """ super(BiblePlugin, self).app_startup() if self.manager.old_bible_databases: - if QtGui.QMessageBox.information(self.main_window, - translate('OpenLP', 'Information'), + if QtGui.QMessageBox.information( + self.main_window, translate('OpenLP', 'Information'), translate('OpenLP', 'Bible format has changed.\nYou have to upgrade your existing Bibles.\n' 'Should OpenLP upgrade now?'), QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No)) == \ diff --git a/openlp/plugins/bibles/forms/bibleimportform.py b/openlp/plugins/bibles/forms/bibleimportform.py index 8c7b62d10..ee5bee2d0 100644 --- a/openlp/plugins/bibles/forms/bibleimportform.py +++ b/openlp/plugins/bibles/forms/bibleimportform.py @@ -44,6 +44,7 @@ from openlp.plugins.bibles.lib.db import BiblesResourcesDB, clean_filename log = logging.getLogger(__name__) + class WebDownload(object): """ Provides an enumeration for the web bible types available to OpenLP. @@ -81,258 +82,257 @@ class BibleImportForm(OpenLPWizard): Set up the UI for the bible wizard. """ super(BibleImportForm, self).setupUi(image) - self.formatComboBox.currentIndexChanged.connect(self.onCurrentIndexChanged) + self.format_combo_box.currentIndexChanged.connect(self.on_current_index_changed) - def onCurrentIndexChanged(self, index): + def on_current_index_changed(self, index): """ Called when the format combo box's index changed. We have to check if the import is available and accordingly to disable or enable the next button. """ - self.selectStack.setCurrentIndex(index) + self.select_stack.setCurrentIndex(index) def custom_init(self): """ Perform any custom initialisation for bible importing. """ self.manager.set_process_dialog(self) - self.loadWebBibles() + self.load_Web_Bibles() self.restart() - self.selectStack.setCurrentIndex(0) + self.select_stack.setCurrentIndex(0) def custom_signals(self): """ Set up the signals used in the bible importer. """ - self.webSourceComboBox.currentIndexChanged.connect(self.onWebSourceComboBoxIndexChanged) - self.osisBrowseButton.clicked.connect(self.onOsisBrowseButtonClicked) - self.csvBooksButton.clicked.connect(self.onCsvBooksBrowseButtonClicked) - self.csvVersesButton.clicked.connect(self.onCsvVersesBrowseButtonClicked) - self.openSongBrowseButton.clicked.connect(self.onOpenSongBrowseButtonClicked) + self.web_source_combo_box.currentIndexChanged.connect(self.on_web_source_combo_box_index_changed) + self.osis_browse_button.clicked.connect(self.on_osis_browse_button_clicked) + self.csv_books_button.clicked.connect(self.on_csv_books_browse_button_clicked) + self.csv_verses_button.clicked.connect(self.on_csv_verses_browse_button_clicked) + self.open_song_browse_button.clicked.connect(self.on_open_song_browse_button_clicked) def add_custom_pages(self): """ Add the bible import specific wizard pages. """ # Select Page - self.selectPage = QtGui.QWizardPage() - self.selectPage.setObjectName('SelectPage') - self.selectPageLayout = QtGui.QVBoxLayout(self.selectPage) - self.selectPageLayout.setObjectName('SelectPageLayout') - self.formatLayout = QtGui.QFormLayout() - self.formatLayout.setObjectName('FormatLayout') - self.formatLabel = QtGui.QLabel(self.selectPage) - self.formatLabel.setObjectName('FormatLabel') - self.formatComboBox = QtGui.QComboBox(self.selectPage) - self.formatComboBox.addItems(['', '', '', '']) - self.formatComboBox.setObjectName('FormatComboBox') - self.formatLayout.addRow(self.formatLabel, self.formatComboBox) + self.select_page = QtGui.QWizardPage() + self.select_page.setObjectName('SelectPage') + self.select_page_layout = QtGui.QVBoxLayout(self.select_page) + self.select_page_layout.setObjectName('SelectPageLayout') + self.format_layout = QtGui.QFormLayout() + self.format_layout.setObjectName('FormatLayout') + self.format_label = QtGui.QLabel(self.select_page) + self.format_label.setObjectName('FormatLabel') + self.format_combo_box = QtGui.QComboBox(self.select_page) + self.format_combo_box.addItems(['', '', '', '']) + self.format_combo_box.setObjectName('FormatComboBox') + self.format_layout.addRow(self.format_label, self.format_combo_box) self.spacer = QtGui.QSpacerItem(10, 0, QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Minimum) - self.formatLayout.setItem(1, QtGui.QFormLayout.LabelRole, self.spacer) - self.selectPageLayout.addLayout(self.formatLayout) - self.selectStack = QtGui.QStackedLayout() - self.selectStack.setObjectName('SelectStack') - self.osisWidget = QtGui.QWidget(self.selectPage) - self.osisWidget.setObjectName('OsisWidget') - self.osisLayout = QtGui.QFormLayout(self.osisWidget) - self.osisLayout.setMargin(0) - self.osisLayout.setObjectName('OsisLayout') - self.osisFileLabel = QtGui.QLabel(self.osisWidget) - self.osisFileLabel.setObjectName('OsisFileLabel') - self.osisFileLayout = QtGui.QHBoxLayout() - self.osisFileLayout.setObjectName('OsisFileLayout') - self.osisFileEdit = QtGui.QLineEdit(self.osisWidget) - self.osisFileEdit.setObjectName('OsisFileEdit') - self.osisFileLayout.addWidget(self.osisFileEdit) - self.osisBrowseButton = QtGui.QToolButton(self.osisWidget) - self.osisBrowseButton.setIcon(self.open_icon) - self.osisBrowseButton.setObjectName('OsisBrowseButton') - self.osisFileLayout.addWidget(self.osisBrowseButton) - self.osisLayout.addRow(self.osisFileLabel, self.osisFileLayout) - self.osisLayout.setItem(1, QtGui.QFormLayout.LabelRole, self.spacer) - self.selectStack.addWidget(self.osisWidget) - self.csvWidget = QtGui.QWidget(self.selectPage) - self.csvWidget.setObjectName('CsvWidget') - self.csvLayout = QtGui.QFormLayout(self.csvWidget) - self.csvLayout.setMargin(0) - self.csvLayout.setObjectName('CsvLayout') - self.csvBooksLabel = QtGui.QLabel(self.csvWidget) - self.csvBooksLabel.setObjectName('CsvBooksLabel') - self.csvBooksLayout = QtGui.QHBoxLayout() - self.csvBooksLayout.setObjectName('CsvBooksLayout') - self.csvBooksEdit = QtGui.QLineEdit(self.csvWidget) - self.csvBooksEdit.setObjectName('CsvBooksEdit') - self.csvBooksLayout.addWidget(self.csvBooksEdit) - self.csvBooksButton = QtGui.QToolButton(self.csvWidget) - self.csvBooksButton.setIcon(self.open_icon) - self.csvBooksButton.setObjectName('CsvBooksButton') - self.csvBooksLayout.addWidget(self.csvBooksButton) - self.csvLayout.addRow(self.csvBooksLabel, self.csvBooksLayout) - self.csvVersesLabel = QtGui.QLabel(self.csvWidget) - self.csvVersesLabel.setObjectName('CsvVersesLabel') - self.csvVersesLayout = QtGui.QHBoxLayout() - self.csvVersesLayout.setObjectName('CsvVersesLayout') - self.csvVersesEdit = QtGui.QLineEdit(self.csvWidget) - self.csvVersesEdit.setObjectName('CsvVersesEdit') - self.csvVersesLayout.addWidget(self.csvVersesEdit) - self.csvVersesButton = QtGui.QToolButton(self.csvWidget) - self.csvVersesButton.setIcon(self.open_icon) - self.csvVersesButton.setObjectName('CsvVersesButton') - self.csvVersesLayout.addWidget(self.csvVersesButton) - self.csvLayout.addRow(self.csvVersesLabel, self.csvVersesLayout) - self.csvLayout.setItem(3, QtGui.QFormLayout.LabelRole, self.spacer) - self.selectStack.addWidget(self.csvWidget) - self.openSongWidget = QtGui.QWidget(self.selectPage) - self.openSongWidget.setObjectName('OpenSongWidget') - self.openSongLayout = QtGui.QFormLayout(self.openSongWidget) - self.openSongLayout.setMargin(0) - self.openSongLayout.setObjectName('OpenSongLayout') - self.openSongFileLabel = QtGui.QLabel(self.openSongWidget) - self.openSongFileLabel.setObjectName('OpenSongFileLabel') - self.openSongFileLayout = QtGui.QHBoxLayout() - self.openSongFileLayout.setObjectName('OpenSongFileLayout') - self.openSongFileEdit = QtGui.QLineEdit(self.openSongWidget) - self.openSongFileEdit.setObjectName('OpenSongFileEdit') - self.openSongFileLayout.addWidget(self.openSongFileEdit) - self.openSongBrowseButton = QtGui.QToolButton(self.openSongWidget) - self.openSongBrowseButton.setIcon(self.open_icon) - self.openSongBrowseButton.setObjectName('OpenSongBrowseButton') - self.openSongFileLayout.addWidget(self.openSongBrowseButton) - self.openSongLayout.addRow(self.openSongFileLabel, self.openSongFileLayout) - self.openSongLayout.setItem(1, QtGui.QFormLayout.LabelRole, self.spacer) - self.selectStack.addWidget(self.openSongWidget) - self.webTabWidget = QtGui.QTabWidget(self.selectPage) - self.webTabWidget.setObjectName('WebTabWidget') - self.webBibleTab = QtGui.QWidget() - self.webBibleTab.setObjectName('WebBibleTab') - self.webBibleLayout = QtGui.QFormLayout(self.webBibleTab) - self.webBibleLayout.setObjectName('WebBibleLayout') - self.webSourceLabel = QtGui.QLabel(self.webBibleTab) - self.webSourceLabel.setObjectName('WebSourceLabel') - self.webBibleLayout.setWidget(0, QtGui.QFormLayout.LabelRole, self.webSourceLabel) - self.webSourceComboBox = QtGui.QComboBox(self.webBibleTab) - self.webSourceComboBox.setObjectName('WebSourceComboBox') - self.webSourceComboBox.addItems(['', '', '']) - self.webBibleLayout.setWidget(0, QtGui.QFormLayout.FieldRole, self.webSourceComboBox) - self.webTranslationLabel = QtGui.QLabel(self.webBibleTab) - self.webTranslationLabel.setObjectName('webTranslationLabel') - self.webBibleLayout.setWidget(1, QtGui.QFormLayout.LabelRole, self.webTranslationLabel) - self.webTranslationComboBox = QtGui.QComboBox(self.webBibleTab) - self.webTranslationComboBox.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToContents) - self.webTranslationComboBox.setObjectName('WebTranslationComboBox') - self.webBibleLayout.setWidget(1, QtGui.QFormLayout.FieldRole, self.webTranslationComboBox) - self.webTabWidget.addTab(self.webBibleTab, '') - self.webProxyTab = QtGui.QWidget() - self.webProxyTab.setObjectName('WebProxyTab') - self.webProxyLayout = QtGui.QFormLayout(self.webProxyTab) - self.webProxyLayout.setObjectName('WebProxyLayout') - self.webServerLabel = QtGui.QLabel(self.webProxyTab) - self.webServerLabel.setObjectName('WebServerLabel') - self.webProxyLayout.setWidget(0, QtGui.QFormLayout.LabelRole, self.webServerLabel) - self.webServerEdit = QtGui.QLineEdit(self.webProxyTab) - self.webServerEdit.setObjectName('WebServerEdit') - self.webProxyLayout.setWidget(0, QtGui.QFormLayout.FieldRole, self.webServerEdit) - self.webUserLabel = QtGui.QLabel(self.webProxyTab) - self.webUserLabel.setObjectName('WebUserLabel') - self.webProxyLayout.setWidget(1, QtGui.QFormLayout.LabelRole, self.webUserLabel) - self.webUserEdit = QtGui.QLineEdit(self.webProxyTab) - self.webUserEdit.setObjectName('WebUserEdit') - self.webProxyLayout.setWidget(1, QtGui.QFormLayout.FieldRole, self.webUserEdit) - self.webPasswordLabel = QtGui.QLabel(self.webProxyTab) - self.webPasswordLabel.setObjectName('WebPasswordLabel') - self.webProxyLayout.setWidget(2, QtGui.QFormLayout.LabelRole, self.webPasswordLabel) - self.webPasswordEdit = QtGui.QLineEdit(self.webProxyTab) - self.webPasswordEdit.setObjectName('WebPasswordEdit') - self.webProxyLayout.setWidget(2, QtGui.QFormLayout.FieldRole, self.webPasswordEdit) - self.webTabWidget.addTab(self.webProxyTab, '') - self.selectStack.addWidget(self.webTabWidget) - self.selectPageLayout.addLayout(self.selectStack) - self.addPage(self.selectPage) + self.format_layout.setItem(1, QtGui.QFormLayout.LabelRole, self.spacer) + self.select_page_layout.addLayout(self.format_layout) + self.select_stack = QtGui.QStackedLayout() + self.select_stack.setObjectName('SelectStack') + self.osis_widget = QtGui.QWidget(self.select_page) + self.osis_widget.setObjectName('OsisWidget') + self.osis_layout = QtGui.QFormLayout(self.osis_widget) + self.osis_layout.setMargin(0) + self.osis_layout.setObjectName('OsisLayout') + self.osis_file_label = QtGui.QLabel(self.osis_widget) + self.osis_file_label.setObjectName('OsisFileLabel') + self.osis_file_layout = QtGui.QHBoxLayout() + self.osis_file_layout.setObjectName('OsisFileLayout') + self.osis_file_edit = QtGui.QLineEdit(self.osis_widget) + self.osis_file_edit.setObjectName('OsisFileEdit') + self.osis_file_layout.addWidget(self.osis_file_edit) + self.osis_browse_button = QtGui.QToolButton(self.osis_widget) + self.osis_browse_button.setIcon(self.open_icon) + self.osis_browse_button.setObjectName('OsisBrowseButton') + self.osis_file_layout.addWidget(self.osis_browse_button) + self.osis_layout.addRow(self.osis_file_label, self.osis_file_layout) + self.osis_layout.setItem(1, QtGui.QFormLayout.LabelRole, self.spacer) + self.select_stack.addWidget(self.osis_widget) + self.csv_widget = QtGui.QWidget(self.select_page) + self.csv_widget.setObjectName('CsvWidget') + self.csv_layout = QtGui.QFormLayout(self.csv_widget) + self.csv_layout.setMargin(0) + self.csv_layout.setObjectName('CsvLayout') + self.csv_books_label = QtGui.QLabel(self.csv_widget) + self.csv_books_label.setObjectName('CsvBooksLabel') + self.csv_books_layout = QtGui.QHBoxLayout() + self.csv_books_layout.setObjectName('CsvBooksLayout') + self.csv_books_edit = QtGui.QLineEdit(self.csv_widget) + self.csv_books_edit.setObjectName('CsvBooksEdit') + self.csv_books_layout.addWidget(self.csv_books_edit) + self.csv_books_button = QtGui.QToolButton(self.csv_widget) + self.csv_books_button.setIcon(self.open_icon) + self.csv_books_button.setObjectName('CsvBooksButton') + self.csv_books_layout.addWidget(self.csv_books_button) + self.csv_layout.addRow(self.csv_books_label, self.csv_books_layout) + self.csv_verses_label = QtGui.QLabel(self.csv_widget) + self.csv_verses_label.setObjectName('CsvVersesLabel') + self.csv_verses_layout = QtGui.QHBoxLayout() + self.csv_verses_layout.setObjectName('CsvVersesLayout') + self.csv_verses_edit = QtGui.QLineEdit(self.csv_widget) + self.csv_verses_edit.setObjectName('CsvVersesEdit') + self.csv_verses_layout.addWidget(self.csv_verses_edit) + self.csv_verses_button = QtGui.QToolButton(self.csv_widget) + self.csv_verses_button.setIcon(self.open_icon) + self.csv_verses_button.setObjectName('CsvVersesButton') + self.csv_verses_layout.addWidget(self.csv_verses_button) + self.csv_layout.addRow(self.csv_verses_label, self.csv_verses_layout) + self.csv_layout.setItem(3, QtGui.QFormLayout.LabelRole, self.spacer) + self.select_stack.addWidget(self.csv_widget) + self.open_song_widget = QtGui.QWidget(self.select_page) + self.open_song_widget.setObjectName('OpenSongWidget') + self.open_song_layout = QtGui.QFormLayout(self.open_song_widget) + self.open_song_layout.setMargin(0) + self.open_song_layout.setObjectName('OpenSongLayout') + self.open_song_file_label = QtGui.QLabel(self.open_song_widget) + self.open_song_file_label.setObjectName('OpenSongFileLabel') + self.open_song_file_layout = QtGui.QHBoxLayout() + self.open_song_file_layout.setObjectName('OpenSongFileLayout') + self.open_song_file_edit = QtGui.QLineEdit(self.open_song_widget) + self.open_song_file_edit.setObjectName('OpenSongFileEdit') + self.open_song_file_layout.addWidget(self.open_song_file_edit) + self.open_song_browse_button = QtGui.QToolButton(self.open_song_widget) + self.open_song_browse_button.setIcon(self.open_icon) + self.open_song_browse_button.setObjectName('OpenSongBrowseButton') + self.open_song_file_layout.addWidget(self.open_song_browse_button) + self.open_song_layout.addRow(self.open_song_file_label, self.open_song_file_layout) + self.open_song_layout.setItem(1, QtGui.QFormLayout.LabelRole, self.spacer) + self.select_stack.addWidget(self.open_song_widget) + self.web_tab_widget = QtGui.QTabWidget(self.select_page) + self.web_tab_widget.setObjectName('WebTabWidget') + self.web_bible_tab = QtGui.QWidget() + self.web_bible_tab.setObjectName('WebBibleTab') + self.web_bible_layout = QtGui.QFormLayout(self.web_bible_tab) + self.web_bible_layout.setObjectName('WebBibleLayout') + self.web_source_label = QtGui.QLabel(self.web_bible_tab) + self.web_source_label.setObjectName('WebSourceLabel') + self.web_bible_layout.setWidget(0, QtGui.QFormLayout.LabelRole, self.web_source_label) + self.web_source_combo_box = QtGui.QComboBox(self.web_bible_tab) + self.web_source_combo_box.setObjectName('WebSourceComboBox') + self.web_source_combo_box.addItems(['', '', '']) + self.web_bible_layout.setWidget(0, QtGui.QFormLayout.FieldRole, self.web_source_combo_box) + self.web_translation_label = QtGui.QLabel(self.web_bible_tab) + self.web_translation_label.setObjectName('web_translation_label') + self.web_bible_layout.setWidget(1, QtGui.QFormLayout.LabelRole, self.web_translation_label) + self.web_translation_combo_box = QtGui.QComboBox(self.web_bible_tab) + self.web_translation_combo_box.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToContents) + self.web_translation_combo_box.setObjectName('WebTranslationComboBox') + self.web_bible_layout.setWidget(1, QtGui.QFormLayout.FieldRole, self.web_translation_combo_box) + self.web_tab_widget.addTab(self.web_bible_tab, '') + self.web_proxy_tab = QtGui.QWidget() + self.web_proxy_tab.setObjectName('WebProxyTab') + self.web_proxy_layout = QtGui.QFormLayout(self.web_proxy_tab) + self.web_proxy_layout.setObjectName('WebProxyLayout') + self.web_server_label = QtGui.QLabel(self.web_proxy_tab) + self.web_server_label.setObjectName('WebServerLabel') + self.web_proxy_layout.setWidget(0, QtGui.QFormLayout.LabelRole, self.web_server_label) + self.web_server_edit = QtGui.QLineEdit(self.web_proxy_tab) + self.web_server_edit.setObjectName('WebServerEdit') + self.web_proxy_layout.setWidget(0, QtGui.QFormLayout.FieldRole, self.web_server_edit) + self.web_user_label = QtGui.QLabel(self.web_proxy_tab) + self.web_user_label.setObjectName('WebUserLabel') + self.web_proxy_layout.setWidget(1, QtGui.QFormLayout.LabelRole, self.web_user_label) + self.web_user_edit = QtGui.QLineEdit(self.web_proxy_tab) + self.web_user_edit.setObjectName('WebUserEdit') + self.web_proxy_layout.setWidget(1, QtGui.QFormLayout.FieldRole, self.web_user_edit) + self.web_password_label = QtGui.QLabel(self.web_proxy_tab) + self.web_password_label.setObjectName('WebPasswordLabel') + self.web_proxy_layout.setWidget(2, QtGui.QFormLayout.LabelRole, self.web_password_label) + self.web_password_edit = QtGui.QLineEdit(self.web_proxy_tab) + self.web_password_edit.setObjectName('WebPasswordEdit') + self.web_proxy_layout.setWidget(2, QtGui.QFormLayout.FieldRole, self.web_password_edit) + self.web_tab_widget.addTab(self.web_proxy_tab, '') + self.select_stack.addWidget(self.web_tab_widget) + self.select_page_layout.addLayout(self.select_stack) + self.addPage(self.select_page) # License Page - self.licenseDetailsPage = QtGui.QWizardPage() - self.licenseDetailsPage.setObjectName('LicenseDetailsPage') - self.licenseDetailsLayout = QtGui.QFormLayout(self.licenseDetailsPage) - self.licenseDetailsLayout.setObjectName('LicenseDetailsLayout') - self.versionNameLabel = QtGui.QLabel(self.licenseDetailsPage) - self.versionNameLabel.setObjectName('VersionNameLabel') - self.licenseDetailsLayout.setWidget(0, QtGui.QFormLayout.LabelRole, self.versionNameLabel) - self.versionNameEdit = QtGui.QLineEdit(self.licenseDetailsPage) - self.versionNameEdit.setObjectName('VersionNameEdit') - self.licenseDetailsLayout.setWidget(0, QtGui.QFormLayout.FieldRole, self.versionNameEdit) - self.copyrightLabel = QtGui.QLabel(self.licenseDetailsPage) - self.copyrightLabel.setObjectName('CopyrightLabel') - self.licenseDetailsLayout.setWidget(1, QtGui.QFormLayout.LabelRole, self.copyrightLabel) - self.copyrightEdit = QtGui.QLineEdit(self.licenseDetailsPage) - self.copyrightEdit.setObjectName('CopyrightEdit') - self.licenseDetailsLayout.setWidget(1, QtGui.QFormLayout.FieldRole, self.copyrightEdit) - self.permissionsLabel = QtGui.QLabel(self.licenseDetailsPage) - self.permissionsLabel.setObjectName('PermissionsLabel') - self.licenseDetailsLayout.setWidget(2, QtGui.QFormLayout.LabelRole, - self.permissionsLabel) - self.permissionsEdit = QtGui.QLineEdit(self.licenseDetailsPage) - self.permissionsEdit.setObjectName('PermissionsEdit') - self.licenseDetailsLayout.setWidget(2, QtGui.QFormLayout.FieldRole, self.permissionsEdit) - self.addPage(self.licenseDetailsPage) + self.license_details_page = QtGui.QWizardPage() + self.license_details_page.setObjectName('LicenseDetailsPage') + self.license_details_layout = QtGui.QFormLayout(self.license_details_page) + self.license_details_layout.setObjectName('LicenseDetailsLayout') + self.version_name_label = QtGui.QLabel(self.license_details_page) + self.version_name_label.setObjectName('VersionNameLabel') + self.license_details_layout.setWidget(0, QtGui.QFormLayout.LabelRole, self.version_name_label) + self.version_name_edit = QtGui.QLineEdit(self.license_details_page) + self.version_name_edit.setObjectName('VersionNameEdit') + self.license_details_layout.setWidget(0, QtGui.QFormLayout.FieldRole, self.version_name_edit) + self.copyright_label = QtGui.QLabel(self.license_details_page) + self.copyright_label.setObjectName('CopyrightLabel') + self.license_details_layout.setWidget(1, QtGui.QFormLayout.LabelRole, self.copyright_label) + self.copyright_edit = QtGui.QLineEdit(self.license_details_page) + self.copyright_edit.setObjectName('CopyrightEdit') + self.license_details_layout.setWidget(1, QtGui.QFormLayout.FieldRole, self.copyright_edit) + self.permissions_label = QtGui.QLabel(self.license_details_page) + self.permissions_label.setObjectName('PermissionsLabel') + self.license_details_layout.setWidget(2, QtGui.QFormLayout.LabelRole, self.permissions_label) + self.permissions_edit = QtGui.QLineEdit(self.license_details_page) + self.permissions_edit.setObjectName('PermissionsEdit') + self.license_details_layout.setWidget(2, QtGui.QFormLayout.FieldRole, self.permissions_edit) + self.addPage(self.license_details_page) def retranslateUi(self): """ Allow for localisation of the bible import wizard. """ self.setWindowTitle(translate('BiblesPlugin.ImportWizardForm', 'Bible Import Wizard')) - self.title_label.setText(WizardStrings.HeaderStyle % - translate('OpenLP.Ui', 'Welcome to the Bible Import Wizard')) + self.title_label.setText(WizardStrings.HeaderStyle % translate('OpenLP.Ui', + 'Welcome to the Bible Import Wizard')) self.information_label.setText( translate('BiblesPlugin.ImportWizardForm', - 'This wizard will help you to import Bibles from a variety of ' - 'formats. Click the next button below to start the process by ' - 'selecting a format to import from.')) - self.selectPage.setTitle(WizardStrings.ImportSelect) - self.selectPage.setSubTitle(WizardStrings.ImportSelectLong) - self.formatLabel.setText(WizardStrings.FormatLabel) - self.formatComboBox.setItemText(BibleFormat.OSIS, WizardStrings.OSIS) - self.formatComboBox.setItemText(BibleFormat.CSV, WizardStrings.CSV) - self.formatComboBox.setItemText(BibleFormat.OpenSong, WizardStrings.OS) - self.formatComboBox.setItemText(BibleFormat.WebDownload, - translate('BiblesPlugin.ImportWizardForm', 'Web Download')) - self.osisFileLabel.setText(translate('BiblesPlugin.ImportWizardForm', 'Bible file:')) - self.csvBooksLabel.setText(translate('BiblesPlugin.ImportWizardForm', 'Books file:')) - self.csvVersesLabel.setText(translate('BiblesPlugin.ImportWizardForm', 'Verses file:')) - self.openSongFileLabel.setText(translate('BiblesPlugin.ImportWizardForm', 'Bible file:')) - self.webSourceLabel.setText(translate('BiblesPlugin.ImportWizardForm', 'Location:')) - self.webSourceComboBox.setItemText(WebDownload.Crosswalk, - translate('BiblesPlugin.ImportWizardForm', 'Crosswalk')) - self.webSourceComboBox.setItemText(WebDownload.BibleGateway, - translate('BiblesPlugin.ImportWizardForm', 'BibleGateway')) - self.webSourceComboBox.setItemText(WebDownload.Bibleserver, - translate('BiblesPlugin.ImportWizardForm', 'Bibleserver')) - self.webTranslationLabel.setText(translate('BiblesPlugin.ImportWizardForm', 'Bible:')) - self.webTabWidget.setTabText(self.webTabWidget.indexOf(self.webBibleTab), - translate('BiblesPlugin.ImportWizardForm', 'Download Options')) - self.webServerLabel.setText(translate('BiblesPlugin.ImportWizardForm', 'Server:')) - self.webUserLabel.setText(translate('BiblesPlugin.ImportWizardForm', 'Username:')) - self.webPasswordLabel.setText(translate('BiblesPlugin.ImportWizardForm', 'Password:')) - self.webTabWidget.setTabText(self.webTabWidget.indexOf(self.webProxyTab), - translate('BiblesPlugin.ImportWizardForm', - 'Proxy Server (Optional)')) - self.licenseDetailsPage.setTitle( + 'This wizard will help you to import Bibles from a variety of ' + 'formats. Click the next button below to start the process by ' + 'selecting a format to import from.')) + self.select_page.setTitle(WizardStrings.ImportSelect) + self.select_page.setSubTitle(WizardStrings.ImportSelectLong) + self.format_label.setText(WizardStrings.FormatLabel) + self.format_combo_box.setItemText(BibleFormat.OSIS, WizardStrings.OSIS) + self.format_combo_box.setItemText(BibleFormat.CSV, WizardStrings.CSV) + self.format_combo_box.setItemText(BibleFormat.OpenSong, WizardStrings.OS) + self.format_combo_box.setItemText(BibleFormat.WebDownload, translate('BiblesPlugin.ImportWizardForm', + 'Web Download')) + self.osis_file_label.setText(translate('BiblesPlugin.ImportWizardForm', 'Bible file:')) + self.csv_books_label.setText(translate('BiblesPlugin.ImportWizardForm', 'Books file:')) + self.csv_verses_label.setText(translate('BiblesPlugin.ImportWizardForm', 'Verses file:')) + self.open_song_file_label.setText(translate('BiblesPlugin.ImportWizardForm', 'Bible file:')) + self.web_source_label.setText(translate('BiblesPlugin.ImportWizardForm', 'Location:')) + self.web_source_combo_box.setItemText(WebDownload.Crosswalk, translate('BiblesPlugin.ImportWizardForm', + 'Crosswalk')) + self.web_source_combo_box.setItemText(WebDownload.BibleGateway, translate('BiblesPlugin.ImportWizardForm', + 'BibleGateway')) + self.web_source_combo_box.setItemText(WebDownload.Bibleserver, translate('BiblesPlugin.ImportWizardForm', + 'Bibleserver')) + self.web_translation_label.setText(translate('BiblesPlugin.ImportWizardForm', 'Bible:')) + self.web_tab_widget.setTabText(self.web_tab_widget.indexOf(self.web_bible_tab), + translate('BiblesPlugin.ImportWizardForm', 'Download Options')) + self.web_server_label.setText(translate('BiblesPlugin.ImportWizardForm', 'Server:')) + self.web_user_label.setText(translate('BiblesPlugin.ImportWizardForm', 'Username:')) + self.web_password_label.setText(translate('BiblesPlugin.ImportWizardForm', 'Password:')) + self.web_tab_widget.setTabText( + self.web_tab_widget.indexOf(self.web_proxy_tab), translate('BiblesPlugin.ImportWizardForm', + 'Proxy Server (Optional)')) + self.license_details_page.setTitle( translate('BiblesPlugin.ImportWizardForm', 'License Details')) - self.licenseDetailsPage.setSubTitle(translate('BiblesPlugin.ImportWizardForm', - 'Set up the Bible\'s license details.')) - self.versionNameLabel.setText(translate('BiblesPlugin.ImportWizardForm', 'Version name:')) - self.copyrightLabel.setText(translate('BiblesPlugin.ImportWizardForm', 'Copyright:')) - self.permissionsLabel.setText(translate('BiblesPlugin.ImportWizardForm', 'Permissions:')) + self.license_details_page.setSubTitle(translate('BiblesPlugin.ImportWizardForm', + 'Set up the Bible\'s license details.')) + self.version_name_label.setText(translate('BiblesPlugin.ImportWizardForm', 'Version name:')) + self.copyright_label.setText(translate('BiblesPlugin.ImportWizardForm', 'Copyright:')) + self.permissions_label.setText(translate('BiblesPlugin.ImportWizardForm', 'Permissions:')) self.progress_page.setTitle(WizardStrings.Importing) self.progress_page.setSubTitle(translate('BiblesPlugin.ImportWizardForm', - 'Please wait while your Bible is imported.')) + 'Please wait while your Bible is imported.')) self.progress_label.setText(WizardStrings.Ready) self.progress_bar.setFormat('%p%') # Align all QFormLayouts towards each other. - labelWidth = max(self.formatLabel.minimumSizeHint().width(), - self.osisFileLabel.minimumSizeHint().width(), - self.csvBooksLabel.minimumSizeHint().width(), - self.csvVersesLabel.minimumSizeHint().width(), - self.openSongFileLabel.minimumSizeHint().width()) - self.spacer.changeSize(labelWidth, 0, QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) + label_width = max(self.format_label.minimumSizeHint().width(), + self.osis_file_label.minimumSizeHint().width(), + self.csv_books_label.minimumSizeHint().width(), + self.csv_verses_label.minimumSizeHint().width(), + self.open_song_file_label.minimumSizeHint().width()) + self.spacer.changeSize(label_width, 0, QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) def validateCurrentPage(self): """ @@ -340,122 +340,130 @@ class BibleImportForm(OpenLPWizard): """ if self.currentPage() == self.welcome_page: return True - elif self.currentPage() == self.selectPage: + elif self.currentPage() == self.select_page: if self.field('source_format') == BibleFormat.OSIS: if not self.field('osis_location'): critical_error_message_box(UiStrings().NFSs, WizardStrings.YouSpecifyFile % WizardStrings.OSIS) - self.osisFileEdit.setFocus() + self.osis_file_edit.setFocus() return False elif self.field('source_format') == BibleFormat.CSV: if not self.field('csv_booksfile'): - critical_error_message_box(UiStrings().NFSs, translate('BiblesPlugin.ImportWizardForm', - 'You need to specify a file with books of the Bible to use in the import.')) - self.csvBooksEdit.setFocus() + critical_error_message_box( + UiStrings().NFSs, translate('BiblesPlugin.ImportWizardForm', + 'You need to specify a file with books of the Bible to use in the ' + 'import.')) + self.csv_books_edit.setFocus() return False elif not self.field('csv_versefile'): - critical_error_message_box(UiStrings().NFSs, - translate('BiblesPlugin.ImportWizardForm', - 'You need to specify a file of Bible verses to import.')) - self.csvVersesEdit.setFocus() + critical_error_message_box( + UiStrings().NFSs, + translate('BiblesPlugin.ImportWizardForm', 'You need to specify a file of Bible verses to ' + 'import.')) + self.csv_verses_edit.setFocus() return False elif self.field('source_format') == BibleFormat.OpenSong: if not self.field('opensong_file'): critical_error_message_box(UiStrings().NFSs, WizardStrings.YouSpecifyFile % WizardStrings.OS) - self.openSongFileEdit.setFocus() + self.open_song_file_edit.setFocus() return False elif self.field('source_format') == BibleFormat.WebDownload: - self.versionNameEdit.setText(self.webTranslationComboBox.currentText()) + self.version_name_edit.setText(self.web_translation_combo_box.currentText()) return True return True - elif self.currentPage() == self.licenseDetailsPage: + elif self.currentPage() == self.license_details_page: license_version = self.field('license_version') license_copyright = self.field('license_copyright') path = AppLocation.get_section_data_path('bibles') if not license_version: - critical_error_message_box(UiStrings().EmptyField, + critical_error_message_box( + UiStrings().EmptyField, translate('BiblesPlugin.ImportWizardForm', 'You need to specify a version name for your Bible.')) - self.versionNameEdit.setFocus() + self.version_name_edit.setFocus() return False elif not license_copyright: - critical_error_message_box(UiStrings().EmptyField, + critical_error_message_box( + UiStrings().EmptyField, translate('BiblesPlugin.ImportWizardForm', 'You need to set a copyright for your Bible. ' - 'Bibles in the Public Domain need to be marked as such.')) - self.copyrightEdit.setFocus() + 'Bibles in the Public Domain need to be marked as such.')) + self.copyright_edit.setFocus() return False elif self.manager.exists(license_version): - critical_error_message_box(translate('BiblesPlugin.ImportWizardForm', 'Bible Exists'), + critical_error_message_box( + translate('BiblesPlugin.ImportWizardForm', 'Bible Exists'), translate('BiblesPlugin.ImportWizardForm', - 'This Bible already exists. Please import a different Bible or first delete the existing one.')) - self.versionNameEdit.setFocus() + 'This Bible already exists. Please import a different Bible or first delete the ' + 'existing one.')) + self.version_name_edit.setFocus() return False - elif os.path.exists(os.path.join(path, clean_filename( - license_version))): + elif os.path.exists(os.path.join(path, clean_filename(license_version))): critical_error_message_box( translate('BiblesPlugin.ImportWizardForm', 'Bible Exists'), translate('BiblesPlugin.ImportWizardForm', 'This Bible already exists. Please import ' - 'a different Bible or first delete the existing one.')) - self.versionNameEdit.setFocus() + 'a different Bible or first delete the existing one.')) + self.version_name_edit.setFocus() return False return True if self.currentPage() == self.progress_page: return True - def onWebSourceComboBoxIndexChanged(self, index): + def on_web_source_combo_box_index_changed(self, index): """ - Setup the list of Bibles when you select a different source on the web - download page. + Setup the list of Bibles when you select a different source on the web download page. - ``index`` - The index of the combo box. + :param index: The index of the combo box. """ - self.webTranslationComboBox.clear() + self.web_translation_combo_box.clear() bibles = list(self.web_bible_list[index].keys()) bibles.sort(key=get_locale_key) - self.webTranslationComboBox.addItems(bibles) + self.web_translation_combo_box.addItems(bibles) - def onOsisBrowseButtonClicked(self): + def on_osis_browse_button_clicked(self): """ Show the file open dialog for the OSIS file. """ - self.get_file_name(WizardStrings.OpenTypeFile % WizardStrings.OSIS, self.osisFileEdit, 'last directory import') + self.get_file_name(WizardStrings.OpenTypeFile % WizardStrings.OSIS, self.osis_file_edit, + 'last directory import') - def onCsvBooksBrowseButtonClicked(self): + def on_csv_books_browse_button_clicked(self): """ Show the file open dialog for the books CSV file. """ - self.get_file_name(WizardStrings.OpenTypeFile % WizardStrings.CSV, self.csvBooksEdit, 'last directory import', - '%s (*.csv)' % translate('BiblesPlugin.ImportWizardForm', 'CSV File')) + self.get_file_name( + WizardStrings.OpenTypeFile % WizardStrings.CSV, self.csv_books_edit, 'last directory import', '%s (*.csv)' % + translate('BiblesPlugin.ImportWizardForm', 'CSV File')) - def onCsvVersesBrowseButtonClicked(self): + def on_csv_verses_browse_button_clicked(self): """ Show the file open dialog for the verses CSV file. """ - self.get_file_name(WizardStrings.OpenTypeFile % WizardStrings.CSV, self.csvVersesEdit, 'last directory import', - '%s (*.csv)' % translate('BiblesPlugin.ImportWizardForm', 'CSV File')) + self.get_file_name(WizardStrings.OpenTypeFile % WizardStrings.CSV, self.csv_verses_edit, + 'last directory import', '%s (*.csv)' % + translate('BiblesPlugin.ImportWizardForm', 'CSV File')) - def onOpenSongBrowseButtonClicked(self): + def on_open_song_browse_button_clicked(self): """ Show the file open dialog for the OpenSong file. """ - self.get_file_name(WizardStrings.OpenTypeFile % WizardStrings.OS, self.openSongFileEdit, 'last directory import') + self.get_file_name(WizardStrings.OpenTypeFile % WizardStrings.OS, self.open_song_file_edit, + 'last directory import') def register_fields(self): """ Register the bible import wizard fields. """ - self.selectPage.registerField('source_format', self.formatComboBox) - self.selectPage.registerField('osis_location', self.osisFileEdit) - self.selectPage.registerField('csv_booksfile', self.csvBooksEdit) - self.selectPage.registerField('csv_versefile', self.csvVersesEdit) - self.selectPage.registerField('opensong_file', self.openSongFileEdit) - self.selectPage.registerField('web_location', self.webSourceComboBox) - self.selectPage.registerField('web_biblename', self.webTranslationComboBox) - self.selectPage.registerField('proxy_server', self.webServerEdit) - self.selectPage.registerField('proxy_username', self.webUserEdit) - self.selectPage.registerField('proxy_password', self.webPasswordEdit) - self.licenseDetailsPage.registerField('license_version', self.versionNameEdit) - self.licenseDetailsPage.registerField('license_copyright', self.copyrightEdit) - self.licenseDetailsPage.registerField('license_permissions', self.permissionsEdit) + self.select_page.registerField('source_format', self.format_combo_box) + self.select_page.registerField('osis_location', self.osis_file_edit) + self.select_page.registerField('csv_booksfile', self.csv_books_edit) + self.select_page.registerField('csv_versefile', self.csv_verses_edit) + self.select_page.registerField('opensong_file', self.open_song_file_edit) + self.select_page.registerField('web_location', self.web_source_combo_box) + self.select_page.registerField('web_biblename', self.web_translation_combo_box) + self.select_page.registerField('proxy_server', self.web_server_edit) + self.select_page.registerField('proxy_username', self.web_user_edit) + self.select_page.registerField('proxy_password', self.web_password_edit) + self.license_details_page.registerField('license_version', self.version_name_edit) + self.license_details_page.registerField('license_copyright', self.copyright_edit) + self.license_details_page.registerField('license_permissions', self.permissions_edit) def setDefaults(self): """ @@ -472,33 +480,32 @@ class BibleImportForm(OpenLPWizard): self.setField('csv_versefile', '') self.setField('opensong_file', '') self.setField('web_location', WebDownload.Crosswalk) - self.setField('web_biblename', self.webTranslationComboBox.currentIndex()) + self.setField('web_biblename', self.web_translation_combo_box.currentIndex()) self.setField('proxy_server', settings.value('proxy address')) self.setField('proxy_username', settings.value('proxy username')) self.setField('proxy_password', settings.value('proxy password')) - self.setField('license_version', self.versionNameEdit.text()) - self.setField('license_copyright', self.copyrightEdit.text()) - self.setField('license_permissions', self.permissionsEdit.text()) - self.onWebSourceComboBoxIndexChanged(WebDownload.Crosswalk) + self.setField('license_version', self.version_name_edit.text()) + self.setField('license_copyright', self.copyright_edit.text()) + self.setField('license_permissions', self.permissions_edit.text()) + self.on_web_source_combo_box_index_changed(WebDownload.Crosswalk) settings.endGroup() - def loadWebBibles(self): + def load_Web_Bibles(self): """ Load the lists of Crosswalk, BibleGateway and Bibleserver bibles. """ # Load Crosswalk Bibles. - self.loadBibleResource(WebDownload.Crosswalk) + self.load_Bible_Resource(WebDownload.Crosswalk) # Load BibleGateway Bibles. - self.loadBibleResource(WebDownload.BibleGateway) + self.load_Bible_Resource(WebDownload.BibleGateway) # Load and Bibleserver Bibles. - self.loadBibleResource(WebDownload.Bibleserver) + self.load_Bible_Resource(WebDownload.Bibleserver) - def loadBibleResource(self, download_type): + def load_Bible_Resource(self, download_type): """ Loads a web bible from bible_resources.sqlite. - ``download_type`` - The WebDownload type e.g. bibleserver. + :param download_type: The WebDownload type e.g. bibleserver. """ self.web_bible_list[download_type] = {} bibles = BiblesResourcesDB.get_webbibles(WebDownload.Names[download_type]) @@ -530,28 +537,22 @@ class BibleImportForm(OpenLPWizard): importer = None if bible_type == BibleFormat.OSIS: # Import an OSIS bible. - importer = self.manager.import_bible(BibleFormat.OSIS, - name=license_version, - filename=self.field('osis_location') - ) + importer = self.manager.import_bible(BibleFormat.OSIS, name=license_version, + filename=self.field('osis_location')) elif bible_type == BibleFormat.CSV: # Import a CSV bible. - importer = self.manager.import_bible(BibleFormat.CSV, - name=license_version, - booksfile=self.field('csv_booksfile'), - versefile=self.field('csv_versefile') - ) + importer = self.manager.import_bible(BibleFormat.CSV, name=license_version, + booksfile=self.field('csv_booksfile'), + versefile=self.field('csv_versefile')) elif bible_type == BibleFormat.OpenSong: # Import an OpenSong bible. - importer = self.manager.import_bible(BibleFormat.OpenSong, - name=license_version, - filename=self.field('opensong_file') - ) + importer = self.manager.import_bible(BibleFormat.OpenSong, name=license_version, + filename=self.field('opensong_file')) elif bible_type == BibleFormat.WebDownload: # Import a bible from the web. self.progress_bar.setMaximum(1) download_location = self.field('web_location') - bible_version = self.webTranslationComboBox.currentText() + bible_version = self.web_translation_combo_box.currentText() bible = self.web_bible_list[download_location][bible_version] importer = self.manager.import_bible( BibleFormat.WebDownload, name=license_version, @@ -562,13 +563,12 @@ class BibleImportForm(OpenLPWizard): proxy_password=self.field('proxy_password') ) if importer.do_import(license_version): - self.manager.save_meta_data(license_version, license_version, - license_copyright, license_permissions) + self.manager.save_meta_data(license_version, license_version, license_copyright, license_permissions) self.manager.reload_bibles() if bible_type == BibleFormat.WebDownload: self.progress_label.setText( translate('BiblesPlugin.ImportWizardForm', 'Registered Bible. Please note, that verses will be ' - 'downloaded on\ndemand and thus an internet connection is required.')) + 'downloaded on\ndemand and thus an internet connection is required.')) else: self.progress_label.setText(WizardStrings.FinishedImport) else: diff --git a/openlp/plugins/bibles/forms/bibleupgradeform.py b/openlp/plugins/bibles/forms/bibleupgradeform.py index 2b574f778..9925b1ebc 100644 --- a/openlp/plugins/bibles/forms/bibleupgradeform.py +++ b/openlp/plugins/bibles/forms/bibleupgradeform.py @@ -96,34 +96,34 @@ class BibleUpgradeForm(OpenLPWizard): if not self.currentPage() == self.progress_page: self.done(QtGui.QDialog.Rejected) - def onCurrentIdChanged(self, pageId): + def onCurrentIdChanged(self, page_id): """ Perform necessary functions depending on which wizard page is active. """ - if self.page(pageId) == self.progress_page: + if self.page(page_id) == self.progress_page: self.pre_wizard() self.perform_wizard() self.post_wizard() - elif self.page(pageId) == self.selectPage and not self.files: + elif self.page(page_id) == self.selectPage and not self.files: self.next() - def onBackupBrowseButtonClicked(self): + def on_backup_browse_button_clicked(self): """ Show the file open dialog for the OSIS file. """ - filename = QtGui.QFileDialog.getExistingDirectory(self, - translate('BiblesPlugin.UpgradeWizardForm', 'Select a Backup Directory'), '') + filename = QtGui.QFileDialog.getExistingDirectory(self, translate('BiblesPlugin.UpgradeWizardForm', + 'Select a Backup Directory'), '') if filename: self.backupDirectoryEdit.setText(filename) - def onNoBackupCheckBoxToggled(self, checked): + def on_no_backup_check_box_toggled(self, checked): """ Enable or disable the backup directory widgets. """ self.backupDirectoryEdit.setEnabled(not checked) self.backupBrowseButton.setEnabled(not checked) - def backupOldBibles(self, backup_directory): + def backup_old_bibles(self, backup_directory): """ Backup old bible databases in a given folder. """ @@ -147,8 +147,8 @@ class BibleUpgradeForm(OpenLPWizard): """ Set up the signals used in the bible importer. """ - self.backupBrowseButton.clicked.connect(self.onBackupBrowseButtonClicked) - self.noBackupCheckBox.toggled.connect(self.onNoBackupCheckBoxToggled) + self.backupBrowseButton.clicked.connect(self.on_backup_browse_button_clicked) + self.noBackupCheckBox.toggled.connect(self.on_no_backup_check_box_toggled) def add_custom_pages(self): """ @@ -238,32 +238,34 @@ class BibleUpgradeForm(OpenLPWizard): Allow for localisation of the bible import wizard. """ self.setWindowTitle(translate('BiblesPlugin.UpgradeWizardForm', 'Bible Upgrade Wizard')) - self.title_label.setText(WizardStrings.HeaderStyle % - translate('OpenLP.Ui', 'Welcome to the Bible Upgrade Wizard')) - self.information_label.setText(translate('BiblesPlugin.UpgradeWizardForm', - 'This wizard will help you to upgrade your existing Bibles from a prior version of OpenLP 2. ' - 'Click the next button below to start the upgrade process.')) + self.title_label.setText(WizardStrings.HeaderStyle % translate('OpenLP.Ui', + 'Welcome to the Bible Upgrade Wizard')) + self.information_label.setText( + translate('BiblesPlugin.UpgradeWizardForm', + 'This wizard will help you to upgrade your existing Bibles from a prior version of OpenLP 2. ' + 'Click the next button below to start the upgrade process.')) self.backup_page.setTitle(translate('BiblesPlugin.UpgradeWizardForm', 'Select Backup Directory')) - self.backup_page.setSubTitle(translate('BiblesPlugin.UpgradeWizardForm', - 'Please select a backup directory for your Bibles')) - self.backupInfoLabel.setText(translate('BiblesPlugin.UpgradeWizardForm', - 'Previous releases of OpenLP 2.0 are unable to use upgraded Bibles.' - ' This will create a backup of your current Bibles so that you can ' - 'simply copy the files back to your OpenLP data directory if you ' - 'need to revert to a previous release of OpenLP. Instructions on ' - 'how to restore the files can be found in our <a href="' - 'http://wiki.openlp.org/faq">Frequently Asked Questions</a>.')) - self.selectLabel.setText(translate('BiblesPlugin.UpgradeWizardForm', - 'Please select a backup location for your Bibles.')) + self.backup_page.setSubTitle( + translate('BiblesPlugin.UpgradeWizardForm', 'Please select a backup directory for your Bibles')) + self.backupInfoLabel.setText( + translate('BiblesPlugin.UpgradeWizardForm', + 'Previous releases of OpenLP 2.0 are unable to use upgraded Bibles.' + ' This will create a backup of your current Bibles so that you can ' + 'simply copy the files back to your OpenLP data directory if you ' + 'need to revert to a previous release of OpenLP. Instructions on ' + 'how to restore the files can be found in our <a href="' + 'http://wiki.openlp.org/faq">Frequently Asked Questions</a>.')) + self.selectLabel.setText( + translate('BiblesPlugin.UpgradeWizardForm', 'Please select a backup location for your Bibles.')) self.backupDirectoryLabel.setText(translate('BiblesPlugin.UpgradeWizardForm', 'Backup Directory:')) self.noBackupCheckBox.setText( translate('BiblesPlugin.UpgradeWizardForm', 'There is no need to backup my Bibles')) self.selectPage.setTitle(translate('BiblesPlugin.UpgradeWizardForm', 'Select Bibles')) - self.selectPage.setSubTitle(translate('BiblesPlugin.UpgradeWizardForm', - 'Please select the Bibles to upgrade')) + self.selectPage.setSubTitle( + translate('BiblesPlugin.UpgradeWizardForm', 'Please select the Bibles to upgrade')) self.progress_page.setTitle(translate('BiblesPlugin.UpgradeWizardForm', 'Upgrading')) - self.progress_page.setSubTitle(translate('BiblesPlugin.UpgradeWizardForm', - 'Please wait while your Bibles are upgraded.')) + self.progress_page.setSubTitle( + translate('BiblesPlugin.UpgradeWizardForm', 'Please wait while your Bibles are upgraded.')) self.progress_label.setText(WizardStrings.Ready) self.progress_bar.setFormat('%p%') @@ -277,16 +279,18 @@ class BibleUpgradeForm(OpenLPWizard): if not self.noBackupCheckBox.checkState() == QtCore.Qt.Checked: backup_path = self.backupDirectoryEdit.text() if not backup_path: - critical_error_message_box(UiStrings().EmptyField, - translate('BiblesPlugin.UpgradeWizardForm', - 'You need to specify a backup directory for your Bibles.')) + critical_error_message_box( + UiStrings().EmptyField, + translate('BiblesPlugin.UpgradeWizardForm', 'You need to specify a backup directory for ' + 'your Bibles.')) self.backupDirectoryEdit.setFocus() return False else: - if not self.backupOldBibles(backup_path): - critical_error_message_box(UiStrings().Error, - translate('BiblesPlugin.UpgradeWizardForm', 'The backup was not successful.\nTo backup your ' - 'Bibles you need permission to write to the given directory.')) + if not self.backup_old_bibles(backup_path): + critical_error_message_box( + UiStrings().Error, + translate('BiblesPlugin.UpgradeWizardForm', 'The backup was not successful.\nTo backup ' + 'your Bibles you need permission to write to the given directory.')) return False return True elif self.currentPage() == self.selectPage: @@ -340,8 +344,8 @@ class BibleUpgradeForm(OpenLPWizard): self.includeWebBible = False proxy_server = None if not self.files: - self.progress_label.setText(translate('BiblesPlugin.UpgradeWizardForm', - 'There are no Bibles that need to be upgraded.')) + self.progress_label.setText( + translate('BiblesPlugin.UpgradeWizardForm', 'There are no Bibles that need to be upgraded.')) self.progress_bar.hide() return max_bibles = 0 @@ -363,11 +367,11 @@ class BibleUpgradeForm(OpenLPWizard): self.success[number] = False continue self.progress_bar.reset() - old_bible = OldBibleDB(self.media_item, path=self.temp_dir, - file=filename[0]) + old_bible = OldBibleDB(self.media_item, path=self.temp_dir, file=filename[0]) name = filename[1] - self.progress_label.setText(translate('BiblesPlugin.UpgradeWizardForm', - 'Upgrading Bible %s of %s: "%s"\nUpgrading ...') % (number + 1, max_bibles, name)) + self.progress_label.setText( + translate('BiblesPlugin.UpgradeWizardForm', + 'Upgrading Bible %s of %s: "%s"\nUpgrading ...') % (number + 1, max_bibles, name)) self.new_bibles[number] = BibleDB(self.media_item, path=self.path, name=name, file=filename[0]) self.new_bibles[number].register(self.plugin.upgrade_wizard) metadata = old_bible.get_metadata() @@ -404,7 +408,7 @@ class BibleUpgradeForm(OpenLPWizard): critical_error_message_box( translate('BiblesPlugin.UpgradeWizardForm', 'Download Error'), translate('BiblesPlugin.UpgradeWizardForm', - 'To upgrade your Web Bibles an Internet connection is required.')) + 'To upgrade your Web Bibles an Internet connection is required.')) self.increment_progress_bar(translate( 'BiblesPlugin.UpgradeWizardForm', 'Upgrading Bible %s of %s: "%s"\nFailed') % (number + 1, max_bibles, name), self.progress_bar.maximum() - self.progress_bar.value()) @@ -415,16 +419,16 @@ class BibleUpgradeForm(OpenLPWizard): meta_data['download_source'].lower()) if bible and bible['language_id']: language_id = bible['language_id'] - self.new_bibles[number].save_meta('language_id', - language_id) + self.new_bibles[number].save_meta('language_id', language_id) else: language_id = self.new_bibles[number].get_language(name) if not language_id: log.warn('Upgrading from "%s" failed' % filename[0]) self.new_bibles[number].session.close() del self.new_bibles[number] - self.increment_progress_bar(translate('BiblesPlugin.UpgradeWizardForm', - 'Upgrading Bible %s of %s: "%s"\nFailed') % (number + 1, max_bibles, name), + self.increment_progress_bar( + translate('BiblesPlugin.UpgradeWizardForm', + 'Upgrading Bible %s of %s: "%s"\nFailed') % (number + 1, max_bibles, name), self.progress_bar.maximum() - self.progress_bar.value()) self.success[number] = False continue @@ -433,8 +437,10 @@ class BibleUpgradeForm(OpenLPWizard): if self.stop_import_flag: self.success[number] = False break - self.increment_progress_bar(translate('BiblesPlugin.UpgradeWizardForm', - 'Upgrading Bible %s of %s: "%s"\nUpgrading %s ...') % (number + 1, max_bibles, name, book)) + self.increment_progress_bar( + translate('BiblesPlugin.UpgradeWizardForm', + 'Upgrading Bible %s of %s: "%s"\nUpgrading %s ...') % + (number + 1, max_bibles, name, book)) book_ref_id = self.new_bibles[number].\ get_book_ref_id_by_name(book, len(books), language_id) if not book_ref_id: @@ -445,8 +451,7 @@ class BibleUpgradeForm(OpenLPWizard): self.success[number] = False break book_details = BiblesResourcesDB.get_book_by_id(book_ref_id) - db_book = self.new_bibles[number].create_book(book, - book_ref_id, book_details['testament_id']) + db_book = self.new_bibles[number].create_book(book, book_ref_id, book_details['testament_id']) # Try to import already downloaded verses. oldbook = old_bible.get_book(book) if oldbook: @@ -458,9 +463,8 @@ class BibleUpgradeForm(OpenLPWizard): if self.stop_import_flag: self.success[number] = False break - self.new_bibles[number].create_verse(db_book.id, - int(verse['chapter']), - int(verse['verse']), str(verse['text'])) + self.new_bibles[number].create_verse(db_book.id, int(verse['chapter']), + int(verse['verse']), str(verse['text'])) self.application.process_events() self.new_bibles[number].session.commit() else: @@ -471,8 +475,9 @@ class BibleUpgradeForm(OpenLPWizard): log.warn('Upgrading books from "%s" failed' % name) self.new_bibles[number].session.close() del self.new_bibles[number] - self.increment_progress_bar(translate('BiblesPlugin.UpgradeWizardForm', - 'Upgrading Bible %s of %s: "%s"\nFailed') % (number + 1, max_bibles, name), + self.increment_progress_bar( + translate('BiblesPlugin.UpgradeWizardForm', + 'Upgrading Bible %s of %s: "%s"\nFailed') % (number + 1, max_bibles, name), self.progress_bar.maximum() - self.progress_bar.value()) self.success[number] = False continue @@ -482,8 +487,9 @@ class BibleUpgradeForm(OpenLPWizard): if self.stop_import_flag: self.success[number] = False break - self.increment_progress_bar(translate('BiblesPlugin.UpgradeWizardForm', - 'Upgrading Bible %s of %s: "%s"\nUpgrading %s ...') % + self.increment_progress_bar( + translate('BiblesPlugin.UpgradeWizardForm', + 'Upgrading Bible %s of %s: "%s"\nUpgrading %s ...') % (number + 1, max_bibles, name, book['name'])) book_ref_id = self.new_bibles[number].get_book_ref_id_by_name(book['name'], len(books), language_id) if not book_ref_id: @@ -493,8 +499,8 @@ class BibleUpgradeForm(OpenLPWizard): self.success[number] = False break book_details = BiblesResourcesDB.get_book_by_id(book_ref_id) - db_book = self.new_bibles[number].create_book(book['name'], - book_ref_id, book_details['testament_id']) + db_book = self.new_bibles[number].create_book(book['name'], book_ref_id, + book_details['testament_id']) verses = old_bible.get_verses(book['id']) if not verses: log.warn('No verses found to import for book "%s"', book['name']) @@ -504,20 +510,21 @@ class BibleUpgradeForm(OpenLPWizard): if self.stop_import_flag: self.success[number] = False break - self.new_bibles[number].create_verse(db_book.id, - int(verse['chapter']), - int(verse['verse']), str(verse['text'])) + self.new_bibles[number].create_verse(db_book.id, int(verse['chapter']), int(verse['verse']), + str(verse['text'])) self.application.process_events() self.new_bibles[number].session.commit() if not self.success.get(number, True): - self.increment_progress_bar(translate('BiblesPlugin.UpgradeWizardForm', - 'Upgrading Bible %s of %s: "%s"\nFailed') % (number + 1, max_bibles, name), + self.increment_progress_bar( + translate('BiblesPlugin.UpgradeWizardForm', + 'Upgrading Bible %s of %s: "%s"\nFailed') % (number + 1, max_bibles, name), self.progress_bar.maximum() - self.progress_bar.value()) else: self.success[number] = True self.new_bibles[number].save_meta('name', name) - self.increment_progress_bar(translate('BiblesPlugin.UpgradeWizardForm', - 'Upgrading Bible %s of %s: "%s"\nComplete') % (number + 1, max_bibles, name)) + self.increment_progress_bar( + translate('BiblesPlugin.UpgradeWizardForm', + 'Upgrading Bible %s of %s: "%s"\nComplete') % (number + 1, max_bibles, name)) if number in self.new_bibles: self.new_bibles[number].session.close() # Close the last bible's connection if possible. @@ -545,12 +552,15 @@ class BibleUpgradeForm(OpenLPWizard): failed_import_text = '' if successful_import > 0: if self.includeWebBible: - self.progress_label.setText(translate('BiblesPlugin.UpgradeWizardForm', - 'Upgrading Bible(s): %s successful%s\nPlease note that verses from Web Bibles will be downloaded ' - 'on demand and so an Internet connection is required.') % (successful_import, failed_import_text)) + self.progress_label.setText( + translate('BiblesPlugin.UpgradeWizardForm', + 'Upgrading Bible(s): %s successful%s\nPlease note that verses from Web Bibles will be ' + 'downloaded on demand and so an Internet connection is required.') % + (successful_import, failed_import_text)) else: - self.progress_label.setText(translate('BiblesPlugin.UpgradeWizardForm', - 'Upgrading Bible(s): %s successful%s') % (successful_import, failed_import_text)) + self.progress_label.setText( + translate('BiblesPlugin.UpgradeWizardForm', 'Upgrading Bible(s): %s successful%s') % ( + successful_import, failed_import_text)) else: self.progress_label.setText(translate('BiblesPlugin.UpgradeWizardForm', 'Upgrade failed.')) # Remove temp directory. diff --git a/openlp/plugins/bibles/forms/booknamedialog.py b/openlp/plugins/bibles/forms/booknamedialog.py index 66eae6b09..5903391c3 100644 --- a/openlp/plugins/bibles/forms/booknamedialog.py +++ b/openlp/plugins/bibles/forms/booknamedialog.py @@ -32,6 +32,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.common import translate from openlp.core.lib.ui import create_button_box + class Ui_BookNameDialog(object): def setupUi(self, book_name_dialog): book_name_dialog.setObjectName('book_name_dialog') @@ -48,15 +49,15 @@ class Ui_BookNameDialog(object): self.corresponding_layout.setColumnStretch(1, 1) self.corresponding_layout.setSpacing(8) self.corresponding_layout.setObjectName('corresponding_layout') - self.currentLabel = QtGui.QLabel(book_name_dialog) - self.currentLabel.setObjectName('currentLabel') - self.corresponding_layout.addWidget(self.currentLabel, 0, 0, 1, 1) + self.current_label = QtGui.QLabel(book_name_dialog) + self.current_label.setObjectName('current_label') + self.corresponding_layout.addWidget(self.current_label, 0, 0, 1, 1) self.current_book_label = QtGui.QLabel(book_name_dialog) self.current_book_label.setObjectName('current_book_label') self.corresponding_layout.addWidget(self.current_book_label, 0, 1, 1, 1) - self.correspondingLabel = QtGui.QLabel(book_name_dialog) - self.correspondingLabel.setObjectName('correspondingLabel') - self.corresponding_layout.addWidget(self.correspondingLabel, 1, 0, 1, 1) + self.corresponding_label = QtGui.QLabel(book_name_dialog) + self.corresponding_label.setObjectName('corresponding_label') + self.corresponding_layout.addWidget(self.corresponding_label, 1, 0, 1, 1) self.corresponding_combo_box = QtGui.QComboBox(book_name_dialog) self.corresponding_combo_box.setObjectName('corresponding_combo_box') self.corresponding_layout.addWidget(self.corresponding_combo_box, 1, 1, 1, 1) @@ -87,11 +88,11 @@ class Ui_BookNameDialog(object): def retranslateUi(self, book_name_dialog): book_name_dialog.setWindowTitle(translate('BiblesPlugin.BookNameDialog', 'Select Book Name')) - self.info_label.setText(translate('BiblesPlugin.BookNameDialog', - 'The following book name cannot be matched up internally. ' - 'Please select the corresponding name from the list.')) - self.currentLabel.setText(translate('BiblesPlugin.BookNameDialog', 'Current name:')) - self.correspondingLabel.setText(translate('BiblesPlugin.BookNameDialog', 'Corresponding name:')) + self.info_label.setText( + translate('BiblesPlugin.BookNameDialog', 'The following book name cannot be matched up internally. ' + 'Please select the corresponding name from the list.')) + self.current_label.setText(translate('BiblesPlugin.BookNameDialog', 'Current name:')) + self.corresponding_label.setText(translate('BiblesPlugin.BookNameDialog', 'Corresponding name:')) self.options_group_box.setTitle(translate('BiblesPlugin.BookNameDialog', 'Show Books From')) self.old_testament_check_box.setText(translate('BiblesPlugin.BookNameDialog', 'Old Testament')) self.new_testament_check_box.setText(translate('BiblesPlugin.BookNameDialog', 'New Testament')) diff --git a/openlp/plugins/bibles/forms/booknameform.py b/openlp/plugins/bibles/forms/booknameform.py index 063a6618c..7d1242b7c 100644 --- a/openlp/plugins/bibles/forms/booknameform.py +++ b/openlp/plugins/bibles/forms/booknameform.py @@ -52,7 +52,7 @@ class BookNameForm(QDialog, Ui_BookNameDialog): """ log.info('BookNameForm loaded') - def __init__(self, parent = None): + def __init__(self, parent=None): """ Constructor """ @@ -66,11 +66,11 @@ class BookNameForm(QDialog, Ui_BookNameDialog): """ Set up the signals used in the booknameform. """ - self.old_testament_check_box.stateChanged.connect(self.onCheckBoxIndexChanged) - self.new_testament_check_box.stateChanged.connect(self.onCheckBoxIndexChanged) - self.apocrypha_check_box.stateChanged.connect(self.onCheckBoxIndexChanged) + self.old_testament_check_box.stateChanged.connect(self.on_check_box_index_changed) + self.new_testament_check_box.stateChanged.connect(self.on_check_box_index_changed) + self.apocrypha_check_box.stateChanged.connect(self.on_check_box_index_changed) - def onCheckBoxIndexChanged(self, index): + def on_check_box_index_changed(self, index): """ Reload Combobox if CheckBox state has changed """ @@ -119,7 +119,8 @@ class BookNameForm(QDialog, Ui_BookNameDialog): cor_book = self.corresponding_combo_box.currentText() for character in '\\.^$*+?{}[]()': cor_book = cor_book.replace(character, '\\' + character) - books = [key for key in list(self.book_names.keys()) if re.match(cor_book, str(self.book_names[key]), re.UNICODE)] + books = [key for key in list(self.book_names.keys()) if re.match(cor_book, str(self.book_names[key]), + re.UNICODE)] books = [_f for _f in map(BiblesResourcesDB.get_book, books) if _f] if books: self.book_id = books[0]['id'] diff --git a/openlp/plugins/bibles/forms/editbibledialog.py b/openlp/plugins/bibles/forms/editbibledialog.py index 48649daad..1fbaa2f1e 100644 --- a/openlp/plugins/bibles/forms/editbibledialog.py +++ b/openlp/plugins/bibles/forms/editbibledialog.py @@ -107,7 +107,7 @@ class Ui_EditBibleDialog(object): self.book_name_widget_layout = QtGui.QFormLayout(self.book_name_widget) self.book_name_widget_layout.setObjectName('book_name_widget_layout') self.book_name_label = {} - self.book_name_edit= {} + self.book_name_edit = {} for book in BiblesResourcesDB.get_books(): self.book_name_label[book['abbreviation']] = QtGui.QLabel(self.book_name_widget) self.book_name_label[book['abbreviation']].setObjectName('book_name_label[%s]' % book['abbreviation']) @@ -131,24 +131,28 @@ class Ui_EditBibleDialog(object): self.book_names = BibleStrings().BookNames edit_bible_dialog.setWindowTitle(translate('BiblesPlugin.EditBibleForm', 'Bible Editor')) # Meta tab - self.bible_tab_widget.setTabText( self.bible_tab_widget.indexOf(self.meta_tab), - translate('SongsPlugin.EditBibleForm', 'Meta Data')) + self.bible_tab_widget.setTabText( + self.bible_tab_widget.indexOf(self.meta_tab), translate('SongsPlugin.EditBibleForm', 'Meta Data')) self.license_details_group_box.setTitle(translate('BiblesPlugin.EditBibleForm', 'License Details')) self.version_name_label.setText(translate('BiblesPlugin.EditBibleForm', 'Version name:')) self.copyright_label.setText(translate('BiblesPlugin.EditBibleForm', 'Copyright:')) self.permissions_label.setText(translate('BiblesPlugin.EditBibleForm', 'Permissions:')) self.language_selection_group_box.setTitle(translate('BiblesPlugin.EditBibleForm', 'Default Bible Language')) - self.language_selection_label.setText(translate('BiblesPlugin.EditBibleForm', - 'Book name language in search field, search results and on display:')) + self.language_selection_label.setText( + translate('BiblesPlugin.EditBibleForm', 'Book name language in search field, search results and ' + 'on display:')) self.language_selection_combo_box.setItemText(0, translate('BiblesPlugin.EditBibleForm', 'Global Settings')) - self.language_selection_combo_box.setItemText(LanguageSelection.Bible + 1, + self.language_selection_combo_box.setItemText( + LanguageSelection.Bible + 1, translate('BiblesPlugin.EditBibleForm', 'Bible Language')) - self.language_selection_combo_box.setItemText(LanguageSelection.Application + 1, - translate('BiblesPlugin.EditBibleForm', 'Application Language')) - self.language_selection_combo_box.setItemText(LanguageSelection.English + 1, + self.language_selection_combo_box.setItemText( + LanguageSelection.Application + 1, translate('BiblesPlugin.EditBibleForm', 'Application Language')) + self.language_selection_combo_box.setItemText( + LanguageSelection.English + 1, translate('BiblesPlugin.EditBibleForm', 'English')) # Book name tab - self.bible_tab_widget.setTabText(self.bible_tab_widget.indexOf(self.book_name_tab), + self.bible_tab_widget.setTabText( + self.bible_tab_widget.indexOf(self.book_name_tab), translate('SongsPlugin.EditBibleForm', 'Custom Book Names')) for book in BiblesResourcesDB.get_books(): self.book_name_label[book['abbreviation']].setText('%s:' % str(self.book_names[book['abbreviation']])) diff --git a/openlp/plugins/bibles/forms/editbibleform.py b/openlp/plugins/bibles/forms/editbibleform.py index f2d97cc08..2d10fedfa 100644 --- a/openlp/plugins/bibles/forms/editbibleform.py +++ b/openlp/plugins/bibles/forms/editbibleform.py @@ -176,7 +176,7 @@ class EditBibleForm(QtGui.QDialog, Ui_EditBibleDialog, RegistryProperties): critical_error_message_box( UiStrings().EmptyField, translate('BiblesPlugin.BibleEditForm', 'You need to specify a book name for "%s".') % - self.book_names[abbreviation]) + self.book_names[abbreviation]) return False elif not book_regex.match(new_book_name): self.book_name_edit[abbreviation].setFocus() diff --git a/openlp/plugins/bibles/forms/languagedialog.py b/openlp/plugins/bibles/forms/languagedialog.py index a75df8ded..10382ea13 100644 --- a/openlp/plugins/bibles/forms/languagedialog.py +++ b/openlp/plugins/bibles/forms/languagedialog.py @@ -32,6 +32,7 @@ from PyQt4 import QtGui from openlp.core.common import translate from openlp.core.lib.ui import create_button_box + class Ui_LanguageDialog(object): def setupUi(self, language_dialog): language_dialog.setObjectName('language_dialog') @@ -54,11 +55,11 @@ class Ui_LanguageDialog(object): self.language_label.setObjectName('language_label') self.language_h_box_layout.addWidget(self.language_label) self.language_combo_box = QtGui.QComboBox(language_dialog) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.language_combo_box.sizePolicy().hasHeightForWidth()) - self.language_combo_box.setSizePolicy(sizePolicy) + size_policy = QtGui.QSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Fixed) + size_policy.setHorizontalStretch(0) + size_policy.setVerticalStretch(0) + size_policy.setHeightForWidth(self.language_combo_box.sizePolicy().hasHeightForWidth()) + self.language_combo_box.setSizePolicy(size_policy) self.language_combo_box.setObjectName('language_combo_box') self.language_h_box_layout.addWidget(self.language_combo_box) self.language_layout.addLayout(self.language_h_box_layout) @@ -70,7 +71,8 @@ class Ui_LanguageDialog(object): def retranslateUi(self, language_dialog): language_dialog.setWindowTitle(translate('BiblesPlugin.LanguageDialog', 'Select Language')) self.bible_label.setText(translate('BiblesPlugin.LanguageDialog', '')) - self.info_label.setText(translate('BiblesPlugin.LanguageDialog', - 'OpenLP is unable to determine the language of this translation of the Bible. Please select the language ' - 'from the list below.')) + self.info_label.setText( + translate('BiblesPlugin.LanguageDialog', + 'OpenLP is unable to determine the language of this translation of the Bible. Please select ' + 'the language from the list below.')) self.language_label.setText(translate('BiblesPlugin.LanguageDialog', 'Language:')) diff --git a/openlp/plugins/bibles/forms/languageform.py b/openlp/plugins/bibles/forms/languageform.py index 317132a57..f2c450494 100644 --- a/openlp/plugins/bibles/forms/languageform.py +++ b/openlp/plugins/bibles/forms/languageform.py @@ -36,8 +36,7 @@ from PyQt4.QtGui import QDialog from openlp.core.common import translate from openlp.core.lib.ui import critical_error_message_box -from openlp.plugins.bibles.forms.languagedialog import \ - Ui_LanguageDialog +from openlp.plugins.bibles.forms.languagedialog import Ui_LanguageDialog from openlp.plugins.bibles.lib.db import BiblesResourcesDB diff --git a/openlp/plugins/bibles/lib/__init__.py b/openlp/plugins/bibles/lib/__init__.py index 39bee992f..50a0e2a63 100644 --- a/openlp/plugins/bibles/lib/__init__.py +++ b/openlp/plugins/bibles/lib/__init__.py @@ -218,9 +218,10 @@ def update_reference_separators(): REFERENCE_MATCHES['range'] = re.compile('^\s*%s\s*$' % range_regex, re.UNICODE) REFERENCE_MATCHES['range_separator'] = re.compile(REFERENCE_SEPARATORS['sep_l'], re.UNICODE) # full reference match: <book>(<range>(,(?!$)|(?=$)))+ - REFERENCE_MATCHES['full'] = re.compile('^\s*(?!\s)(?P<book>[\d]*[^\d]+)(?<!\s)\s*' - '(?P<ranges>(?:%(range_regex)s(?:%(sep_l)s(?!\s*$)|(?=\s*$)))+)\s*$' \ - % dict(list(REFERENCE_SEPARATORS.items()) + [('range_regex', range_regex)]), re.UNICODE) + REFERENCE_MATCHES['full'] = \ + re.compile('^\s*(?!\s)(?P<book>[\d]*[^\d]+)(?<!\s)\s*' + '(?P<ranges>(?:%(range_regex)s(?:%(sep_l)s(?!\s*$)|(?=\s*$)))+)\s*$' + % dict(list(REFERENCE_SEPARATORS.items()) + [('range_regex', range_regex)]), re.UNICODE) def get_reference_separator(separator_type): diff --git a/openlp/plugins/bibles/lib/biblestab.py b/openlp/plugins/bibles/lib/biblestab.py index 8e5c50178..8f16bf830 100644 --- a/openlp/plugins/bibles/lib/biblestab.py +++ b/openlp/plugins/bibles/lib/biblestab.py @@ -422,7 +422,7 @@ class BiblesTab(SettingsTab): color.setAlpha(128) palette.setColor(QtGui.QPalette.Active, QtGui.QPalette.Text, color) return palette - + def check_is_verse_number_visible(self): """ Enables / Disables verse settings dependent on is_verse_number_visible @@ -430,4 +430,3 @@ class BiblesTab(SettingsTab): self.new_chapters_check_box.setEnabled(self.is_verse_number_visible) self.display_style_label.setEnabled(self.is_verse_number_visible) self.display_style_combo_box.setEnabled(self.is_verse_number_visible) - diff --git a/openlp/plugins/bibles/lib/db.py b/openlp/plugins/bibles/lib/db.py index a41545377..743bb01c6 100644 --- a/openlp/plugins/bibles/lib/db.py +++ b/openlp/plugins/bibles/lib/db.py @@ -80,23 +80,20 @@ def init_schema(url): meta_table = Table('metadata', metadata, Column('key', types.Unicode(255), primary_key=True, index=True), - Column('value', types.Unicode(255)), - ) + Column('value', types.Unicode(255)),) book_table = Table('book', metadata, Column('id', types.Integer, primary_key=True), Column('book_reference_id', types.Integer, index=True), Column('testament_reference_id', types.Integer), - Column('name', types.Unicode(50), index=True), - ) + Column('name', types.Unicode(50), index=True),) verse_table = Table('verse', metadata, Column('id', types.Integer, primary_key=True, index=True), Column('book_id', types.Integer, ForeignKey( 'book.id'), index=True), Column('chapter', types.Integer, index=True), Column('verse', types.Integer, index=True), - Column('text', types.UnicodeText, index=True), - ) + Column('text', types.UnicodeText, index=True),) try: class_mapper(BibleMeta) @@ -225,7 +222,8 @@ class BibleDB(QtCore.QObject, Manager, RegistryProperties): :param book_id: The id of the book being appended. :param chapter: The chapter number. - :param text_list: A dict of the verses to be inserted. The key is the verse number, and the value is the verse text. + :param text_list: A dict of the verses to be inserted. The key is the verse number, and the value is the + verse text. """ log.debug('BibleDBcreate_chapter("%s", "%s")', book_id, chapter) # Text list has book and chapter as first two elements of the array. @@ -437,7 +435,7 @@ class BibleDB(QtCore.QObject, Manager, RegistryProperties): """ log.debug('BibleDB.get_chapter_count("%s")', book.name) count = self.session.query(func.max(Verse.chapter)).join(Book).filter( - Book.book_reference_id==book.book_reference_id).scalar() + Book.book_reference_id == book.book_reference_id).scalar() if not count: return 0 return count @@ -718,8 +716,8 @@ class BiblesResourcesDB(QtCore.QObject, Manager): if not isinstance(source, str): source = str(source) source = BiblesResourcesDB.get_download_source(source) - bibles = BiblesResourcesDB.run_sql('SELECT id, name, abbreviation, ' - 'language_id, download_source_id FROM webbibles WHERE download_source_id = ?', (source['id'],)) + bibles = BiblesResourcesDB.run_sql('SELECT id, name, abbreviation, language_id, download_source_id ' + 'FROM webbibles WHERE download_source_id = ?', (source['id'],)) if bibles: return [{ 'id': bible[0], @@ -824,10 +822,9 @@ class BiblesResourcesDB(QtCore.QObject, Manager): log.debug('BiblesResourcesDB.get_testament_reference()') testaments = BiblesResourcesDB.run_sql('SELECT id, name FROM testament_reference ORDER BY id') return [ - { - 'id': testament[0], - 'name': str(testament[1]) - } + {'id': testament[0], + 'name': str(testament[1]) + } for testament in testaments ] @@ -934,7 +931,7 @@ class OldBibleDB(QtCore.QObject, Manager): QtCore.QObject.__init__(self) if 'path' not in kwargs: raise KeyError('Missing keyword argument "path".') - if 'file' not in kwargs: + if 'file' not in kwargs: raise KeyError('Missing keyword argument "file".') if 'path' in kwargs: self.path = kwargs['path'] diff --git a/openlp/plugins/bibles/lib/http.py b/openlp/plugins/bibles/lib/http.py index 3400c9a56..340d8ef92 100644 --- a/openlp/plugins/bibles/lib/http.py +++ b/openlp/plugins/bibles/lib/http.py @@ -32,7 +32,9 @@ The :mod:`http` module enables OpenLP to retrieve scripture from bible websites. import logging import re import socket -import urllib.request, urllib.parse, urllib.error +import urllib.request +import urllib.parse +import urllib.error from html.parser import HTMLParseError from bs4 import BeautifulSoup, NavigableString, Tag @@ -487,7 +489,7 @@ class HTTPBible(BibleDB, RegistryProperties): (self.download_source, self.download_name)) return False self.wizard.progress_bar.setMaximum(len(books) + 2) - self.wizard.increment_progress_bar(translate( 'BiblesPlugin.HTTPBible', 'Registering Language...')) + self.wizard.increment_progress_bar(translate('BiblesPlugin.HTTPBible', 'Registering Language...')) bible = BiblesResourcesDB.get_webbible(self.download_name, self.download_source.lower()) if bible['language_id']: language_id = bible['language_id'] diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index 4b8b8bc83..10fef8e31 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -664,7 +664,7 @@ class BibleMediaItem(MediaManagerItem): db_book = bibles[second_bible].get_book_by_book_ref_id(verse.book.book_reference_id) if not db_book: log.debug('Passage "%s %d:%d" not found in Second Bible' % - (verse.book.name, verse.chapter, verse.verse)) + (verse.book.name, verse.chapter, verse.verse)) passage_not_found = True count += 1 continue @@ -910,8 +910,8 @@ class BibleMediaItem(MediaManagerItem): elif old_verse + 1 != verse and old_chapter == chapter: # We are still in the same chapter, but a verse has been skipped. return True - elif old_chapter + 1 == chapter and (verse != 1 or - old_verse != self.plugin.manager.get_verse_count(old_bible, old_book, old_chapter)): + elif old_chapter + 1 == chapter and (verse != 1 or old_verse != + self.plugin.manager.get_verse_count(old_bible, old_book, old_chapter)): # We are in the following chapter, but the last verse was not the # last verse of the chapter or the current verse is not the # first one of the chapter. diff --git a/openlp/plugins/images/forms/addgroupform.py b/openlp/plugins/images/forms/addgroupform.py index d5f26f308..414a8e4b9 100644 --- a/openlp/plugins/images/forms/addgroupform.py +++ b/openlp/plugins/images/forms/addgroupform.py @@ -50,7 +50,8 @@ class AddGroupForm(QtGui.QDialog, Ui_AddGroupDialog): Show the form. :param clear: Set to False if the text input box should not be cleared when showing the dialog (default: True). - :param show_top_level_group: Set to True when "-- Top level group --" should be showed as first item (default: False). + :param show_top_level_group: Set to True when "-- Top level group --" should be showed as first item + (default: False). :param selected_group: The ID of the group that should be selected by default when showing the dialog. """ if clear: diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index 5b88bfb36..b2eb5b816 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -327,7 +327,8 @@ class ImageMediaItem(MediaManagerItem): :param images: A List of Image Filenames objects that will be used to reload the mediamanager list. :param initial_load: When set to False, the busy cursor and progressbar will be shown while loading images. - :param open_group: ImageGroups object of the group that must be expanded after reloading the list in the interface. + :param open_group: ImageGroups object of the group that must be expanded after reloading the list in the + interface. """ if not initial_load: self.application.set_busy_cursor() @@ -462,7 +463,8 @@ class ImageMediaItem(MediaManagerItem): :param images_list: A List of strings containing image filenames :param group_id: The ID of the group to save the images in - :param reload_list: This boolean is set to True when the list in the interface should be reloaded after saving the new images + :param reload_list: This boolean is set to True when the list in the interface should be reloaded after saving + the new images """ for filename in images_list: if not isinstance(filename, str): diff --git a/openlp/plugins/media/lib/mediaitem.py b/openlp/plugins/media/lib/mediaitem.py index 01a28f9ca..56d3a49f4 100644 --- a/openlp/plugins/media/lib/mediaitem.py +++ b/openlp/plugins/media/lib/mediaitem.py @@ -34,7 +34,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.common import Registry, RegistryProperties, AppLocation, Settings, check_directory_exists, UiStrings,\ translate -from openlp.core.lib import ItemCapabilities, MediaManagerItem,MediaType, ServiceItem, ServiceItemContext, \ +from openlp.core.lib import ItemCapabilities, MediaManagerItem, MediaType, ServiceItem, ServiceItemContext, \ build_icon, check_item_selected from openlp.core.lib.ui import critical_error_message_box, create_horizontal_adjusting_combo_box from openlp.core.ui import DisplayController, Display, DisplayControllerType @@ -161,7 +161,7 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties): Called to replace Live background with the media selected. """ if check_item_selected(self.list_view, - translate('MediaPlugin.MediaItem', + translate('MediaPlugin.MediaItem', 'You must select a media file to replace the background with.')): item = self.list_view.currentItem() filename = item.data(QtCore.Qt.UserRole) @@ -170,12 +170,12 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties): service_item.title = 'webkit' service_item.processor = 'webkit' (path, name) = os.path.split(filename) - service_item.add_from_command(path, name,CLAPPERBOARD) + service_item.add_from_command(path, name, CLAPPERBOARD) if self.media_controller.video(DisplayControllerType.Live, service_item, video_behind_text=True): self.reset_action.setVisible(True) else: critical_error_message_box(UiStrings().LiveBGError, - translate('MediaPlugin.MediaItem', + translate('MediaPlugin.MediaItem', 'There was no display item to amend.')) else: critical_error_message_box(UiStrings().LiveBGError, diff --git a/openlp/plugins/songusage/forms/songusagedeleteform.py b/openlp/plugins/songusage/forms/songusagedeleteform.py index f7c05e0e6..64bd56768 100644 --- a/openlp/plugins/songusage/forms/songusagedeleteform.py +++ b/openlp/plugins/songusage/forms/songusagedeleteform.py @@ -70,4 +70,4 @@ class SongUsageDeleteForm(QtGui.QDialog, Ui_SongUsageDeleteDialog, RegistryPrope ) self.accept() else: - self.reject() \ No newline at end of file + self.reject() diff --git a/openlp/plugins/songusage/forms/songusagedetailform.py b/openlp/plugins/songusage/forms/songusagedetailform.py index eb5963228..e8111c058 100644 --- a/openlp/plugins/songusage/forms/songusagedetailform.py +++ b/openlp/plugins/songusage/forms/songusagedetailform.py @@ -87,15 +87,14 @@ class SongUsageDetailForm(QtGui.QDialog, Ui_SongUsageDetailDialog, RegistryPrope ) return check_directory_exists(path) - file_name = translate('SongUsagePlugin.SongUsageDetailForm', 'usage_detail_%s_%s.txt') % ( - self.from_date_calendar.selectedDate().toString('ddMMyyyy'), - self.to_date_calendar.selectedDate().toString('ddMMyyyy')) + file_name = translate('SongUsagePlugin.SongUsageDetailForm', 'usage_detail_%s_%s.txt') % \ + (self.from_date_calendar.selectedDate().toString('ddMMyyyy'), + self.to_date_calendar.selectedDate().toString('ddMMyyyy')) Settings().setValue(self.plugin.settings_section + '/from date', self.from_date_calendar.selectedDate()) Settings().setValue(self.plugin.settings_section + '/to date', self.to_date_calendar.selectedDate()) usage = self.plugin.manager.get_all_objects( - SongUsageItem, and_( - SongUsageItem.usagedate >= self.from_date_calendar.selectedDate().toPyDate(), - SongUsageItem.usagedate < self.to_date_calendar.selectedDate().toPyDate()), + SongUsageItem, and_(SongUsageItem.usagedate >= self.from_date_calendar.selectedDate().toPyDate(), + SongUsageItem.usagedate < self.to_date_calendar.selectedDate().toPyDate()), [SongUsageItem.usagedate, SongUsageItem.usagetime]) report_file_name = os.path.join(path, file_name) file_handle = None @@ -103,9 +102,9 @@ class SongUsageDetailForm(QtGui.QDialog, Ui_SongUsageDetailDialog, RegistryPrope file_handle = open(report_file_name, 'w') for instance in usage: record = '\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",' \ - '\"%s\",\"%s\"\n' % (instance.usagedate, - instance.usagetime, instance.title, instance.copyright, - instance.ccl_number, instance.authors, instance.plugin_name, instance.source) + '\"%s\",\"%s\"\n' % \ + (instance.usagedate, instance.usagetime, instance.title, instance.copyright, + instance.ccl_number, instance.authors, instance.plugin_name, instance.source) file_handle.write(record.encode('utf-8')) self.main_window.information_message( translate('SongUsagePlugin.SongUsageDetailForm', 'Report Creation'), diff --git a/openlp/plugins/songusage/lib/db.py b/openlp/plugins/songusage/lib/db.py index b7b9c6801..9d9fff460 100644 --- a/openlp/plugins/songusage/lib/db.py +++ b/openlp/plugins/songusage/lib/db.py @@ -62,7 +62,7 @@ def init_schema(url): Column('ccl_number', types.Unicode(65)), Column('plugin_name', types.Unicode(20)), Column('source', types.Unicode(10)) - ) + ) mapper(SongUsageItem, songusage_table) From ed29edec3abf0c7f1dae4388fadf5850e3d2b361 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat <andreaspreikschat@openlp.org> Date: Fri, 21 Mar 2014 21:04:53 +0100 Subject: [PATCH 030/155] imported duplicate check speed --- .../songs/forms/duplicatesongremovalform.py | 54 +++++++++++++------ openlp/plugins/songs/lib/songcompare.py | 11 ++-- 2 files changed, 44 insertions(+), 21 deletions(-) diff --git a/openlp/plugins/songs/forms/duplicatesongremovalform.py b/openlp/plugins/songs/forms/duplicatesongremovalform.py index 2da9113d6..7f65bf3ac 100644 --- a/openlp/plugins/songs/forms/duplicatesongremovalform.py +++ b/openlp/plugins/songs/forms/duplicatesongremovalform.py @@ -31,6 +31,7 @@ The duplicate song removal logic for OpenLP. """ import logging +import multiprocessing import os from PyQt4 import QtCore, QtGui @@ -45,6 +46,17 @@ from openlp.plugins.songs.lib.songcompare import songs_probably_equal log = logging.getLogger(__name__) +class SongIterator(object): + def __init__(self, songs): + self.songs = songs + + def __iter__(self): + for outer_song_counter in range(len(self.songs) - 1): + for inner_song_counter in range(outer_song_counter + 1, len(self.songs)): + yield (self.songs[outer_song_counter], self.songs[inner_song_counter]) + + + class DuplicateSongRemovalForm(OpenLPWizard): """ This is the Duplicate Song Removal Wizard. It provides functionality to search for and remove duplicate songs @@ -167,24 +179,32 @@ class DuplicateSongRemovalForm(OpenLPWizard): max_progress_count = max_songs * (max_songs - 1) // 2 self.duplicate_search_progress_bar.setMaximum(max_progress_count) songs = self.plugin.manager.get_all_objects(Song) - for outer_song_counter in range(max_songs - 1): - for inner_song_counter in range(outer_song_counter + 1, max_songs): - if songs_probably_equal(songs[outer_song_counter], songs[inner_song_counter]): - duplicate_added = self.add_duplicates_to_song_list( - songs[outer_song_counter], songs[inner_song_counter]) - if duplicate_added: - self.found_duplicates_edit.appendPlainText( - songs[outer_song_counter].title + " = " + songs[inner_song_counter].title) - self.duplicate_search_progress_bar.setValue(self.duplicate_search_progress_bar.value() + 1) - # The call to process_events() will keep the GUI responsive. - self.application.process_events() - if self.break_search: - return - self.review_total_count = len(self.duplicate_song_list) - if self.review_total_count == 0: - self.notify_no_duplicates() - else: + # Create a worker/process pool to check the songs. + process_number = max(1, multiprocessing.cpu_count() - 1) + pool = multiprocessing.Pool(process_number) + song_list = SongIterator(songs) + #song_list = [(songs[outer_song_counter], songs[inner_song_counter]) for outer_song_counter in range(max_songs - 1) for inner_song_counter in range(outer_song_counter + 1, max_songs)] + result = pool.imap_unordered(songs_probably_equal, song_list, 30) + # Do not accept any further tasks. Also this closes the processes if all tasks are done. + pool.close() + # While the processes are still working, start to look at the results. + for song_tuple in result: + self.duplicate_search_progress_bar.setValue(self.duplicate_search_progress_bar.value() + 1) + # The call to process_events() will keep the GUI responsive. + self.application.process_events() + if self.break_search: + pool.terminate() + return + if song_tuple is None: + continue + song1, song2 = song_tuple + duplicate_added = self.add_duplicates_to_song_list(song1, song2) + if duplicate_added: + self.found_duplicates_edit.appendPlainText(song1.title + " = " + song2.title) + if self.duplicate_song_list: self.button(QtGui.QWizard.NextButton).show() + else: + self.notify_no_duplicates() finally: self.application.set_normal_cursor() elif page_id == self.review_page_id: diff --git a/openlp/plugins/songs/lib/songcompare.py b/openlp/plugins/songs/lib/songcompare.py index 99a04beb2..09dacd649 100644 --- a/openlp/plugins/songs/lib/songcompare.py +++ b/openlp/plugins/songs/lib/songcompare.py @@ -52,13 +52,15 @@ MIN_BLOCK_SIZE = 70 MAX_TYPO_SIZE = 3 -def songs_probably_equal(song1, song2): +def songs_probably_equal(song1, song2=None): """ Calculate and return whether two songs are probably equal. :param song1: The first song to compare. :param song2: The second song to compare. """ + if song2 is None: + song1, song2 = song1 if len(song1.search_lyrics) < len(song2.search_lyrics): small = song1.search_lyrics large = song2.search_lyrics @@ -75,8 +77,9 @@ def songs_probably_equal(song1, song2): for element in diff_no_typos: if element[0] == "equal" and _op_length(element) >= MIN_BLOCK_SIZE: length_of_equal_blocks += _op_length(element) + if length_of_equal_blocks >= MIN_BLOCK_SIZE: - return True + return song1, song2 # Check 2: Similarity based on the relative length of the longest equal block. # Calculate the length of the largest equal block of the diff set. length_of_longest_equal_block = 0 @@ -84,9 +87,9 @@ def songs_probably_equal(song1, song2): if element[0] == "equal" and _op_length(element) > length_of_longest_equal_block: length_of_longest_equal_block = _op_length(element) if length_of_equal_blocks >= MIN_BLOCK_SIZE or length_of_longest_equal_block > len(small) * 2 // 3: - return True + return song1, song2 # Both checks failed. We assume the songs are not equal. - return False + return None def _op_length(opcode): From bbe4169c24b2e518c23a2c4c3ad342e39d138b58 Mon Sep 17 00:00:00 2001 From: Tim Bentley <tim.bentley@gmail.com> Date: Fri, 21 Mar 2014 21:38:08 +0000 Subject: [PATCH 031/155] Fix all formatting errors --- .../custom/forms/editcustomslidedialog.py | 3 +- openlp/plugins/custom/lib/mediaitem.py | 22 +++--- .../presentations/lib/impresscontroller.py | 8 +- .../presentations/lib/pdfcontroller.py | 6 +- .../presentations/lib/pptviewlib/ppttest.py | 10 +-- openlp/plugins/remotes/lib/httpserver.py | 3 - openlp/plugins/remotes/lib/remotetab.py | 7 +- openlp/plugins/remotes/remoteplugin.py | 16 ++-- .../songs/forms/duplicatesongremovalform.py | 6 +- openlp/plugins/songs/forms/editsongform.py | 8 +- .../plugins/songs/forms/mediafilesdialog.py | 1 - openlp/plugins/songs/forms/mediafilesform.py | 1 - openlp/plugins/songs/forms/songimportform.py | 8 +- .../songs/forms/songmaintenancedialog.py | 4 +- .../songs/forms/songmaintenanceform.py | 2 +- .../plugins/songs/forms/songreviewwidget.py | 2 +- openlp/plugins/songs/lib/__init__.py | 1 - openlp/plugins/songs/lib/cclifileimport.py | 4 +- openlp/plugins/songs/lib/db.py | 34 +++++---- openlp/plugins/songs/lib/dreambeamimport.py | 5 +- .../plugins/songs/lib/foilpresenterimport.py | 15 ++-- openlp/plugins/songs/lib/mediaitem.py | 6 +- openlp/plugins/songs/lib/olpimport.py | 6 +- openlp/plugins/songs/lib/openlyricsexport.py | 5 +- openlp/plugins/songs/lib/powersongimport.py | 4 +- openlp/plugins/songs/lib/sofimport.py | 11 ++- openlp/plugins/songs/lib/songcompare.py | 2 +- openlp/plugins/songs/lib/songimport.py | 2 +- .../plugins/songs/lib/songshowplusimport.py | 4 +- openlp/plugins/songs/lib/sundayplusimport.py | 3 +- openlp/plugins/songs/lib/test/test.opensong | 72 ------------------ .../plugins/songs/lib/test/test.opensong.zip | Bin 855 -> 0 bytes openlp/plugins/songs/lib/test/test2.opensong | 45 ----------- openlp/plugins/songs/lib/test/test3.opensong | 10 --- .../songs/lib/test/test_import_file.py | 50 ------------ .../songs/lib/test/test_importing_lots.py | 61 --------------- openlp/plugins/songs/lib/ui.py | 4 +- openlp/plugins/songs/lib/upgrade.py | 1 - .../songs/lib/worshipcenterproimport.py | 6 +- openlp/plugins/songs/lib/xml.py | 3 +- openlp/plugins/songs/lib/zionworximport.py | 2 +- openlp/plugins/songs/songsplugin.py | 2 +- 42 files changed, 111 insertions(+), 354 deletions(-) delete mode 100644 openlp/plugins/songs/lib/test/test.opensong delete mode 100644 openlp/plugins/songs/lib/test/test.opensong.zip delete mode 100644 openlp/plugins/songs/lib/test/test2.opensong delete mode 100644 openlp/plugins/songs/lib/test/test3.opensong delete mode 100644 openlp/plugins/songs/lib/test/test_import_file.py delete mode 100644 openlp/plugins/songs/lib/test/test_importing_lots.py diff --git a/openlp/plugins/custom/forms/editcustomslidedialog.py b/openlp/plugins/custom/forms/editcustomslidedialog.py index a94bdd109..3b74566fc 100644 --- a/openlp/plugins/custom/forms/editcustomslidedialog.py +++ b/openlp/plugins/custom/forms/editcustomslidedialog.py @@ -33,6 +33,7 @@ from openlp.core.common import UiStrings, translate from openlp.core.lib import SpellTextEdit from openlp.core.lib.ui import create_button, create_button_box + class Ui_CustomSlideEditDialog(object): def setupUi(self, custom_slide_edit_dialog): custom_slide_edit_dialog.setObjectName('custom_slide_edit_dialog') @@ -45,7 +46,7 @@ class Ui_CustomSlideEditDialog(object): self.insert_button = create_button(custom_slide_edit_dialog, 'insertButton', icon=':/general/general_add.png') self.button_box = create_button_box(custom_slide_edit_dialog, 'button_box', ['cancel', 'save'], - [self.split_button, self.insert_button]) + [self.split_button, self.insert_button]) self.dialog_layout.addWidget(self.button_box) self.retranslateUi(custom_slide_edit_dialog) diff --git a/openlp/plugins/custom/lib/mediaitem.py b/openlp/plugins/custom/lib/mediaitem.py index cfea319e6..9ecfac779 100644 --- a/openlp/plugins/custom/lib/mediaitem.py +++ b/openlp/plugins/custom/lib/mediaitem.py @@ -33,8 +33,8 @@ from PyQt4 import QtCore, QtGui from sqlalchemy.sql import or_, func, and_ from openlp.core.common import Registry, Settings, UiStrings, translate -from openlp.core.lib import MediaManagerItem, ItemCapabilities, ServiceItemContext, PluginStatus,\ - check_item_selected +from openlp.core.lib import MediaManagerItem, ItemCapabilities, ServiceItemContext, PluginStatus, \ + check_item_selected from openlp.plugins.custom.forms.editcustomform import EditCustomForm from openlp.plugins.custom.lib import CustomXMLParser, CustomXMLBuilder from openlp.plugins.custom.lib.db import CustomSlide @@ -105,12 +105,10 @@ class CustomMediaItem(MediaManagerItem): """ Initialise the UI so it can provide Searches """ - self.search_text_edit.set_search_types([(CustomSearch.Titles, ':/songs/song_search_title.png', - translate('SongsPlugin.MediaItem', 'Titles'), - translate('SongsPlugin.MediaItem', 'Search Titles...')), - (CustomSearch.Themes, ':/slides/slide_theme.png', UiStrings().Themes, - UiStrings().SearchThemes) - ]) + self.search_text_edit.set_search_types( + [(CustomSearch.Titles, ':/songs/song_search_title.png', translate('SongsPlugin.MediaItem', 'Titles'), + translate('SongsPlugin.MediaItem', 'Search Titles...')), + (CustomSearch.Themes, ':/slides/slide_theme.png', UiStrings().Themes, UiStrings().SearchThemes)]) self.search_text_edit.set_current_search_type(Settings().value('%s/last search type' % self.settings_section)) self.load_list(self.plugin.db_manager.get_all_objects(CustomSlide, order_by_ref=CustomSlide.title)) self.config_update() @@ -191,10 +189,9 @@ class CustomMediaItem(MediaManagerItem): if QtGui.QMessageBox.question(self, UiStrings().ConfirmDelete, translate('CustomPlugin.MediaItem', 'Are you sure you want to delete the %n selected custom slide(s)?', - '', - QtCore.QCoreApplication.CodecForTr, len(items)), - QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes - | QtGui.QMessageBox.No), + '', QtCore.QCoreApplication.CodecForTr, len(items)), + QtGui.QMessageBox.StandardButtons( + QtGui.QMessageBox.Yes | QtGui.QMessageBox.No), QtGui.QMessageBox.Yes) == QtGui.QMessageBox.No: return row_list = [item.row() for item in self.list_view.selectedIndexes()] @@ -348,4 +345,3 @@ class CustomMediaItem(MediaManagerItem): func.lower(CustomSlide.text).like(search)), order_by_ref=CustomSlide.title) return [[custom.id, custom.title] for custom in search_results] - diff --git a/openlp/plugins/presentations/lib/impresscontroller.py b/openlp/plugins/presentations/lib/impresscontroller.py index 356a6cceb..c55873c5f 100644 --- a/openlp/plugins/presentations/lib/impresscontroller.py +++ b/openlp/plugins/presentations/lib/impresscontroller.py @@ -29,7 +29,8 @@ # OOo API documentation: # http://api.openoffice.org/docs/common/ref/com/sun/star/presentation/XSlideShowController.html -# http://wiki.services.openoffice.org/wiki/Documentation/DevGuide/ProUNO/Basic/Getting_Information_about_UNO_Objects#Inspecting_interfaces_during_debugging +# http://wiki.services.openoffice.org/wiki/Documentation/DevGuide/ProUNO/Basic +# /Getting_Information_about_UNO_Objects#Inspecting_interfaces_during_debugging # http://docs.go-oo.org/sd/html/classsd_1_1SlideShow.html # http://www.oooforum.org/forum/viewtopic.phtml?t=5252 # http://wiki.services.openoffice.org/wiki/Documentation/DevGuide/Working_with_Presentations @@ -45,6 +46,7 @@ if os.name == 'nt': from win32com.client import Dispatch import pywintypes # Declare an empty exception to match the exception imported from UNO + class ErrorCodeIOException(Exception): pass else: @@ -204,7 +206,7 @@ class ImpressDocument(PresentationDocument): Class which holds information and controls a single presentation. """ - def __init__ (self, controller, presentation): + def __init__(self, controller, presentation): """ Constructor, store information about the file and initialise. """ @@ -353,7 +355,7 @@ class ImpressDocument(PresentationDocument): log.debug('unblank screen OpenOffice') return self.control.resume() - def blank_screen (self): + def blank_screen(self): """ Blanks the screen. """ diff --git a/openlp/plugins/presentations/lib/pdfcontroller.py b/openlp/plugins/presentations/lib/pdfcontroller.py index 2dade90a1..597b7d78b 100644 --- a/openlp/plugins/presentations/lib/pdfcontroller.py +++ b/openlp/plugins/presentations/lib/pdfcontroller.py @@ -132,7 +132,8 @@ class PdfController(PresentationController): DEVNULL = open(os.devnull, 'wb') # First try to find mupdf try: - self.mudrawbin = check_output(['which', 'mudraw'], stderr=DEVNULL).decode(encoding='UTF-8').rstrip('\n') + self.mudrawbin = check_output(['which', 'mudraw'], + stderr=DEVNULL).decode(encoding='UTF-8').rstrip('\n') except CalledProcessError: self.mudrawbin = '' # if mupdf isn't installed, fallback to ghostscript @@ -192,7 +193,8 @@ class PdfDocument(PresentationDocument): :return: The resolution dpi to be used. """ # Use a postscript script to get size of the pdf. It is assumed that all pages have same size - gs_resolution_script = AppLocation.get_directory(AppLocation.PluginsDir) + '/presentations/lib/ghostscript_get_resolution.ps' + gs_resolution_script = AppLocation.get_directory( + AppLocation.PluginsDir) + '/presentations/lib/ghostscript_get_resolution.ps' # Run the script on the pdf to get the size runlog = [] try: diff --git a/openlp/plugins/presentations/lib/pptviewlib/ppttest.py b/openlp/plugins/presentations/lib/pptviewlib/ppttest.py index 6c48f9ee0..49a820670 100644 --- a/openlp/plugins/presentations/lib/pptviewlib/ppttest.py +++ b/openlp/plugins/presentations/lib/pptviewlib/ppttest.py @@ -32,6 +32,7 @@ from PyQt4 import QtGui, QtCore from ctypes import * from ctypes.wintypes import RECT + class PPTViewer(QtGui.QWidget): """ Standalone Test Harness for the pptviewlib library @@ -169,16 +170,16 @@ class PPTViewer(QtGui.QWidget): app.processEvents() def openClick(self): - oldid = self.pptid; + oldid = self.pptid rect = RECT(int(self.xEdit.text()), int(self.yEdit.text()), - int(self.widthEdit.text()), int(self.heightEdit.text())) + int(self.widthEdit.text()), int(self.heightEdit.text())) filename = str(self.pptEdit.text().replace('/', '\\')) folder = str(self.folderEdit.text().replace('/', '\\')) print(filename, folder) self.pptid = self.pptdll.OpenPPT(filename, None, rect, folder) print('id: ' + str(self.pptid)) if oldid >= 0: - self.pptdll.ClosePPT(oldid); + self.pptdll.ClosePPT(oldid) slides = self.pptdll.GetSlideCount(self.pptid) print('slidecount: ' + str(slides)) self.total.setNum(self.pptdll.GetSlideCount(self.pptid)) @@ -201,8 +202,7 @@ class PPTViewer(QtGui.QWidget): app.processEvents() def openDialog(self): - self.pptEdit.setText(QtGui.QFileDialog.getOpenFileName(self, - 'Open file')) + self.pptEdit.setText(QtGui.QFileDialog.getOpenFileName(self, 'Open file')) if __name__ == '__main__': pptdll = cdll.LoadLibrary(r'pptviewlib.dll') diff --git a/openlp/plugins/remotes/lib/httpserver.py b/openlp/plugins/remotes/lib/httpserver.py index b57bd29d3..fa578184b 100644 --- a/openlp/plugins/remotes/lib/httpserver.py +++ b/openlp/plugins/remotes/lib/httpserver.py @@ -144,6 +144,3 @@ class HTTPSServer(HTTPServer): server_side=True) self.server_bind() self.server_activate() - - - diff --git a/openlp/plugins/remotes/lib/remotetab.py b/openlp/plugins/remotes/lib/remotetab.py index 540b8d212..d6b96cc1c 100644 --- a/openlp/plugins/remotes/lib/remotetab.py +++ b/openlp/plugins/remotes/lib/remotetab.py @@ -225,7 +225,8 @@ class RemoteTab(SettingsTab): continue for address in interface.addressEntries(): ip = address.ip() - if ip.protocol() == QtNetwork.QAbstractSocket.IPv4Protocol and ip != QtNetwork.QHostAddress.LocalHost: + if ip.protocol() == QtNetwork.QAbstractSocket.IPv4Protocol and \ + ip != QtNetwork.QHostAddress.LocalHost: return ip.toString() return ip_address @@ -262,9 +263,9 @@ class RemoteTab(SettingsTab): Settings().value(self.settings_section + '/port') != self.port_spin_box.value() or \ Settings().value(self.settings_section + '/https port') != self.https_port_spin_box.value() or \ Settings().value(self.settings_section + '/https enabled') != \ - self.https_settings_group_box.isChecked() or \ + self.https_settings_group_box.isChecked() or \ Settings().value(self.settings_section + '/authentication enabled') != \ - self.user_login_group_box.isChecked(): + self.user_login_group_box.isChecked(): self.settings_form.register_post_process('remotes_config_updated') Settings().setValue(self.settings_section + '/port', self.port_spin_box.value()) Settings().setValue(self.settings_section + '/https port', self.https_port_spin_box.value()) diff --git a/openlp/plugins/remotes/remoteplugin.py b/openlp/plugins/remotes/remoteplugin.py index 3517c4f9c..393f08dd9 100644 --- a/openlp/plugins/remotes/remoteplugin.py +++ b/openlp/plugins/remotes/remoteplugin.py @@ -36,14 +36,14 @@ from openlp.plugins.remotes.lib import RemoteTab, OpenLPServer log = logging.getLogger(__name__) __default_settings__ = { - 'remotes/twelve hour': True, - 'remotes/port': 4316, - 'remotes/https port': 4317, - 'remotes/https enabled': False, - 'remotes/user id': 'openlp', - 'remotes/password': 'password', - 'remotes/authentication enabled': False, - 'remotes/ip address': '0.0.0.0' + 'remotes/twelve hour': True, + 'remotes/port': 4316, + 'remotes/https port': 4317, + 'remotes/https enabled': False, + 'remotes/user id': 'openlp', + 'remotes/password': 'password', + 'remotes/authentication enabled': False, + 'remotes/ip address': '0.0.0.0' } diff --git a/openlp/plugins/songs/forms/duplicatesongremovalform.py b/openlp/plugins/songs/forms/duplicatesongremovalform.py index fc5e9a809..22299cde5 100644 --- a/openlp/plugins/songs/forms/duplicatesongremovalform.py +++ b/openlp/plugins/songs/forms/duplicatesongremovalform.py @@ -64,8 +64,8 @@ class DuplicateSongRemovalForm(OpenLPWizard, RegistryProperties): # Used to interrupt ongoing searches when cancel is clicked. self.break_search = False super(DuplicateSongRemovalForm, self).__init__( - Registry().get('main_window'), plugin, 'duplicateSongRemovalWizard', ':/wizards/wizard_duplicateremoval.bmp' - , False) + Registry().get('main_window'), plugin, 'duplicateSongRemovalWizard', + ':/wizards/wizard_duplicateremoval.bmp', False) self.setMinimumWidth(730) def custom_signals(self): @@ -327,4 +327,4 @@ class DuplicateSongRemovalForm(OpenLPWizard, RegistryProperties): self.button(QtGui.QWizard.FinishButton).show() self.button(QtGui.QWizard.FinishButton).setEnabled(True) self.button(QtGui.QWizard.NextButton).hide() - self.button(QtGui.QWizard.CancelButton).hide() \ No newline at end of file + self.button(QtGui.QWizard.CancelButton).hide() diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index 06639fe18..8b1e3a897 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -185,11 +185,11 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): if len(invalid_verses) > 1: msg = translate('SongsPlugin.EditSongForm', 'There are no verses corresponding to "%(invalid)s".' 'Valid entries are %(valid)s.\nPlease enter the verses separated by spaces.') % \ - {'invalid': ', '.join(invalid_verses), 'valid' : valid} + {'invalid': ', '.join(invalid_verses), 'valid': valid} else: msg = translate('SongsPlugin.EditSongForm', 'There is no verse corresponding to "%(invalid)s".' 'Valid entries are %(valid)s.\nPlease enter the verses separated by spaces.') % \ - {'invalid': invalid_verses[0], 'valid' : valid} + {'invalid': invalid_verses[0], 'valid': valid} critical_error_message_box(title=translate('SongsPlugin.EditSongForm', 'Invalid Verse Order'), message=msg) return len(invalid_verses) == 0 @@ -257,7 +257,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): self.song.lyrics = str(sxml.extract_xml(), 'utf-8') for verse in multiple: self.song.verse_order = re.sub('([' + verse.upper() + verse.lower() + '])(\W|$)', - r'\g<1>1\2', self.song.verse_order) + r'\g<1>1\2', self.song.verse_order) except: log.exception('Problem processing song Lyrics \n%s', sxml.dump_xml()) raise @@ -955,4 +955,4 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): log.exception('Could not remove directory: %s', save_path) clean_song(self.manager, self.song) self.manager.save_object(self.song) - self.media_item.auto_select_id = self.song.id \ No newline at end of file + self.media_item.auto_select_id = self.song.id diff --git a/openlp/plugins/songs/forms/mediafilesdialog.py b/openlp/plugins/songs/forms/mediafilesdialog.py index 09fbd96f5..a0392e3db 100644 --- a/openlp/plugins/songs/forms/mediafilesdialog.py +++ b/openlp/plugins/songs/forms/mediafilesdialog.py @@ -71,4 +71,3 @@ class Ui_MediaFilesDialog(object): self.select_label.setText(translate('SongsPlugin.MediaFilesForm', 'Select one or more audio files from the list below, and click OK to import them ' 'into this song.')) - diff --git a/openlp/plugins/songs/forms/mediafilesform.py b/openlp/plugins/songs/forms/mediafilesform.py index 660a9f72d..ec689ddb6 100644 --- a/openlp/plugins/songs/forms/mediafilesform.py +++ b/openlp/plugins/songs/forms/mediafilesform.py @@ -56,4 +56,3 @@ class MediaFilesForm(QtGui.QDialog, Ui_MediaFilesDialog): def get_selected_files(self): return [item.data(QtCore.Qt.UserRole) for item in self.file_list_widget.selectedItems()] - diff --git a/openlp/plugins/songs/forms/songimportform.py b/openlp/plugins/songs/forms/songimportform.py index e87c9f645..27f0d9343 100644 --- a/openlp/plugins/songs/forms/songimportform.py +++ b/openlp/plugins/songs/forms/songimportform.py @@ -99,7 +99,8 @@ class SongImportForm(OpenLPWizard, RegistryProperties): self.format_widgets[song_format]['removeButton'].clicked.connect(self.on_remove_button_clicked) else: self.format_widgets[song_format]['browseButton'].clicked.connect(self.on_browse_button_clicked) - self.format_widgets[song_format]['file_path_edit'].textChanged.connect(self.on_filepath_edit_text_changed) + self.format_widgets[song_format]['file_path_edit'].textChanged.\ + connect(self.on_filepath_edit_text_changed) def add_custom_pages(self): """ @@ -163,7 +164,8 @@ class SongImportForm(OpenLPWizard, RegistryProperties): f_label = 'Filename:' if select_mode == SongFormatSelect.SingleFolder: f_label = 'Folder:' - self.format_widgets[format_list]['filepathLabel'].setText(translate('SongsPlugin.ImportWizardForm', f_label)) + self.format_widgets[format_list]['filepathLabel'].setText( + translate('SongsPlugin.ImportWizardForm', f_label)) for format_list in self.disablable_formats: self.format_widgets[format_list]['disabled_label'].setText(SongFormat.get(format_list, 'disabledLabelText')) self.progress_page.setTitle(WizardStrings.Importing) @@ -244,7 +246,7 @@ class SongImportForm(OpenLPWizard, RegistryProperties): if file_names: listbox.addItems(file_names) Settings().setValue(self.plugin.settings_section + '/last directory import', - os.path.split(str(file_names[0]))[0]) + os.path.split(str(file_names[0]))[0]) def get_list_of_files(self, list_box): """ diff --git a/openlp/plugins/songs/forms/songmaintenancedialog.py b/openlp/plugins/songs/forms/songmaintenancedialog.py index 200b4b1ff..84e3535d3 100644 --- a/openlp/plugins/songs/forms/songmaintenancedialog.py +++ b/openlp/plugins/songs/forms/songmaintenancedialog.py @@ -162,6 +162,6 @@ class Ui_SongMaintenanceDialog(object): self.edit_book_button.setText(UiStrings().Edit) self.delete_book_button.setText(UiStrings().Delete) type_list_width = max(self.fontMetrics().width(SongStrings.Authors), - self.fontMetrics().width(SongStrings.Topics), - self.fontMetrics().width(SongStrings.SongBooks)) + self.fontMetrics().width(SongStrings.Topics), + self.fontMetrics().width(SongStrings.SongBooks)) self.type_list_widget.setFixedWidth(type_list_width + self.type_list_widget.iconSize().width() + 32) diff --git a/openlp/plugins/songs/forms/songmaintenanceform.py b/openlp/plugins/songs/forms/songmaintenanceform.py index 8f92e9cae..4e9bdad93 100644 --- a/openlp/plugins/songs/forms/songmaintenanceform.py +++ b/openlp/plugins/songs/forms/songmaintenanceform.py @@ -531,4 +531,4 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog, RegistryPrope edit_button.setEnabled(False) else: delete_button.setEnabled(True) - edit_button.setEnabled(True) \ No newline at end of file + edit_button.setEnabled(True) diff --git a/openlp/plugins/songs/forms/songreviewwidget.py b/openlp/plugins/songs/forms/songreviewwidget.py index 839174009..02d7b8774 100644 --- a/openlp/plugins/songs/forms/songreviewwidget.py +++ b/openlp/plugins/songs/forms/songreviewwidget.py @@ -191,7 +191,7 @@ class SongReviewWidget(QtGui.QWidget): self.song_remove_button.setObjectName('song_remove_button') self.song_remove_button.setIcon(build_icon(':/songs/song_delete.png')) self.song_remove_button.setSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) - self.song_vertical_layout.addWidget(self.song_remove_button, alignment = QtCore.Qt.AlignHCenter) + self.song_vertical_layout.addWidget(self.song_remove_button, alignment=QtCore.Qt.AlignHCenter) def retranslateUi(self): self.song_remove_button.setText('Remove') diff --git a/openlp/plugins/songs/lib/__init__.py b/openlp/plugins/songs/lib/__init__.py index 1c9141b16..dc198d4b7 100644 --- a/openlp/plugins/songs/lib/__init__.py +++ b/openlp/plugins/songs/lib/__init__.py @@ -549,4 +549,3 @@ def delete_song(song_id, song_plugin): except OSError: log.exception('Could not remove directory: %s', save_path) song_plugin.manager.delete_object(Song, song_id) - diff --git a/openlp/plugins/songs/lib/cclifileimport.py b/openlp/plugins/songs/lib/cclifileimport.py index 54779a194..0866a1cc7 100644 --- a/openlp/plugins/songs/lib/cclifileimport.py +++ b/openlp/plugins/songs/lib/cclifileimport.py @@ -89,8 +89,8 @@ class CCLIFileImport(SongImport): if not self.do_import_txt_file(lines): self.log_error(filename) else: - self.log_error(filename, - translate('SongsPlugin.CCLIFileImport', 'The file does not have a valid extension.')) + self.log_error(filename, translate('SongsPlugin.CCLIFileImport', 'The file does not have a valid ' + 'extension.')) log.info('Extension %s is not valid', filename) if self.stop_import_flag: return diff --git a/openlp/plugins/songs/lib/db.py b/openlp/plugins/songs/lib/db.py index ad5bee1a1..c3965e2ed 100644 --- a/openlp/plugins/songs/lib/db.py +++ b/openlp/plugins/songs/lib/db.py @@ -172,7 +172,8 @@ def init_schema(url): session, metadata = init_db(url) # Definition of the "authors" table - authors_table = Table('authors', metadata, + authors_table = Table( + 'authors', metadata, Column('id', types.Integer(), primary_key=True), Column('first_name', types.Unicode(128)), Column('last_name', types.Unicode(128)), @@ -180,7 +181,8 @@ def init_schema(url): ) # Definition of the "media_files" table - media_files_table = Table('media_files', metadata, + media_files_table = Table( + 'media_files', metadata, Column('id', types.Integer(), primary_key=True), Column('song_id', types.Integer(), ForeignKey('songs.id'), default=None), Column('file_name', types.Unicode(255), nullable=False), @@ -189,14 +191,16 @@ def init_schema(url): ) # Definition of the "song_books" table - song_books_table = Table('song_books', metadata, + song_books_table = Table( + 'song_books', metadata, Column('id', types.Integer(), primary_key=True), Column('name', types.Unicode(128), nullable=False), Column('publisher', types.Unicode(128)) ) # Definition of the "songs" table - songs_table = Table('songs', metadata, + songs_table = Table( + 'songs', metadata, Column('id', types.Integer(), primary_key=True), Column('song_book_id', types.Integer(), ForeignKey('song_books.id'), default=None), Column('title', types.Unicode(255), nullable=False), @@ -216,19 +220,22 @@ def init_schema(url): ) # Definition of the "topics" table - topics_table = Table('topics', metadata, + topics_table = Table( + 'topics', metadata, Column('id', types.Integer(), primary_key=True), Column('name', types.Unicode(128), index=True, nullable=False) ) # Definition of the "authors_songs" table - authors_songs_table = Table('authors_songs', metadata, + authors_songs_table = Table( + 'authors_songs', metadata, Column('author_id', types.Integer(), ForeignKey('authors.id'), primary_key=True), Column('song_id', types.Integer(), ForeignKey('songs.id'), primary_key=True) ) # Definition of the "songs_topics" table - songs_topics_table = Table('songs_topics', metadata, + songs_topics_table = Table( + 'songs_topics', metadata, Column('song_id', types.Integer(), ForeignKey('songs.id'), primary_key=True), Column('topic_id', types.Integer(), ForeignKey('topics.id'), primary_key=True) ) @@ -236,13 +243,12 @@ 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, lazy=False), - 'book': relation(Book, backref='songs'), - 'media_files': relation(MediaFile, backref='songs', order_by=media_files_table.c.weight), - 'topics': relation(Topic, backref='songs', secondary=songs_topics_table) - }) + mapper(Song, songs_table, properties={ + 'authors': relation(Author, backref='songs', secondary=authors_songs_table, lazy=False), + 'book': relation(Book, backref='songs'), + 'media_files': relation(MediaFile, backref='songs', order_by=media_files_table.c.weight), + 'topics': relation(Topic, backref='songs', secondary=songs_topics_table) + }) mapper(Topic, topics_table) metadata.create_all(checkfirst=True) diff --git a/openlp/plugins/songs/lib/dreambeamimport.py b/openlp/plugins/songs/lib/dreambeamimport.py index 18da6d8e3..375867aac 100644 --- a/openlp/plugins/songs/lib/dreambeamimport.py +++ b/openlp/plugins/songs/lib/dreambeamimport.py @@ -120,7 +120,7 @@ class DreamBeamImport(SongImport): author_copyright = song_xml.Author.text if hasattr(song_xml, 'SongLyrics'): for lyrics_item in song_xml.SongLyrics.iterchildren(): - verse_type = lyrics_item.get('Type') + verse_type = lyrics_item.get('Type') verse_number = lyrics_item.get('Number') verse_text = str(lyrics_item.text) self.add_verse(verse_text, ('%s%s' % (verse_type[:1], verse_number))) @@ -145,8 +145,7 @@ class DreamBeamImport(SongImport): author_copyright = song_xml.Text2.Text.text if author_copyright: author_copyright = str(author_copyright) - if author_copyright.find( - str(SongStrings.CopyrightSymbol)) >= 0: + if author_copyright.find(str(SongStrings.CopyrightSymbol)) >= 0: self.add_copyright(author_copyright) else: self.parse_author(author_copyright) diff --git a/openlp/plugins/songs/lib/foilpresenterimport.py b/openlp/plugins/songs/lib/foilpresenterimport.py index 3b6d4e9a8..6f8bd6978 100644 --- a/openlp/plugins/songs/lib/foilpresenterimport.py +++ b/openlp/plugins/songs/lib/foilpresenterimport.py @@ -298,10 +298,10 @@ class FoilPresenter(object): temp = copyright.partition('Rechte') copyright = temp[0] markers = ['Text +u\.?n?d? +Melodie[\w\,\. ]*:', - 'Text +u\.?n?d? +Musik', 'T & M', 'Melodie und Satz', - 'Text[\w\,\. ]*:', 'Melodie', 'Musik', 'Satz', - 'Weise', '[dD]eutsch', '[dD]t[\.\:]', 'Englisch', - '[oO]riginal', 'Bearbeitung', '[R|r]efrain'] + 'Text +u\.?n?d? +Musik', 'T & M', 'Melodie und Satz', + 'Text[\w\,\. ]*:', 'Melodie', 'Musik', 'Satz', + 'Weise', '[dD]eutsch', '[dD]t[\.\:]', 'Englisch', + '[oO]riginal', 'Bearbeitung', '[R|r]efrain'] for marker in markers: copyright = re.compile(marker).sub('<marker>', copyright, re.U) copyright = re.compile('(?<=<marker>) *:').sub('', copyright) @@ -324,12 +324,9 @@ class FoilPresenter(object): for tempx in temp: author_temp.append(tempx) for author in author_temp: - regex = '^[\/,;\-\s\.]+|[\/,;\-\s\.]+$|'\ - '\s*[0-9]{4}\s*[\-\/]?\s*([0-9]{4})?[\/,;\-\s\.]*$' + regex = '^[\/,;\-\s\.]+|[\/,;\-\s\.]+$|\s*[0-9]{4}\s*[\-\/]?\s*([0-9]{4})?[\/,;\-\s\.]*$' author = re.compile(regex).sub('', author) - author = re.compile( - '[0-9]{1,2}\.\s?J(ahr)?h\.|um\s*$|vor\s*$').sub('', - author) + author = re.compile('[0-9]{1,2}\.\s?J(ahr)?h\.|um\s*$|vor\s*$').sub('', author) author = re.compile('[N|n]ach.*$').sub('', author) author = author.strip() if re.search('\w+\.?\s+\w{3,}\s+[a|u]nd\s|\w+\.?\s+\w{3,}\s+&\s', author, re.U): diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index 27571c0ca..ad981135f 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -493,9 +493,9 @@ class SongMediaItem(MediaManagerItem): # FIXME: This file seems to be an old one (prior to 1.9.5), which means, that the search title # (data_string[u'title']) is probably wrong. We add "@" to search title and hope that we do not add any # duplicate. This should work for songs without alternate title. - search_results = self.plugin.manager.get_all_objects(Song, - Song.search_title == (re.compile(r'\W+', re.UNICODE).sub(' ', - item.data_string['title'].strip()) + '@').strip().lower(), Song.search_title.asc()) + temp = (re.compile(r'\W+', re.UNICODE).sub(' ', item.data_string['title'].strip()) + '@').strip().lower() + search_results = \ + self.plugin.manager.get_all_objects(Song, Song.search_title == temp, Song.search_title.asc()) else: search_results = self.plugin.manager.get_all_objects( Song, Song.search_title == item.data_string['title'], Song.search_title.asc()) diff --git a/openlp/plugins/songs/lib/olpimport.py b/openlp/plugins/songs/lib/olpimport.py index 171859432..335ba606a 100644 --- a/openlp/plugins/songs/lib/olpimport.py +++ b/openlp/plugins/songs/lib/olpimport.py @@ -128,11 +128,9 @@ class OpenLPSongImport(SongImport): except UnmappedClassError: mapper(OldMediaFile, source_media_files_table) song_props = { - 'authors': relation(OldAuthor, backref='songs', - secondary=source_authors_songs_table), + '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) + 'topics': relation(OldTopic, backref='songs', secondary=source_songs_topics_table) } if has_media_files: if isinstance(source_media_files_songs_table, Table): diff --git a/openlp/plugins/songs/lib/openlyricsexport.py b/openlp/plugins/songs/lib/openlyricsexport.py index 735ef3afe..72210e89f 100644 --- a/openlp/plugins/songs/lib/openlyricsexport.py +++ b/openlp/plugins/songs/lib/openlyricsexport.py @@ -68,8 +68,8 @@ class OpenLyricsExport(RegistryProperties): self.application.process_events() if self.parent.stop_export_flag: return False - self.parent.increment_progress_bar(translate('SongsPlugin.OpenLyricsExport', 'Exporting "%s"...') % - song.title) + self.parent.increment_progress_bar( + translate('SongsPlugin.OpenLyricsExport', 'Exporting "%s"...') % song.title) xml = open_lyrics.song_to_xml(song) tree = etree.ElementTree(etree.fromstring(xml.encode())) filename = '%s (%s)' % (song.title, ', '.join([author.display_name for author in song.authors])) @@ -81,4 +81,3 @@ class OpenLyricsExport(RegistryProperties): tree.write(open(os.path.join(self.save_path, filename), 'wb'), encoding='utf-8', xml_declaration=True, pretty_print=True) return True - diff --git a/openlp/plugins/songs/lib/powersongimport.py b/openlp/plugins/songs/lib/powersongimport.py index 7c5b9e02e..cd568bc2c 100644 --- a/openlp/plugins/songs/lib/powersongimport.py +++ b/openlp/plugins/songs/lib/powersongimport.py @@ -122,7 +122,7 @@ class PowerSongImport(SongImport): parse_error = True self.log_error(os.path.basename(file), str( translate('SongsPlugin.PowerSongImport', 'Invalid %s file. Unexpected byte value.')) % - ps_string) + ps_string) break else: if label == 'TITLE': @@ -145,7 +145,7 @@ class PowerSongImport(SongImport): if not found_copyright: self.log_error(self.title, str( translate('SongsPlugin.PowerSongImport', 'Invalid %s file. Missing "COPYRIGHTLINE" header.')) % - ps_string) + ps_string) continue # Check that file had at least one verse if not self.verses: diff --git a/openlp/plugins/songs/lib/sofimport.py b/openlp/plugins/songs/lib/sofimport.py index a41472201..e44034648 100644 --- a/openlp/plugins/songs/lib/sofimport.py +++ b/openlp/plugins/songs/lib/sofimport.py @@ -287,8 +287,7 @@ class SofImport(OooImport): :param text: The verse text """ - if self.italics != self.is_chorus and ((len(self.verses) > 0) or - (self.current__verse.count('\n') > 1)): + if self.italics != self.is_chorus and ((len(self.verses) > 0) or (self.current__verse.count('\n') > 1)): self.finish_verse() if self.italics: self.is_chorus = True @@ -348,10 +347,10 @@ class SofImport(OooImport): for i in range(1, len(text_arr)): # Do not translate these. Fixed strings in SOF song file if text_arr[i] in ('JESUS', 'CHRIST', 'KING', 'ALMIGHTY', 'REDEEMER', 'SHEPHERD', 'SON', 'GOD', 'LORD', - 'FATHER', 'HOLY', 'SPIRIT', 'LAMB', 'YOU', 'YOUR', 'I', 'I\'VE', 'I\'M', 'I\'LL', - 'SAVIOUR', 'O', 'YOU\'RE', 'HE', 'HIS', 'HIM', 'ZION', 'EMMANUEL', 'MAJESTY', 'JESUS\'', - 'JIREH', 'JUDAH', 'LION', 'LORD\'S', 'ABRAHAM', 'GOD\'S', 'FATHER\'S', 'ELIJAH' 'MARTHA', - 'CHRISTMAS', 'ALPHA', 'OMEGA'): + 'FATHER', 'HOLY', 'SPIRIT', 'LAMB', 'YOU', 'YOUR', 'I', 'I\'VE', 'I\'M', 'I\'LL', + 'SAVIOUR', 'O', 'YOU\'RE', 'HE', 'HIS', 'HIM', 'ZION', 'EMMANUEL', 'MAJESTY', 'JESUS\'', + 'JIREH', 'JUDAH', 'LION', 'LORD\'S', 'ABRAHAM', 'GOD\'S', 'FATHER\'S', + 'ELIJAH' 'MARTHA', 'CHRISTMAS', 'ALPHA', 'OMEGA'): text_arr[i] = text_arr[i].capitalize() else: text_arr[i] = text_arr[i].lower() diff --git a/openlp/plugins/songs/lib/songcompare.py b/openlp/plugins/songs/lib/songcompare.py index 99a04beb2..195923b46 100644 --- a/openlp/plugins/songs/lib/songcompare.py +++ b/openlp/plugins/songs/lib/songcompare.py @@ -113,7 +113,7 @@ def _remove_typos(diff): if len(diff) >= 3: for index in range(len(diff) - 3, -1, -1): if _op_length(diff[index]) >= MIN_FRAGMENT_SIZE and diff[index + 1][0] != "equal" and \ - _op_length(diff[index + 1]) <= MAX_TYPO_SIZE and _op_length(diff[index + 2]) >= MIN_FRAGMENT_SIZE: + _op_length(diff[index + 1]) <= MAX_TYPO_SIZE and _op_length(diff[index + 2]) >= MIN_FRAGMENT_SIZE: del diff[index + 1] # Remove typo at the end of the string. if len(diff) >= 2: diff --git a/openlp/plugins/songs/lib/songimport.py b/openlp/plugins/songs/lib/songimport.py index bb56f3498..a5fbb99e0 100644 --- a/openlp/plugins/songs/lib/songimport.py +++ b/openlp/plugins/songs/lib/songimport.py @@ -364,7 +364,7 @@ class SongImport(QtCore.QObject): """ if not hasattr(self, 'save_path'): self.save_path = os.path.join(AppLocation.get_section_data_path(self.import_wizard.plugin.name), - 'audio', str(song_id)) + 'audio', str(song_id)) check_directory_exists(self.save_path) if not filename.startswith(self.save_path): old_file, filename = filename, os.path.join(self.save_path, os.path.split(filename)[1]) diff --git a/openlp/plugins/songs/lib/songshowplusimport.py b/openlp/plugins/songs/lib/songshowplusimport.py index 50a8698c3..485a8f047 100644 --- a/openlp/plugins/songs/lib/songshowplusimport.py +++ b/openlp/plugins/songs/lib/songshowplusimport.py @@ -140,7 +140,7 @@ class SongShowPlusImport(SongImport): elif block_key == AUTHOR: authors = self.decode(data).split(" / ") for author in authors: - if author.find(",") !=-1: + if author.find(",") != -1: author_parts = author.split(", ") author = author_parts[1] + " " + author_parts[0] self.parse_author(author) @@ -220,4 +220,4 @@ class SongShowPlusImport(SongImport): try: return str(data, chardet.detect(data)['encoding']) except: - return str(data, retrieve_windows_encoding()) \ No newline at end of file + return str(data, retrieve_windows_encoding()) diff --git a/openlp/plugins/songs/lib/sundayplusimport.py b/openlp/plugins/songs/lib/sundayplusimport.py index f63d625c5..f22f8b058 100644 --- a/openlp/plugins/songs/lib/sundayplusimport.py +++ b/openlp/plugins/songs/lib/sundayplusimport.py @@ -138,7 +138,7 @@ class SundayPlusImport(SongImport): elif name == 'Copyright': self.copyright = self.decode(self.unescape(value)) elif name[0:4] == 'CELL': - self.parse(value, cell = name[4:]) + self.parse(value, cell=name[4:]) # We are in a verse group. else: if name == 'MARKER_NAME': @@ -207,4 +207,3 @@ class SundayPlusImport(SongImport): text = text.replace('^^', '"') text = text.replace('^', '\'') return text.strip() - diff --git a/openlp/plugins/songs/lib/test/test.opensong b/openlp/plugins/songs/lib/test/test.opensong deleted file mode 100644 index c75951492..000000000 --- a/openlp/plugins/songs/lib/test/test.opensong +++ /dev/null @@ -1,72 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<song> - <title>Martins Test - MartiÑ & Martin2 Thómpson - 2010 Martin Thompson - 1 - V1 C V2 C2 3a B1 V1 T U Rap1 Rap2 Rap3 - Blah - - - - - - - - TestTheme - TestAltTheme - - - [3a] -. G A B - V3 Line 1 -. G A B - V3 Line 2 - -. 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 - -[C] - . A B - Chorus 1 - -[C2] -. A B - Chorus 2 - -[T] - T Line 1 - -[Rap] -1 Rap 1 Line 1 -2 Rap 2 Line 1 -1 Rap 1 Line 2 -2 Rap 2 Line 2 - -[rap3] - Rap 3 Line 1 - Rap 3 Line 2 - - -[X] - Unreferenced verse line 1 - - diff --git a/openlp/plugins/songs/lib/test/test.opensong.zip b/openlp/plugins/songs/lib/test/test.opensong.zip deleted file mode 100644 index 62588c8904a2e8e15013fbf5bd9297c92e5cdb8d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 855 zcmWIWW@Zs#U|`^2u$=MIW@{?H(sCvShD3G-23`gkhLY6c621I_)V$*Sy!6lzP6lSa z6muUCF0J5ZU}Sm0%)kI9u7-N&-8K-o`&vY}?Cp!k3wQ$}9di%7Saf&kJ(flX&n+%3 zLadXD9^YG({!*}LBcJEVqDkwxwq&F_JlRtz!*{>u#g}IrMGF$Vif^vm8OBZZC%{)jj+Rk`$AbXjrE?mg1CnpW!g9WK7StNN(PYaY`X_1n4= znRm08alV$&iYN~ejn(|xV7E- z3A5|zeeUsk6~}E3`s>b-CtEV3qe)48g zCf~ukpX@eF`sMoBgtKY4lH2{LkE^z=JDMb@qo!}y>glY#BYfU8{;iW{Xk|3_&UwGo#B6ECxo=v#j>$L1W##>uuU;5 - - 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/test3.opensong b/openlp/plugins/songs/lib/test/test3.opensong deleted file mode 100644 index 388ab53c7..000000000 --- a/openlp/plugins/songs/lib/test/test3.opensong +++ /dev/null @@ -1,10 +0,0 @@ - - - Test single verse - Martin Thompson - 2010 - 123456 - Worship: Declaration - Line 1 -Line 2 - diff --git a/openlp/plugins/songs/lib/test/test_import_file.py b/openlp/plugins/songs/lib/test/test_import_file.py deleted file mode 100644 index 137d8a7e2..000000000 --- a/openlp/plugins/songs/lib/test/test_import_file.py +++ /dev/null @@ -1,50 +0,0 @@ -# -*- coding: utf-8 -*- -# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 - -############################################################################### -# OpenLP - Open Source Lyrics Projection # -# --------------------------------------------------------------------------- # -# Copyright (c) 2008-2014 Raoul Snyman # -# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan # -# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # -# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # -# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, # -# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, # -# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, # -# Frode Woldsund, Martin Zibricky, Patrick Zimmermann # -# --------------------------------------------------------------------------- # -# 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 sys - -from openlp.plugins.songs.lib.opensongimport import OpenSongImport -from openlp.core.lib.db import Manager -from openlp.plugins.songs.lib.db import init_schema - -import logging -LOG_FILENAME = 'test_import_file.log' -logging.basicConfig(filename=LOG_FILENAME,level=logging.INFO) - -from test_opensongimport import wizard_stub - -def test(filenames): - manager = Manager('songs', init_schema) - o = OpenSongImport(manager, filenames=filenames) - o.import_wizard = wizard_stub() - o.commit = False - o.do_import() - o.print_song() - -if __name__ == "__main__": - test(sys.argv[1:]) diff --git a/openlp/plugins/songs/lib/test/test_importing_lots.py b/openlp/plugins/songs/lib/test/test_importing_lots.py deleted file mode 100644 index c7293ae0e..000000000 --- a/openlp/plugins/songs/lib/test/test_importing_lots.py +++ /dev/null @@ -1,61 +0,0 @@ -# -*- coding: utf-8 -*- -# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 - -############################################################################### -# OpenLP - Open Source Lyrics Projection # -# --------------------------------------------------------------------------- # -# Copyright (c) 2008-2014 Raoul Snyman # -# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan # -# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # -# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # -# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, # -# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, # -# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, # -# Frode Woldsund, Martin Zibricky, Patrick Zimmermann # -# --------------------------------------------------------------------------- # -# 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.plugins.songs.lib.db import init_schema -from openlp.core.lib.db import Manager -import os -import codecs - -import logging -LOG_FILENAME = 'import.log' -logging.basicConfig(filename=LOG_FILENAME,level=logging.INFO) - -from test_opensongimport import wizard_stub - -# Useful test function for importing a variety of different files -# Uncomment below depending on what problem trying to make occur! - -def opensong_import_lots(): - ziploc = '/home/mjt/openlp/OpenSong_Data/' - files = [] - files = [os.path.join(ziploc, 'RaoulSongs', 'Songs', 'Jesus Freak')] - # files.extend(glob(ziploc+u'Songs.zip')) - # files.extend(glob(ziploc+u'RaoulSongs.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('import_lots_errors.txt', 'w', 'utf8') - manager = Manager('songs', init_schema) - o = OpenSongImport(manager, filenames=files) - o.import_wizard=wizard_stub() - o.do_import() - -if __name__ == "__main__": - opensong_import_lots() diff --git a/openlp/plugins/songs/lib/ui.py b/openlp/plugins/songs/lib/ui.py index a2fe38c74..14f4777c9 100644 --- a/openlp/plugins/songs/lib/ui.py +++ b/openlp/plugins/songs/lib/ui.py @@ -40,11 +40,11 @@ class SongStrings(object): # These strings should need a good reason to be retranslated elsewhere. Author = translate('OpenLP.Ui', 'Author', 'Singular') Authors = translate('OpenLP.Ui', 'Authors', 'Plural') - AuthorUnknown = 'Author Unknown' # Used to populate the database. + AuthorUnknown = 'Author Unknown' # Used to populate the database. CopyrightSymbol = translate('OpenLP.Ui', '\xa9', 'Copyright symbol.') SongBook = translate('OpenLP.Ui', 'Song Book', 'Singular') SongBooks = translate('OpenLP.Ui', 'Song Books', 'Plural') - SongIncomplete = translate('OpenLP.Ui','Title and/or verses not found') + SongIncomplete = translate('OpenLP.Ui', 'Title and/or verses not found') SongMaintenance = translate('OpenLP.Ui', 'Song Maintenance') Topic = translate('OpenLP.Ui', 'Topic', 'Singular') Topics = translate('OpenLP.Ui', 'Topics', 'Plural') diff --git a/openlp/plugins/songs/lib/upgrade.py b/openlp/plugins/songs/lib/upgrade.py index ee01fb8b0..fdb0f17ef 100644 --- a/openlp/plugins/songs/lib/upgrade.py +++ b/openlp/plugins/songs/lib/upgrade.py @@ -82,4 +82,3 @@ def upgrade_3(session, metadata): op.add_column('songs', Column('temporary', types.Boolean(create_constraint=False), server_default=false())) else: op.add_column('songs', Column('temporary', types.Boolean(), server_default=false())) - diff --git a/openlp/plugins/songs/lib/worshipcenterproimport.py b/openlp/plugins/songs/lib/worshipcenterproimport.py index 80b1b5fe3..b24d2ae83 100644 --- a/openlp/plugins/songs/lib/worshipcenterproimport.py +++ b/openlp/plugins/songs/lib/worshipcenterproimport.py @@ -56,12 +56,12 @@ class WorshipCenterProImport(SongImport): Receive a single file to import. """ try: - conn = pyodbc.connect('DRIVER={Microsoft Access Driver (*.mdb)};DBQ=%s' % self.import_source) + conn = pyodbc.connect('DRIVER={Microsoft Access Driver (*.mdb)};DBQ=%s' % self.import_source) except (pyodbc.DatabaseError, pyodbc.IntegrityError, pyodbc.InternalError, pyodbc.OperationalError) as e: log.warn('Unable to connect the WorshipCenter Pro database %s. %s', self.import_source, str(e)) # Unfortunately no specific exception type - self.log_error(self.import_source, - translate('SongsPlugin.WorshipCenterProImport', 'Unable to connect the WorshipCenter Pro database.')) + self.log_error(self.import_source, translate('SongsPlugin.WorshipCenterProImport', + 'Unable to connect the WorshipCenter Pro database.')) return cursor = conn.cursor() cursor.execute('SELECT ID, Field, Value FROM __SONGDATA') diff --git a/openlp/plugins/songs/lib/xml.py b/openlp/plugins/songs/lib/xml.py index 95ae4fc33..667afebdd 100644 --- a/openlp/plugins/songs/lib/xml.py +++ b/openlp/plugins/songs/lib/xml.py @@ -340,7 +340,8 @@ class OpenLyrics(object): The first unicode string are the start tags (for the next slide). The second unicode string are the end tags. - :param text: The text to test. The text must **not** contain html tags, only OpenLP formatting tags are allowed:: + :param text: The text to test. The text must **not** contain html tags, only OpenLP formatting tags + are allowed:: {st}{r}Text text text """ diff --git a/openlp/plugins/songs/lib/zionworximport.py b/openlp/plugins/songs/lib/zionworximport.py index ab6bcb27d..dfdc2373d 100644 --- a/openlp/plugins/songs/lib/zionworximport.py +++ b/openlp/plugins/songs/lib/zionworximport.py @@ -39,7 +39,7 @@ from openlp.plugins.songs.lib.songimport import SongImport log = logging.getLogger(__name__) # Used to strip control chars (except 10=LF, 13=CR) -CONTROL_CHARS_MAP = dict.fromkeys(list(range(10)) + [11, 12] + list(range(14,32)) + [127]) +CONTROL_CHARS_MAP = dict.fromkeys(list(range(10)) + [11, 12] + list(range(14, 32)) + [127]) class ZionWorxImport(SongImport): diff --git a/openlp/plugins/songs/songsplugin.py b/openlp/plugins/songs/songsplugin.py index 32d3ddbe6..1c000338c 100644 --- a/openlp/plugins/songs/songsplugin.py +++ b/openlp/plugins/songs/songsplugin.py @@ -342,7 +342,7 @@ class SongsPlugin(Plugin): """ Remove temporary songs from the database """ - songs = self.manager.get_all_objects(Song, Song.temporary == True) + songs = self.manager.get_all_objects(Song, Song.temporary is True) for song in songs: self.manager.delete_object(Song, song.id) From 394e5a5be2cc55877e4fc04743ee86678d2ecf67 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sat, 22 Mar 2014 09:52:49 +0100 Subject: [PATCH 032/155] removed code, commment --- openlp/plugins/songs/forms/duplicatesongremovalform.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openlp/plugins/songs/forms/duplicatesongremovalform.py b/openlp/plugins/songs/forms/duplicatesongremovalform.py index 5b8ccebfe..54778de03 100644 --- a/openlp/plugins/songs/forms/duplicatesongremovalform.py +++ b/openlp/plugins/songs/forms/duplicatesongremovalform.py @@ -47,6 +47,9 @@ log = logging.getLogger(__name__) class SongIterator(object): + """ + This class implements an iterator for the song duplicate finder. The iterator returns a tuple of two songs. + """ def __init__(self, songs): self.songs = songs @@ -182,7 +185,6 @@ class DuplicateSongRemovalForm(OpenLPWizard, RegistryProperties): process_number = max(1, multiprocessing.cpu_count() - 1) pool = multiprocessing.Pool(process_number) song_list = SongIterator(songs) - #song_list = [(songs[outer_song_counter], songs[inner_song_counter]) for outer_song_counter in range(max_songs - 1) for inner_song_counter in range(outer_song_counter + 1, max_songs)] result = pool.imap_unordered(songs_probably_equal, song_list, 30) # Do not accept any further tasks. Also this closes the processes if all tasks are done. pool.close() From 11fe2045e50d3750a394c9a7376fc46c27d2f02b Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sat, 22 Mar 2014 10:24:52 +0100 Subject: [PATCH 033/155] fixed tests --- .../songs/forms/duplicatesongremovalform.py | 3 ++- .../functional/openlp_plugins/songs/test_lib.py | 16 ++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/openlp/plugins/songs/forms/duplicatesongremovalform.py b/openlp/plugins/songs/forms/duplicatesongremovalform.py index 54778de03..d63391c08 100644 --- a/openlp/plugins/songs/forms/duplicatesongremovalform.py +++ b/openlp/plugins/songs/forms/duplicatesongremovalform.py @@ -48,7 +48,8 @@ log = logging.getLogger(__name__) class SongIterator(object): """ - This class implements an iterator for the song duplicate finder. The iterator returns a tuple of two songs. + This class implements an iterator for the song duplicate finder. The iterator returns a tuple of two songs. When + completely iterated then all songs have once been returned combined with any other songs. """ def __init__(self, songs): self.songs = songs diff --git a/tests/functional/openlp_plugins/songs/test_lib.py b/tests/functional/openlp_plugins/songs/test_lib.py index beff6d0d5..2683770c6 100644 --- a/tests/functional/openlp_plugins/songs/test_lib.py +++ b/tests/functional/openlp_plugins/songs/test_lib.py @@ -96,10 +96,10 @@ class TestLib(TestCase): self.song2.search_lyrics = self.full_lyrics # WHEN: We compare those songs for equality. - result = songs_probably_equal(self.song1, self.song2) + result = songs_probably_equal((self.song1, self.song2)) # THEN: The result should be True. - assert result == True, 'The result should be True' + assert result == (self.song1, self.song2), 'The result should be the tuble of songs' def songs_probably_equal_short_song_test(self): """ @@ -110,10 +110,10 @@ class TestLib(TestCase): self.song2.search_lyrics = self.short_lyrics # WHEN: We compare those songs for equality. - result = songs_probably_equal(self.song1, self.song2) + result = songs_probably_equal((self.song1, self.song2)) # THEN: The result should be True. - assert result == True, 'The result should be True' + assert result == (self.song1, self.song2), 'The result should be the tuble of songs' def songs_probably_equal_error_song_test(self): """ @@ -124,10 +124,10 @@ class TestLib(TestCase): self.song2.search_lyrics = self.error_lyrics # WHEN: We compare those songs for equality. - result = songs_probably_equal(self.song1, self.song2) + result = songs_probably_equal((self.song1, self.song2)) # THEN: The result should be True. - assert result == True, 'The result should be True' + assert result == (self.song1, self.song2), 'The result should be the tuble of songs' def songs_probably_equal_different_song_test(self): """ @@ -138,10 +138,10 @@ class TestLib(TestCase): self.song2.search_lyrics = self.different_lyrics # WHEN: We compare those songs for equality. - result = songs_probably_equal(self.song1, self.song2) + result = songs_probably_equal((self.song1, self.song2)) # THEN: The result should be False. - assert result == False, 'The result should be False' + assert result is None, 'The result should be None' def remove_typos_beginning_test(self): """ From ad1970f27e8eeaa54f9fcbeab66e6da297172f1d Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sat, 22 Mar 2014 10:53:55 +0100 Subject: [PATCH 034/155] fixed spelling --- openlp/core/ui/mainwindow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index 3526e551f..e1f1dc51f 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -652,7 +652,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow, RegistryProperties): translate('OpenLP.MainWindow', 'Are you sure you want to re-run the First ' 'Time Wizard?\n\nRe-running this wizard may make changes to your ' 'current OpenLP configuration and possibly add songs to your ' - '#existing songs list and change your default theme.'), + 'existing songs list and change your default theme.'), QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No), QtGui.QMessageBox.No) From d5b421aa70acf2ad617382c7f784531f5219678c Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sat, 22 Mar 2014 20:18:27 +0100 Subject: [PATCH 035/155] updated vlc.py --- openlp/core/ui/media/vendor/vlc.py | 88 +++++++++++++++--------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/openlp/core/ui/media/vendor/vlc.py b/openlp/core/ui/media/vendor/vlc.py index 8646450b1..df139ae36 100644 --- a/openlp/core/ui/media/vendor/vlc.py +++ b/openlp/core/ui/media/vendor/vlc.py @@ -52,9 +52,9 @@ build_date = "Tue Jul 2 10:35:53 2013" if sys.version_info[0] > 2: str = str - str = str + unicode = str bytes = bytes - str = (str, bytes) + basestring = (str, bytes) PYTHON3 = True def str_to_bytes(s): """Translate string or bytes to bytes. @@ -73,14 +73,14 @@ if sys.version_info[0] > 2: return b else: str = str - str = str + unicode = unicode bytes = str - str = str + basestring = basestring PYTHON3 = False def str_to_bytes(s): """Translate string or bytes to bytes. """ - if isinstance(s, str): + if isinstance(s, unicode): return s.encode(sys.getfilesystemencoding()) else: return s @@ -89,7 +89,7 @@ else: """Translate bytes to unicode string. """ if isinstance(b, str): - return str(b, sys.getfilesystemencoding()) + return unicode(b, sys.getfilesystemencoding()) else: return b @@ -110,7 +110,7 @@ def find_lib(): p = find_library('libvlc.dll') if p is None: try: # some registry settings - import winreg as w # leaner than win32api, win32con + import _winreg as w # leaner than win32api, win32con for r in w.HKEY_LOCAL_MACHINE, w.HKEY_CURRENT_USER: try: r = w.OpenKey(r, 'Software\\VideoLAN\\VLC') @@ -168,7 +168,7 @@ class VLCException(Exception): pass try: - _Ints = (int, int) + _Ints = (int, long) except NameError: # no long in Python 3+ _Ints = int _Seqs = (list, tuple) @@ -823,7 +823,7 @@ class CallbackDecorators(object): Callback = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p) Callback.__doc__ = '''Callback function notification \param p_event the event triggering the callback - ''' + ''' LogCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_int, Log_ptr, ctypes.c_char_p, ctypes.c_void_p) LogCb.__doc__ = '''Callback prototype for LibVLC log message handler. \param data data pointer as given to L{libvlc_log_set}() @@ -834,7 +834,7 @@ class CallbackDecorators(object): \note Log message handlers must be thread-safe. \warning The message context pointer, the format string parameters and the variable arguments are only valid until the callback returns. - ''' + ''' VideoLockCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ListPOINTER(ctypes.c_void_p)) VideoLockCb.__doc__ = '''Callback prototype to allocate and lock a picture buffer. Whenever a new video frame needs to be decoded, the lock callback is @@ -846,7 +846,7 @@ planes must be aligned on 32-bytes boundaries. of void pointers, this callback must initialize the array) [OUT] \return a private pointer for the display and unlock callbacks to identify the picture buffers - ''' + ''' VideoUnlockCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ListPOINTER(ctypes.c_void_p)) VideoUnlockCb.__doc__ = '''Callback prototype to unlock a picture buffer. When the video frame decoding is complete, the unlock callback is invoked. @@ -859,7 +859,7 @@ but before the picture is displayed. callback [IN] \param planes pixel planes as defined by the @ref libvlc_video_lock_cb callback (this parameter is only for convenience) [IN] - ''' + ''' VideoDisplayCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p) VideoDisplayCb.__doc__ = '''Callback prototype to display a picture. When the video frame needs to be shown, as determined by the media playback @@ -867,7 +867,7 @@ clock, the display callback is invoked. \param opaque private pointer as passed to L{libvlc_video_set_callbacks}() [IN] \param picture private pointer returned from the @ref libvlc_video_lock_cb callback [IN] - ''' + ''' VideoFormatCb = ctypes.CFUNCTYPE(ctypes.POINTER(ctypes.c_uint), ListPOINTER(ctypes.c_void_p), ctypes.c_char_p, ctypes.POINTER(ctypes.c_uint), ctypes.POINTER(ctypes.c_uint), ctypes.POINTER(ctypes.c_uint), ctypes.POINTER(ctypes.c_uint)) VideoFormatCb.__doc__ = '''Callback prototype to configure picture buffers format. This callback gets the format of the video as output by the video decoder @@ -891,47 +891,47 @@ the pixel height. Furthermore, we recommend that pitches and lines be multiple of 32 to not break assumption that might be made by various optimizations in the video decoders, video filters and/or video converters. - ''' + ''' VideoCleanupCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p) VideoCleanupCb.__doc__ = '''Callback prototype to configure picture buffers format. \param opaque private pointer as passed to L{libvlc_video_set_callbacks}() (and possibly modified by @ref libvlc_video_format_cb) [IN] - ''' + ''' AudioPlayCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_uint, ctypes.c_int64) AudioPlayCb.__doc__ = '''Callback prototype for audio playback. \param data data pointer as passed to L{libvlc_audio_set_callbacks}() [IN] \param samples pointer to the first audio sample to play back [IN] \param count number of audio samples to play back \param pts expected play time stamp (see libvlc_delay()) - ''' + ''' AudioPauseCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_int64) AudioPauseCb.__doc__ = '''Callback prototype for audio pause. \note The pause callback is never called if the audio is already paused. \param data data pointer as passed to L{libvlc_audio_set_callbacks}() [IN] \param pts time stamp of the pause request (should be elapsed already) - ''' + ''' AudioResumeCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_int64) AudioResumeCb.__doc__ = '''Callback prototype for audio resumption (i.e. restart from pause). \note The resume callback is never called if the audio is not paused. \param data data pointer as passed to L{libvlc_audio_set_callbacks}() [IN] \param pts time stamp of the resumption request (should be elapsed already) - ''' + ''' AudioFlushCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_int64) AudioFlushCb.__doc__ = '''Callback prototype for audio buffer flush (i.e. discard all pending buffers and stop playback as soon as possible). \param data data pointer as passed to L{libvlc_audio_set_callbacks}() [IN] - ''' + ''' AudioDrainCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p) AudioDrainCb.__doc__ = '''Callback prototype for audio buffer drain (i.e. wait for pending buffers to be played). \param data data pointer as passed to L{libvlc_audio_set_callbacks}() [IN] - ''' + ''' AudioSetVolumeCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_float, ctypes.c_bool) AudioSetVolumeCb.__doc__ = '''Callback prototype for audio volume change. \param data data pointer as passed to L{libvlc_audio_set_callbacks}() [IN] \param volume software volume (1. = nominal, 0. = mute) \param mute muted flag - ''' + ''' AudioSetupCb = ctypes.CFUNCTYPE(ctypes.POINTER(ctypes.c_int), ListPOINTER(ctypes.c_void_p), ctypes.c_char_p, ctypes.POINTER(ctypes.c_uint), ctypes.POINTER(ctypes.c_uint)) AudioSetupCb.__doc__ = '''Callback prototype to setup the audio playback. This is called when the media player needs to create a new audio output. @@ -941,12 +941,12 @@ This is called when the media player needs to create a new audio output. \param rate sample rate [IN/OUT] \param channels channels count [IN/OUT] \return 0 on success, anything else to skip audio playback - ''' + ''' AudioCleanupCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p) AudioCleanupCb.__doc__ = '''Callback prototype for audio playback cleanup. This is called when the media player no longer needs an audio output. \param opaque data pointer as passed to L{libvlc_audio_set_callbacks}() [IN] - ''' + ''' cb = CallbackDecorators # End of generated enum types # @@ -1210,7 +1210,7 @@ class EventManager(_Ctype): @note: Only a single notification can be registered for each event type in an EventManager instance. - + ''' _callback_handler = None @@ -1287,7 +1287,7 @@ class Instance(_Ctype): - a string - a list of strings as first parameters - the parameters given as the constructor parameters (must be strings) - + ''' def __new__(cls, *args): @@ -1297,7 +1297,7 @@ class Instance(_Ctype): i = args[0] if isinstance(i, _Ints): return _Constructor(cls, i) - elif isinstance(i, str): + elif isinstance(i, basestring): args = i.strip().split() elif isinstance(i, _Seqs): args = i @@ -1753,11 +1753,11 @@ class Instance(_Ctype): class Media(_Ctype): '''Create a new Media instance. - + Usage: Media(MRL, *options) See vlc.Instance.media_new documentation for details. - + ''' def __new__(cls, *args): @@ -2053,11 +2053,11 @@ class MediaLibrary(_Ctype): class MediaList(_Ctype): '''Create a new MediaList instance. - + Usage: MediaList(list_of_MRLs) See vlc.Instance.media_list_new documentation for details. - + ''' def __new__(cls, *args): @@ -2073,15 +2073,15 @@ class MediaList(_Ctype): def get_instance(self): return getattr(self, '_instance', None) - + def add_media(self, mrl): """Add media instance to media list. - + The L{lock} should be held upon entering this function. @param mrl: a media instance or a MRL. @return: 0 on success, -1 if the media list is read-only. """ - if isinstance(mrl, str): + if isinstance(mrl, basestring): mrl = (self.get_instance() or get_default_instance()).media_new(mrl) return libvlc_media_list_add_media(self, mrl) @@ -2193,7 +2193,7 @@ class MediaListPlayer(_Ctype): It may take as parameter either: - a vlc.Instance - nothing - + ''' def __new__(cls, arg=None): @@ -2319,13 +2319,13 @@ class MediaPlayer(_Ctype): It may take as parameter either: - a string (media URI), options... In this case, a vlc.Instance will be created. - a vlc.Instance, a string (media URI), options... - + ''' def __new__(cls, *args): if len(args) == 1 and isinstance(args[0], _Ints): return _Constructor(cls, args[0]) - + if args and isinstance(args[0], Instance): instance = args[0] args = args[1:] @@ -2397,13 +2397,13 @@ class MediaPlayer(_Ctype): Specify where the media player should render its video output. If LibVLC was built without Win32/Win64 API output support, then this has no effects. - + @param drawable: windows handle of the drawable. """ if not isinstance(drawable, ctypes.c_void_p): drawable = ctypes.c_void_p(int(drawable)) libvlc_media_player_set_hwnd(self, drawable) - + def video_get_width(self, num=0): """Get the width of a video in pixels. @@ -2556,12 +2556,12 @@ class MediaPlayer(_Ctype): If you want to use it along with Qt4 see the QMacCocoaViewContainer. Then the following code should work: @begincode - + NSView *video = [[NSView alloc] init]; QMacCocoaViewContainer *container = new QMacCocoaViewContainer(video, parent); L{set_nsobject}(mp, video); [video release]; - + @endcode You can find a live example in VLCVideoView in VLCKit.framework. @param drawable: the drawable that is either an NSView or an object following the VLCOpenGLVideoViewEmbedding protocol. @@ -4430,12 +4430,12 @@ def libvlc_media_player_set_nsobject(p_mi, drawable): If you want to use it along with Qt4 see the QMacCocoaViewContainer. Then the following code should work: @begincode - + NSView *video = [[NSView alloc] init]; QMacCocoaViewContainer *container = new QMacCocoaViewContainer(video, parent); L{libvlc_media_player_set_nsobject}(mp, video); [video release]; - + @endcode You can find a live example in VLCVideoView in VLCKit.framework. @param p_mi: the Media Player. @@ -5948,9 +5948,9 @@ def debug_callback(event, *args, **kwds): ''' l = ['event %s' % (event.type,)] if args: - l.extend(list(map(str, args))) + l.extend(map(str, args)) if kwds: - l.extend(sorted('%s=%s' % t for t in list(kwds.items()))) + l.extend(sorted('%s=%s' % t for t in kwds.items())) print('Debug callback (%s)' % ', '.join(l)) if __name__ == '__main__': From 47ad40aaac3569792cb6dfd3d52776c1ca6fbed2 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sat, 22 Mar 2014 20:19:08 +0100 Subject: [PATCH 036/155] removed white spaces --- openlp/core/ui/media/vendor/vlc.py | 64 +++++++++++++++--------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/openlp/core/ui/media/vendor/vlc.py b/openlp/core/ui/media/vendor/vlc.py index df139ae36..0326e4104 100644 --- a/openlp/core/ui/media/vendor/vlc.py +++ b/openlp/core/ui/media/vendor/vlc.py @@ -823,7 +823,7 @@ class CallbackDecorators(object): Callback = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p) Callback.__doc__ = '''Callback function notification \param p_event the event triggering the callback - ''' + ''' LogCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_int, Log_ptr, ctypes.c_char_p, ctypes.c_void_p) LogCb.__doc__ = '''Callback prototype for LibVLC log message handler. \param data data pointer as given to L{libvlc_log_set}() @@ -834,7 +834,7 @@ class CallbackDecorators(object): \note Log message handlers must be thread-safe. \warning The message context pointer, the format string parameters and the variable arguments are only valid until the callback returns. - ''' + ''' VideoLockCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ListPOINTER(ctypes.c_void_p)) VideoLockCb.__doc__ = '''Callback prototype to allocate and lock a picture buffer. Whenever a new video frame needs to be decoded, the lock callback is @@ -846,7 +846,7 @@ planes must be aligned on 32-bytes boundaries. of void pointers, this callback must initialize the array) [OUT] \return a private pointer for the display and unlock callbacks to identify the picture buffers - ''' + ''' VideoUnlockCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ListPOINTER(ctypes.c_void_p)) VideoUnlockCb.__doc__ = '''Callback prototype to unlock a picture buffer. When the video frame decoding is complete, the unlock callback is invoked. @@ -859,7 +859,7 @@ but before the picture is displayed. callback [IN] \param planes pixel planes as defined by the @ref libvlc_video_lock_cb callback (this parameter is only for convenience) [IN] - ''' + ''' VideoDisplayCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p) VideoDisplayCb.__doc__ = '''Callback prototype to display a picture. When the video frame needs to be shown, as determined by the media playback @@ -867,7 +867,7 @@ clock, the display callback is invoked. \param opaque private pointer as passed to L{libvlc_video_set_callbacks}() [IN] \param picture private pointer returned from the @ref libvlc_video_lock_cb callback [IN] - ''' + ''' VideoFormatCb = ctypes.CFUNCTYPE(ctypes.POINTER(ctypes.c_uint), ListPOINTER(ctypes.c_void_p), ctypes.c_char_p, ctypes.POINTER(ctypes.c_uint), ctypes.POINTER(ctypes.c_uint), ctypes.POINTER(ctypes.c_uint), ctypes.POINTER(ctypes.c_uint)) VideoFormatCb.__doc__ = '''Callback prototype to configure picture buffers format. This callback gets the format of the video as output by the video decoder @@ -891,47 +891,47 @@ the pixel height. Furthermore, we recommend that pitches and lines be multiple of 32 to not break assumption that might be made by various optimizations in the video decoders, video filters and/or video converters. - ''' + ''' VideoCleanupCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p) VideoCleanupCb.__doc__ = '''Callback prototype to configure picture buffers format. \param opaque private pointer as passed to L{libvlc_video_set_callbacks}() (and possibly modified by @ref libvlc_video_format_cb) [IN] - ''' + ''' AudioPlayCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_uint, ctypes.c_int64) AudioPlayCb.__doc__ = '''Callback prototype for audio playback. \param data data pointer as passed to L{libvlc_audio_set_callbacks}() [IN] \param samples pointer to the first audio sample to play back [IN] \param count number of audio samples to play back \param pts expected play time stamp (see libvlc_delay()) - ''' + ''' AudioPauseCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_int64) AudioPauseCb.__doc__ = '''Callback prototype for audio pause. \note The pause callback is never called if the audio is already paused. \param data data pointer as passed to L{libvlc_audio_set_callbacks}() [IN] \param pts time stamp of the pause request (should be elapsed already) - ''' + ''' AudioResumeCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_int64) AudioResumeCb.__doc__ = '''Callback prototype for audio resumption (i.e. restart from pause). \note The resume callback is never called if the audio is not paused. \param data data pointer as passed to L{libvlc_audio_set_callbacks}() [IN] \param pts time stamp of the resumption request (should be elapsed already) - ''' + ''' AudioFlushCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_int64) AudioFlushCb.__doc__ = '''Callback prototype for audio buffer flush (i.e. discard all pending buffers and stop playback as soon as possible). \param data data pointer as passed to L{libvlc_audio_set_callbacks}() [IN] - ''' + ''' AudioDrainCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p) AudioDrainCb.__doc__ = '''Callback prototype for audio buffer drain (i.e. wait for pending buffers to be played). \param data data pointer as passed to L{libvlc_audio_set_callbacks}() [IN] - ''' + ''' AudioSetVolumeCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_float, ctypes.c_bool) AudioSetVolumeCb.__doc__ = '''Callback prototype for audio volume change. \param data data pointer as passed to L{libvlc_audio_set_callbacks}() [IN] \param volume software volume (1. = nominal, 0. = mute) \param mute muted flag - ''' + ''' AudioSetupCb = ctypes.CFUNCTYPE(ctypes.POINTER(ctypes.c_int), ListPOINTER(ctypes.c_void_p), ctypes.c_char_p, ctypes.POINTER(ctypes.c_uint), ctypes.POINTER(ctypes.c_uint)) AudioSetupCb.__doc__ = '''Callback prototype to setup the audio playback. This is called when the media player needs to create a new audio output. @@ -941,12 +941,12 @@ This is called when the media player needs to create a new audio output. \param rate sample rate [IN/OUT] \param channels channels count [IN/OUT] \return 0 on success, anything else to skip audio playback - ''' + ''' AudioCleanupCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p) AudioCleanupCb.__doc__ = '''Callback prototype for audio playback cleanup. This is called when the media player no longer needs an audio output. \param opaque data pointer as passed to L{libvlc_audio_set_callbacks}() [IN] - ''' + ''' cb = CallbackDecorators # End of generated enum types # @@ -1210,7 +1210,7 @@ class EventManager(_Ctype): @note: Only a single notification can be registered for each event type in an EventManager instance. - + ''' _callback_handler = None @@ -1287,7 +1287,7 @@ class Instance(_Ctype): - a string - a list of strings as first parameters - the parameters given as the constructor parameters (must be strings) - + ''' def __new__(cls, *args): @@ -1753,11 +1753,11 @@ class Instance(_Ctype): class Media(_Ctype): '''Create a new Media instance. - + Usage: Media(MRL, *options) See vlc.Instance.media_new documentation for details. - + ''' def __new__(cls, *args): @@ -2053,11 +2053,11 @@ class MediaLibrary(_Ctype): class MediaList(_Ctype): '''Create a new MediaList instance. - + Usage: MediaList(list_of_MRLs) See vlc.Instance.media_list_new documentation for details. - + ''' def __new__(cls, *args): @@ -2073,10 +2073,10 @@ class MediaList(_Ctype): def get_instance(self): return getattr(self, '_instance', None) - + def add_media(self, mrl): """Add media instance to media list. - + The L{lock} should be held upon entering this function. @param mrl: a media instance or a MRL. @return: 0 on success, -1 if the media list is read-only. @@ -2193,7 +2193,7 @@ class MediaListPlayer(_Ctype): It may take as parameter either: - a vlc.Instance - nothing - + ''' def __new__(cls, arg=None): @@ -2319,13 +2319,13 @@ class MediaPlayer(_Ctype): It may take as parameter either: - a string (media URI), options... In this case, a vlc.Instance will be created. - a vlc.Instance, a string (media URI), options... - + ''' def __new__(cls, *args): if len(args) == 1 and isinstance(args[0], _Ints): return _Constructor(cls, args[0]) - + if args and isinstance(args[0], Instance): instance = args[0] args = args[1:] @@ -2397,13 +2397,13 @@ class MediaPlayer(_Ctype): Specify where the media player should render its video output. If LibVLC was built without Win32/Win64 API output support, then this has no effects. - + @param drawable: windows handle of the drawable. """ if not isinstance(drawable, ctypes.c_void_p): drawable = ctypes.c_void_p(int(drawable)) libvlc_media_player_set_hwnd(self, drawable) - + def video_get_width(self, num=0): """Get the width of a video in pixels. @@ -2556,12 +2556,12 @@ class MediaPlayer(_Ctype): If you want to use it along with Qt4 see the QMacCocoaViewContainer. Then the following code should work: @begincode - + NSView *video = [[NSView alloc] init]; QMacCocoaViewContainer *container = new QMacCocoaViewContainer(video, parent); L{set_nsobject}(mp, video); [video release]; - + @endcode You can find a live example in VLCVideoView in VLCKit.framework. @param drawable: the drawable that is either an NSView or an object following the VLCOpenGLVideoViewEmbedding protocol. @@ -4430,12 +4430,12 @@ def libvlc_media_player_set_nsobject(p_mi, drawable): If you want to use it along with Qt4 see the QMacCocoaViewContainer. Then the following code should work: @begincode - + NSView *video = [[NSView alloc] init]; QMacCocoaViewContainer *container = new QMacCocoaViewContainer(video, parent); L{libvlc_media_player_set_nsobject}(mp, video); [video release]; - + @endcode You can find a live example in VLCVideoView in VLCKit.framework. @param p_mi: the Media Player. From cc0b736ef627ea1c18594d5c588b22abbb1ec8cc Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sat, 22 Mar 2014 20:35:24 +0100 Subject: [PATCH 037/155] condition already checked above --- openlp/plugins/songs/lib/songcompare.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/plugins/songs/lib/songcompare.py b/openlp/plugins/songs/lib/songcompare.py index 399672fbe..512ad2ad5 100644 --- a/openlp/plugins/songs/lib/songcompare.py +++ b/openlp/plugins/songs/lib/songcompare.py @@ -84,7 +84,7 @@ def songs_probably_equal(song_tupel): for element in diff_no_typos: if element[0] == "equal" and _op_length(element) > length_of_longest_equal_block: length_of_longest_equal_block = _op_length(element) - if length_of_equal_blocks >= MIN_BLOCK_SIZE or length_of_longest_equal_block > len(small) * 2 // 3: + if length_of_longest_equal_block > len(small) * 2 // 3: return song1, song2 # Both checks failed. We assume the songs are not equal. return None From 7a1825a79a44a7bdda5c029734a61dfb4fee9dda Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sun, 23 Mar 2014 11:49:03 +0100 Subject: [PATCH 038/155] tests --- openlp/core/lib/searchedit.py | 13 +- .../openlp_core_lib/test_searchedit.py | 137 ++++++++++++++++++ 2 files changed, 140 insertions(+), 10 deletions(-) create mode 100644 tests/interfaces/openlp_core_lib/test_searchedit.py diff --git a/openlp/core/lib/searchedit.py b/openlp/core/lib/searchedit.py index d6eaafa7d..4e79beaae 100644 --- a/openlp/core/lib/searchedit.py +++ b/openlp/core/lib/searchedit.py @@ -179,15 +179,8 @@ class SearchEdit(QtGui.QLineEdit): correct action on the button, and set the current search type (using the list of identifiers provided by the developer), the ``searchTypeChanged(int)`` signal is emitted with the identifier. """ - sender = self.sender() for action in self.menu_button.menu().actions(): + # Why is this needed? action.setChecked(False) - self.menu_button.setDefaultAction(sender) - self._current_search_type = sender.data() - # setplaceholder_text has been implemented in Qt 4.7 and in at least - # PyQt 4.9 (I am not sure, if it was implemented in PyQt 4.8). - try: - self.setPlaceholderText(self.menu_button.defaultAction().placeholder_text) - except AttributeError: - pass - self.emit(QtCore.SIGNAL('searchTypeChanged(int)'), self._current_search_type) + sender = self.sender() + self.set_current_search_type(sender.data()) diff --git a/tests/interfaces/openlp_core_lib/test_searchedit.py b/tests/interfaces/openlp_core_lib/test_searchedit.py new file mode 100644 index 000000000..5814d10f8 --- /dev/null +++ b/tests/interfaces/openlp_core_lib/test_searchedit.py @@ -0,0 +1,137 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2014 Raoul Snyman # +# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan # +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # +# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, # +# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, # +# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, # +# Frode Woldsund, Martin Zibricky, Patrick Zimmermann # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### +""" +Module to test the EditCustomForm. +""" +from unittest import TestCase +from unittest.mock import MagicMock + +from PyQt4 import QtCore, QtGui, QtTest + +from openlp.core.common import Registry +from openlp.core.lib.searchedit import SearchEdit +from tests.helpers.testmixin import TestMixin + + +class SearchTypes(object): + First = 0 + Second = 1 + + +SECOND_PLACEHOLDER_TEXT = "Second Placeholder Text" +SEARCH_TYPES = [(SearchTypes.First, QtGui.QIcon(), "First", "First Placeholder Text"), + (SearchTypes.Second, QtGui.QIcon(), "Second", SECOND_PLACEHOLDER_TEXT)] + + +class TestSearchEdit(TestCase, TestMixin): + """ + Test the EditCustomForm. + """ + def setUp(self): + """ + Create the UI + """ + Registry.create() + self.get_application() + self.main_window = QtGui.QMainWindow() + Registry().register('main_window', self.main_window) + + self.search_edit = SearchEdit(self.main_window) + # To complete set up we have to set the search types. + self.search_edit.set_search_types(SEARCH_TYPES) + + def tearDown(self): + """ + Delete all the C++ objects at the end so that we don't have a segfault + """ + del self.main_window + + + def set_search_types_test(self): + """ + Test setting the search types of the search edit. + """ + # GIVEN: The search edit with the search types set. NOTE: The set_search_types(types) is called in the setUp() + # method! + + # WHEN: + + # THEN: The first search type should be the first one in the list. + assert self.search_edit.current_search_type() == SearchTypes.First, "The first search type should be selected." + + def set_current_search_type_test(self): + """ + Test if changing the search type works. + """ + # GIVEN: + # WHEN: Change the search type + result = self.search_edit.set_current_search_type(SearchTypes.Second) + + # THEN: + assert result, "The call should return success (True)." + assert self.search_edit.current_search_type() == SearchTypes.Second,\ + "The search type should be SearchTypes.Second" + assert self.search_edit.placeholderText() == SECOND_PLACEHOLDER_TEXT,\ + "The correct placeholder text should be 'Second Placeholder Text'." + + def clear_button_visibility_test(self): + """ + Test if the clear button is hidden/shown correctly. + """ + # GIVEN: Everything is left to its defaults (hidden). + assert self.search_edit.clear_button.isHidden(), "Pre condition not met. Button should be hidden." + + # WHEN: Type something in the search edit. + QtTest.QTest.keyPress(self.search_edit, QtCore.Qt.Key_A) + QtTest.QTest.keyRelease(self.search_edit, QtCore.Qt.Key_A) + + # THEN: The clear button should not be hidden any more. + assert not self.search_edit.clear_button.isHidden(), "The clear button should be visible." + + def press_clear_button_test(self): + """ + Check if the search edit behaves correctly when pressing the clear button. + """ + # GIVEN: A search edit with text. + QtTest.QTest.keyPress(self.search_edit, QtCore.Qt.Key_A) + QtTest.QTest.keyRelease(self.search_edit, QtCore.Qt.Key_A) + + # WHEN: Press the clear button. + QtTest.QTest.mouseClick(self.search_edit.clear_button, QtCore.Qt.LeftButton) + + # THEN: The search edit text should be cleared and the button be hidden. + assert not self.search_edit.text(), "The search edit should not have any text." + assert self.search_edit.clear_button.isHidden(), "The clear button should be hidden." + + def resize_event_test(self): + """ + Just check if the resizeEvent() method is re-implemented. + """ + assert hasattr(self.search_edit, "resizeEvent"), "The search edit should re-implement the resizeEvent method." + \ No newline at end of file From fcf4d0ce1767f0f211d6497cdc5f37a9b99eb2e2 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sun, 23 Mar 2014 11:53:11 +0100 Subject: [PATCH 039/155] missing line at the end --- tests/interfaces/openlp_core_lib/test_searchedit.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/interfaces/openlp_core_lib/test_searchedit.py b/tests/interfaces/openlp_core_lib/test_searchedit.py index 5814d10f8..07db05dd7 100644 --- a/tests/interfaces/openlp_core_lib/test_searchedit.py +++ b/tests/interfaces/openlp_core_lib/test_searchedit.py @@ -134,4 +134,5 @@ class TestSearchEdit(TestCase, TestMixin): Just check if the resizeEvent() method is re-implemented. """ assert hasattr(self.search_edit, "resizeEvent"), "The search edit should re-implement the resizeEvent method." + \ No newline at end of file From 20533624e0aef3ea3822e1991409ca9d367e32d1 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Sun, 23 Mar 2014 23:27:07 +0100 Subject: [PATCH 040/155] Fix method calls (broken with rev. 2329) --- openlp/plugins/songs/forms/editsongform.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index 8b1e3a897..b655c0f73 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -601,7 +601,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): def on_verse_add_button_clicked(self): self.verse_form.set_verse('', True) if self.verse_form.exec_(): - after_text, verse_tag, verse_num = self.verse_form.get_verse + after_text, verse_tag, verse_num = self.verse_form.get_verse() verse_def = '%s%s' % (verse_tag, verse_num) item = QtGui.QTableWidgetItem(after_text) item.setData(QtCore.Qt.UserRole, verse_def) @@ -619,7 +619,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): verse_id = item.data(QtCore.Qt.UserRole) self.verse_form.set_verse(temp_text, True, verse_id) if self.verse_form.exec_(): - after_text, verse_tag, verse_num = self.verse_form.get_verse + after_text, verse_tag, verse_num = self.verse_form.get_verse() verse_def = '%s%s' % (verse_tag, verse_num) item.setData(QtCore.Qt.UserRole, verse_def) item.setText(after_text) @@ -661,7 +661,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): self.verse_form.set_verse('') if not self.verse_form.exec_(): return - verse_list = self.verse_form.get_all_verses + verse_list = self.verse_form.get_all_verses() verse_list = str(verse_list.replace('\r\n', '\n')) self.verse_list_widget.clear() self.verse_list_widget.setRowCount(0) From 78e7cb58732da6ed9928b62419d12161f4285eeb Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Mon, 24 Mar 2014 14:32:28 +0100 Subject: [PATCH 041/155] Test for add_welcome_page --- tests/functional/openlp_core_lib/test_ui.py | 55 +++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 tests/functional/openlp_core_lib/test_ui.py diff --git a/tests/functional/openlp_core_lib/test_ui.py b/tests/functional/openlp_core_lib/test_ui.py new file mode 100644 index 000000000..747005bd8 --- /dev/null +++ b/tests/functional/openlp_core_lib/test_ui.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2014 Raoul Snyman # +# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan # +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # +# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, # +# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, # +# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, # +# Frode Woldsund, Martin Zibricky, Patrick Zimmermann # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### +""" +Package to test the openlp.core.lib.ui package. +""" +from PyQt4 import QtGui +from unittest import TestCase + +from openlp.core.lib.ui import * + + +class TestUi(TestCase): + """ + Test the functions in the ui module + """ + + def test_add_welcome_page(self): + """ + Test appending a welcome page to a wizard + """ + # GIVEN: A wizard + wizard = QtGui.QWizard() + + # WHEN: A welcome page has been added to the wizard + add_welcome_page(wizard, ':/wizards/wizard_firsttime.bmp') + + # THEN: The wizard should have one page with a pixmap. + self.assertEqual(1, len(wizard.pageIds()), 'The wizard should have one page.') + self.assertIsInstance(wizard.page(0).pixmap(QtGui.QWizard.WatermarkPixmap), QtGui.QPixmap) From 8ad47cb888c4700271db911d09ed2291fa60d38b Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Mon, 24 Mar 2014 17:49:50 +0100 Subject: [PATCH 042/155] Fixes for windows tests and for starting OpenLP on windows. --- .../presentations/lib/pptviewcontroller.py | 5 +- .../presentations/test_pptviewcontroller.py | 65 ++++++++++++++++++- tests/helpers/testmixin.py | 5 +- .../openlp_core_lib/test_pluginmanager.py | 4 ++ 4 files changed, 71 insertions(+), 8 deletions(-) diff --git a/openlp/plugins/presentations/lib/pptviewcontroller.py b/openlp/plugins/presentations/lib/pptviewcontroller.py index 46195478d..16008f7e9 100644 --- a/openlp/plugins/presentations/lib/pptviewcontroller.py +++ b/openlp/plugins/presentations/lib/pptviewcontroller.py @@ -34,6 +34,7 @@ if os.name == 'nt': from ctypes import cdll from ctypes.wintypes import RECT +from openlp.core.utils import AppLocation from openlp.core.lib import ScreenList from .presentationcontroller import PresentationController, PresentationDocument @@ -85,8 +86,8 @@ class PptviewController(PresentationController): if self.process: return log.debug('start PPTView') - dll_path = os.path.join( - self.plugin_manager.base_path, 'presentations', 'lib', 'pptviewlib', 'pptviewlib.dll') + dll_path = os.path.join(AppLocation.get_directory(AppLocation.AppDir), + 'presentations', 'lib', 'pptviewlib', 'pptviewlib.dll') self.process = cdll.LoadLibrary(dll_path) if log.isEnabledFor(logging.DEBUG): self.process.SetDebug(1) diff --git a/tests/functional/openlp_plugins/presentations/test_pptviewcontroller.py b/tests/functional/openlp_plugins/presentations/test_pptviewcontroller.py index 5f25c4d3a..1b43615f3 100644 --- a/tests/functional/openlp_plugins/presentations/test_pptviewcontroller.py +++ b/tests/functional/openlp_plugins/presentations/test_pptviewcontroller.py @@ -30,19 +30,78 @@ This module contains tests for the pptviewcontroller module of the Presentations plugin. """ import os +import shutil +if os.name == 'nt': + from ctypes import cdll + +from tempfile import mkdtemp from unittest import TestCase from tests.functional import MagicMock, patch +from tests.helpers.testmixin import TestMixin -from openlp.plugins.presentations.lib.pptviewcontroller import PptviewDocument +from openlp.plugins.presentations.lib.pptviewcontroller import PptviewDocument, PptviewController + +class TestPptviewController(TestCase, TestMixin): + """ + Test the PptviewController Class + """ #TODO: Items left to test # PptviewController -# __init__ -# check_availablecheck_installed # start_process(self) # kill + def setUp(self): + """ + Set up the patches and mocks need for all tests. + """ + self.get_application() + self.build_settings() + self.mock_plugin = MagicMock() + self.temp_folder = mkdtemp() + self.mock_plugin.settings_section = self.temp_folder + + def tearDown(self): + """ + Stop the patches + """ + self.destroy_settings() + shutil.rmtree(self.temp_folder) + + def constructor_test(self): + """ + Test the Constructor from the PptViewController + """ + # GIVEN: No presentation controller + controller = None + + # WHEN: The presentation controller object is created + controller = PptviewController(plugin=self.mock_plugin) + + # THEN: The name of the presentation controller should be correct + self.assertEqual('Powerpoint Viewer', controller.name, 'The name of the presentation controller should be correct') + + def check_available_test(self): + """ + Test check_available / check_installed + """ + # GIVEN: A mocked dll loader and a controller + with patch('ctypes.cdll.LoadLibrary') as mocked_load_library: + mocked_process = MagicMock() + mocked_process.CheckInstalled.return_value = True + mocked_load_library.return_value = mocked_process + controller = PptviewController(plugin=self.mock_plugin) + + # WHEN: check_available is called + available = controller.check_available() + + # THEN: On windows it should return True, on other platforms False + if os.name == 'nt': + self.assertTrue(available, 'check_available should return True on windows.') + else: + self.assertFalse(available, 'check_available should return False when not on windows.') + class TestPptviewDocument(TestCase): """ diff --git a/tests/helpers/testmixin.py b/tests/helpers/testmixin.py index bda6d7291..b4e8a5c59 100644 --- a/tests/helpers/testmixin.py +++ b/tests/helpers/testmixin.py @@ -53,13 +53,12 @@ class TestMixin(object): Build the settings Object and initialise it """ Settings.setDefaultFormat(Settings.IniFormat) - fd, self.ini_file = mkstemp('.ini') + self.fd, self.ini_file = mkstemp('.ini') Settings().set_filename(self.ini_file) def destroy_settings(self): """ Destroy the Settings Object """ - if hasattr(self, 'fd'): - os.close(self.fd) + os.close(self.fd) os.unlink(Settings().fileName()) diff --git a/tests/interfaces/openlp_core_lib/test_pluginmanager.py b/tests/interfaces/openlp_core_lib/test_pluginmanager.py index 2949a46fb..054aa6dc1 100644 --- a/tests/interfaces/openlp_core_lib/test_pluginmanager.py +++ b/tests/interfaces/openlp_core_lib/test_pluginmanager.py @@ -31,6 +31,7 @@ Package to test the openlp.core.lib.pluginmanager package. """ import sys import shutil +import gc from tempfile import mkdtemp from unittest import TestCase @@ -65,6 +66,9 @@ class TestPluginManager(TestCase, TestMixin): del self.main_window Settings().remove('advanced/data path') self.destroy_settings() + # On windows we need to manually garbage collect to close sqlalchemy files + # to avoid errors when temporary files are deleted. + gc.collect() shutil.rmtree(self.temp_dir) def find_plugins_test(self): From a4829c62ba1f408065ffb48b89239ab019e440b3 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 26 Mar 2014 12:26:16 +0100 Subject: [PATCH 043/155] Add missing arguments for debug mode Fixes: https://launchpad.net/bugs/1296574 --- openlp/core/ui/slidecontroller.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index eb1f13fa6..7faf10ca2 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -471,7 +471,7 @@ class SlideController(DisplayController, RegistryProperties): category=self.category, triggers=self.live_escape) - def live_escape(self): + def live_escape(self, field=None): """ If you press ESC on the live screen it should close the display temporarily. """ @@ -1243,7 +1243,7 @@ class SlideController(DisplayController, RegistryProperties): if self.service_item: self.service_manager.add_service_item(self.service_item) - def on_go_live_click(self): + def on_go_live_click(self, field=None): """ triggered by clicking the Preview slide items """ @@ -1256,7 +1256,7 @@ class SlideController(DisplayController, RegistryProperties): self.on_media_close() self.on_go_live() - def on_go_live(self): + def on_go_live(self, field=None): """ If preview copy slide item to live controller from Preview Controller """ From bf32fe164a380cf18c3915976d57254f4a6f4cbe Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 26 Mar 2014 13:06:48 +0100 Subject: [PATCH 044/155] Test --- openlp/core/lib/ui.py | 6 +++-- tests/functional/openlp_core_lib/test_ui.py | 28 +++++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/openlp/core/lib/ui.py b/openlp/core/lib/ui.py index 6d9e9e8f8..3126d1a56 100644 --- a/openlp/core/lib/ui.py +++ b/openlp/core/lib/ui.py @@ -74,11 +74,13 @@ def create_button_box(dialog, name, standard_buttons, custom_buttons=None): :param name: A string which is set as object name. :param standard_buttons: A list of strings for the used buttons. It might contain: ``ok``, ``save``, ``cancel``, ``close``, and ``defaults``. - :param custom_buttons: A list of additional buttons. If a item is a instance of QtGui.QAbstractButton it is added - with QDialogButtonBox.ActionRole. Other wise the item has to be a tuple of a button and a ButtonRole. + :param custom_buttons: A list of additional buttons. If an item is an instance of QtGui.QAbstractButton it is added + with QDialogButtonBox.ActionRole. Otherwise the item has to be a tuple of a Button and a ButtonRole. """ if custom_buttons is None: custom_buttons = [] + if standard_buttons is None: + standard_buttons = [] buttons = QtGui.QDialogButtonBox.NoButton if 'ok' in standard_buttons: buttons |= QtGui.QDialogButtonBox.Ok diff --git a/tests/functional/openlp_core_lib/test_ui.py b/tests/functional/openlp_core_lib/test_ui.py index 747005bd8..babc94a81 100644 --- a/tests/functional/openlp_core_lib/test_ui.py +++ b/tests/functional/openlp_core_lib/test_ui.py @@ -53,3 +53,31 @@ class TestUi(TestCase): # THEN: The wizard should have one page with a pixmap. self.assertEqual(1, len(wizard.pageIds()), 'The wizard should have one page.') self.assertIsInstance(wizard.page(0).pixmap(QtGui.QWizard.WatermarkPixmap), QtGui.QPixmap) + + def test_create_button_box(self): + """ + Test creating a button box for a dialog + """ + # GIVEN: A dialog + dialog = QtGui.QDialog() + + # WHEN: We create the button box with five buttons + btnbox = create_button_box(dialog, 'my_btns', ['ok', 'save', 'cancel', 'close', 'defaults']) + + # THEN: We should get a QDialogButtonBox with five buttons + self.assertIsInstance(btnbox, QtGui.QDialogButtonBox) + self.assertEqual(5, len(btnbox.buttons())) + + # WHEN: We create the button box with a custom button + btnbox = create_button_box(dialog, 'my_btns', None, [QtGui.QPushButton('Custom')]) + # THEN: We should get a QDialogButtonBox with one button + self.assertIsInstance(btnbox, QtGui.QDialogButtonBox) + self.assertEqual(1, len(btnbox.buttons())) + + # WHEN: We create the button box with a custom button and a custom role + btnbox = create_button_box(dialog, 'my_btns', None, + [(QtGui.QPushButton('Help'), QtGui.QDialogButtonBox.HelpRole)]) + # THEN: We should get a QDialogButtonBox with one button with a certain role + self.assertIsInstance(btnbox, QtGui.QDialogButtonBox) + self.assertEqual(1, len(btnbox.buttons())) + self.assertEqual(QtGui.QDialogButtonBox.HelpRole, btnbox.buttonRole(btnbox.buttons()[0])) From b4bf94a4f073b281f8626cadb23a5d8afe7bde1a Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Fri, 28 Mar 2014 21:41:27 +0100 Subject: [PATCH 045/155] pep8 --- tests/interfaces/openlp_core_lib/test_searchedit.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/interfaces/openlp_core_lib/test_searchedit.py b/tests/interfaces/openlp_core_lib/test_searchedit.py index 07db05dd7..98c942885 100644 --- a/tests/interfaces/openlp_core_lib/test_searchedit.py +++ b/tests/interfaces/openlp_core_lib/test_searchedit.py @@ -72,7 +72,6 @@ class TestSearchEdit(TestCase, TestMixin): """ del self.main_window - def set_search_types_test(self): """ Test setting the search types of the search edit. From e4c3f08a2a82a70eb9b4bb0e20f2308b63d102eb Mon Sep 17 00:00:00 2001 From: Raoul Snyman Date: Sat, 29 Mar 2014 15:31:28 +0200 Subject: [PATCH 046/155] Fix some of the tests which were failing in Python 3.4 --- openlp/plugins/songs/lib/easyslidesimport.py | 2 +- openlp/plugins/songs/lib/ewimport.py | 2 +- openlp/plugins/songs/lib/songbeamerimport.py | 2 +- .../plugins/songs/lib/songshowplusimport.py | 2 +- .../openlp_plugins/songs/test_ewimport.py | 22 +++++++++---------- .../songs/test_songbeamerimport.py | 8 +++---- .../songs/test_songshowplusimport.py | 10 ++++----- tests/helpers/songfileimport.py | 2 +- 8 files changed, 25 insertions(+), 25 deletions(-) diff --git a/openlp/plugins/songs/lib/easyslidesimport.py b/openlp/plugins/songs/lib/easyslidesimport.py index 628d64880..e28e7bf97 100644 --- a/openlp/plugins/songs/lib/easyslidesimport.py +++ b/openlp/plugins/songs/lib/easyslidesimport.py @@ -49,7 +49,7 @@ class EasySlidesImport(SongImport): """ Initialise the class. """ - SongImport.__init__(self, manager, **kwargs) + super(EasySlidesImport, self).__init__(manager, **kwargs) def do_import(self): log.info('Importing EasySlides XML file %s', self.import_source) diff --git a/openlp/plugins/songs/lib/ewimport.py b/openlp/plugins/songs/lib/ewimport.py index 9e3ae566e..cde0b1692 100644 --- a/openlp/plugins/songs/lib/ewimport.py +++ b/openlp/plugins/songs/lib/ewimport.py @@ -73,7 +73,7 @@ class EasyWorshipSongImport(SongImport): ability to import EasyWorship song files. """ def __init__(self, manager, **kwargs): - SongImport.__init__(self, manager, **kwargs) + super(EasyWorshipSongImport, self).__init__(manager, **kwargs) def do_import(self): """ diff --git a/openlp/plugins/songs/lib/songbeamerimport.py b/openlp/plugins/songs/lib/songbeamerimport.py index 4de763554..0106ca687 100644 --- a/openlp/plugins/songs/lib/songbeamerimport.py +++ b/openlp/plugins/songs/lib/songbeamerimport.py @@ -97,7 +97,7 @@ class SongBeamerImport(SongImport): """ Initialise the Song Beamer importer. """ - SongImport.__init__(self, manager, **kwargs) + super(SongBeamerImport, self).__init__(manager, **kwargs) def do_import(self): """ diff --git a/openlp/plugins/songs/lib/songshowplusimport.py b/openlp/plugins/songs/lib/songshowplusimport.py index 485a8f047..0a8dc4650 100644 --- a/openlp/plugins/songs/lib/songshowplusimport.py +++ b/openlp/plugins/songs/lib/songshowplusimport.py @@ -92,7 +92,7 @@ class SongShowPlusImport(SongImport): """ Initialise the SongShow Plus importer. """ - SongImport.__init__(self, manager, **kwargs) + super(SongShowPlusImport, self).__init__(manager, **kwargs) def do_import(self): """ diff --git a/tests/functional/openlp_plugins/songs/test_ewimport.py b/tests/functional/openlp_plugins/songs/test_ewimport.py index a22519bec..f98ba57d2 100644 --- a/tests/functional/openlp_plugins/songs/test_ewimport.py +++ b/tests/functional/openlp_plugins/songs/test_ewimport.py @@ -77,7 +77,7 @@ class EasyWorshipSongImportLogger(EasyWorshipSongImport): _title_assignment_list = [] def __init__(self, manager): - EasyWorshipSongImport.__init__(self, manager) + EasyWorshipSongImport.__init__(self, manager, filenames=[]) @property def title(self): @@ -147,7 +147,7 @@ class TestEasyWorshipSongImport(TestCase): mocked_manager = MagicMock() # WHEN: An importer object is created - importer = EasyWorshipSongImport(mocked_manager) + importer = EasyWorshipSongImport(mocked_manager, filenames=[]) # THEN: The importer object should not be None self.assertIsNotNone(importer, 'Import should not be none') @@ -159,7 +159,7 @@ class TestEasyWorshipSongImport(TestCase): # GIVEN: A mocked out SongImport class, a mocked out "manager" and a list of field descriptions. with patch('openlp.plugins.songs.lib.ewimport.SongImport'): mocked_manager = MagicMock() - importer = EasyWorshipSongImport(mocked_manager) + importer = EasyWorshipSongImport(mocked_manager, filenames=[]) importer.field_descriptions = TEST_FIELD_DESCS # WHEN: Called with a field name that exists @@ -177,7 +177,7 @@ class TestEasyWorshipSongImport(TestCase): # GIVEN: A mocked out SongImport class, a mocked out "manager" and a list of field descriptions with patch('openlp.plugins.songs.lib.ewimport.SongImport'): mocked_manager = MagicMock() - importer = EasyWorshipSongImport(mocked_manager) + importer = EasyWorshipSongImport(mocked_manager, filenames=[]) importer.field_descriptions = TEST_FIELD_DESCS # WHEN: Called with a field name that does not exist @@ -196,7 +196,7 @@ class TestEasyWorshipSongImport(TestCase): with patch('openlp.plugins.songs.lib.ewimport.SongImport'), \ patch('openlp.plugins.songs.lib.ewimport.struct') as mocked_struct: mocked_manager = MagicMock() - importer = EasyWorshipSongImport(mocked_manager) + importer = EasyWorshipSongImport(mocked_manager, filenames=[]) # WHEN: set_record_struct is called with a list of field descriptions return_value = importer.set_record_struct(TEST_FIELD_DESCS) @@ -213,7 +213,7 @@ class TestEasyWorshipSongImport(TestCase): # GIVEN: A mocked out SongImport class, a mocked out "manager", an encoding and some test data and known results with patch('openlp.plugins.songs.lib.ewimport.SongImport'): mocked_manager = MagicMock() - importer = EasyWorshipSongImport(mocked_manager) + importer = EasyWorshipSongImport(mocked_manager, filenames=[]) importer.encoding = TEST_DATA_ENCODING importer.fields = TEST_FIELDS importer.field_descriptions = TEST_FIELD_DESCS @@ -236,7 +236,7 @@ class TestEasyWorshipSongImport(TestCase): with patch('openlp.plugins.songs.lib.ewimport.SongImport'): mocked_manager = MagicMock() mocked_memo_file = MagicMock() - importer = EasyWorshipSongImport(mocked_manager) + importer = EasyWorshipSongImport(mocked_manager, filenames=[]) importer.memo_file = mocked_memo_file importer.encoding = TEST_DATA_ENCODING @@ -267,7 +267,7 @@ class TestEasyWorshipSongImport(TestCase): with patch('openlp.plugins.songs.lib.ewimport.SongImport'), \ patch('openlp.plugins.songs.lib.ewimport.os.path') as mocked_os_path: mocked_manager = MagicMock() - importer = EasyWorshipSongImport(mocked_manager) + importer = EasyWorshipSongImport(mocked_manager, filenames=[]) mocked_os_path.isfile.side_effect = [True, False] # WHEN: Supplied with an import source @@ -286,7 +286,7 @@ class TestEasyWorshipSongImport(TestCase): with patch('openlp.plugins.songs.lib.ewimport.SongImport'), \ patch('openlp.plugins.songs.lib.ewimport.os.path') as mocked_os_path: mocked_manager = MagicMock() - importer = EasyWorshipSongImport(mocked_manager) + importer = EasyWorshipSongImport(mocked_manager, filenames=[]) mocked_os_path.isfile.return_value = True importer.import_source = 'Songs.DB' @@ -307,7 +307,7 @@ class TestEasyWorshipSongImport(TestCase): patch('builtins.open') as mocked_open, \ patch('openlp.plugins.songs.lib.ewimport.struct') as mocked_struct: mocked_manager = MagicMock() - importer = EasyWorshipSongImport(mocked_manager) + importer = EasyWorshipSongImport(mocked_manager, filenames=[]) mocked_os_path.isfile.return_value = True mocked_os_path.getsize.return_value = 0x800 importer.import_source = 'Songs.DB' @@ -334,7 +334,7 @@ class TestEasyWorshipSongImport(TestCase): patch('builtins.open'), patch('openlp.plugins.songs.lib.ewimport.struct') as mocked_struct, \ patch('openlp.plugins.songs.lib.ewimport.retrieve_windows_encoding') as mocked_retrieve_windows_encoding: mocked_manager = MagicMock() - importer = EasyWorshipSongImport(mocked_manager) + importer = EasyWorshipSongImport(mocked_manager, filenames=[]) mocked_os_path.isfile.return_value = True mocked_os_path.getsize.return_value = 0x800 importer.import_source = 'Songs.DB' diff --git a/tests/functional/openlp_plugins/songs/test_songbeamerimport.py b/tests/functional/openlp_plugins/songs/test_songbeamerimport.py index 72901eb47..a968975b3 100644 --- a/tests/functional/openlp_plugins/songs/test_songbeamerimport.py +++ b/tests/functional/openlp_plugins/songs/test_songbeamerimport.py @@ -66,7 +66,7 @@ class TestSongBeamerImport(TestCase): mocked_manager = MagicMock() # WHEN: An importer object is created - importer = SongBeamerImport(mocked_manager) + importer = SongBeamerImport(mocked_manager, filenames=[]) # THEN: The importer object should not be None self.assertIsNotNone(importer, 'Import should not be none') @@ -79,7 +79,7 @@ class TestSongBeamerImport(TestCase): with patch('openlp.plugins.songs.lib.songbeamerimport.SongImport'): mocked_manager = MagicMock() mocked_import_wizard = MagicMock() - importer = SongBeamerImport(mocked_manager) + importer = SongBeamerImport(mocked_manager, filenames=[]) importer.import_wizard = mocked_import_wizard importer.stop_import_flag = True @@ -100,7 +100,7 @@ class TestSongBeamerImport(TestCase): with patch('openlp.plugins.songs.lib.songbeamerimport.SongImport'): mocked_manager = MagicMock() mocked_import_wizard = MagicMock() - importer = SongBeamerImport(mocked_manager) + importer = SongBeamerImport(mocked_manager, filenames=[]) importer.import_wizard = mocked_import_wizard importer.stop_import_flag = True @@ -127,7 +127,7 @@ class TestSongBeamerImport(TestCase): mocked_add_verse = MagicMock() mocked_finish = MagicMock() mocked_finish.return_value = True - importer = SongBeamerImport(mocked_manager) + importer = SongBeamerImport(mocked_manager, filenames=[]) importer.import_wizard = mocked_import_wizard importer.stop_import_flag = False importer.add_verse = mocked_add_verse diff --git a/tests/functional/openlp_plugins/songs/test_songshowplusimport.py b/tests/functional/openlp_plugins/songs/test_songshowplusimport.py index 5c945efb3..16af347b2 100644 --- a/tests/functional/openlp_plugins/songs/test_songshowplusimport.py +++ b/tests/functional/openlp_plugins/songs/test_songshowplusimport.py @@ -72,7 +72,7 @@ class TestSongShowPlusImport(TestCase): mocked_manager = MagicMock() # WHEN: An importer object is created - importer = SongShowPlusImport(mocked_manager) + importer = SongShowPlusImport(mocked_manager, filenames=[]) # THEN: The importer object should not be None self.assertIsNotNone(importer, 'Import should not be none') @@ -85,7 +85,7 @@ class TestSongShowPlusImport(TestCase): with patch('openlp.plugins.songs.lib.songshowplusimport.SongImport'): mocked_manager = MagicMock() mocked_import_wizard = MagicMock() - importer = SongShowPlusImport(mocked_manager) + importer = SongShowPlusImport(mocked_manager, filenames=[]) importer.import_wizard = mocked_import_wizard importer.stop_import_flag = True @@ -106,7 +106,7 @@ class TestSongShowPlusImport(TestCase): with patch('openlp.plugins.songs.lib.songshowplusimport.SongImport'): mocked_manager = MagicMock() mocked_import_wizard = MagicMock() - importer = SongShowPlusImport(mocked_manager) + importer = SongShowPlusImport(mocked_manager, filenames=[]) importer.import_wizard = mocked_import_wizard importer.stop_import_flag = True @@ -126,7 +126,7 @@ class TestSongShowPlusImport(TestCase): # GIVEN: A mocked out SongImport class, and a mocked out "manager" with patch('openlp.plugins.songs.lib.songshowplusimport.SongImport'): mocked_manager = MagicMock() - importer = SongShowPlusImport(mocked_manager) + importer = SongShowPlusImport(mocked_manager, filenames=[]) # WHEN: Supplied with the following arguments replicating verses being added test_values = [('Verse 1', VerseType.tags[VerseType.Verse] + '1'), @@ -153,7 +153,7 @@ class TestSongShowPlusImport(TestCase): # GIVEN: A mocked out SongImport class, and a mocked out "manager" with patch('openlp.plugins.songs.lib.songshowplusimport.SongImport'): mocked_manager = MagicMock() - importer = SongShowPlusImport(mocked_manager) + importer = SongShowPlusImport(mocked_manager, filenames=[]) # WHEN: Supplied with the following arguments replicating a verse order being added test_values = [('Verse 1', VerseType.tags[VerseType.Verse] + '1'), diff --git a/tests/helpers/songfileimport.py b/tests/helpers/songfileimport.py index 2ffecb11a..12754f55a 100644 --- a/tests/helpers/songfileimport.py +++ b/tests/helpers/songfileimport.py @@ -89,7 +89,7 @@ class SongImportTestHelper(TestCase): """ Import the given file and check that it has imported correctly """ - importer = self.importer_class(self.mocked_manager) + importer = self.importer_class(self.mocked_manager, filenames=[source_file_name]) importer.import_wizard = self.mocked_import_wizard importer.stop_import_flag = False importer.topics = [] From 0f0e09a2825cf743a10f40d03415dccd41b36412 Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Sat, 29 Mar 2014 20:56:20 +0100 Subject: [PATCH 047/155] Fixed wrong dll load path. --- openlp/plugins/presentations/lib/pptviewcontroller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/plugins/presentations/lib/pptviewcontroller.py b/openlp/plugins/presentations/lib/pptviewcontroller.py index 16008f7e9..a9090dd1e 100644 --- a/openlp/plugins/presentations/lib/pptviewcontroller.py +++ b/openlp/plugins/presentations/lib/pptviewcontroller.py @@ -87,7 +87,7 @@ class PptviewController(PresentationController): return log.debug('start PPTView') dll_path = os.path.join(AppLocation.get_directory(AppLocation.AppDir), - 'presentations', 'lib', 'pptviewlib', 'pptviewlib.dll') + 'plugins', 'presentations', 'lib', 'pptviewlib', 'pptviewlib.dll') self.process = cdll.LoadLibrary(dll_path) if log.isEnabledFor(logging.DEBUG): self.process.SetDebug(1) From 80284f6f8d7f6e69b9e80fa6cc4c63380a1682d9 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Mon, 31 Mar 2014 10:08:07 +0200 Subject: [PATCH 048/155] =?UTF-8?q?Added=20'Neue=20evangelistische=20?= =?UTF-8?q?=C3=9Cbersetzung'=20to=20the=20list=20of=20Online=20bible=20tra?= =?UTF-8?q?nslations?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bibles/resources/bibles_resources.sqlite | Bin 104448 -> 104448 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/openlp/plugins/bibles/resources/bibles_resources.sqlite b/openlp/plugins/bibles/resources/bibles_resources.sqlite index c0fa931d1bb88b6e6c0224feff9cbc5aa61c7312..8f1777124032ee11b547fd6e1897f7e9b8b717a9 100644 GIT binary patch delta 140 zcmZqJz}B#VZGtr8lZi6UtWOwppD}GrvEyc2pv27V$vF9Sk=*8E+;$J-cp0@sn0GQy zV$NjtVEV;$nrRkOJQF+PE5_Z7HH=yt8#x#a{Tmh785(&d85#W;QW;8tSOJL37!n!s n7}9~P9EMDWVulhhnaq&EkUIIpOKG-LhC2)z%$sZ8wyXjGKRhSf delta 78 zcmV-U0I~moum*sz29O&8*oM;vcbOJ_`n)A_D_S0h8-;9<#;^KHwY-0eln# kxdV~|X#+?C`U1-WngU+}2Lb8 Date: Mon, 31 Mar 2014 14:13:50 +0200 Subject: [PATCH 049/155] cleanup: remove local variable --- openlp/core/lib/searchedit.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openlp/core/lib/searchedit.py b/openlp/core/lib/searchedit.py index 13e388775..de329236c 100644 --- a/openlp/core/lib/searchedit.py +++ b/openlp/core/lib/searchedit.py @@ -181,5 +181,4 @@ class SearchEdit(QtGui.QLineEdit): for action in self.menu_button.menu().actions(): # Why is this needed? action.setChecked(False) - sender = self.sender() - self.set_current_search_type(sender.data()) + self.set_current_search_type(self.sender().data()) From 5a02ab9471498575b48dfba981965f1d1fa7445a Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 31 Mar 2014 16:07:05 +0200 Subject: [PATCH 050/155] test: splash screen test --- .../openlp_core_ui/test_splashscreen.py | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 tests/interfaces/openlp_core_ui/test_splashscreen.py diff --git a/tests/interfaces/openlp_core_ui/test_splashscreen.py b/tests/interfaces/openlp_core_ui/test_splashscreen.py new file mode 100644 index 000000000..35c15f9ec --- /dev/null +++ b/tests/interfaces/openlp_core_ui/test_splashscreen.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2014 Raoul Snyman # +# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan # +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # +# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, # +# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, # +# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, # +# Frode Woldsund, Martin Zibricky, Patrick Zimmermann # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### +""" +Test the openlp.core.ui.splashscreen class. +""" +from unittest import TestCase + +from PyQt4 import QtGui + +from openlp.core.ui import SplashScreen +from tests.helpers.testmixin import TestMixin + + +class TestSplashScreen(TestCase, TestMixin): + def setUp(self): + self.get_application() + self.main_window = QtGui.QMainWindow() + + def tearDown(self): + """ + Delete all the C++ objects at the end so that we don't have a segfault + """ + del self.app + del self.main_window + + def setupUi_test(self): + """ + Test if the setupUi method.... + """ + # GIVEN: A splash screen instance. + splash = SplashScreen() + + # THEN: Check if the splash has a setupUi method. + assert hasattr(splash, 'setupUi'), 'The Splash Screen should have a setupUi() method.' From c64723371af2589b5916074d07631eca3fdd64ce Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 31 Mar 2014 16:47:43 +0200 Subject: [PATCH 051/155] test of ci script --- tests/interfaces/openlp_core_ui/test_filerenamedialog.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/interfaces/openlp_core_ui/test_filerenamedialog.py b/tests/interfaces/openlp_core_ui/test_filerenamedialog.py index 905f167e9..c618e680e 100644 --- a/tests/interfaces/openlp_core_ui/test_filerenamedialog.py +++ b/tests/interfaces/openlp_core_ui/test_filerenamedialog.py @@ -62,6 +62,7 @@ class TestStartFileRenameForm(TestCase, TestMixin): """ Test the windowTitle of the FileRenameDialog """ + assert False, "reason" # GIVEN: A mocked QDialog.exec_() method with patch('PyQt4.QtGui.QDialog.exec_') as mocked_exec: From d77c6fe7c0f6d53fc3e33b683dbda6b9fff0f82a Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 31 Mar 2014 17:17:40 +0200 Subject: [PATCH 052/155] added renderer test case --- .../openlp_core_lib/test_renderer.py | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/tests/functional/openlp_core_lib/test_renderer.py b/tests/functional/openlp_core_lib/test_renderer.py index d0aa82b74..e4e7f0051 100644 --- a/tests/functional/openlp_core_lib/test_renderer.py +++ b/tests/functional/openlp_core_lib/test_renderer.py @@ -49,7 +49,7 @@ class TestRenderer(TestCase): def setUp(self): """ - Set up the components need for all tests. + Set up the components need for all tests """ # Mocked out desktop object self.desktop = MagicMock() @@ -67,7 +67,7 @@ class TestRenderer(TestCase): def initial_renderer_test(self): """ - Test the initial renderer state . + Test the initial renderer state """ # GIVEN: A new renderer instance. renderer = Renderer() @@ -77,7 +77,7 @@ class TestRenderer(TestCase): def default_screen_layout_test(self): """ - Test the default layout calculations. + Test the default layout calculations """ # GIVEN: A new renderer instance. renderer = Renderer() @@ -86,4 +86,20 @@ class TestRenderer(TestCase): self.assertEqual(renderer.width, 1024, 'The base renderer should be a live controller') self.assertEqual(renderer.height, 768, 'The base renderer should be a live controller') self.assertEqual(renderer.screen_ratio, 0.75, 'The base renderer should be a live controller') - self.assertEqual(renderer.footer_start, 691, 'The base renderer should be a live controller') \ No newline at end of file + self.assertEqual(renderer.footer_start, 691, 'The base renderer should be a live controller') + + def _get_start_tags_test(self): + """ + Test the _get_start_tags() method + """ + # GIVEN: A new renderer instance. + renderer = Renderer() + given_raw_text = '{st}{r}Text text text' + expected_tuple = ('{st}{r}Text text text{/r}{/st}', '{st}{r}', + '') + + # WHEN: + result = renderer._get_start_tags(given_raw_text) + + # THEN: + self.assertEqual(result, expected_tuple) \ No newline at end of file From b391072e9f74f5ed765a164a24ff1bff2ad806a6 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 31 Mar 2014 17:20:17 +0200 Subject: [PATCH 053/155] removed test code --- tests/functional/openlp_core_lib/test_renderer.py | 2 +- tests/interfaces/openlp_core_ui/test_filerenamedialog.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/functional/openlp_core_lib/test_renderer.py b/tests/functional/openlp_core_lib/test_renderer.py index e4e7f0051..aa27f93e7 100644 --- a/tests/functional/openlp_core_lib/test_renderer.py +++ b/tests/functional/openlp_core_lib/test_renderer.py @@ -92,7 +92,7 @@ class TestRenderer(TestCase): """ Test the _get_start_tags() method """ - # GIVEN: A new renderer instance. + # GIVEN: A new renderer instance. Broken raw_text (missing closing tags). renderer = Renderer() given_raw_text = '{st}{r}Text text text' expected_tuple = ('{st}{r}Text text text{/r}{/st}', '{st}{r}', diff --git a/tests/interfaces/openlp_core_ui/test_filerenamedialog.py b/tests/interfaces/openlp_core_ui/test_filerenamedialog.py index c618e680e..905f167e9 100644 --- a/tests/interfaces/openlp_core_ui/test_filerenamedialog.py +++ b/tests/interfaces/openlp_core_ui/test_filerenamedialog.py @@ -62,7 +62,6 @@ class TestStartFileRenameForm(TestCase, TestMixin): """ Test the windowTitle of the FileRenameDialog """ - assert False, "reason" # GIVEN: A mocked QDialog.exec_() method with patch('PyQt4.QtGui.QDialog.exec_') as mocked_exec: From 3f038ad5c5c82ecfd329fa845aa8e6ac44156f41 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 31 Mar 2014 17:21:41 +0200 Subject: [PATCH 054/155] break --- tests/functional/openlp_core_lib/test_renderer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/openlp_core_lib/test_renderer.py b/tests/functional/openlp_core_lib/test_renderer.py index aa27f93e7..a17ddb88f 100644 --- a/tests/functional/openlp_core_lib/test_renderer.py +++ b/tests/functional/openlp_core_lib/test_renderer.py @@ -96,7 +96,7 @@ class TestRenderer(TestCase): renderer = Renderer() given_raw_text = '{st}{r}Text text text' expected_tuple = ('{st}{r}Text text text{/r}{/st}', '{st}{r}', - '') + '') # WHEN: result = renderer._get_start_tags(given_raw_text) From 9dd5f3025411ade09fde61d8c51896e05e5af38e Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 31 Mar 2014 17:24:31 +0200 Subject: [PATCH 055/155] be a bit more talky --- tests/functional/openlp_core_lib/test_renderer.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/functional/openlp_core_lib/test_renderer.py b/tests/functional/openlp_core_lib/test_renderer.py index a17ddb88f..cd07ad9a2 100644 --- a/tests/functional/openlp_core_lib/test_renderer.py +++ b/tests/functional/openlp_core_lib/test_renderer.py @@ -96,10 +96,11 @@ class TestRenderer(TestCase): renderer = Renderer() given_raw_text = '{st}{r}Text text text' expected_tuple = ('{st}{r}Text text text{/r}{/st}', '{st}{r}', - '') + '') # WHEN: result = renderer._get_start_tags(given_raw_text) - # THEN: - self.assertEqual(result, expected_tuple) \ No newline at end of file + # THEN: Check if the correct tuple is returned. + self.assertEqual(result, expected_tuple), 'A tuple should be returned ' + '(fixed-text, opening tags, html opening tags).' From 3260de7b7c351b9d3a134211a78b9264bbc1095e Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 31 Mar 2014 17:31:05 +0200 Subject: [PATCH 056/155] added another test --- tests/functional/openlp_core_lib/test_renderer.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/functional/openlp_core_lib/test_renderer.py b/tests/functional/openlp_core_lib/test_renderer.py index cd07ad9a2..22b7c4a7b 100644 --- a/tests/functional/openlp_core_lib/test_renderer.py +++ b/tests/functional/openlp_core_lib/test_renderer.py @@ -104,3 +104,18 @@ class TestRenderer(TestCase): # THEN: Check if the correct tuple is returned. self.assertEqual(result, expected_tuple), 'A tuple should be returned ' '(fixed-text, opening tags, html opening tags).' + + def _word_split_test(self): + """ + Test the _word_split() method + """ + # GIVEN: A line of text + renderer = Renderer() + given_line = 'beginning asdf \n end asdf' + expected_words = ['beginning', 'asdf', 'end', 'asdf'] + + # WHEN: Split the line + result_words = renderer._words_split(given_line) + + # THEN: The word lists should be the same. + self.assertListEqual(result_words, expected_words) From 4da90f0c6a2d830783655cf029cbb314e33924ee Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 31 Mar 2014 17:36:11 +0200 Subject: [PATCH 057/155] comments: updated comments --- tests/functional/openlp_plugins/songs/test_lib.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/functional/openlp_plugins/songs/test_lib.py b/tests/functional/openlp_plugins/songs/test_lib.py index 2683770c6..67d54d3f0 100644 --- a/tests/functional/openlp_plugins/songs/test_lib.py +++ b/tests/functional/openlp_plugins/songs/test_lib.py @@ -98,7 +98,7 @@ class TestLib(TestCase): # WHEN: We compare those songs for equality. result = songs_probably_equal((self.song1, self.song2)) - # THEN: The result should be True. + # THEN: The result should be a tuple.. assert result == (self.song1, self.song2), 'The result should be the tuble of songs' def songs_probably_equal_short_song_test(self): @@ -112,7 +112,7 @@ class TestLib(TestCase): # WHEN: We compare those songs for equality. result = songs_probably_equal((self.song1, self.song2)) - # THEN: The result should be True. + # THEN: The result should be a tuple.. assert result == (self.song1, self.song2), 'The result should be the tuble of songs' def songs_probably_equal_error_song_test(self): @@ -126,7 +126,7 @@ class TestLib(TestCase): # WHEN: We compare those songs for equality. result = songs_probably_equal((self.song1, self.song2)) - # THEN: The result should be True. + # THEN: The result should be a tuple of songs.. assert result == (self.song1, self.song2), 'The result should be the tuble of songs' def songs_probably_equal_different_song_test(self): @@ -140,7 +140,7 @@ class TestLib(TestCase): # WHEN: We compare those songs for equality. result = songs_probably_equal((self.song1, self.song2)) - # THEN: The result should be False. + # THEN: The result should be Nonw. assert result is None, 'The result should be None' def remove_typos_beginning_test(self): From 729edf1d21ba81e936f5f1114837f4fb61575fa4 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 31 Mar 2014 17:44:18 +0200 Subject: [PATCH 058/155] pep8 failure test --- tests/functional/openlp_core_lib/test_renderer.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/functional/openlp_core_lib/test_renderer.py b/tests/functional/openlp_core_lib/test_renderer.py index 22b7c4a7b..4a3e54c0d 100644 --- a/tests/functional/openlp_core_lib/test_renderer.py +++ b/tests/functional/openlp_core_lib/test_renderer.py @@ -119,3 +119,5 @@ class TestRenderer(TestCase): # THEN: The word lists should be the same. self.assertListEqual(result_words, expected_words) + + pep_error = 'sdfjalksdjfl kajsdlfj lkasdjflkjaslkdjlkasjdljklsdjflkajsdljalksdflkajsdlfj laskdflkjsdlfkjaslkdflksajdlfk' From 13c053bc5cb438ef933f4fa2352c6d764857e3f0 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 31 Mar 2014 18:08:42 +0200 Subject: [PATCH 059/155] added jenkins script --- tests/utils/jenkins-script.py | 188 ++++++++++++++++++++++++++++++++++ 1 file changed, 188 insertions(+) create mode 100644 tests/utils/jenkins-script.py diff --git a/tests/utils/jenkins-script.py b/tests/utils/jenkins-script.py new file mode 100644 index 000000000..3a6f34a13 --- /dev/null +++ b/tests/utils/jenkins-script.py @@ -0,0 +1,188 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2014 Raoul Snyman # +# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan # +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # +# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, # +# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, # +# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, # +# Frode Woldsund, Martin Zibricky, Patrick Zimmermann # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### +""" +This script helps to trigger builds of branches. To use it you have to install the jenkins-webapi package: + + pip3 isntall jenkins-webapi + +You probably want to create an alias. Add this to your ~/.bashrc file and then logout and login: + + alias ci="python PATH-TO-THIS-SCRIPT TOKEN" + +If you do not know/have the token, then please ask for it (e. g. IRC). +""" + +from optparse import OptionParser +from subprocess import Popen, PIPE +import sys +import time + +from jenkins import Jenkins +from PyQt4 import QtCore, QtGui + + +JENKINS_URL = 'http://ci.openlp.org/' + + +class OpenLPJobs(object): + """ + This class holds any jobs we have on jenkins and we actually need in this script. + """ + Branch_Pull = 'Branch-01-Pull' + Branch_Functional = 'Branch-02-Functional-Tests' + Branch_Interface = 'Branch-03-Interface-Tests' + Branch_Windows = 'Branch-04-Windows_Tests' + + Jobs = [Branch_Pull, Branch_Functional, Branch_Interface, Branch_Windows] + + +class JenkinsTrigger(object): + def __init__(self, token): + """ + Create the JenkinsTrigger instance. + + :param token: The token we need to trigger the build. If you do not have this token, ask in IRC. + """ + self.token = token + self.repo_name = get_repo_name() + self.jenkins_instance = Jenkins(JENKINS_URL) + + def trigger_build(self): + """ + Ask our jenkins server to build the "Branch-01-Pull" job. + """ + self.jenkins_instance.job(OpenLPJobs.Branch_Pull).build({'BRANCH_NAME': self.repo_name}, token=self.token) + + def print_output(self): + """ + Print the status information of the build tirggered. + """ + print("Add this to your merge proposal:") + print("--------------------------------") + for job in OpenLPJobs.Jobs: + self.__print_build_info(job) + + def open_browser(self): + """ + Opens the browser. + """ + url = self.jenkins_instance.job(OpenLPJobs.Branch_Pull).info['url'] + # Open the url + Popen(('xdg-open', url), stderr=PIPE) + + def __print_build_info(self, job_name): + """ + This helper method prints the job information of the given ``job_name`` + + :param job_name: The name of the job we want the information from. For example *Branch-01-Pull*. Use the class + variables from the :class:`OpenLPJobs` class. + """ + job = self.jenkins_instance.job(job_name) + while job.info['inQueue']: + # Give other processes the possibility to take over. Like Thread.yield(). + time.sleep(0) + build = job.last_build + build.wait() + result_string = build.info['result'] + url = build.info['url'] + print('[%s] %s' % (result_string, url)) + # On failure open the browser. + if result_string == "FAILURE": + url += 'console' + Popen(('xdg-open', url), stderr=PIPE) + + +def get_repo_name(): + """ + This returns the name of branch of the wokring directory. For example it returns *lp:~googol/openlp/render*. + """ + # Run the bzr command. + bzr = Popen(('bzr', 'info'), stdout=PIPE, stderr=PIPE) + raw_output, error = bzr.communicate() + # Detect any errors + if error: + print('This is not a branch.') + return + # Clean the output. + raw_output = raw_output.decode() + output_list = list(map(str.strip, raw_output.split('\n'))) + # Determine the branch's name + repo_name = '' + for line in output_list: + # Check if it is remote branch. + if 'push branch' in line: + repo_name = line.replace('push branch: bzr+ssh://bazaar.launchpad.net/', 'lp:') + break + # Check if trunk. Note, bzr info can return both 'push branch' as well as 'checkout of branch'. The later can + # occur before the first. So we do not want to break here to make sure we find any orrucances of + elif 'checkout of branch' in line: + repo_name = line.replace('checkout of branch: bzr+ssh://bazaar.launchpad.net/+branch/', 'lp:') + repo_name = repo_name.strip('/') + + # Did we find the branch name? + if not repo_name: + for line in output_list: + # Check if the branch was pushed. + if 'Shared repository with trees (format: 2a)' in line: + print('Not a branch. cd to a branch.') + return + print('Not a branch. Have you pushed it to launchpad?') + return + return repo_name + + +def main(): + usage = 'Usage: python %prog TOKEN [options]' + + parser = OptionParser(usage=usage) + parser.add_option('-d', '--disable-output', dest='enable_output', action="store_false", default=True, + help='Print output for the merge request.') + parser.add_option('-b', '--open-browser', dest='open_browser', action="store_true", default=False, + help='Opens the jenkins page in your browser.') + #parser.add_option('-e', '--open-browser-on-error', dest='open_browser_on_error', action="store_true", default=False, + # help='Opens the jenkins page in your browser in case a test fails.') + options, args = parser.parse_args(sys.argv) + + if len(args) == 2: + if not get_repo_name(): + return + token = args[-1] + jenkins_trigger = JenkinsTrigger(token) + jenkins_trigger.trigger_build() + # Open the browser before printing the output. + if options.open_browser: + jenkins_trigger.open_browser() + if options.enable_output: + jenkins_trigger.print_output() + else: + raise Exception() + +if __name__ == '__main__': + main() From 5f0b40fe3017a41222f85056d6be65d8c2c07b5a Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 31 Mar 2014 18:27:26 +0200 Subject: [PATCH 060/155] Moved script --- tests/utils/jenkins-script.py => scripts/jenkins_script.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/utils/jenkins-script.py => scripts/jenkins_script.py (100%) diff --git a/tests/utils/jenkins-script.py b/scripts/jenkins_script.py similarity index 100% rename from tests/utils/jenkins-script.py rename to scripts/jenkins_script.py From 4bed39869a7846720cf60d564f44585e8d9ca6e0 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 31 Mar 2014 18:30:07 +0200 Subject: [PATCH 061/155] updated check_dependencies.py sciprt --- scripts/check_dependencies.py | 1 + scripts/jenkins_script.py | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/check_dependencies.py b/scripts/check_dependencies.py index adb6a47f2..bab1c6419 100755 --- a/scripts/check_dependencies.py +++ b/scripts/check_dependencies.py @@ -94,6 +94,7 @@ OPTIONAL_MODULES = [ ('psycopg2', '(PostgreSQL support)', True), ('nose', '(testing framework)', True), ('mock', '(testing module)', sys.version_info[1] < 3), + ('jenkins', '(access jenkins api - package name: enkins-webapi)', True), ] w = sys.stdout.write diff --git a/scripts/jenkins_script.py b/scripts/jenkins_script.py index 3a6f34a13..eefe74150 100644 --- a/scripts/jenkins_script.py +++ b/scripts/jenkins_script.py @@ -45,7 +45,6 @@ import sys import time from jenkins import Jenkins -from PyQt4 import QtCore, QtGui JENKINS_URL = 'http://ci.openlp.org/' From 716a79bcc576547512982d7dc24cd5e5dc9fd894 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 31 Mar 2014 18:37:43 +0200 Subject: [PATCH 062/155] cleaned doc --- scripts/jenkins_script.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/scripts/jenkins_script.py b/scripts/jenkins_script.py index eefe74150..d275463c3 100644 --- a/scripts/jenkins_script.py +++ b/scripts/jenkins_script.py @@ -40,6 +40,7 @@ If you do not know/have the token, then please ask for it (e. g. IRC). """ from optparse import OptionParser +from requests.exceptions import HTTPError from subprocess import Popen, PIPE import sys import time @@ -58,8 +59,9 @@ class OpenLPJobs(object): Branch_Functional = 'Branch-02-Functional-Tests' Branch_Interface = 'Branch-03-Interface-Tests' Branch_Windows = 'Branch-04-Windows_Tests' + Branch_PEP = 'Branch-05-Code-Analysis' - Jobs = [Branch_Pull, Branch_Functional, Branch_Interface, Branch_Windows] + Jobs = [Branch_Pull, Branch_Functional, Branch_Interface, Branch_Windows, Branch_PEP] class JenkinsTrigger(object): @@ -113,9 +115,9 @@ class JenkinsTrigger(object): url = build.info['url'] print('[%s] %s' % (result_string, url)) # On failure open the browser. - if result_string == "FAILURE": - url += 'console' - Popen(('xdg-open', url), stderr=PIPE) + #if result_string == "FAILURE": + # url += 'console' + # Popen(('xdg-open', url), stderr=PIPE) def get_repo_name(): @@ -162,7 +164,7 @@ def main(): parser = OptionParser(usage=usage) parser.add_option('-d', '--disable-output', dest='enable_output', action="store_false", default=True, - help='Print output for the merge request.') + help='Disable output.') parser.add_option('-b', '--open-browser', dest='open_browser', action="store_true", default=False, help='Opens the jenkins page in your browser.') #parser.add_option('-e', '--open-browser-on-error', dest='open_browser_on_error', action="store_true", default=False, @@ -174,14 +176,18 @@ def main(): return token = args[-1] jenkins_trigger = JenkinsTrigger(token) - jenkins_trigger.trigger_build() + try: + jenkins_trigger.trigger_build() + except HTTPError as e: + print("Wrong token.") + return # Open the browser before printing the output. if options.open_browser: jenkins_trigger.open_browser() if options.enable_output: jenkins_trigger.print_output() else: - raise Exception() + parser.print_help() if __name__ == '__main__': main() From 6204454bfbe70fffbe51eb531ebc60c48c09554c Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 31 Mar 2014 18:40:36 +0200 Subject: [PATCH 063/155] updated doc --- scripts/jenkins_script.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/jenkins_script.py b/scripts/jenkins_script.py index d275463c3..1a655799b 100644 --- a/scripts/jenkins_script.py +++ b/scripts/jenkins_script.py @@ -32,11 +32,11 @@ This script helps to trigger builds of branches. To use it you have to install t pip3 isntall jenkins-webapi -You probably want to create an alias. Add this to your ~/.bashrc file and then logout and login: +You probably want to create an alias. Add this to your ~/.bashrc file and then logout and login (to apply the alias): - alias ci="python PATH-TO-THIS-SCRIPT TOKEN" + alias ci="python3 ~/Projects/OpenLP/trunk/script/jenkins_script.py TOKEN" -If you do not know/have the token, then please ask for it (e. g. IRC). +You can look up the token in the Branch-01-Pull job configuration or ask in IRC. """ from optparse import OptionParser From c451a8eafa71d7c3ed67b393551f7e20159188f8 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 31 Mar 2014 19:01:09 +0200 Subject: [PATCH 064/155] added test --- openlp/core/common/registry.py | 1 + scripts/jenkins_script.py | 2 +- .../functional/openlp_core_common/test_registry.py | 14 ++++++++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/openlp/core/common/registry.py b/openlp/core/common/registry.py index 014a534f7..33eb64880 100644 --- a/openlp/core/common/registry.py +++ b/openlp/core/common/registry.py @@ -151,6 +151,7 @@ class Registry(object): if result: results.append(result) except TypeError: + print("sdf") # Who has called me can help in debugging trace_error_handler(log) log.exception('Exception for function %s', function) diff --git a/scripts/jenkins_script.py b/scripts/jenkins_script.py index 1a655799b..0e29271a2 100644 --- a/scripts/jenkins_script.py +++ b/scripts/jenkins_script.py @@ -30,7 +30,7 @@ """ This script helps to trigger builds of branches. To use it you have to install the jenkins-webapi package: - pip3 isntall jenkins-webapi + pip3 install jenkins-webapi You probably want to create an alias. Add this to your ~/.bashrc file and then logout and login (to apply the alias): diff --git a/tests/functional/openlp_core_common/test_registry.py b/tests/functional/openlp_core_common/test_registry.py index a57d7ea85..e27b69d10 100644 --- a/tests/functional/openlp_core_common/test_registry.py +++ b/tests/functional/openlp_core_common/test_registry.py @@ -100,6 +100,20 @@ class TestRegistry(TestCase): # THEN: I expect then function to have been called and a return given self.assertEqual(return_value[0], 'function_2', 'A return value is provided and matches') + def remove_function_test(self): + """ + Test the remove_function() method + """ + # GIVEN: An existing registry register a function + Registry.create() + Registry().register_function('test1', self.dummy_function_1) + + # WHEN: Remove the function. + Registry().remove_function('test1', self.dummy_function_1) + + # THEN: The method should not be available. + assert not Registry().functions_list['test1'], 'The function should not be in the dict anymore.' + def dummy_function_1(self): return "function_1" From 52279248462f191b9aeb0f103c39c0803db2b25c Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 31 Mar 2014 19:29:14 +0200 Subject: [PATCH 065/155] fix: removed stray print --- openlp/core/common/registry.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openlp/core/common/registry.py b/openlp/core/common/registry.py index 33eb64880..014a534f7 100644 --- a/openlp/core/common/registry.py +++ b/openlp/core/common/registry.py @@ -151,7 +151,6 @@ class Registry(object): if result: results.append(result) except TypeError: - print("sdf") # Who has called me can help in debugging trace_error_handler(log) log.exception('Exception for function %s', function) From 20e7517b033c82f8b9ebcee157fd86e9cf969737 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 31 Mar 2014 19:30:14 +0200 Subject: [PATCH 066/155] doc: shortened path --- scripts/jenkins_script.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/jenkins_script.py b/scripts/jenkins_script.py index 0e29271a2..cd75bbc6e 100644 --- a/scripts/jenkins_script.py +++ b/scripts/jenkins_script.py @@ -34,7 +34,7 @@ This script helps to trigger builds of branches. To use it you have to install t You probably want to create an alias. Add this to your ~/.bashrc file and then logout and login (to apply the alias): - alias ci="python3 ~/Projects/OpenLP/trunk/script/jenkins_script.py TOKEN" + alias ci="python3 ./scripts/jenkins_script.py TOKEN" You can look up the token in the Branch-01-Pull job configuration or ask in IRC. """ From 3db984a846290345b589b294a18d9dfc13cee05e Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 31 Mar 2014 19:32:49 +0200 Subject: [PATCH 067/155] clean up: simplify code --- openlp/core/common/registry.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/openlp/core/common/registry.py b/openlp/core/common/registry.py index 014a534f7..218325823 100644 --- a/openlp/core/common/registry.py +++ b/openlp/core/common/registry.py @@ -62,11 +62,9 @@ class Registry(object): registry = cls() registry.service_list = {} registry.functions_list = {} - registry.running_under_test = False - registry.initialising = True # Allow the tests to remove Registry entries but not the live system - if 'nose' in sys.argv[0]: - registry.running_under_test = True + registry.running_under_test = 'nose' in sys.argv[0] + registry.initialising = True return registry def get(self, key): @@ -128,7 +126,7 @@ class Registry(object): :param event: The function description.. :param function: The function to be called when the event happens. """ - if self.running_under_test is False: + if not self.running_under_test: trace_error_handler(log) log.error('Invalid Method call for key %s' % event) raise KeyError('Invalid Method call for key %s' % event) From 6235fa19a912e57e4a64c0b3841670bc91af77ca Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 31 Mar 2014 19:52:49 +0200 Subject: [PATCH 068/155] long line --- scripts/jenkins_script.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/jenkins_script.py b/scripts/jenkins_script.py index cd75bbc6e..75d4f6376 100644 --- a/scripts/jenkins_script.py +++ b/scripts/jenkins_script.py @@ -167,8 +167,8 @@ def main(): help='Disable output.') parser.add_option('-b', '--open-browser', dest='open_browser', action="store_true", default=False, help='Opens the jenkins page in your browser.') - #parser.add_option('-e', '--open-browser-on-error', dest='open_browser_on_error', action="store_true", default=False, - # help='Opens the jenkins page in your browser in case a test fails.') + #parser.add_option('-e', '--open-browser-on-error', dest='open_browser_on_error', action="store_true", + # default=False, help='Opens the jenkins page in your browser in case a test fails.') options, args = parser.parse_args(sys.argv) if len(args) == 2: From ab4cbfd85ee86295a3bf65508b27a98735fdab49 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 31 Mar 2014 19:57:15 +0200 Subject: [PATCH 069/155] fix: spelling mistake --- scripts/check_dependencies.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/check_dependencies.py b/scripts/check_dependencies.py index bab1c6419..5298139be 100755 --- a/scripts/check_dependencies.py +++ b/scripts/check_dependencies.py @@ -94,7 +94,7 @@ OPTIONAL_MODULES = [ ('psycopg2', '(PostgreSQL support)', True), ('nose', '(testing framework)', True), ('mock', '(testing module)', sys.version_info[1] < 3), - ('jenkins', '(access jenkins api - package name: enkins-webapi)', True), + ('jenkins', '(access jenkins api - package name: jenkins-webapi)', True), ] w = sys.stdout.write From 9d9a8145f64fc59ac98c3dc8637f4c04cb5ed4ce Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 31 Mar 2014 20:10:14 +0200 Subject: [PATCH 070/155] fixed dectection --- scripts/jenkins_script.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/scripts/jenkins_script.py b/scripts/jenkins_script.py index 75d4f6376..386ab69ef 100644 --- a/scripts/jenkins_script.py +++ b/scripts/jenkins_script.py @@ -141,10 +141,9 @@ def get_repo_name(): if 'push branch' in line: repo_name = line.replace('push branch: bzr+ssh://bazaar.launchpad.net/', 'lp:') break - # Check if trunk. Note, bzr info can return both 'push branch' as well as 'checkout of branch'. The later can - # occur before the first. So we do not want to break here to make sure we find any orrucances of elif 'checkout of branch' in line: - repo_name = line.replace('checkout of branch: bzr+ssh://bazaar.launchpad.net/+branch/', 'lp:') + repo_name = line.replace('checkout of branch: bzr+ssh://bazaar.launchpad.net/', 'lp:') + break repo_name = repo_name.strip('/') # Did we find the branch name? From 81a8868a5a26bc8005a3d135d28f95337c2903d3 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Mon, 31 Mar 2014 20:49:47 +0200 Subject: [PATCH 071/155] Fix merge --- openlp/core/ui/servicemanager.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 4e60cf19e..60e725103 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -1487,7 +1487,6 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ServiceManage if new_item: self.add_service_item(new_item, replace=True) -<<<<<<< TREE def on_service_item_rename(self): """ Opens a dialog to rename the service item. @@ -1503,10 +1502,7 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ServiceManage self.repaint_service_list(item, -1) self.set_modified() - def create_custom(self): -======= def create_custom(self, field=None): ->>>>>>> MERGE-SOURCE """ Saves the current text item as a custom slide :param field: From a7dc47e80f3eb2710fea8c709ebd92fa5770e4a5 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Mon, 31 Mar 2014 21:07:54 +0200 Subject: [PATCH 072/155] Test --- tests/interfaces/openlp_core_ui/test_servicemanager.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/interfaces/openlp_core_ui/test_servicemanager.py b/tests/interfaces/openlp_core_ui/test_servicemanager.py index f4f5f26fc..78df788ab 100644 --- a/tests/interfaces/openlp_core_ui/test_servicemanager.py +++ b/tests/interfaces/openlp_core_ui/test_servicemanager.py @@ -313,6 +313,7 @@ class TestServiceManager(TestCase, TestMixin): self.service_manager.notes_action.setVisible = MagicMock() self.service_manager.time_action.setVisible = MagicMock() self.service_manager.auto_start_action.setVisible = MagicMock() + self.service_manager.rename_action.setVisible = MagicMock() # WHEN: Show the context menu. self.service_manager.context_menu(q_point) @@ -329,6 +330,8 @@ class TestServiceManager(TestCase, TestMixin): 'The action should be set invisible.' self.service_manager.auto_start_action.setVisible.assert_called_with(True), \ 'The action should be set visible.' + self.service_manager.rename_action.setVisible.assert_called_once_with(False), \ + 'The action should be set invisible.' def click_on_new_service_test(self): """ From 6ca37531a057e32870b6bf9dc0759d523a9f4312 Mon Sep 17 00:00:00 2001 From: Jonathan Springer Date: Tue, 1 Apr 2014 13:07:25 -0400 Subject: [PATCH 073/155] Remove unnecessary platform specific code, remove orphaned and unused registry execute, and fix two functions to register to the registry instead of executing. --- openlp/core/ui/mainwindow.py | 16 ++++++---------- openlp/core/ui/slidecontroller.py | 1 - openlp/plugins/bibles/forms/bibleupgradeform.py | 2 +- openlp/plugins/bibles/lib/db.py | 2 +- 4 files changed, 8 insertions(+), 13 deletions(-) diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index 703307e18..6c56bee37 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -322,14 +322,8 @@ class Ui_MainWindow(object): # i18n add Language Actions add_actions(self.settings_language_menu, (self.auto_language_item, None)) add_actions(self.settings_language_menu, self.language_group.actions()) - # Order things differently in OS X so that Preferences menu item in the - # app menu is correct (this gets picked up automatically by Qt). - if sys.platform == 'darwin': - add_actions(self.settings_menu, (self.settings_plugin_list_item, self.settings_language_menu.menuAction(), - None, self.settings_configure_item, self.settings_shortcuts_item, self.formatting_tag_item)) - else: - add_actions(self.settings_menu, (self.settings_plugin_list_item, self.settings_language_menu.menuAction(), - None, self.formatting_tag_item, self.settings_shortcuts_item, self.settings_configure_item)) + add_actions(self.settings_menu, (self.settings_plugin_list_item, self.settings_language_menu.menuAction(), + None, self.formatting_tag_item, self.settings_shortcuts_item, self.settings_configure_item)) add_actions(self.tools_menu, (self.tools_add_tool_item, None)) add_actions(self.tools_menu, (self.tools_open_data_folder, None)) add_actions(self.tools_menu, (self.tools_first_time_wizard, None)) @@ -393,8 +387,10 @@ class Ui_MainWindow(object): self.import_language_item.setText(translate('OpenLP.MainWindow', '&Language')) self.export_theme_item.setText(translate('OpenLP.MainWindow', '&Theme')) self.export_language_item.setText(translate('OpenLP.MainWindow', '&Language')) - self.settings_shortcuts_item.setText(translate('OpenLP.MainWindow', 'Configure &Shortcuts...')) - self.formatting_tag_item.setText(translate('OpenLP.MainWindow', 'Configure &Formatting Tags...')) + # Do not use config, options, setup, settings or preferences in menu item name unless it is OpenLP's preferences. + # Qt automatically detects the Preferences entry for the Mac OS X menu based on the name of the menu item. + self.settings_shortcuts_item.setText(translate('OpenLP.MainWindow', '&Shortcuts...')) + self.formatting_tag_item.setText(translate('OpenLP.MainWindow', '&Formatting Tags...')) self.settings_configure_item.setText(translate('OpenLP.MainWindow', '&Configure OpenLP...')) self.settings_export_item.setStatusTip(translate('OpenLP.MainWindow', 'Export OpenLP settings to a specified *.config file')) diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index 7faf10ca2..ed3ddaeda 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -1039,7 +1039,6 @@ class SlideController(DisplayController, RegistryProperties): """ self.preview_widget.change_slide(row) self.update_preview() - Registry().execute('slidecontroller_%s_changed' % self.type_prefix, row) def update_preview(self): """ diff --git a/openlp/plugins/bibles/forms/bibleupgradeform.py b/openlp/plugins/bibles/forms/bibleupgradeform.py index 9925b1ebc..d9936dfe6 100644 --- a/openlp/plugins/bibles/forms/bibleupgradeform.py +++ b/openlp/plugins/bibles/forms/bibleupgradeform.py @@ -78,7 +78,7 @@ class BibleUpgradeForm(OpenLPWizard): Set up the UI for the bible wizard. """ super(BibleUpgradeForm, self).setupUi(image) - Registry().execute('openlp_stop_wizard', self.stop_import) + Registry().register_function('openlp_stop_wizard', self.stop_import) def stop_import(self): """ diff --git a/openlp/plugins/bibles/lib/db.py b/openlp/plugins/bibles/lib/db.py index 743bb01c6..48c11fa5e 100644 --- a/openlp/plugins/bibles/lib/db.py +++ b/openlp/plugins/bibles/lib/db.py @@ -154,7 +154,7 @@ class BibleDB(QtCore.QObject, Manager, RegistryProperties): if 'path' in kwargs: self.path = kwargs['path'] self.wizard = None - Registry().execute('openlp_stop_wizard', self.stop_import) + Registry().register_function('openlp_stop_wizard', self.stop_import) def stop_import(self): """ From edd1ed849a8d2a5561a4adea9af7be36d659d9ee Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Tue, 1 Apr 2014 18:32:19 +0100 Subject: [PATCH 074/155] minor fixes --- openlp/core/common/__init__.py | 4 +++- openlp/plugins/bibles/lib/db.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/openlp/core/common/__init__.py b/openlp/core/common/__init__.py index 03bf964bc..8df86032e 100644 --- a/openlp/core/common/__init__.py +++ b/openlp/core/common/__init__.py @@ -51,8 +51,10 @@ def trace_error_handler(logger): :param logger: logger to use so traceback is logged to correct class """ + log_string = "OpenLP Error trace" for tb in traceback.extract_stack(): - logger.error('Called by ' + tb[3] + ' at line ' + str(tb[1]) + ' in ' + tb[0]) + log_string = ('%s\n File %s at line %d \n\t called %s' % (log_string, tb[0], tb[1], tb[3])) + logger.error(log_string) def check_directory_exists(directory, do_not_log=False): diff --git a/openlp/plugins/bibles/lib/db.py b/openlp/plugins/bibles/lib/db.py index 743bb01c6..9ffa5d53e 100644 --- a/openlp/plugins/bibles/lib/db.py +++ b/openlp/plugins/bibles/lib/db.py @@ -561,7 +561,7 @@ class BiblesResourcesDB(QtCore.QObject, Manager): Return a book by name or abbreviation. :param name: The name or abbreviation of the book. - :param lower: True if the comparsion should be only lowercase + :param lower: True if the comparison should be only lowercase """ log.debug('BiblesResourcesDB.get_book("%s")', name) if not isinstance(name, str): From d26b34aee0837267c68df61c685cd8de9c9c3700 Mon Sep 17 00:00:00 2001 From: Jonathan Springer Date: Tue, 1 Apr 2014 14:58:22 -0400 Subject: [PATCH 075/155] Modify loading file from the command line to only load files ending with osz, oszl, or otz. --- openlp/core/ui/mainwindow.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index 6c56bee37..b14694ccc 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -597,10 +597,12 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow, RegistryProperties): args = [] for a in self.arguments: args.extend([a]) - filename = args[0] - if not isinstance(filename, str): - filename = str(filename, sys.getfilesystemencoding()) - self.service_manager_contents.load_file(filename) + for arg in args: + filename = arg + if not isinstance(filename, str): + filename = str(filename, sys.getfilesystemencoding()) + if filename.endswith(('.osz', '.oszl', '.otz')): + self.service_manager_contents.load_file(filename) elif Settings().value(self.general_settings_section + '/auto open'): self.service_manager_contents.load_Last_file() self.timer_version_id = self.startTimer(1000) From d064549eab5fe5683801b08dcc3af58c0ce1fbee Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Tue, 1 Apr 2014 22:39:13 +0200 Subject: [PATCH 076/155] Icon, indentation --- openlp/core/lib/serviceitem.py | 2 +- openlp/core/ui/servicemanager.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/openlp/core/lib/serviceitem.py b/openlp/core/lib/serviceitem.py index a53a94a61..5d7bf4053 100644 --- a/openlp/core/lib/serviceitem.py +++ b/openlp/core/lib/serviceitem.py @@ -107,7 +107,7 @@ class ItemCapabilities(object): ``CanAutoStartForLive`` The capability to ignore the do not play if display blank flag. - + ``CanEditTitle`` The capability to edit the title of the item diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 60e725103..4489360da 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -235,7 +235,7 @@ class Ui_ServiceManager(object): self.edit_action = create_widget_action(self.menu, text=translate('OpenLP.ServiceManager', '&Edit Item'), icon=':/general/general_edit.png', triggers=self.remote_edit) self.rename_action = create_widget_action(self.menu, text=translate('OpenLP.ServiceManager', '&Rename...'), - triggers=self.on_service_item_rename) + icon=':/general/general_edit.png', triggers=self.on_service_item_rename) self.maintain_action = create_widget_action(self.menu, text=translate('OpenLP.ServiceManager', '&Reorder Item'), icon=':/general/general_edit.png', triggers=self.on_service_item_edit_form) @@ -1496,7 +1496,8 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ServiceManage return title = self.service_items[item]['service_item'].title title, ok = QtGui.QInputDialog.getText(self, translate('OpenLP.ServiceManager', 'Rename item title'), - translate('OpenLP.ServiceManager', 'Title:'), QtGui.QLineEdit.Normal, self.trUtf8(title)) + translate('OpenLP.ServiceManager', 'Title:'), + QtGui.QLineEdit.Normal, self.trUtf8(title)) if ok: self.service_items[item]['service_item'].title = title self.repaint_service_list(item, -1) From ce9ca0b44b5362addd39b0a0faea38f636a3ddaa Mon Sep 17 00:00:00 2001 From: Raoul Snyman Date: Wed, 2 Apr 2014 01:29:23 +0200 Subject: [PATCH 078/155] Updated the look of the Library tabs --- openlp/core/ui/mainwindow.py | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index 703307e18..c863f6dff 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -56,29 +56,27 @@ from openlp.core.ui.firsttimeform import FirstTimeForm log = logging.getLogger(__name__) MEDIA_MANAGER_STYLE = """ - QToolBox { +QToolBox { padding-bottom: 2px; - } - QToolBox::tab { +} +QToolBox::tab { background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 palette(button), stop: 0.5 palette(button), - stop: 1.0 palette(mid)); - border: 1px groove palette(mid); - border-radius: 5px; - } - QToolBox::tab:selected { + stop: 0 palette(button), stop: 1.0 palette(mid)); + border: 1px solid palette(mid); + border-radius: 3px; +} +QToolBox::tab:selected { background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 palette(light), stop: 0.5 palette(midlight), - stop: 1.0 palette(dark)); - border: 1px groove palette(dark); + stop: 0 palette(light), stop: 1.0 palette(button)); + border: 1px solid palette(mid); font-weight: bold; - } +} """ PROGRESSBAR_STYLE = """ - QProgressBar{ - height: 10px; - } +QProgressBar{ + height: 10px; +} """ From 73941ca56f7a4427c98400512ad19def89d58572 Mon Sep 17 00:00:00 2001 From: Raoul Snyman Date: Wed, 2 Apr 2014 01:49:58 +0200 Subject: [PATCH 079/155] Renamed a test case and added a test for the trace_error_handler() method. --- openlp/core/common/__init__.py | 3 + .../openlp_core_common/test_init.py | 66 ------------------- tests/functional/openlp_core_lib/test_db.py | 6 +- 3 files changed, 6 insertions(+), 69 deletions(-) delete mode 100644 tests/functional/openlp_core_common/test_init.py diff --git a/openlp/core/common/__init__.py b/openlp/core/common/__init__.py index 03bf964bc..6146049c8 100644 --- a/openlp/core/common/__init__.py +++ b/openlp/core/common/__init__.py @@ -74,6 +74,9 @@ def check_directory_exists(directory, do_not_log=False): def get_frozen_path(frozen_option, non_frozen_option): """ Return a path based on the system status. + + :param frozen_option: + :param non_frozen_option: """ if hasattr(sys, 'frozen') and sys.frozen == 1: return frozen_option diff --git a/tests/functional/openlp_core_common/test_init.py b/tests/functional/openlp_core_common/test_init.py deleted file mode 100644 index dcc4fe32a..000000000 --- a/tests/functional/openlp_core_common/test_init.py +++ /dev/null @@ -1,66 +0,0 @@ -# -*- coding: utf-8 -*- -# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 - -############################################################################### -# OpenLP - Open Source Lyrics Projection # -# --------------------------------------------------------------------------- # -# Copyright (c) 2008-2014 Raoul Snyman # -# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan # -# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # -# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # -# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, # -# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, # -# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, # -# Frode Woldsund, Martin Zibricky, Patrick Zimmermann # -# --------------------------------------------------------------------------- # -# 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 # -############################################################################### -""" -Functional tests to test the AppLocation class and related methods. -""" - -from unittest import TestCase - -from openlp.core.common import de_hump - - -class TestInitFunctions(TestCase): - """ - A test suite to test out various functions in the __init__ class. - """ - def de_hump_conversion_test(self): - """ - Test the de_hump function with a class name - """ - # GIVEN: a Class name in Camel Case - string = "MyClass" - - # WHEN: we call de_hump - new_string = de_hump(string) - - # THEN: the new string should be converted to python format - self.assertTrue(new_string == "my_class", 'The class name should have been converted') - - def de_hump_static_test(self): - """ - Test the de_hump function with a python string - """ - # GIVEN: a Class name in Camel Case - string = "my_class" - - # WHEN: we call de_hump - new_string = de_hump(string) - - # THEN: the new string should be converted to python format - self.assertTrue(new_string == "my_class", 'The class name should have been preserved') diff --git a/tests/functional/openlp_core_lib/test_db.py b/tests/functional/openlp_core_lib/test_db.py index 470bd0636..071a3904b 100644 --- a/tests/functional/openlp_core_lib/test_db.py +++ b/tests/functional/openlp_core_lib/test_db.py @@ -50,9 +50,9 @@ class TestDB(TestCase): """ # GIVEN: Mocked out SQLAlchemy calls and return objects, and an in-memory SQLite database URL with patch('openlp.core.lib.db.create_engine') as mocked_create_engine, \ - patch('openlp.core.lib.db.MetaData') as MockedMetaData, \ - patch('openlp.core.lib.db.sessionmaker') as mocked_sessionmaker, \ - patch('openlp.core.lib.db.scoped_session') as mocked_scoped_session: + patch('openlp.core.lib.db.MetaData') as MockedMetaData, \ + patch('openlp.core.lib.db.sessionmaker') as mocked_sessionmaker, \ + patch('openlp.core.lib.db.scoped_session') as mocked_scoped_session: mocked_engine = MagicMock() mocked_metadata = MagicMock() mocked_sessionmaker_object = MagicMock() From c38c576f946de6f5b82c3d6568d61926606fa9b4 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Wed, 2 Apr 2014 19:51:21 +0100 Subject: [PATCH 080/155] test fixes --- openlp/plugins/remotes/lib/httpserver.py | 25 +++++-- resources/__init__.py | 1 - scripts/translation_utils.py | 74 ++++++++++--------- setup.py | 10 ++- .../openlp_core_common/test_applocation.py | 22 +++--- tests/functional/openlp_core_lib/__init__.py | 2 +- tests/functional/openlp_core_lib/test_db.py | 2 +- .../openlp_core_lib/test_file_dialog.py | 7 +- .../openlp_core_lib/test_formattingtags.py | 1 - .../openlp_core_lib/test_htmlbuilder.py | 1 - tests/functional/openlp_core_lib/test_lib.py | 4 +- .../openlp_core_lib/test_renderer.py | 2 +- .../functional/openlp_core_lib/test_theme.py | 1 - .../functional/openlp_core_utils/__init__.py | 2 +- .../openlp_plugins/bibles/__init__.py | 1 - .../openlp_plugins/bibles/test_http.py | 6 +- .../openlp_plugins/bibles/test_lib.py | 2 - .../openlp_plugins/images/__init__.py | 2 +- .../openlp_plugins/images/test_lib.py | 6 +- .../songs/test_songshowplusimport.py | 12 +-- .../songs/test_worshipcenterproimport.py | 74 ++++++++++--------- tests/interfaces/openlp_core_lib/__init__.py | 2 +- .../interfaces/openlp_core_utils/__init__.py | 2 +- tests/interfaces/openlp_plugins/__init__.py | 2 +- .../openlp_plugins/custom/__init__.py | 1 + .../openlp_plugins/remotes/__init__.py | 2 +- .../openlp_plugins/songs/__init__.py | 2 +- tests/utils/__init__.py | 1 - 28 files changed, 142 insertions(+), 127 deletions(-) diff --git a/openlp/plugins/remotes/lib/httpserver.py b/openlp/plugins/remotes/lib/httpserver.py index fa578184b..22d0349f8 100644 --- a/openlp/plugins/remotes/lib/httpserver.py +++ b/openlp/plugins/remotes/lib/httpserver.py @@ -28,15 +28,15 @@ ############################################################################### """ -The :mod:`http` module contains the API web server. This is a lightweight web -server used by remotes to interact with OpenLP. It uses JSON to communicate with -the remotes. +The :mod:`http` module contains the API web server. This is a lightweight web server used by remotes to interact +with OpenLP. It uses JSON to communicate with the remotes. """ import ssl import socket import os import logging +import time from PyQt4 import QtCore @@ -53,8 +53,8 @@ log = logging.getLogger(__name__) class CustomHandler(BaseHTTPRequestHandler, HttpRouter): """ Stateless session handler to handle the HTTP request and process it. - This class handles just the overrides to the base methods and the logic to invoke the - methods within the HttpRouter class. + This class handles just the overrides to the base methods and the logic to invoke the methods within the HttpRouter + class. DO not try change the structure as this is as per the documentation. """ @@ -116,9 +116,20 @@ class OpenLPServer(): log.debug('Started ssl httpd...') else: port = Settings().value(self.settings_section + '/port') - self.httpd = ThreadingHTTPServer((address, port), CustomHandler) + loop = 1 + while loop < 3: + try: + self.httpd = ThreadingHTTPServer((address, port), CustomHandler) + except OSError: + loop += 1 + time.sleep(0.1) + except: + log.error('Failed to start server ') log.debug('Started non ssl httpd...') - self.httpd.serve_forever() + if hasattr(self, 'httpd') and self.httpd: + self.httpd.serve_forever() + else: + log.debug('Failed to start server') def stop_server(self): """ diff --git a/resources/__init__.py b/resources/__init__.py index 20e2bd293..1bfef8133 100644 --- a/resources/__init__.py +++ b/resources/__init__.py @@ -32,4 +32,3 @@ The :mod:`resources` module contains a bunch of resources for OpenLP. DO NOT REMOVE THIS FILE, IT IS REQUIRED FOR INCLUDING THE RESOURCES ON SOME PLATFORMS! """ - diff --git a/scripts/translation_utils.py b/scripts/translation_utils.py index 402813f5b..5aa320806 100755 --- a/scripts/translation_utils.py +++ b/scripts/translation_utils.py @@ -52,7 +52,9 @@ This is done easily via the ``-d``, ``-p`` and ``-u`` options:: """ import os -import urllib.request, urllib.error, urllib.parse +import urllib.request +import urllib.error +import urllib.parse from getpass import getpass import base64 import json @@ -70,6 +72,7 @@ quiet_mode = False username = '' password = '' + class Command(object): """ Provide an enumeration of commands. @@ -80,6 +83,7 @@ class Command(object): Update = 4 Generate = 5 + class CommandStack(object): """ This class provides an iterable stack. @@ -134,13 +138,13 @@ class CommandStack(object): results.append(str((item['command'], ))) return '[%s]' % ', '.join(results) + def print_quiet(text, linefeed=True): """ - This method checks to see if we are in quiet mode, and if not prints - ``text`` out. + This method checks to see if we are in quiet mode, and if not prints ``text`` out. - ``text`` - The text to print. + :param text: The text to print. + :param linefeed: Linefeed required """ global quiet_mode if not quiet_mode: @@ -149,33 +153,33 @@ def print_quiet(text, linefeed=True): else: print(text, end=' ') + def print_verbose(text): """ - This method checks to see if we are in verbose mode, and if so prints - ``text`` out. + This method checks to see if we are in verbose mode, and if so prints ``text`` out. - ``text`` - The text to print. + :param text: The text to print. """ global verbose_mode, quiet_mode if not quiet_mode and verbose_mode: print(' %s' % text) + def run(command): """ This method runs an external application. - ``command`` - The command to run. + :param command: The command to run. """ print_verbose(command) process = QtCore.QProcess() process.start(command) - while (process.waitForReadyRead()): + while process.waitForReadyRead(): print_verbose('ReadyRead: %s' % QtCore.QString(process.readAll())) print_verbose('Error(s):\n%s' % process.readAllStandardError()) print_verbose('Output:\n%s' % process.readAllStandardOutput()) + def download_translations(): """ This method downloads the translation files from the Pootle server. @@ -190,9 +194,8 @@ def download_translations(): password = getpass(' Transifex password: ') # First get the list of languages url = SERVER_URL + 'resource/ents/' - base64string = base64.encodestring( - '%s:%s' % (username, password))[:-1] - auth_header = 'Basic %s' % base64string + base64string = base64.encodbytes('%s:%s' % (username, password))[:-1] + auth_header = 'Basic %s' % base64string request = urllib.request.Request(url + '?details') request.add_header('Authorization', auth_header) print_verbose('Downloading list of languages from: %s' % url) @@ -207,8 +210,7 @@ def download_translations(): lang_url = url + 'translation/%s/?file' % language request = urllib.request.Request(lang_url) request.add_header('Authorization', auth_header) - filename = os.path.join(os.path.abspath('..'), 'resources', 'i18n', - language + '.ts') + filename = os.path.join(os.path.abspath('..'), 'resources', 'i18n', language + '.ts') print_verbose('Get Translation File: %s' % filename) response = urllib.request.urlopen(request) fd = open(filename, 'w') @@ -217,10 +219,10 @@ def download_translations(): print_quiet(' Done.') return True + def prepare_project(): """ - This method creates the project file needed to update the translation files - and compile them into .qm files. + This method creates the project file needed to update the translation files and compile them into .qm files. """ print_quiet('Generating the openlp.pro file') lines = [] @@ -229,9 +231,9 @@ def prepare_project(): print_verbose('Starting directory: %s' % start_dir) for root, dirs, files in os.walk(start_dir): for file in files: - path = root.replace(start_dir, '').replace('\\', '/') #.replace(u'..', u'.') + path = root.replace(start_dir, '').replace('\\', '/') if file.startswith('hook-') or file.startswith('test_'): - continue + continue ignore = False for ignored_path in IGNORED_PATHS: if path.startswith(ignored_path): @@ -263,6 +265,7 @@ def prepare_project(): file.close() print_quiet(' Done.') + def update_translations(): print_quiet('Update the translation files') if not os.path.exists(os.path.join(os.path.abspath('..'), 'openlp.pro')): @@ -273,11 +276,12 @@ def update_translations(): run('pylupdate4 -verbose -noobsolete openlp.pro') os.chdir(os.path.abspath('scripts')) + def generate_binaries(): print_quiet('Generate the related *.qm files') if not os.path.exists(os.path.join(os.path.abspath('..'), 'openlp.pro')): print('You have not generated a project file yet, please run this script with the -p option. It is also ' + - 'recommended that you this script with the -u option to update the translation files as well.') + 'recommended that you this script with the -u option to update the translation files as well.') return else: os.chdir(os.path.abspath('..')) @@ -290,12 +294,11 @@ def create_translation(): This method opens a browser to the OpenLP project page at Transifex so that the user can request a new language. """ - print_quiet('Please request a new language at the OpenLP project on ' - 'Transifex.') - webbrowser.open('https://www.transifex.net/projects/p/openlp/' - 'resource/ents/') + print_quiet('Please request a new language at the OpenLP project on Transifex.') + webbrowser.open('https://www.transifex.net/projects/p/openlp/resource/ents/') print_quiet('Opening browser to OpenLP project...') + def process_stack(command_stack): """ This method looks at the commands in the command stack, and processes them @@ -323,6 +326,7 @@ def process_stack(command_stack): else: print_quiet('No commands to process.') + def main(): global verbose_mode, quiet_mode, username, password # Set up command line options. @@ -331,23 +335,23 @@ def main(): 'This script is used to manage OpenLP\'s translation files.' parser = OptionParser(usage=usage) parser.add_option('-U', '--username', dest='username', metavar='USERNAME', - help='Transifex username, used for authentication') + help='Transifex username, used for authentication') parser.add_option('-P', '--password', dest='password', metavar='PASSWORD', - help='Transifex password, used for authentication') + help='Transifex password, used for authentication') parser.add_option('-d', '--download-ts', dest='download', - action='store_true', help='download language files from Transifex') + action='store_true', help='download language files from Transifex') parser.add_option('-c', '--create', dest='create', action='store_true', - help='go to Transifex to request a new translation file') + help='go to Transifex to request a new translation file') parser.add_option('-p', '--prepare', dest='prepare', action='store_true', - help='generate a project file, used to update the translations') + help='generate a project file, used to update the translations') parser.add_option('-u', '--update', action='store_true', dest='update', - help='update translation files (needs a project file)') + help='update translation files (needs a project file)') parser.add_option('-g', '--generate', dest='generate', action='store_true', - help='compile .ts files into .qm files') + help='compile .ts files into .qm files') parser.add_option('-v', '--verbose', dest='verbose', action='store_true', - help='show extra information while processing translations') + help='show extra information while processing translations') parser.add_option('-q', '--quiet', dest='quiet', action='store_true', - help='suppress all output other than errors') + help='suppress all output other than errors') (options, args) = parser.parse_args() # Create and populate the command stack command_stack = CommandStack() diff --git a/setup.py b/setup.py index 276188188..fc4a6f89b 100755 --- a/setup.py +++ b/setup.py @@ -106,9 +106,9 @@ try: # If they are equal, then this tree is tarball with the source for the release. We do not want the revision number # in the version string. if tree_revision == tag_revision: - version_string = tag_version + version_string = tag_version else: - version_string = '%s-bzr%s' % (tag_version, tree_revision) + version_string = '%s-bzr%s' % (tag_version, tree_revision) ver_file = open(VERSION_FILE, 'w') ver_file.write(version_string) except: @@ -123,7 +123,9 @@ setup( version=version_string, description="Open source Church presentation and lyrics projection application.", long_description="""\ -OpenLP (previously openlp.org) is free church presentation software, or lyrics projection software, used to display slides of songs, Bible verses, videos, images, and even presentations (if PowerPoint is installed) for church worship using a computer and a data projector.""", +OpenLP (previously openlp.org) is free church presentation software, or lyrics projection software, used to display +slides of songs, Bible verses, videos, images, and even presentations (if PowerPoint is installed) for church worship +using a computer and a data projector.""", classifiers=[ 'Development Status :: 4 - Beta', 'Environment :: MacOS X', @@ -158,7 +160,7 @@ OpenLP (previously openlp.org) is free church presentation software, or lyrics p 'Topic :: Multimedia :: Sound/Audio', 'Topic :: Multimedia :: Video', 'Topic :: Religion' - ], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers + ], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers keywords='open source church presentation lyrics projection song bible display project', author='Raoul Snyman', author_email='raoulsnyman@openlp.org', diff --git a/tests/functional/openlp_core_common/test_applocation.py b/tests/functional/openlp_core_common/test_applocation.py index 8bac4abbf..27a47de6e 100644 --- a/tests/functional/openlp_core_common/test_applocation.py +++ b/tests/functional/openlp_core_common/test_applocation.py @@ -54,9 +54,9 @@ class TestAppLocation(TestCase): # GIVEN: A mocked out Settings class and a mocked out AppLocation.get_directory() mocked_settings = mocked_class.return_value mocked_settings.contains.return_value = False - mocked_get_directory.return_value = os.path.join('test','dir') + mocked_get_directory.return_value = os.path.join('test', 'dir') mocked_check_directory_exists.return_value = True - mocked_os.path.normpath.return_value = os.path.join('test','dir') + mocked_os.path.normpath.return_value = os.path.join('test', 'dir') # WHEN: we call AppLocation.get_data_path() data_path = AppLocation.get_data_path() @@ -64,8 +64,8 @@ class TestAppLocation(TestCase): # THEN: check that all the correct methods were called, and the result is correct mocked_settings.contains.assert_called_with('advanced/data path') mocked_get_directory.assert_called_with(AppLocation.DataDir) - mocked_check_directory_exists.assert_called_with(os.path.join('test','dir')) - self.assertEqual(os.path.join('test','dir'), data_path, 'Result should be "test/dir"') + mocked_check_directory_exists.assert_called_with(os.path.join('test', 'dir')) + self.assertEqual(os.path.join('test', 'dir'), data_path, 'Result should be "test/dir"') def get_data_path_with_custom_location_test(self): """ @@ -110,14 +110,14 @@ class TestAppLocation(TestCase): with patch('openlp.core.common.AppLocation.get_data_path') as mocked_get_data_path, \ patch('openlp.core.common.applocation.os.listdir') as mocked_listdir: # GIVEN: Our mocked modules/methods. - mocked_get_data_path.return_value = os.path.join('test','dir') + mocked_get_data_path.return_value = os.path.join('test', 'dir') mocked_listdir.return_value = copy.deepcopy(FILE_LIST) # When: Get the list of files. result = AppLocation.get_files('section', '.mp3') # Then: Check if the section parameter was used correctly. - mocked_listdir.assert_called_with(os.path.join('test','dir','section')) + mocked_listdir.assert_called_with(os.path.join('test', 'dir', 'section')) # Then: check if the file lists are identical. self.assertListEqual(['file5.mp3', 'file6.mp3'], result, 'The file lists should be identical.') @@ -129,15 +129,15 @@ class TestAppLocation(TestCase): with patch('openlp.core.common.AppLocation.get_data_path') as mocked_get_data_path, \ patch('openlp.core.common.applocation.check_directory_exists') as mocked_check_directory_exists: # GIVEN: A mocked out AppLocation.get_data_path() - mocked_get_data_path.return_value = os.path.join('test','dir') + mocked_get_data_path.return_value = os.path.join('test', 'dir') mocked_check_directory_exists.return_value = True # WHEN: we call AppLocation.get_data_path() data_path = AppLocation.get_section_data_path('section') # THEN: check that all the correct methods were called, and the result is correct - mocked_check_directory_exists.assert_called_with(os.path.join('test','dir','section')) - self.assertEqual(os.path.join('test','dir','section'), data_path, 'Result should be "test/dir/section"') + mocked_check_directory_exists.assert_called_with(os.path.join('test', 'dir', 'section')) + self.assertEqual(os.path.join('test', 'dir', 'section'), data_path, 'Result should be "test/dir/section"') def get_directory_for_app_dir_test(self): """ @@ -145,13 +145,13 @@ class TestAppLocation(TestCase): """ # GIVEN: A mocked out _get_frozen_path function with patch('openlp.core.common.applocation.get_frozen_path') as mocked_get_frozen_path: - mocked_get_frozen_path.return_value = os.path.join('app','dir') + mocked_get_frozen_path.return_value = os.path.join('app', 'dir') # WHEN: We call AppLocation.get_directory directory = AppLocation.get_directory(AppLocation.AppDir) # THEN: check that the correct directory is returned - self.assertEqual(os.path.join('app','dir'), directory, 'Directory should be "app/dir"') + self.assertEqual(os.path.join('app', 'dir'), directory, 'Directory should be "app/dir"') def get_directory_for_plugins_dir_test(self): """ diff --git a/tests/functional/openlp_core_lib/__init__.py b/tests/functional/openlp_core_lib/__init__.py index 70585a1d1..0f0461449 100644 --- a/tests/functional/openlp_core_lib/__init__.py +++ b/tests/functional/openlp_core_lib/__init__.py @@ -28,4 +28,4 @@ ############################################################################### """ Package to test the openlp.core.lib package. -""" \ No newline at end of file +""" diff --git a/tests/functional/openlp_core_lib/test_db.py b/tests/functional/openlp_core_lib/test_db.py index 470bd0636..f11292209 100644 --- a/tests/functional/openlp_core_lib/test_db.py +++ b/tests/functional/openlp_core_lib/test_db.py @@ -52,7 +52,7 @@ class TestDB(TestCase): with patch('openlp.core.lib.db.create_engine') as mocked_create_engine, \ patch('openlp.core.lib.db.MetaData') as MockedMetaData, \ patch('openlp.core.lib.db.sessionmaker') as mocked_sessionmaker, \ - patch('openlp.core.lib.db.scoped_session') as mocked_scoped_session: + patch('openlp.core.lib.db.scoped_session') as mocked_scoped_session: mocked_engine = MagicMock() mocked_metadata = MagicMock() mocked_sessionmaker_object = MagicMock() diff --git a/tests/functional/openlp_core_lib/test_file_dialog.py b/tests/functional/openlp_core_lib/test_file_dialog.py index 1d8040525..b2c2178a9 100644 --- a/tests/functional/openlp_core_lib/test_file_dialog.py +++ b/tests/functional/openlp_core_lib/test_file_dialog.py @@ -42,7 +42,8 @@ class TestFileDialog(TestCase): # THEN: The returned value should be an empty QStringList and os.path.exists should not have been called assert not self.mocked_os.path.exists.called self.assertEqual(result, [], - 'FileDialog.getOpenFileNames should return and empty list when QFileDialog.getOpenFileNames is canceled') + 'FileDialog.getOpenFileNames should return and empty list when QFileDialog.getOpenFileNames ' + 'is canceled') def returned_file_list_test(self): """ @@ -70,5 +71,5 @@ class TestFileDialog(TestCase): self.mocked_os.path.exists.assert_callde_with('/non-existing') self.mocked_os.path.exists.assert_callde_with('/non-existing') self.mocked_qt_gui.QmessageBox.information.called_with(self.mocked_parent, UiStrings().FileNotFound, - UiStrings().FileNotFoundMessage % '/non-existing') - self.assertEqual(result, ['/Valid File', '/url encoded file #1'], 'The returned file list is incorrect') \ No newline at end of file + UiStrings().FileNotFoundMessage % '/non-existing') + self.assertEqual(result, ['/Valid File', '/url encoded file #1'], 'The returned file list is incorrect') diff --git a/tests/functional/openlp_core_lib/test_formattingtags.py b/tests/functional/openlp_core_lib/test_formattingtags.py index 17f044d0f..c1311c543 100644 --- a/tests/functional/openlp_core_lib/test_formattingtags.py +++ b/tests/functional/openlp_core_lib/test_formattingtags.py @@ -108,4 +108,3 @@ class TestFormattingTags(TestCase): # THEN: The lists should now be identical. assert old_tags_list == FormattingTags.get_html_tags(), 'The lists should be identical.' - diff --git a/tests/functional/openlp_core_lib/test_htmlbuilder.py b/tests/functional/openlp_core_lib/test_htmlbuilder.py index 05d15a1c2..ef5ffdf43 100644 --- a/tests/functional/openlp_core_lib/test_htmlbuilder.py +++ b/tests/functional/openlp_core_lib/test_htmlbuilder.py @@ -321,4 +321,3 @@ class Htmbuilder(TestCase): # THEN: THE css should be the same. assert FOOTER_CSS == css, 'The footer strings should be equal.' - diff --git a/tests/functional/openlp_core_lib/test_lib.py b/tests/functional/openlp_core_lib/test_lib.py index 8faf7747a..bb3a17ebb 100644 --- a/tests/functional/openlp_core_lib/test_lib.py +++ b/tests/functional/openlp_core_lib/test_lib.py @@ -311,8 +311,8 @@ class TestLib(TestCase): mocked_buffer.open.assert_called_with('writeonly') mocked_image.save.assert_called_with(mocked_buffer, "PNG") mocked_byte_array.toBase64.assert_called_with() - self.assertEqual('base64mock', result, - 'The result should be the return value of the mocked out base64 method') + self.assertEqual('base64mock', result, 'The result should be the return value of the mocked out ' + 'base64 method') def create_thumb_with_size_test(self): """ diff --git a/tests/functional/openlp_core_lib/test_renderer.py b/tests/functional/openlp_core_lib/test_renderer.py index d0aa82b74..51e915e3f 100644 --- a/tests/functional/openlp_core_lib/test_renderer.py +++ b/tests/functional/openlp_core_lib/test_renderer.py @@ -86,4 +86,4 @@ class TestRenderer(TestCase): self.assertEqual(renderer.width, 1024, 'The base renderer should be a live controller') self.assertEqual(renderer.height, 768, 'The base renderer should be a live controller') self.assertEqual(renderer.screen_ratio, 0.75, 'The base renderer should be a live controller') - self.assertEqual(renderer.footer_start, 691, 'The base renderer should be a live controller') \ No newline at end of file + self.assertEqual(renderer.footer_start, 691, 'The base renderer should be a live controller') diff --git a/tests/functional/openlp_core_lib/test_theme.py b/tests/functional/openlp_core_lib/test_theme.py index f0229e64c..bcdced35f 100644 --- a/tests/functional/openlp_core_lib/test_theme.py +++ b/tests/functional/openlp_core_lib/test_theme.py @@ -69,4 +69,3 @@ class TestTheme(TestCase): 'The theme should have a font_footer_name of Arial') self.assertTrue(default_theme.font_main_bold is False, 'The theme should have a font_main_bold of false') self.assertTrue(len(default_theme.__dict__) == 47, 'The theme should have 47 variables') - diff --git a/tests/functional/openlp_core_utils/__init__.py b/tests/functional/openlp_core_utils/__init__.py index 1f4f74a33..6b241e7fc 100644 --- a/tests/functional/openlp_core_utils/__init__.py +++ b/tests/functional/openlp_core_utils/__init__.py @@ -25,4 +25,4 @@ # 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 # -############################################################################### \ No newline at end of file +############################################################################### diff --git a/tests/functional/openlp_plugins/bibles/__init__.py b/tests/functional/openlp_plugins/bibles/__init__.py index 4c8c00fec..6b241e7fc 100644 --- a/tests/functional/openlp_plugins/bibles/__init__.py +++ b/tests/functional/openlp_plugins/bibles/__init__.py @@ -26,4 +26,3 @@ # with this program; if not, write to the Free Software Foundation, Inc., 59 # # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### - diff --git a/tests/functional/openlp_plugins/bibles/test_http.py b/tests/functional/openlp_plugins/bibles/test_http.py index 9a1be8d0e..b9bb8f11c 100644 --- a/tests/functional/openlp_plugins/bibles/test_http.py +++ b/tests/functional/openlp_plugins/bibles/test_http.py @@ -116,7 +116,8 @@ class TestBSExtract(TestCase): self.mock_get_soup_for_bible_ref.assert_called_once_with( 'http://m.bibleserver.com/overlay/selectBook?translation=NIV') self.assertIsNone(result, - 'BSExtract.get_books_from_http should return None when get_soup_for_bible_ref returns a false value') + 'BSExtract.get_books_from_http should return None when get_soup_for_bible_ref returns a ' + 'false value') def get_books_from_http_no_content_test(self): """ @@ -146,7 +147,8 @@ class TestBSExtract(TestCase): self.mock_log.error.assert_called_once_with('No books found in the Bibleserver response.') self.mock_send_error_message.assert_called_once_with('parse') self.assertIsNone(result, - 'BSExtract.get_books_from_http should return None when get_soup_for_bible_ref returns a false value') + 'BSExtract.get_books_from_http should return None when get_soup_for_bible_ref returns a ' + 'false value') def get_books_from_http_content_test(self): """ diff --git a/tests/functional/openlp_plugins/bibles/test_lib.py b/tests/functional/openlp_plugins/bibles/test_lib.py index 9b3e79591..0c8bb8211 100644 --- a/tests/functional/openlp_plugins/bibles/test_lib.py +++ b/tests/functional/openlp_plugins/bibles/test_lib.py @@ -85,5 +85,3 @@ class TestLib(TestCase): # THEN: It should be False self.assertFalse(has_verse_list, 'The SearchResults object should have a verse list') - - diff --git a/tests/functional/openlp_plugins/images/__init__.py b/tests/functional/openlp_plugins/images/__init__.py index 1f4f74a33..6b241e7fc 100644 --- a/tests/functional/openlp_plugins/images/__init__.py +++ b/tests/functional/openlp_plugins/images/__init__.py @@ -25,4 +25,4 @@ # 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 # -############################################################################### \ No newline at end of file +############################################################################### diff --git a/tests/functional/openlp_plugins/images/test_lib.py b/tests/functional/openlp_plugins/images/test_lib.py index 80d452644..202b5a42b 100644 --- a/tests/functional/openlp_plugins/images/test_lib.py +++ b/tests/functional/openlp_plugins/images/test_lib.py @@ -108,7 +108,7 @@ class TestImageMediaItem(TestCase): Test that the save_new_images_list() saves all images in the list """ # GIVEN: A list with 3 images - image_list = [ 'test_image_1.jpg', 'test_image_2.jpg', 'test_image_3.jpg' ] + image_list = ['test_image_1.jpg', 'test_image_2.jpg', 'test_image_3.jpg'] with patch('openlp.plugins.images.lib.mediaitem.ImageMediaItem.load_full_list') as mocked_load_full_list: self.media_item.manager = MagicMock() @@ -124,7 +124,7 @@ class TestImageMediaItem(TestCase): Test that the save_new_images_list() ignores everything in the provided list except strings """ # GIVEN: A list with images and objects - image_list = [ 'test_image_1.jpg', None, True, ImageFilenames(), 'test_image_2.jpg' ] + image_list = ['test_image_1.jpg', None, True, ImageFilenames(), 'test_image_2.jpg'] with patch('openlp.plugins.images.lib.mediaitem.ImageMediaItem.load_full_list') as mocked_load_full_list: self.media_item.manager = MagicMock() @@ -171,7 +171,7 @@ class TestImageMediaItem(TestCase): assert self.media_item.manager.delete_object.call_count == 7, \ 'manager.delete_object() should be called exactly 7 times' - # CLEANUP: Remove added attribute from ImageFilenames and ImageGroups + # CLEANUP: Remove added attribute from Image Filenames and ImageGroups delattr(ImageFilenames, 'group_id') delattr(ImageGroups, 'parent_id') diff --git a/tests/functional/openlp_plugins/songs/test_songshowplusimport.py b/tests/functional/openlp_plugins/songs/test_songshowplusimport.py index 16af347b2..f3fe231d0 100644 --- a/tests/functional/openlp_plugins/songs/test_songshowplusimport.py +++ b/tests/functional/openlp_plugins/songs/test_songshowplusimport.py @@ -115,8 +115,8 @@ class TestSongShowPlusImport(TestCase): # THEN: do_import should return none and the progress bar setMaximum should be called with the length of # import_source. - self.assertIsNone(importer.do_import(), - 'do_import should return None when import_source is a list and stop_import_flag is True') + self.assertIsNone(importer.do_import(), 'do_import should return None when import_source is a list ' + 'and stop_import_flag is True') mocked_import_wizard.progress_bar.setMaximum.assert_called_with(len(importer.import_source)) def to_openlp_verse_tag_test(self): @@ -143,8 +143,8 @@ class TestSongShowPlusImport(TestCase): # THEN: The returned value should should correlate with the input arguments for original_tag, openlp_tag in test_values: self.assertEquals(importer.to_openlp_verse_tag(original_tag), openlp_tag, - 'SongShowPlusImport.to_openlp_verse_tag should return "%s" when called with "%s"' - % (openlp_tag, original_tag)) + 'SongShowPlusImport.to_openlp_verse_tag should return "%s" when called with "%s"' % + (openlp_tag, original_tag)) def to_openlp_verse_tag_verse_order_test(self): """ @@ -171,5 +171,5 @@ class TestSongShowPlusImport(TestCase): # THEN: The returned value should should correlate with the input arguments for original_tag, openlp_tag in test_values: self.assertEquals(importer.to_openlp_verse_tag(original_tag, ignore_unique=True), openlp_tag, - 'SongShowPlusImport.to_openlp_verse_tag should return "%s" when called with "%s"' - % (openlp_tag, original_tag)) + 'SongShowPlusImport.to_openlp_verse_tag should return "%s" when called with "%s"' % + (openlp_tag, original_tag)) diff --git a/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py b/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py index 630fb572e..c6acbe965 100644 --- a/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py +++ b/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py @@ -73,35 +73,37 @@ class WorshipCenterProImportLogger(WorshipCenterProImport): RECORDSET_TEST_DATA = [TestRecord(1, 'TITLE', 'Amazing Grace'), - TestRecord(1, 'LYRICS', - 'Amazing grace! How&crlf;sweet the sound&crlf;That saved a wretch like me!&crlf;' - 'I once was lost,&crlf;but now am found;&crlf;Was blind, but now I see.&crlf;&crlf;' - '\'Twas grace that&crlf;taught my heart to fear,&crlf;And grace my fears relieved;&crlf;' - 'How precious did&crlf;that grace appear&crlf;The hour I first believed.&crlf;&crlf;' - 'Through many dangers,&crlf;toils and snares,&crlf;I have already come;&crlf;' - '\'Tis grace hath brought&crlf;me safe thus far,&crlf;' - 'And grace will lead me home.&crlf;&crlf;The Lord has&crlf;promised good to me,&crlf;' - 'His Word my hope secures;&crlf;He will my Shield&crlf;and Portion be,&crlf;' - 'As long as life endures.&crlf;&crlf;Yea, when this flesh&crlf;and heart shall fail,&crlf;' - 'And mortal life shall cease,&crlf;I shall possess,&crlf;within the veil,&crlf;' - 'A life of joy and peace.&crlf;&crlf;The earth shall soon&crlf;dissolve like snow,&crlf;' - 'The sun forbear to shine;&crlf;But God, Who called&crlf;me here below,&crlf;' - 'Shall be forever mine.&crlf;&crlf;When we\'ve been there&crlf;ten thousand years,&crlf;' - 'Bright shining as the sun,&crlf;We\'ve no less days to&crlf;sing God\'s praise&crlf;' - 'Than when we\'d first begun.&crlf;&crlf;'), + TestRecord( + 1, 'LYRICS', + 'Amazing grace! How&crlf;sweet the sound&crlf;That saved a wretch like me!&crlf;' + 'I once was lost,&crlf;but now am found;&crlf;Was blind, but now I see.&crlf;&crlf;' + '\'Twas grace that&crlf;taught my heart to fear,&crlf;And grace my fears relieved;&crlf;' + 'How precious did&crlf;that grace appear&crlf;The hour I first believed.&crlf;&crlf;' + 'Through many dangers,&crlf;toils and snares,&crlf;I have already come;&crlf;' + '\'Tis grace hath brought&crlf;me safe thus far,&crlf;' + 'And grace will lead me home.&crlf;&crlf;The Lord has&crlf;promised good to me,&crlf;' + 'His Word my hope secures;&crlf;He will my Shield&crlf;and Portion be,&crlf;' + 'As long as life endures.&crlf;&crlf;Yea, when this flesh&crlf;and heart shall fail,&crlf;' + 'And mortal life shall cease,&crlf;I shall possess,&crlf;within the veil,&crlf;' + 'A life of joy and peace.&crlf;&crlf;The earth shall soon&crlf;dissolve like snow,&crlf;' + 'The sun forbear to shine;&crlf;But God, Who called&crlf;me here below,&crlf;' + 'Shall be forever mine.&crlf;&crlf;When we\'ve been there&crlf;ten thousand years,&crlf;' + 'Bright shining as the sun,&crlf;We\'ve no less days to&crlf;sing God\'s praise&crlf;' + 'Than when we\'d first begun.&crlf;&crlf;'), TestRecord(2, 'TITLE', 'Beautiful Garden Of Prayer, The'), - TestRecord(2, 'LYRICS', - 'There\'s a garden where&crlf;Jesus is waiting,&crlf;' - 'There\'s a place that&crlf;is wondrously fair,&crlf;For it glows with the&crlf;' - 'light of His presence.&crlf;\'Tis the beautiful&crlf;garden of prayer.&crlf;&crlf;' - 'Oh, the beautiful garden,&crlf;the garden of prayer!&crlf;Oh, the beautiful&crlf;' - 'garden of prayer!&crlf;There my Savior awaits,&crlf;and He opens the gates&crlf;' - 'To the beautiful&crlf;garden of prayer.&crlf;&crlf;There\'s a garden where&crlf;' - 'Jesus is waiting,&crlf;And I go with my&crlf;burden and care,&crlf;' - 'Just to learn from His&crlf;lips words of comfort&crlf;In the beautiful&crlf;' - 'garden of prayer.&crlf;&crlf;There\'s a garden where&crlf;Jesus is waiting,&crlf;' - 'And He bids you to come,&crlf;meet Him there;&crlf;Just to bow and&crlf;' - 'receive a new blessing&crlf;In the beautiful&crlf;garden of prayer.&crlf;&crlf;')] + TestRecord( + 2, 'LYRICS', + 'There\'s a garden where&crlf;Jesus is waiting,&crlf;' + 'There\'s a place that&crlf;is wondrously fair,&crlf;For it glows with the&crlf;' + 'light of His presence.&crlf;\'Tis the beautiful&crlf;garden of prayer.&crlf;&crlf;' + 'Oh, the beautiful garden,&crlf;the garden of prayer!&crlf;Oh, the beautiful&crlf;' + 'garden of prayer!&crlf;There my Savior awaits,&crlf;and He opens the gates&crlf;' + 'To the beautiful&crlf;garden of prayer.&crlf;&crlf;There\'s a garden where&crlf;' + 'Jesus is waiting,&crlf;And I go with my&crlf;burden and care,&crlf;' + 'Just to learn from His&crlf;lips words of comfort&crlf;In the beautiful&crlf;' + 'garden of prayer.&crlf;&crlf;There\'s a garden where&crlf;Jesus is waiting,&crlf;' + 'And He bids you to come,&crlf;meet Him there;&crlf;Just to bow and&crlf;' + 'receive a new blessing&crlf;In the beautiful&crlf;garden of prayer.&crlf;&crlf;')] SONG_TEST_DATA = [{'title': 'Amazing Grace', 'verses': [ ('Amazing grace! How\nsweet the sound\nThat saved a wretch like me!\nI once was lost,\n' @@ -118,7 +120,7 @@ SONG_TEST_DATA = [{'title': 'Amazing Grace', 'me here below,\nShall be forever mine.'), ('When we\'ve been there\nten thousand years,\nBright shining as the sun,\n' 'We\'ve no less days to\nsing God\'s praise\nThan when we\'d first begun.')]}, - {'title': 'Beautiful Garden Of Prayer, The', + {'title': 'Beautiful Garden Of Prayer, The', 'verses': [ ('There\'s a garden where\nJesus is waiting,\nThere\'s a place that\nis wondrously fair,\n' 'For it glows with the\nlight of His presence.\n\'Tis the beautiful\ngarden of prayer.'), @@ -129,6 +131,7 @@ SONG_TEST_DATA = [{'title': 'Amazing Grace', ('There\'s a garden where\nJesus is waiting,\nAnd He bids you to come,\nmeet Him there;\n' 'Just to bow and\nreceive a new blessing\nIn the beautiful\ngarden of prayer.')]}] + class TestWorshipCenterProSongImport(TestCase): """ Test the functions in the :mod:`worshipcenterproimport` module. @@ -155,7 +158,7 @@ class TestWorshipCenterProSongImport(TestCase): # a mocked "manager" and a mocked out log_error method. with patch('openlp.plugins.songs.lib.worshipcenterproimport.SongImport'), \ patch('openlp.plugins.songs.lib.worshipcenterproimport.pyodbc.connect') as mocked_pyodbc_connect, \ - patch('openlp.plugins.songs.lib.worshipcenterproimport.translate') as mocked_translate: + patch('openlp.plugins.songs.lib.worshipcenterproimport.translate') as mocked_translate: mocked_manager = MagicMock() mocked_log_error = MagicMock() mocked_translate.return_value = 'Translated Text' @@ -171,9 +174,9 @@ class TestWorshipCenterProSongImport(TestCase): # THEN: do_import should return None, and pyodbc, translate & log_error are called with known calls self.assertIsNone(return_value, 'do_import should return None when pyodbc raises an exception.') - mocked_pyodbc_connect.assert_called_with( 'DRIVER={Microsoft Access Driver (*.mdb)};DBQ=import_source') + mocked_pyodbc_connect.assert_called_with('DRIVER={Microsoft Access Driver (*.mdb)};DBQ=import_source') mocked_translate.assert_called_with('SongsPlugin.WorshipCenterProImport', - 'Unable to connect the WorshipCenter Pro database.') + 'Unable to connect the WorshipCenter Pro database.') mocked_log_error.assert_called_with('import_source', 'Translated Text') def song_import_test(self): @@ -184,7 +187,7 @@ class TestWorshipCenterProSongImport(TestCase): # translate method, a mocked "manager", add_verse method & mocked_finish method. with patch('openlp.plugins.songs.lib.worshipcenterproimport.SongImport'), \ patch('openlp.plugins.songs.lib.worshipcenterproimport.pyodbc') as mocked_pyodbc, \ - patch('openlp.plugins.songs.lib.worshipcenterproimport.translate') as mocked_translate: + patch('openlp.plugins.songs.lib.worshipcenterproimport.translate') as mocked_translate: mocked_manager = MagicMock() mocked_import_wizard = MagicMock() mocked_add_verse = MagicMock() @@ -201,7 +204,6 @@ class TestWorshipCenterProSongImport(TestCase): # WHEN: Calling the do_import method return_value = importer.do_import() - # THEN: do_import should return None, and pyodbc, import_wizard, importer.title and add_verse are called with # known calls self.assertIsNone(return_value, 'do_import should return None when pyodbc raises an exception.') @@ -214,10 +216,10 @@ class TestWorshipCenterProSongImport(TestCase): for song_data in SONG_TEST_DATA: title_value = song_data['title'] self.assertIn(title_value, importer._title_assignment_list, - 'title should have been set to %s' % title_value) + 'title should have been set to %s' % title_value) verse_calls = song_data['verses'] add_verse_call_count += len(verse_calls) for call in verse_calls: mocked_add_verse.assert_any_call(call) self.assertEqual(mocked_add_verse.call_count, add_verse_call_count, - 'Incorrect number of calls made to add_verse') + 'Incorrect number of calls made to add_verse') diff --git a/tests/interfaces/openlp_core_lib/__init__.py b/tests/interfaces/openlp_core_lib/__init__.py index 1f4f74a33..6b241e7fc 100644 --- a/tests/interfaces/openlp_core_lib/__init__.py +++ b/tests/interfaces/openlp_core_lib/__init__.py @@ -25,4 +25,4 @@ # 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 # -############################################################################### \ No newline at end of file +############################################################################### diff --git a/tests/interfaces/openlp_core_utils/__init__.py b/tests/interfaces/openlp_core_utils/__init__.py index 1f4f74a33..6b241e7fc 100644 --- a/tests/interfaces/openlp_core_utils/__init__.py +++ b/tests/interfaces/openlp_core_utils/__init__.py @@ -25,4 +25,4 @@ # 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 # -############################################################################### \ No newline at end of file +############################################################################### diff --git a/tests/interfaces/openlp_plugins/__init__.py b/tests/interfaces/openlp_plugins/__init__.py index 1f4f74a33..6b241e7fc 100644 --- a/tests/interfaces/openlp_plugins/__init__.py +++ b/tests/interfaces/openlp_plugins/__init__.py @@ -25,4 +25,4 @@ # 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 # -############################################################################### \ No newline at end of file +############################################################################### diff --git a/tests/interfaces/openlp_plugins/custom/__init__.py b/tests/interfaces/openlp_plugins/custom/__init__.py index 6b241e7fc..4c8c00fec 100644 --- a/tests/interfaces/openlp_plugins/custom/__init__.py +++ b/tests/interfaces/openlp_plugins/custom/__init__.py @@ -26,3 +26,4 @@ # with this program; if not, write to the Free Software Foundation, Inc., 59 # # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### + diff --git a/tests/interfaces/openlp_plugins/remotes/__init__.py b/tests/interfaces/openlp_plugins/remotes/__init__.py index 1f4f74a33..6b241e7fc 100644 --- a/tests/interfaces/openlp_plugins/remotes/__init__.py +++ b/tests/interfaces/openlp_plugins/remotes/__init__.py @@ -25,4 +25,4 @@ # 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 # -############################################################################### \ No newline at end of file +############################################################################### diff --git a/tests/interfaces/openlp_plugins/songs/__init__.py b/tests/interfaces/openlp_plugins/songs/__init__.py index 1f4f74a33..6b241e7fc 100644 --- a/tests/interfaces/openlp_plugins/songs/__init__.py +++ b/tests/interfaces/openlp_plugins/songs/__init__.py @@ -25,4 +25,4 @@ # 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 # -############################################################################### \ No newline at end of file +############################################################################### diff --git a/tests/utils/__init__.py b/tests/utils/__init__.py index 22f4ad8e5..e76e095ea 100644 --- a/tests/utils/__init__.py +++ b/tests/utils/__init__.py @@ -48,4 +48,3 @@ def convert_file_service_item(test_path, name, row=0): finally: open_file.close() return first_line - From 151f1017c538a3612b9627ea5902348c5bb4ba82 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Wed, 2 Apr 2014 20:35:09 +0100 Subject: [PATCH 081/155] finished --- .../openlp_core_common/test_registry.py | 1 - .../test_registryproperties.py | 2 +- .../openlp_core_common/test_uistrings.py | 2 - .../test_formattingtagscontroller.py | 19 ++-- .../openlp_core_ui/test_formattingtagsform.py | 1 - .../openlp_core_ui/test_servicemanager.py | 2 +- .../openlp_core_utils/test_actions.py | 2 - .../openlp_core_utils/test_utils.py | 8 +- .../openlp_plugins/presentations/__init__.py | 2 +- .../presentations/test_pptviewcontroller.py | 7 +- .../openlp_plugins/remotes/__init__.py | 2 +- .../openlp_plugins/remotes/test_router.py | 5 +- .../openlp_plugins/songs/test_ewimport.py | 105 ++++++++++-------- .../openlp_plugins/songs/test_lib.py | 32 +++--- .../songs/test_songbeamerimport.py | 10 +- .../openlp_plugins/songs/test_songselect.py | 2 +- .../songs/test_songshowplusimport.py | 6 +- .../songs/test_worshipcenterproimport.py | 4 +- tests/helpers/songfileimport.py | 21 ++-- .../openlp_core_lib/test_pluginmanager.py | 1 - .../openlp_core_lib/test_searchedit.py | 2 - .../openlp_core_ui/test_mainwindow.py | 1 - .../openlp_core_ui/test_servicenotedialog.py | 5 +- .../openlp_core_utils/test_utils.py | 1 - .../openlp_plugins/bibles/test_lib_http.py | 1 - .../openlp_plugins/bibles/test_lib_manager.py | 1 - .../bibles/test_lib_parse_reference.py | 7 +- .../openlp_plugins/custom/__init__.py | 1 - .../openlp_plugins/custom/forms/__init__.py | 2 +- .../custom/forms/test_customform.py | 1 - .../custom/forms/test_customslideform.py | 1 - .../openlp_plugins/songs/forms/__init__.py | 2 +- .../songs/forms/test_authorsform.py | 1 - .../songs/forms/test_editverseform.py | 1 - 34 files changed, 128 insertions(+), 133 deletions(-) diff --git a/tests/functional/openlp_core_common/test_registry.py b/tests/functional/openlp_core_common/test_registry.py index e27b69d10..8776f4bfb 100644 --- a/tests/functional/openlp_core_common/test_registry.py +++ b/tests/functional/openlp_core_common/test_registry.py @@ -119,4 +119,3 @@ class TestRegistry(TestCase): def dummy_function_2(self): return "function_2" - diff --git a/tests/functional/openlp_core_common/test_registryproperties.py b/tests/functional/openlp_core_common/test_registryproperties.py index 441860544..fa8a2b540 100644 --- a/tests/functional/openlp_core_common/test_registryproperties.py +++ b/tests/functional/openlp_core_common/test_registryproperties.py @@ -63,4 +63,4 @@ class TestRegistryProperties(TestCase, RegistryProperties): # WHEN the application is registered Registry().register('application', application) # THEN the application should be none - self.assertEquals(self.application, application, 'The application value should match') \ No newline at end of file + self.assertEquals(self.application, application, 'The application value should match') diff --git a/tests/functional/openlp_core_common/test_uistrings.py b/tests/functional/openlp_core_common/test_uistrings.py index 606bff732..742934b94 100644 --- a/tests/functional/openlp_core_common/test_uistrings.py +++ b/tests/functional/openlp_core_common/test_uistrings.py @@ -46,5 +46,3 @@ class TestUiStrings(TestCase): # THEN: Check if the instances are the same. self.assertIs(first_instance, second_instance, 'Two UiStrings objects should be the same instance') - - diff --git a/tests/functional/openlp_core_ui/test_formattingtagscontroller.py b/tests/functional/openlp_core_ui/test_formattingtagscontroller.py index b09b095be..1d8512940 100644 --- a/tests/functional/openlp_core_ui/test_formattingtagscontroller.py +++ b/tests/functional/openlp_core_ui/test_formattingtagscontroller.py @@ -72,9 +72,10 @@ class TestFormattingTagController(TestCase): # THEN: The result should match the predetermined value. self.assertTrue(result == test['gen'], - 'Function should handle end tag correctly : %s and %s for %s ' % (test['gen'], result, test['start'])) - self.assertTrue(error == test['valid'], - 'Function should not generate unexpected error messages : %s ' % error) + 'Function should handle end tag correctly : %s and %s for %s ' % + (test['gen'], result, test['start'])) + self.assertTrue(error == test['valid'], 'Function should not generate unexpected error messages : %s ' % + error) def test_start_tag_changed_processes_correctly(self): """ @@ -94,10 +95,10 @@ class TestFormattingTagController(TestCase): error, result = self.services.start_tag_changed(test['start'], test['end']) # THEN: The result should match the predetermined value. - self.assertTrue(result == test['gen'], - 'Function should handle end tag correctly : %s and %s ' % (test['gen'], result)) - self.assertTrue(error == test['valid'], - 'Function should not generate unexpected error messages : %s ' % error) + self.assertTrue(result == test['gen'], 'Function should handle end tag correctly : %s and %s ' % + (test['gen'], result)) + self.assertTrue(error == test['valid'], 'Function should not generate unexpected error messages : %s ' % + error) def test_start_html_to_end_html(self): """ @@ -112,5 +113,5 @@ class TestFormattingTagController(TestCase): result = self.services.start_html_to_end_html(test1) # THEN: The result should match the predetermined value. - self.assertTrue(result == test2, 'Calculated end tag should be valid: %s and %s = %s' - % (test1, test2, result)) \ No newline at end of file + self.assertTrue(result == test2, 'Calculated end tag should be valid: %s and %s = %s' % + (test1, test2, result)) diff --git a/tests/functional/openlp_core_ui/test_formattingtagsform.py b/tests/functional/openlp_core_ui/test_formattingtagsform.py index f9a9621aa..05b5fed74 100644 --- a/tests/functional/openlp_core_ui/test_formattingtagsform.py +++ b/tests/functional/openlp_core_ui/test_formattingtagsform.py @@ -74,4 +74,3 @@ class TestFormattingTagForm(TestCase): # THEN: setEnabled and setDefault should have been called on save_push_button #form.save_button.setEnabled.assert_called_with(True) - diff --git a/tests/functional/openlp_core_ui/test_servicemanager.py b/tests/functional/openlp_core_ui/test_servicemanager.py index 3de560786..260f88b6b 100644 --- a/tests/functional/openlp_core_ui/test_servicemanager.py +++ b/tests/functional/openlp_core_ui/test_servicemanager.py @@ -74,4 +74,4 @@ class TestServiceManager(TestCase): # THEN: The the controller should be registered in the registry. self.assertNotEqual(service, None, 'The base service should be created') self.assertEqual(service['openlp_core']['service-theme'], 'test_theme', 'The test theme should be saved') - self.assertEqual(service['openlp_core']['lite-service'], False, 'The lite service should be saved') \ No newline at end of file + self.assertEqual(service['openlp_core']['lite-service'], False, 'The lite service should be saved') diff --git a/tests/functional/openlp_core_utils/test_actions.py b/tests/functional/openlp_core_utils/test_actions.py index 092c96ac1..2868f8555 100644 --- a/tests/functional/openlp_core_utils/test_actions.py +++ b/tests/functional/openlp_core_utils/test_actions.py @@ -149,5 +149,3 @@ class TestActionList(TestCase, TestMixin): # THEN: Both action should keep their shortcuts. assert len(action3.shortcuts()) == 2, 'The action should have two shortcut assigned.' assert len(action_with_same_shortcuts3.shortcuts()) == 2, 'The action should have two shortcuts assigned.' - - diff --git a/tests/functional/openlp_core_utils/test_utils.py b/tests/functional/openlp_core_utils/test_utils.py index d6405d77b..a97d757ea 100644 --- a/tests/functional/openlp_core_utils/test_utils.py +++ b/tests/functional/openlp_core_utils/test_utils.py @@ -107,7 +107,7 @@ class TestUtils(TestCase): """ # GIVEN: sys.getfilesystemencoding returns "cp1252" with patch('openlp.core.utils.sys.getfilesystemencoding') as mocked_getfilesystemencoding, \ - patch('openlp.core.utils.sys.getdefaultencoding') as mocked_getdefaultencoding: + patch('openlp.core.utils.sys.getdefaultencoding') as mocked_getdefaultencoding: mocked_getfilesystemencoding.return_value = 'cp1252' # WHEN: get_filesystem_encoding() is called @@ -124,7 +124,7 @@ class TestUtils(TestCase): """ # GIVEN: sys.getfilesystemencoding returns None and sys.getdefaultencoding returns "utf-8" with patch('openlp.core.utils.sys.getfilesystemencoding') as mocked_getfilesystemencoding, \ - patch('openlp.core.utils.sys.getdefaultencoding') as mocked_getdefaultencoding: + patch('openlp.core.utils.sys.getdefaultencoding') as mocked_getdefaultencoding: mocked_getfilesystemencoding.return_value = None mocked_getdefaultencoding.return_value = 'utf-8' @@ -175,7 +175,7 @@ class TestUtils(TestCase): # THEN: A tuple should be returned. self.assertEqual(wanted_result, result, - 'A two-entry tuple with the directory and file name (empty) should have been returned.') + 'A two-entry tuple with the directory and file name (empty) should have been returned.') def clean_filename_test(self): """ @@ -206,7 +206,7 @@ class TestUtils(TestCase): # THEN: We get a properly sorted list self.assertEqual(['Aushang', '\u00C4u\u00DFerung', 'Auszug'], sorted_list, - 'Strings should be sorted properly') + 'Strings should be sorted properly') def get_natural_key_test(self): """ diff --git a/tests/functional/openlp_plugins/presentations/__init__.py b/tests/functional/openlp_plugins/presentations/__init__.py index 1f4f74a33..6b241e7fc 100644 --- a/tests/functional/openlp_plugins/presentations/__init__.py +++ b/tests/functional/openlp_plugins/presentations/__init__.py @@ -25,4 +25,4 @@ # 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 # -############################################################################### \ No newline at end of file +############################################################################### diff --git a/tests/functional/openlp_plugins/presentations/test_pptviewcontroller.py b/tests/functional/openlp_plugins/presentations/test_pptviewcontroller.py index 1b43615f3..8a8897cec 100644 --- a/tests/functional/openlp_plugins/presentations/test_pptviewcontroller.py +++ b/tests/functional/openlp_plugins/presentations/test_pptviewcontroller.py @@ -80,7 +80,8 @@ class TestPptviewController(TestCase, TestMixin): controller = PptviewController(plugin=self.mock_plugin) # THEN: The name of the presentation controller should be correct - self.assertEqual('Powerpoint Viewer', controller.name, 'The name of the presentation controller should be correct') + self.assertEqual('Powerpoint Viewer', controller.name, + 'The name of the presentation controller should be correct') def check_available_test(self): """ @@ -98,9 +99,9 @@ class TestPptviewController(TestCase, TestMixin): # THEN: On windows it should return True, on other platforms False if os.name == 'nt': - self.assertTrue(available, 'check_available should return True on windows.') + self.assertTrue(available, 'check_available should return True on windows.') else: - self.assertFalse(available, 'check_available should return False when not on windows.') + self.assertFalse(available, 'check_available should return False when not on windows.') class TestPptviewDocument(TestCase): diff --git a/tests/functional/openlp_plugins/remotes/__init__.py b/tests/functional/openlp_plugins/remotes/__init__.py index 1f4f74a33..6b241e7fc 100644 --- a/tests/functional/openlp_plugins/remotes/__init__.py +++ b/tests/functional/openlp_plugins/remotes/__init__.py @@ -25,4 +25,4 @@ # 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 # -############################################################################### \ No newline at end of file +############################################################################### diff --git a/tests/functional/openlp_plugins/remotes/test_router.py b/tests/functional/openlp_plugins/remotes/test_router.py index 774ce5fcd..9e13a448b 100644 --- a/tests/functional/openlp_plugins/remotes/test_router.py +++ b/tests/functional/openlp_plugins/remotes/test_router.py @@ -111,7 +111,8 @@ class TestRouter(TestCase, TestMixin): Test the get_content_type logic """ # GIVEN: a set of files and their corresponding types - headers = [ ['test.html', 'text/html'], ['test.css', 'text/css'], + headers = [ + ['test.html', 'text/html'], ['test.css', 'text/css'], ['test.js', 'application/javascript'], ['test.jpg', 'image/jpeg'], ['test.gif', 'image/gif'], ['test.ico', 'image/x-icon'], ['test.png', 'image/png'], ['test.whatever', 'text/plain'], @@ -142,7 +143,7 @@ class TestRouter(TestCase, TestMixin): # THEN: it should return a 404 self.router.send_response.assert_called_once_with(404) - self.router.send_header.assert_called_once_with('Content-type','text/html') + self.router.send_header.assert_called_once_with('Content-type', 'text/html') self.assertEqual(self.router.end_headers.call_count, 1, 'end_headers called once') def serve_file_with_valid_params_test(self): diff --git a/tests/functional/openlp_plugins/songs/test_ewimport.py b/tests/functional/openlp_plugins/songs/test_ewimport.py index f98ba57d2..9e327517c 100644 --- a/tests/functional/openlp_plugins/songs/test_ewimport.py +++ b/tests/functional/openlp_plugins/songs/test_ewimport.py @@ -41,33 +41,33 @@ TEST_PATH = os.path.abspath( os.path.join(os.path.dirname(__file__), '..', '..', '..', 'resources', 'easyworshipsongs')) SONG_TEST_DATA = [ {'title': 'Amazing Grace', - 'authors': ['John Newton'], - 'copyright': 'Public Domain', - 'ccli_number': 0, - 'verses': - [('Amazing grace how sweet the sound,\nThat saved a wretch like me;\n' - 'I once was lost, but now am found\nWas blind, but now I see.', 'v1'), - ('T\'was grace that taught my heart to fear,\nAnd grace my fears relieved;\n' - 'How precious did that grace appear\nThe hour I first believed.', 'v2'), - ('Through many dangers, toil and snares,\nI have already come;\n' - '\'Tis grace has brought me safe thus far,\nAnd grace will lead me home.', 'v3'), - ('When we\'ve been there ten thousand years\nBright shining as the sun,\n' - 'We\'ve no less days to sing God\'s praise\nThan when we\'ve first begun.', 'v4')], - 'verse_order_list': []}, + 'authors': ['John Newton'], + 'copyright': 'Public Domain', + 'ccli_number': 0, + 'verses': + [('Amazing grace how sweet the sound,\nThat saved a wretch like me;\n' + 'I once was lost, but now am found\nWas blind, but now I see.', 'v1'), + ('T\'was grace that taught my heart to fear,\nAnd grace my fears relieved;\n' + 'How precious did that grace appear\nThe hour I first believed.', 'v2'), + ('Through many dangers, toil and snares,\nI have already come;\n' + '\'Tis grace has brought me safe thus far,\nAnd grace will lead me home.', 'v3'), + ('When we\'ve been there ten thousand years\nBright shining as the sun,\n' + 'We\'ve no less days to sing God\'s praise\nThan when we\'ve first begun.', 'v4')], + 'verse_order_list': []}, {'title': 'Beautiful Garden Of Prayer', - 'authors': ['Eleanor Allen Schroll James H. Fillmore'], - 'copyright': 'Public Domain', - 'ccli_number': 0, - 'verses': - [('O the beautiful garden, the garden of prayer,\nO the beautiful garden of prayer.\n' - 'There my Savior awaits, and He opens the gates\nTo the beautiful garden of prayer.', 'c1'), - ('There\'s a garden where Jesus is waiting,\nThere\'s a place that is wondrously fair.\n' - 'For it glows with the light of His presence,\n\'Tis the beautiful garden of prayer.', 'v1'), - ('There\'s a garden where Jesus is waiting,\nAnd I go with my burden and care.\n' - 'Just to learn from His lips, words of comfort,\nIn the beautiful garden of prayer.', 'v2'), - ('There\'s a garden where Jesus is waiting,\nAnd He bids you to come meet Him there,\n' - 'Just to bow and receive a new blessing,\nIn the beautiful garden of prayer.', 'v3')], - 'verse_order_list': []}] + 'authors': ['Eleanor Allen Schroll James H. Fillmore'], + 'copyright': 'Public Domain', + 'ccli_number': 0, + 'verses': + [('O the beautiful garden, the garden of prayer,\nO the beautiful garden of prayer.\n' + 'There my Savior awaits, and He opens the gates\nTo the beautiful garden of prayer.', 'c1'), + ('There\'s a garden where Jesus is waiting,\nThere\'s a place that is wondrously fair.\n' + 'For it glows with the light of His presence,\n\'Tis the beautiful garden of prayer.', 'v1'), + ('There\'s a garden where Jesus is waiting,\nAnd I go with my burden and care.\n' + 'Just to learn from His lips, words of comfort,\nIn the beautiful garden of prayer.', 'v2'), + ('There\'s a garden where Jesus is waiting,\nAnd He bids you to come meet Him there,\n' + 'Just to bow and receive a new blessing,\nIn the beautiful garden of prayer.', 'v3')], + 'verse_order_list': []}] class EasyWorshipSongImportLogger(EasyWorshipSongImport): @@ -95,26 +95,32 @@ class TestFieldDesc: self.size = size TEST_DATA_ENCODING = 'cp1252' -CODE_PAGE_MAPPINGS = [(852, 'cp1250'), (737, 'cp1253'), (775, 'cp1257'), (855, 'cp1251'), (857, 'cp1254'), +CODE_PAGE_MAPPINGS = [ + (852, 'cp1250'), (737, 'cp1253'), (775, 'cp1257'), (855, 'cp1251'), (857, 'cp1254'), (866, 'cp1251'), (869, 'cp1253'), (862, 'cp1255'), (874, 'cp874')] -TEST_FIELD_DESCS = [TestFieldDesc('Title', FieldType.String, 50), +TEST_FIELD_DESCS = [ + TestFieldDesc('Title', FieldType.String, 50), TestFieldDesc('Text Percentage Bottom', FieldType.Int16, 2), TestFieldDesc('RecID', FieldType.Int32, 4), TestFieldDesc('Default Background', FieldType.Logical, 1), TestFieldDesc('Words', FieldType.Memo, 250), TestFieldDesc('Words', FieldType.Memo, 250), TestFieldDesc('BK Bitmap', FieldType.Blob, 10), TestFieldDesc('Last Modified', FieldType.Timestamp, 10)] -TEST_FIELDS = [b'A Heart Like Thine\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0', 32868, 2147483750, +TEST_FIELDS = [ + b'A Heart Like Thine\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0', 32868, 2147483750, 129, b'{\\rtf1\\ansi\\deff0\\deftab254{\\fonttbl{\\f0\\fnil\\fcharset0 Arial;}{\\f1\\fnil\\fcharset0 Verdana;}}' - b'{\\colortbl\\red0\\green0\\blue0;\\red255\\green0\\blue0;\\red0\\green128\\blue0;\\red0\\green0\\blue255;' - b'\\red255\\green255\\blue0;\\red255\\green0\\blue255;\\red128\\g\xBF\xBD\7\0f\r\0\0\1\0', - b'{\\rtf1\\ansi\\deff0\\deftab254{\\fonttbl{\\f0\\fnil\\fcharset0 Arial;}{\\f1\\fnil\\fcharset0 Verdana;}}' - b'{\\colortbl\\red0\\green0\\blue0;\\red255\\green0\\blue0;\\red0\\green128\\blue0;\\red0\\green0\\blue255;\\red255' - b'\\green255\\blue0;\\red255\\green0\\blue255;\\red128\\g\6\0\xEF\xBF\xBD\6\0\0\1\0', b'\0\0\0\0\0\0\0\0\0\0', 0] + b'{\\colortbl\\red0\\green0\\blue0;\\red255\\green0\\blue0;\\red0\\green128\\blue0;\\red0\\green0\\blue255;' + b'\\red255\\green255\\blue0;\\red255\\green0\\blue255;\\red128\\g\xBF\xBD\7\0f\r\0\0\1\0', + b'{\\rtf1\\ansi\\deff0\\deftab254{\\fonttbl{\\f0\\fnil\\fcharset0 Arial;}{\\f1\\fnil\\fcharset0 Verdana;}}' + b'{\\colortbl\\red0\\green0\\blue0;\\red255\\green0\\blue0;\\red0\\green128\\blue0;\\red0\\green0' + b'\\blue255;\\red255' + b'\\green255\\blue0;\\red255\\green0\\blue255;\\red128\\g\6\0\xEF\xBF\xBD\6\0\0\1\0', + b'\0\0\0\0\0\0\0\0\0\0', 0] GET_MEMO_FIELD_TEST_RESULTS = [ - (4, b'\2', {'return': b'\2','read': (1, 3430), 'seek': (507136, (8, os.SEEK_CUR))}), + (4, b'\2', {'return': b'\2', 'read': (1, 3430), 'seek': (507136, (8, os.SEEK_CUR))}), (4, b'\3', {'return': b'', 'read': (1, ), 'seek': (507136, )}), (5, b'\3', {'return': b'\3', 'read': (1, 1725), 'seek': (3220111360, (41, os.SEEK_CUR), 3220111408)}), (5, b'\4', {'return': b'', 'read': (), 'seek': ()})] + class TestEasyWorshipSongImport(TestCase): """ Test the functions in the :mod:`ewimport` module. @@ -135,7 +141,7 @@ class TestEasyWorshipSongImport(TestCase): self.assertIsNotNone(field_desc_entry, 'Import should not be none') self.assertEquals(field_desc_entry.name, name, 'FieldDescEntry.name should be the same as the name argument') self.assertEquals(field_desc_entry.field_type, field_type, - 'FieldDescEntry.type should be the same as the typeargument') + 'FieldDescEntry.type should be the same as the type argument') self.assertEquals(field_desc_entry.size, size, 'FieldDescEntry.size should be the same as the size argument') def create_importer_test(self): @@ -164,7 +170,7 @@ class TestEasyWorshipSongImport(TestCase): # WHEN: Called with a field name that exists existing_fields = ['Title', 'Text Percentage Bottom', 'RecID', 'Default Background', 'Words', - 'BK Bitmap', 'Last Modified'] + 'BK Bitmap', 'Last Modified'] for field_name in existing_fields: # THEN: The item corresponding the index returned should have the same name attribute @@ -194,7 +200,7 @@ class TestEasyWorshipSongImport(TestCase): # GIVEN: A mocked out SongImport class, a mocked out struct class, and a mocked out "manager" and a list of # field descriptions with patch('openlp.plugins.songs.lib.ewimport.SongImport'), \ - patch('openlp.plugins.songs.lib.ewimport.struct') as mocked_struct: + patch('openlp.plugins.songs.lib.ewimport.struct') as mocked_struct: mocked_manager = MagicMock() importer = EasyWorshipSongImport(mocked_manager, filenames=[]) @@ -225,7 +231,8 @@ class TestEasyWorshipSongImport(TestCase): # THEN: get_field should return the known results self.assertEquals(return_value, result, - 'get_field should return "%s" when called with "%s"' % (result, TEST_FIELDS[field_index])) + 'get_field should return "%s" when called with "%s"' % + (result, TEST_FIELDS[field_index])) def get_memo_field_test(self): """ @@ -265,7 +272,7 @@ class TestEasyWorshipSongImport(TestCase): """ # GIVEN: A mocked out SongImport class, a mocked out "manager" with patch('openlp.plugins.songs.lib.ewimport.SongImport'), \ - patch('openlp.plugins.songs.lib.ewimport.os.path') as mocked_os_path: + patch('openlp.plugins.songs.lib.ewimport.os.path') as mocked_os_path: mocked_manager = MagicMock() importer = EasyWorshipSongImport(mocked_manager, filenames=[]) mocked_os_path.isfile.side_effect = [True, False] @@ -284,7 +291,7 @@ class TestEasyWorshipSongImport(TestCase): """ # GIVEN: A mocked out SongImport class, os.path and a mocked out "manager" with patch('openlp.plugins.songs.lib.ewimport.SongImport'), \ - patch('openlp.plugins.songs.lib.ewimport.os.path') as mocked_os_path: + patch('openlp.plugins.songs.lib.ewimport.os.path') as mocked_os_path: mocked_manager = MagicMock() importer = EasyWorshipSongImport(mocked_manager, filenames=[]) mocked_os_path.isfile.return_value = True @@ -305,7 +312,7 @@ class TestEasyWorshipSongImport(TestCase): with patch('openlp.plugins.songs.lib.ewimport.SongImport'), \ patch('openlp.plugins.songs.lib.ewimport.os.path') as mocked_os_path, \ patch('builtins.open') as mocked_open, \ - patch('openlp.plugins.songs.lib.ewimport.struct') as mocked_struct: + patch('openlp.plugins.songs.lib.ewimport.struct') as mocked_struct: mocked_manager = MagicMock() importer = EasyWorshipSongImport(mocked_manager, filenames=[]) mocked_os_path.isfile.return_value = True @@ -320,7 +327,7 @@ class TestEasyWorshipSongImport(TestCase): for effect in struct_unpack_return_values: self.assertIsNone(importer.do_import(), 'do_import should return None when db_size is less than 0x800') self.assertEqual(mocked_open().close.call_count, 2, - 'The open db and memo files should have been closed') + 'The open db and memo files should have been closed') mocked_open().close.reset_mock() self.assertIs(mocked_open().seek.called, False, 'db_file.seek should not have been called.') @@ -332,7 +339,8 @@ class TestEasyWorshipSongImport(TestCase): with patch('openlp.plugins.songs.lib.ewimport.SongImport'), \ patch('openlp.plugins.songs.lib.ewimport.os.path') as mocked_os_path, \ patch('builtins.open'), patch('openlp.plugins.songs.lib.ewimport.struct') as mocked_struct, \ - patch('openlp.plugins.songs.lib.ewimport.retrieve_windows_encoding') as mocked_retrieve_windows_encoding: + patch('openlp.plugins.songs.lib.ewimport.retrieve_windows_encoding') as \ + mocked_retrieve_windows_encoding: mocked_manager = MagicMock() importer = EasyWorshipSongImport(mocked_manager, filenames=[]) mocked_os_path.isfile.return_value = True @@ -357,7 +365,8 @@ class TestEasyWorshipSongImport(TestCase): # GIVEN: Test files with a mocked out SongImport class, a mocked out "manager", a mocked out "import_wizard", # and mocked out "author", "add_copyright", "add_verse", "finish" methods. with patch('openlp.plugins.songs.lib.ewimport.SongImport'), \ - patch('openlp.plugins.songs.lib.ewimport.retrieve_windows_encoding') as mocked_retrieve_windows_encoding: + patch('openlp.plugins.songs.lib.ewimport.retrieve_windows_encoding') as \ + mocked_retrieve_windows_encoding: mocked_retrieve_windows_encoding.return_value = 'cp1252' mocked_manager = MagicMock() mocked_import_wizard = MagicMock() @@ -395,10 +404,10 @@ class TestEasyWorshipSongImport(TestCase): self.assertEqual(importer.copyright, song_copyright) if ccli_number: self.assertEquals(importer.ccli_number, ccli_number, 'ccli_number for %s should be %s' - % (title, ccli_number)) + % (title, ccli_number)) for verse_text, verse_tag in add_verse_calls: mocked_add_verse.assert_any_call(verse_text, verse_tag) if verse_order_list: - self.assertEquals(importer.verse_order_list, verse_order_list, 'verse_order_list for %s should be %s' - % (title, verse_order_list)) + self.assertEquals(importer.verse_order_list, verse_order_list, + 'verse_order_list for %s should be %s' % (title, verse_order_list)) mocked_finish.assert_called_with() diff --git a/tests/functional/openlp_plugins/songs/test_lib.py b/tests/functional/openlp_plugins/songs/test_lib.py index beff6d0d5..f6e5d98b9 100644 --- a/tests/functional/openlp_plugins/songs/test_lib.py +++ b/tests/functional/openlp_plugins/songs/test_lib.py @@ -44,20 +44,20 @@ class TestLib(TestCase): """ Mock up two songs and provide a set of lyrics for the songs_probably_equal tests. """ - self.full_lyrics ='''amazing grace how sweet the sound that saved a wretch like me i once was lost but now am + self.full_lyrics = '''amazing grace how sweet the sound that saved a wretch like me i once was lost but now am found was blind but now i see twas grace that taught my heart to fear and grace my fears relieved how - precious did that grace appear the hour i first believed through many dangers toils and snares i have already - come tis grace that brought me safe thus far and grace will lead me home''' - self.short_lyrics ='''twas grace that taught my heart to fear and grace my fears relieved how precious did that - grace appear the hour i first believed''' - self.error_lyrics ='''amazing how sweet the trumpet that saved a wrench like me i once was losst but now am + precious did that grace appear the hour i first believed through many dangers toils and snares i have + already come tis grace that brought me safe thus far and grace will lead me home''' + self.short_lyrics = '''twas grace that taught my heart to fear and grace my fears relieved how precious did + that grace appear the hour i first believed''' + self.error_lyrics = '''amazing how sweet the trumpet that saved a wrench like me i once was losst but now am found waf blind but now i see it was grace that taught my heart to fear and grace my fears relieved how precious did that grace appppppppear the hour i first believedxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx snares i have already come to this grace that brought me safe so far and grace will lead me home''' - self.different_lyrics='''on a hill far away stood an old rugged cross the emblem of suffering and shame and i love - that old cross where the dearest and best for a world of lost sinners was slain so ill cherish the old rugged - cross till my trophies at last i lay down i will cling to the old rugged cross and exchange it some day for a - crown''' + self.different_lyrics = '''on a hill far away stood an old rugged cross the emblem of suffering and shame and + i love that old cross where the dearest and best for a world of lost sinners was slain so ill cherish the + old rugged cross till my trophies at last i lay down i will cling to the old rugged cross and exchange it + some day for a crown''' self.song1 = MagicMock() self.song2 = MagicMock() @@ -99,7 +99,7 @@ class TestLib(TestCase): result = songs_probably_equal(self.song1, self.song2) # THEN: The result should be True. - assert result == True, 'The result should be True' + assert result is True, 'The result should be True' def songs_probably_equal_short_song_test(self): """ @@ -113,7 +113,7 @@ class TestLib(TestCase): result = songs_probably_equal(self.song1, self.song2) # THEN: The result should be True. - assert result == True, 'The result should be True' + assert result is True, 'The result should be True' def songs_probably_equal_error_song_test(self): """ @@ -127,7 +127,7 @@ class TestLib(TestCase): result = songs_probably_equal(self.song1, self.song2) # THEN: The result should be True. - assert result == True, 'The result should be True' + assert result is True, 'The result should be True' def songs_probably_equal_different_song_test(self): """ @@ -141,7 +141,7 @@ class TestLib(TestCase): result = songs_probably_equal(self.song1, self.song2) # THEN: The result should be False. - assert result == False, 'The result should be False' + assert result is False, 'The result should be False' def remove_typos_beginning_test(self): """ @@ -267,8 +267,8 @@ class TestLib(TestCase): # WHEN: We call strip_rtf on the input RTF result, result_enc = strip_rtf( - '{\\rtf1 \\ansi \\ansicpg1252 {\\fonttbl \\f0 \\fswiss \\fcharset%s Helvetica;}' \ - '{\\colortbl ;\\red0 \\green0 \\blue0 ;}\\pard \\f0 %s}' % (charset, input)) + '{\\rtf1 \\ansi \\ansicpg1252 {\\fonttbl \\f0 \\fswiss \\fcharset%s Helvetica;}' + '{\\colortbl ;\\red0 \\green0 \\blue0 ;}\\pard \\f0 %s}' % (charset, input)) # THEN: The stripped text matches thed expected result assert result == exp_result, 'The result should be %s' % exp_result diff --git a/tests/functional/openlp_plugins/songs/test_songbeamerimport.py b/tests/functional/openlp_plugins/songs/test_songbeamerimport.py index a968975b3..e7bd891d3 100644 --- a/tests/functional/openlp_plugins/songs/test_songbeamerimport.py +++ b/tests/functional/openlp_plugins/songs/test_songbeamerimport.py @@ -110,7 +110,7 @@ class TestSongBeamerImport(TestCase): # THEN: do_import should return none and the progress bar setMaximum should be called with the length of # import_source. self.assertIsNone(importer.do_import(), - 'do_import should return None when import_source is a list and stop_import_flag is True') + 'do_import should return None when import_source is a list and stop_import_flag is True') mocked_import_wizard.progress_bar.setMaximum.assert_called_with(len(importer.import_source)) def file_import_test(self): @@ -147,9 +147,9 @@ class TestSongBeamerImport(TestCase): for verse_text, verse_tag in add_verse_calls: mocked_add_verse.assert_any_call(verse_text, verse_tag) if song_book_name: - self.assertEquals(importer.song_book_name, song_book_name, 'song_book_name for %s should be "%s"' - % (song_file, song_book_name)) + self.assertEquals(importer.song_book_name, song_book_name, 'song_book_name for %s should be "%s"' % + (song_file, song_book_name)) if song_number: - self.assertEquals(importer.song_number, song_number, 'song_number for %s should be %s' - % (song_file, song_number)) + self.assertEquals(importer.song_number, song_number, 'song_number for %s should be %s' % + (song_file, song_number)) mocked_finish.assert_called_with() diff --git a/tests/functional/openlp_plugins/songs/test_songselect.py b/tests/functional/openlp_plugins/songs/test_songselect.py index 0b32cff95..8d1237190 100644 --- a/tests/functional/openlp_plugins/songs/test_songselect.py +++ b/tests/functional/openlp_plugins/songs/test_songselect.py @@ -352,7 +352,7 @@ class TestSongSelect(TestCase): """ # GIVEN: A song to save, and some mocked out objects with patch('openlp.plugins.songs.lib.songselect.clean_song') as mocked_clean_song, \ - patch('openlp.plugins.songs.lib.songselect.Author') as MockedAuthor: + patch('openlp.plugins.songs.lib.songselect.Author') as MockedAuthor: song_dict = { 'title': 'Arky Arky', 'authors': ['Public Domain'], diff --git a/tests/functional/openlp_plugins/songs/test_songshowplusimport.py b/tests/functional/openlp_plugins/songs/test_songshowplusimport.py index f3fe231d0..7876558e9 100644 --- a/tests/functional/openlp_plugins/songs/test_songshowplusimport.py +++ b/tests/functional/openlp_plugins/songs/test_songshowplusimport.py @@ -129,7 +129,8 @@ class TestSongShowPlusImport(TestCase): importer = SongShowPlusImport(mocked_manager, filenames=[]) # WHEN: Supplied with the following arguments replicating verses being added - test_values = [('Verse 1', VerseType.tags[VerseType.Verse] + '1'), + test_values = [ + ('Verse 1', VerseType.tags[VerseType.Verse] + '1'), ('Verse 2', VerseType.tags[VerseType.Verse] + '2'), ('verse1', VerseType.tags[VerseType.Verse] + '1'), ('Verse', VerseType.tags[VerseType.Verse] + '1'), @@ -156,7 +157,8 @@ class TestSongShowPlusImport(TestCase): importer = SongShowPlusImport(mocked_manager, filenames=[]) # WHEN: Supplied with the following arguments replicating a verse order being added - test_values = [('Verse 1', VerseType.tags[VerseType.Verse] + '1'), + test_values = [ + ('Verse 1', VerseType.tags[VerseType.Verse] + '1'), ('Verse 2', VerseType.tags[VerseType.Verse] + '2'), ('verse1', VerseType.tags[VerseType.Verse] + '1'), ('Verse', VerseType.tags[VerseType.Verse] + '1'), diff --git a/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py b/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py index c6acbe965..9a58a6c2b 100644 --- a/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py +++ b/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py @@ -204,8 +204,8 @@ class TestWorshipCenterProSongImport(TestCase): # WHEN: Calling the do_import method return_value = importer.do_import() - # THEN: do_import should return None, and pyodbc, import_wizard, importer.title and add_verse are called with - # known calls + # THEN: do_import should return None, and pyodbc, import_wizard, importer.title and add_verse are called + # with known calls self.assertIsNone(return_value, 'do_import should return None when pyodbc raises an exception.') mocked_pyodbc.connect.assert_called_with('DRIVER={Microsoft Access Driver (*.mdb)};DBQ=import_source') mocked_pyodbc.connect().cursor.assert_any_call() diff --git a/tests/helpers/songfileimport.py b/tests/helpers/songfileimport.py index 12754f55a..cc67770c1 100644 --- a/tests/helpers/songfileimport.py +++ b/tests/helpers/songfileimport.py @@ -116,28 +116,27 @@ class SongImportTestHelper(TestCase): if song_copyright: self.mocked_add_copyright.assert_called_with(song_copyright) if ccli_number: - self.assertEquals(importer.ccli_number, ccli_number, 'ccli_number for %s should be %s' - % (source_file_name, ccli_number)) + self.assertEquals(importer.ccli_number, ccli_number, 'ccli_number for %s should be %s' % + (source_file_name, ccli_number)) for verse_text, verse_tag in add_verse_calls: self.mocked_add_verse.assert_any_call(verse_text, verse_tag) if topics: self.assertEquals(importer.topics, topics, 'topics for %s should be %s' % (source_file_name, topics)) if comments: - self.assertEquals(importer.comments, comments, 'comments for %s should be "%s"' - % (source_file_name, comments)) + self.assertEquals(importer.comments, comments, 'comments for %s should be "%s"' % + (source_file_name, comments)) if song_book_name: - self.assertEquals(importer.song_book_name, song_book_name, 'song_book_name for %s should be "%s"' - % (source_file_name, song_book_name)) + self.assertEquals(importer.song_book_name, song_book_name, 'song_book_name for %s should be "%s"' % + (source_file_name, song_book_name)) if song_number: - self.assertEquals(importer.song_number, song_number, 'song_number for %s should be %s' - % (source_file_name, song_number)) + self.assertEquals(importer.song_number, song_number, 'song_number for %s should be %s' % + (source_file_name, song_number)) if verse_order_list: - self.assertEquals(importer.verse_order_list, [], 'verse_order_list for %s should be %s' - % (source_file_name, verse_order_list)) + self.assertEquals(importer.verse_order_list, [], 'verse_order_list for %s should be %s' % + (source_file_name, verse_order_list)) self.mocked_finish.assert_called_with() def _get_data(self, data, key): if key in data: return data[key] return '' - diff --git a/tests/interfaces/openlp_core_lib/test_pluginmanager.py b/tests/interfaces/openlp_core_lib/test_pluginmanager.py index 054aa6dc1..262f9f2f3 100644 --- a/tests/interfaces/openlp_core_lib/test_pluginmanager.py +++ b/tests/interfaces/openlp_core_lib/test_pluginmanager.py @@ -95,4 +95,3 @@ class TestPluginManager(TestCase, TestMixin): assert 'songusage' in plugin_names, 'There should be a "songusage" plugin.' assert 'alerts' in plugin_names, 'There should be a "alerts" plugin.' assert 'remotes' in plugin_names, 'There should be a "remotes" plugin.' - diff --git a/tests/interfaces/openlp_core_lib/test_searchedit.py b/tests/interfaces/openlp_core_lib/test_searchedit.py index 98c942885..22bf6fae3 100644 --- a/tests/interfaces/openlp_core_lib/test_searchedit.py +++ b/tests/interfaces/openlp_core_lib/test_searchedit.py @@ -133,5 +133,3 @@ class TestSearchEdit(TestCase, TestMixin): Just check if the resizeEvent() method is re-implemented. """ assert hasattr(self.search_edit, "resizeEvent"), "The search edit should re-implement the resizeEvent method." - - \ No newline at end of file diff --git a/tests/interfaces/openlp_core_ui/test_mainwindow.py b/tests/interfaces/openlp_core_ui/test_mainwindow.py index de5452632..b79036547 100644 --- a/tests/interfaces/openlp_core_ui/test_mainwindow.py +++ b/tests/interfaces/openlp_core_ui/test_mainwindow.py @@ -85,4 +85,3 @@ class TestMainWindow(TestCase, TestMixin): # THEN: The current widget should have been set. self.main_window.media_tool_box.setCurrentIndex.assert_called_with(2) - diff --git a/tests/interfaces/openlp_core_ui/test_servicenotedialog.py b/tests/interfaces/openlp_core_ui/test_servicenotedialog.py index 7344dc633..86fc425c1 100644 --- a/tests/interfaces/openlp_core_ui/test_servicenotedialog.py +++ b/tests/interfaces/openlp_core_ui/test_servicenotedialog.py @@ -90,9 +90,8 @@ class TestStartNoteDialog(TestCase, TestMixin): with patch('PyQt4.QtGui.QDialog.exec_'): self.form.exec_() self.form.text_edit.setPlainText(text) - okWidget = self.form.button_box.button(self.form.button_box.Save) - QtTest.QTest.mouseClick(okWidget, QtCore.Qt.LeftButton) + ok_widget = self.form.button_box.button(self.form.button_box.Save) + QtTest.QTest.mouseClick(ok_widget, QtCore.Qt.LeftButton) # THEN the following text is returned self.assertEqual(self.form.text_edit.toPlainText(), text, 'The new text should be returned') - diff --git a/tests/interfaces/openlp_core_utils/test_utils.py b/tests/interfaces/openlp_core_utils/test_utils.py index 3c3879b34..3da1db2e2 100644 --- a/tests/interfaces/openlp_core_utils/test_utils.py +++ b/tests/interfaces/openlp_core_utils/test_utils.py @@ -86,4 +86,3 @@ class TestUtils(TestCase, TestMixin): # THEN the result is false assert result is True, 'The file is not an image file so the test should return True' - diff --git a/tests/interfaces/openlp_plugins/bibles/test_lib_http.py b/tests/interfaces/openlp_plugins/bibles/test_lib_http.py index 9f90df64e..517732e4d 100644 --- a/tests/interfaces/openlp_plugins/bibles/test_lib_http.py +++ b/tests/interfaces/openlp_plugins/bibles/test_lib_http.py @@ -97,4 +97,3 @@ class TestBibleHTTP(TestCase): # THEN: We should get back a valid service item assert len(results.verse_list) == 36, 'The book of John should not have had any verses added or removed' - diff --git a/tests/interfaces/openlp_plugins/bibles/test_lib_manager.py b/tests/interfaces/openlp_plugins/bibles/test_lib_manager.py index b00bbee00..7296cfc08 100644 --- a/tests/interfaces/openlp_plugins/bibles/test_lib_manager.py +++ b/tests/interfaces/openlp_plugins/bibles/test_lib_manager.py @@ -115,4 +115,3 @@ class TestBibleManager(TestCase, TestMixin): verses = self.manager.get_verse_count_by_book_ref_id('tests', 54, 3) # THEN the chapter count should be returned self.assertEqual(16, verses, '1 Timothy v3 should have 16 verses returned from the bible') - diff --git a/tests/interfaces/openlp_plugins/bibles/test_lib_parse_reference.py b/tests/interfaces/openlp_plugins/bibles/test_lib_parse_reference.py index 811d048db..b085bd1df 100644 --- a/tests/interfaces/openlp_plugins/bibles/test_lib_parse_reference.py +++ b/tests/interfaces/openlp_plugins/bibles/test_lib_parse_reference.py @@ -83,7 +83,7 @@ class TestBibleManager(TestCase, TestMixin): # WHEN asking to parse the bible reference results = parse_reference('1 Timothy 1', self.manager.db_cache['tests'], MagicMock(), 54) # THEN a verse array should be returned - self.assertEquals([(54, 1, 1, -1)], results , "The bible verses should matches the expected results") + self.assertEquals([(54, 1, 1, -1)], results, "The bible verses should matches the expected results") def parse_reference_two_test(self): """ @@ -93,7 +93,7 @@ class TestBibleManager(TestCase, TestMixin): # WHEN asking to parse the bible reference results = parse_reference('1 Timothy 1:1-2', self.manager.db_cache['tests'], MagicMock(), 54) # THEN a verse array should be returned - self.assertEquals([(54, 1, 1, 2)], results , "The bible verses should matches the expected results") + self.assertEquals([(54, 1, 1, 2)], results, "The bible verses should matches the expected results") def parse_reference_three_test(self): """ @@ -103,4 +103,5 @@ class TestBibleManager(TestCase, TestMixin): # WHEN asking to parse the bible reference results = parse_reference('1 Timothy 1:1-2:1', self.manager.db_cache['tests'], MagicMock(), 54) # THEN a verse array should be returned - self.assertEquals([(54,1,1,-1),(54,2,1,1)], results , "The bible verses should matches the expected results") \ No newline at end of file + self.assertEquals([(54, 1, 1, -1), (54, 2, 1, 1)], results, "The bible verses should matches the expected " + "results") diff --git a/tests/interfaces/openlp_plugins/custom/__init__.py b/tests/interfaces/openlp_plugins/custom/__init__.py index 4c8c00fec..6b241e7fc 100644 --- a/tests/interfaces/openlp_plugins/custom/__init__.py +++ b/tests/interfaces/openlp_plugins/custom/__init__.py @@ -26,4 +26,3 @@ # with this program; if not, write to the Free Software Foundation, Inc., 59 # # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### - diff --git a/tests/interfaces/openlp_plugins/custom/forms/__init__.py b/tests/interfaces/openlp_plugins/custom/forms/__init__.py index 1f4f74a33..6b241e7fc 100644 --- a/tests/interfaces/openlp_plugins/custom/forms/__init__.py +++ b/tests/interfaces/openlp_plugins/custom/forms/__init__.py @@ -25,4 +25,4 @@ # 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 # -############################################################################### \ No newline at end of file +############################################################################### diff --git a/tests/interfaces/openlp_plugins/custom/forms/test_customform.py b/tests/interfaces/openlp_plugins/custom/forms/test_customform.py index 93b271797..ca238b1a6 100644 --- a/tests/interfaces/openlp_plugins/custom/forms/test_customform.py +++ b/tests/interfaces/openlp_plugins/custom/forms/test_customform.py @@ -88,7 +88,6 @@ class TestEditCustomForm(TestCase, TestMixin): self.assertEqual(self.form.title_edit.text(), '', 'The title edit should be empty') self.assertEqual(self.form.credit_edit.text(), '', 'The credit edit should be empty') - def on_add_button_clicked_test(self): """ Test the on_add_button_clicked_test method / add_button button. diff --git a/tests/interfaces/openlp_plugins/custom/forms/test_customslideform.py b/tests/interfaces/openlp_plugins/custom/forms/test_customslideform.py index a2bfb1f10..261519362 100644 --- a/tests/interfaces/openlp_plugins/custom/forms/test_customslideform.py +++ b/tests/interfaces/openlp_plugins/custom/forms/test_customslideform.py @@ -92,4 +92,3 @@ class TestEditCustomSlideForm(TestCase, TestMixin): # THEN: The dialog should have focus. mocked_set_focus.assert_called_with() - diff --git a/tests/interfaces/openlp_plugins/songs/forms/__init__.py b/tests/interfaces/openlp_plugins/songs/forms/__init__.py index 1f4f74a33..6b241e7fc 100644 --- a/tests/interfaces/openlp_plugins/songs/forms/__init__.py +++ b/tests/interfaces/openlp_plugins/songs/forms/__init__.py @@ -25,4 +25,4 @@ # 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 # -############################################################################### \ No newline at end of file +############################################################################### diff --git a/tests/interfaces/openlp_plugins/songs/forms/test_authorsform.py b/tests/interfaces/openlp_plugins/songs/forms/test_authorsform.py index 5d965c042..2257bbc84 100644 --- a/tests/interfaces/openlp_plugins/songs/forms/test_authorsform.py +++ b/tests/interfaces/openlp_plugins/songs/forms/test_authorsform.py @@ -145,4 +145,3 @@ class TestAuthorsForm(TestCase, TestMixin): # THEN: The display_name_edit should have the correct value self.assertEqual(self.form.display_edit.text(), display_name, 'The display name should be set correctly') - diff --git a/tests/interfaces/openlp_plugins/songs/forms/test_editverseform.py b/tests/interfaces/openlp_plugins/songs/forms/test_editverseform.py index 284d850c4..178b1cd7c 100644 --- a/tests/interfaces/openlp_plugins/songs/forms/test_editverseform.py +++ b/tests/interfaces/openlp_plugins/songs/forms/test_editverseform.py @@ -120,4 +120,3 @@ class TestEditVerseForm(TestCase, TestMixin): # THEN: The verse text edit should have a Chorus:1 in it self.assertIn('---[Chorus:1]---', self.form.verse_text_edit.toPlainText(), 'The verse text edit should have a "Chorus 1" marker') - From bc55bcd7fa7719cf212c001ad5256986bab0935f Mon Sep 17 00:00:00 2001 From: Jonathan Springer Date: Thu, 3 Apr 2014 08:38:39 -0400 Subject: [PATCH 082/155] Fix argurment to pass correct type. --- openlp/core/utils/languagemanager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/core/utils/languagemanager.py b/openlp/core/utils/languagemanager.py index cd5ce7add..bb584f7bd 100644 --- a/openlp/core/utils/languagemanager.py +++ b/openlp/core/utils/languagemanager.py @@ -74,7 +74,7 @@ class LanguageManager(object): log.debug('Translation files: %s', AppLocation.get_directory( AppLocation.LanguageDir)) trans_dir = QtCore.QDir(AppLocation.get_directory(AppLocation.LanguageDir)) - file_names = trans_dir.entryList('*.qm', QtCore.QDir.Files, QtCore.QDir.Name) + file_names = trans_dir.entryList(['*.qm'], QtCore.QDir.Files, QtCore.QDir.Name) # Remove qm files from the list which start with "qt_". file_names = [file_ for file_ in file_names if not file_.startswith('qt_')] return list(map(trans_dir.filePath, file_names)) From 6e3b02fb0528eb6ef7411878c0587d34164e4656 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Thu, 3 Apr 2014 21:46:12 +0200 Subject: [PATCH 083/155] attempt to fix detection --- scripts/jenkins_script.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/scripts/jenkins_script.py b/scripts/jenkins_script.py index 386ab69ef..1e3f3945d 100644 --- a/scripts/jenkins_script.py +++ b/scripts/jenkins_script.py @@ -49,6 +49,7 @@ from jenkins import Jenkins JENKINS_URL = 'http://ci.openlp.org/' +REPO_REGEX = r'(.*+)(~.*)' class OpenLPJobs(object): @@ -107,8 +108,7 @@ class JenkinsTrigger(object): """ job = self.jenkins_instance.job(job_name) while job.info['inQueue']: - # Give other processes the possibility to take over. Like Thread.yield(). - time.sleep(0) + time.sleep(1) build = job.last_build build.wait() result_string = build.info['result'] @@ -137,12 +137,9 @@ def get_repo_name(): # Determine the branch's name repo_name = '' for line in output_list: - # Check if it is remote branch. - if 'push branch' in line: - repo_name = line.replace('push branch: bzr+ssh://bazaar.launchpad.net/', 'lp:') - break - elif 'checkout of branch' in line: - repo_name = line.replace('checkout of branch: bzr+ssh://bazaar.launchpad.net/', 'lp:') + match = re.match(REPO_REGEX, line) + if match: + repo_name = 'lp:%s' % match.group(2) break repo_name = repo_name.strip('/') From eb27eead4c186c2c155311497b47a74a23c02c9c Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Thu, 3 Apr 2014 21:58:03 +0200 Subject: [PATCH 084/155] attempt to fix detection --- scripts/jenkins_script.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/scripts/jenkins_script.py b/scripts/jenkins_script.py index 1e3f3945d..8767910d0 100644 --- a/scripts/jenkins_script.py +++ b/scripts/jenkins_script.py @@ -40,6 +40,7 @@ You can look up the token in the Branch-01-Pull job configuration or ask in IRC. """ from optparse import OptionParser +import re from requests.exceptions import HTTPError from subprocess import Popen, PIPE import sys @@ -137,10 +138,17 @@ def get_repo_name(): # Determine the branch's name repo_name = '' for line in output_list: - match = re.match(REPO_REGEX, line) - if match: - repo_name = 'lp:%s' % match.group(2) - break + # Check if it is remote branch. + if 'push branch' in line: + match = re.match(REPO_REGEX, line) + if match: + repo_name = 'lp:%s' % match.group(2) + break + elif 'checkout of branch' in line: + match = re.match(REPO_REGEX, line) + if match: + repo_name = 'lp:%s' % match.group(2) + break repo_name = repo_name.strip('/') # Did we find the branch name? From 7ef62293f3392e9fd714c0cbb7466e975ff54e54 Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Thu, 3 Apr 2014 22:22:10 +0200 Subject: [PATCH 085/155] Added test --- .../presentations/test_pdfcontroller.py | 51 ++++++++++++++++--- 1 file changed, 44 insertions(+), 7 deletions(-) diff --git a/tests/functional/openlp_plugins/presentations/test_pdfcontroller.py b/tests/functional/openlp_plugins/presentations/test_pdfcontroller.py index 65c7bf916..b1e356249 100644 --- a/tests/functional/openlp_plugins/presentations/test_pdfcontroller.py +++ b/tests/functional/openlp_plugins/presentations/test_pdfcontroller.py @@ -33,6 +33,7 @@ import os import shutil from unittest import TestCase, SkipTest from tempfile import mkdtemp +from PyQt4 import QtCore, QtGui from openlp.plugins.presentations.lib.pdfcontroller import PdfController, PdfDocument from tests.functional import MagicMock @@ -45,6 +46,11 @@ __default_settings__ = { 'presentations/enable_pdf_program': False } +SCREEN = { + 'primary': False, + 'number': 1, + 'size': QtCore.QRect(0, 0, 1024, 768) +} class TestPdfController(TestCase, TestMixin): """ @@ -56,7 +62,12 @@ class TestPdfController(TestCase, TestMixin): """ self.get_application() self.build_settings() - ScreenList.create(self.app.desktop()) + # Mocked out desktop object + self.desktop = MagicMock() + self.desktop.primaryScreen.return_value = SCREEN['primary'] + self.desktop.screenCount.return_value = SCREEN['number'] + self.desktop.screenGeometry.return_value = SCREEN['size'] + self.screens = ScreenList.create(self.desktop) Settings().extend_default_settings(__default_settings__) self.temp_folder = mkdtemp() self.thumbnail_folder = mkdtemp() @@ -67,12 +78,11 @@ class TestPdfController(TestCase, TestMixin): """ Delete all the C++ objects at the end so that we don't have a segfault """ - try: - self.destroy_settings() - shutil.rmtree(self.thumbnail_folder) - shutil.rmtree(self.temp_folder) - except OSError: - pass + del self.screens + self.destroy_settings() + shutil.rmtree(self.thumbnail_folder) + shutil.rmtree(self.temp_folder) + def constructor_test(self): """ @@ -106,3 +116,30 @@ class TestPdfController(TestCase, TestMixin): # THEN: The load should succeed and we should be able to get a pagecount self.assertTrue(loaded, 'The loading of the PDF should succeed.') self.assertEqual(3, document.get_slide_count(), 'The pagecount of the PDF should be 3.') + + def load_pdf_pictures_test(self): + """ + Test loading of a Pdf and check size of generate pictures + """ + # GIVEN: A Pdf-file + test_file = os.path.join(TEST_RESOURCES_PATH, 'presentations', 'pdf_test1.pdf') + + # WHEN: The Pdf is loaded + controller = PdfController(plugin=self.mock_plugin) + if not controller.check_available(): + raise SkipTest('Could not detect mudraw or ghostscript, so skipping PDF test') + controller.temp_folder = self.temp_folder + controller.thumbnail_folder = self.thumbnail_folder + document = PdfDocument(controller, test_file) + loaded = document.load_presentation() + + # THEN: The load should succeed and pictures should be created and have been scales to fit the screen + self.assertTrue(loaded, 'The loading of the PDF should succeed.') + image = QtGui.QImage(os.path.join(self.temp_folder, 'pdf_test1.pdf', 'mainslide001.png')) + # Based on the converter used the resolution will differ a bit + if controller.gsbin: + self.assertEqual(760, image.height(), 'The height should be 760') + self.assertEqual(537, image.width(), 'The width should be 537') + else: + self.assertEqual(767, image.height(), 'The height should be 767') + self.assertEqual(543, image.width(), 'The width should be 543') From 576ab6e1f95e6469987a1fccdc7c26ac759064b1 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Fri, 4 Apr 2014 21:20:00 +0100 Subject: [PATCH 086/155] head --- openlp/core/common/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/core/common/__init__.py b/openlp/core/common/__init__.py index 8df86032e..8a8e6eef3 100644 --- a/openlp/core/common/__init__.py +++ b/openlp/core/common/__init__.py @@ -53,7 +53,7 @@ def trace_error_handler(logger): """ log_string = "OpenLP Error trace" for tb in traceback.extract_stack(): - log_string = ('%s\n File %s at line %d \n\t called %s' % (log_string, tb[0], tb[1], tb[3])) + log_string = '%s\n File %s at line %d \n\t called %s' % (log_string, tb[0], tb[1], tb[3]) logger.error(log_string) From 8813490badca58d8395b6ba1b654837852d16f60 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Fri, 4 Apr 2014 21:24:11 +0100 Subject: [PATCH 087/155] fix tests --- .../openlp_plugins/presentations/test_pdfcontroller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/openlp_plugins/presentations/test_pdfcontroller.py b/tests/functional/openlp_plugins/presentations/test_pdfcontroller.py index b1e356249..277e83a5b 100644 --- a/tests/functional/openlp_plugins/presentations/test_pdfcontroller.py +++ b/tests/functional/openlp_plugins/presentations/test_pdfcontroller.py @@ -52,6 +52,7 @@ SCREEN = { 'size': QtCore.QRect(0, 0, 1024, 768) } + class TestPdfController(TestCase, TestMixin): """ Test the PdfController. @@ -83,7 +84,6 @@ class TestPdfController(TestCase, TestMixin): shutil.rmtree(self.thumbnail_folder) shutil.rmtree(self.temp_folder) - def constructor_test(self): """ Test the Constructor from the PdfController From d1b867988dbf49c3f01ebb331a7103a53def31f4 Mon Sep 17 00:00:00 2001 From: Raoul Snyman Date: Fri, 4 Apr 2014 22:25:49 +0200 Subject: [PATCH 088/155] missed the test somehow --- .../openlp_core_common/test_common.py | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 tests/functional/openlp_core_common/test_common.py diff --git a/tests/functional/openlp_core_common/test_common.py b/tests/functional/openlp_core_common/test_common.py new file mode 100644 index 000000000..4f9a471e4 --- /dev/null +++ b/tests/functional/openlp_core_common/test_common.py @@ -0,0 +1,82 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2014 Raoul Snyman # +# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan # +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # +# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, # +# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, # +# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, # +# Frode Woldsund, Martin Zibricky, Patrick Zimmermann # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### +""" +Functional tests to test the AppLocation class and related methods. +""" + +from unittest import TestCase + +from openlp.core.common import de_hump, trace_error_handler +from tests.functional import MagicMock, patch + + +class TestCommonFunctions(TestCase): + """ + A test suite to test out various functions in the openlp.core.common module. + """ + def de_hump_conversion_test(self): + """ + Test the de_hump function with a class name + """ + # GIVEN: a Class name in Camel Case + string = "MyClass" + + # WHEN: we call de_hump + new_string = de_hump(string) + + # THEN: the new string should be converted to python format + self.assertTrue(new_string == "my_class", 'The class name should have been converted') + + def de_hump_static_test(self): + """ + Test the de_hump function with a python string + """ + # GIVEN: a Class name in Camel Case + string = "my_class" + + # WHEN: we call de_hump + new_string = de_hump(string) + + # THEN: the new string should be converted to python format + self.assertTrue(new_string == "my_class", 'The class name should have been preserved') + + def trace_error_handler_test(self): + """ + Test the trace_error_handler() method + """ + # GIVEN: Mocked out objects + with patch('openlp.core.common.traceback') as mocked_traceback: + mocked_traceback.extract_stack.return_value = [('openlp.fake', 56, None, 'trace_error_handler_test')] + mocked_logger = MagicMock() + + # WHEN: trace_error_handler() is called + trace_error_handler(mocked_logger) + + # THEN: The mocked_logger.error() method should have been called with the correct parameters + mocked_logger.error.assert_called_with('Called by trace_error_handler_test at line 56 in openlp.fake') From 8b2c2f9eefba1c9f2ccbef4c567fedf0c2a8de12 Mon Sep 17 00:00:00 2001 From: Raoul Snyman Date: Fri, 4 Apr 2014 23:12:44 +0200 Subject: [PATCH 089/155] fixed a test --- tests/functional/openlp_core_common/test_common.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/functional/openlp_core_common/test_common.py b/tests/functional/openlp_core_common/test_common.py index 4f9a471e4..90b7d0520 100644 --- a/tests/functional/openlp_core_common/test_common.py +++ b/tests/functional/openlp_core_common/test_common.py @@ -79,4 +79,5 @@ class TestCommonFunctions(TestCase): trace_error_handler(mocked_logger) # THEN: The mocked_logger.error() method should have been called with the correct parameters - mocked_logger.error.assert_called_with('Called by trace_error_handler_test at line 56 in openlp.fake') + mocked_logger.error.assert_called_with('OpenLP Error trace\n File openlp.fake at line 56 \n\t called trace_error_handler_test') + From a134543e7d38c47f9d60e418bd5761770d7058da Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sat, 5 Apr 2014 11:42:23 +0200 Subject: [PATCH 090/155] fix regex --- scripts/jenkins_script.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/jenkins_script.py b/scripts/jenkins_script.py index 8767910d0..1ef4e21b4 100644 --- a/scripts/jenkins_script.py +++ b/scripts/jenkins_script.py @@ -50,7 +50,7 @@ from jenkins import Jenkins JENKINS_URL = 'http://ci.openlp.org/' -REPO_REGEX = r'(.*+)(~.*)' +REPO_REGEX = r'(.*/+)(~.*)' class OpenLPJobs(object): From d9d8ca6478740597920f7e558958080c1b52a199 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sat, 5 Apr 2014 11:43:13 +0200 Subject: [PATCH 091/155] pep test --- scripts/jenkins_script.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/jenkins_script.py b/scripts/jenkins_script.py index 1ef4e21b4..c2920bed1 100644 --- a/scripts/jenkins_script.py +++ b/scripts/jenkins_script.py @@ -62,6 +62,7 @@ class OpenLPJobs(object): Branch_Interface = 'Branch-03-Interface-Tests' Branch_Windows = 'Branch-04-Windows_Tests' Branch_PEP = 'Branch-05-Code-Analysis' + PEP_TEST = "sdafajsdklfj lajsldfk jlasdkjf lkajdf lkasjdlfkjalskdjflkajflkadsjkfl jasdlkfj laskdjflka sjdlkfjlaksdjflksajdf" Jobs = [Branch_Pull, Branch_Functional, Branch_Interface, Branch_Windows, Branch_PEP] From 4c3e2ac45c41d8e9f27a477d8cac50daee51ce47 Mon Sep 17 00:00:00 2001 From: Raoul Snyman Date: Sun, 6 Apr 2014 01:06:37 +0200 Subject: [PATCH 092/155] Trying to fix pylint --- openlp/__init__.py | 4 ++-- openlp/core/ui/mainwindow.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/openlp/__init__.py b/openlp/__init__.py index ac5f063d4..bf7cbc680 100644 --- a/openlp/__init__.py +++ b/openlp/__init__.py @@ -30,7 +30,7 @@ The :mod:`openlp` module contains all the project produced OpenLP functionality """ -import openlp.core -import openlp.plugins +import core +import plugins __all__ = ['core', 'plugins'] diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index aa220a76b..8ac3130a0 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -367,7 +367,7 @@ class Ui_MainWindow(object): self.settings_menu.setTitle(translate('OpenLP.MainWindow', '&Settings')) self.settings_language_menu.setTitle(translate('OpenLP.MainWindow', '&Language')) self.help_menu.setTitle(translate('OpenLP.MainWindow', '&Help')) - self.media_manager_dock.setWindowTitle(translate('OpenLP.MainWindow', 'Media Manager')) + self.media_manager_dock.setWindowTitle(translate('OpenLP.MainWindow', 'Library')) self.service_manager_dock.setWindowTitle(translate('OpenLP.MainWindow', 'Service Manager')) self.theme_manager_dock.setWindowTitle(translate('OpenLP.MainWindow', 'Theme Manager')) self.file_new_item.setText(translate('OpenLP.MainWindow', '&New')) From e5d5af38c2f45b279f43a698b6c99ca22ba0609c Mon Sep 17 00:00:00 2001 From: Jonathan Springer Date: Sun, 6 Apr 2014 22:03:05 -0400 Subject: [PATCH 093/155] Move opening of files passed in via command line to function. --- openlp/core/ui/mainwindow.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index b14694ccc..b70192039 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -594,15 +594,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow, RegistryProperties): self.live_controller.display.setFocus() self.activateWindow() if self.arguments: - args = [] - for a in self.arguments: - args.extend([a]) - for arg in args: - filename = arg - if not isinstance(filename, str): - filename = str(filename, sys.getfilesystemencoding()) - if filename.endswith(('.osz', '.oszl', '.otz')): - self.service_manager_contents.load_file(filename) + self.open_cmd_line_files() elif Settings().value(self.general_settings_section + '/auto open'): self.service_manager_contents.load_Last_file() self.timer_version_id = self.startTimer(1000) @@ -1362,3 +1354,17 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow, RegistryProperties): if self.new_data_path == AppLocation.get_directory(AppLocation.DataDir): settings.remove('advanced/data path') self.application.set_normal_cursor() + + def open_cmd_line_files(self): + """ + Open files passed in through command line arguments + """ + args = [] + for a in self.arguments: + args.extend([a]) + for arg in args: + filename = arg + if not isinstance(filename, str): + filename = str(filename, sys.getfilesystemencoding()) + if filename.endswith(('.osz', '.oszl')): + self.service_manager_contents.load_file(filename) From 1945b4c380001d4b774986584b815c40ad14bd44 Mon Sep 17 00:00:00 2001 From: Jonathan Springer Date: Sun, 6 Apr 2014 22:07:19 -0400 Subject: [PATCH 094/155] Remove changes to platform specific code. --- openlp/core/ui/mainwindow.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index b70192039..d11fae767 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -322,8 +322,14 @@ class Ui_MainWindow(object): # i18n add Language Actions add_actions(self.settings_language_menu, (self.auto_language_item, None)) add_actions(self.settings_language_menu, self.language_group.actions()) - add_actions(self.settings_menu, (self.settings_plugin_list_item, self.settings_language_menu.menuAction(), - None, self.formatting_tag_item, self.settings_shortcuts_item, self.settings_configure_item)) + # Order things differently in OS X so that Preferences menu item in the + # app menu is correct (this gets picked up automatically by Qt). + if sys.platform == 'darwin': + add_actions(self.settings_menu, (self.settings_plugin_list_item, self.settings_language_menu.menuAction(), + None, self.settings_configure_item, self.settings_shortcuts_item, self.formatting_tag_item)) + else: + add_actions(self.settings_menu, (self.settings_plugin_list_item, self.settings_language_menu.menuAction(), + None, self.formatting_tag_item, self.settings_shortcuts_item, self.settings_configure_item)) add_actions(self.tools_menu, (self.tools_add_tool_item, None)) add_actions(self.tools_menu, (self.tools_open_data_folder, None)) add_actions(self.tools_menu, (self.tools_first_time_wizard, None)) @@ -387,10 +393,8 @@ class Ui_MainWindow(object): self.import_language_item.setText(translate('OpenLP.MainWindow', '&Language')) self.export_theme_item.setText(translate('OpenLP.MainWindow', '&Theme')) self.export_language_item.setText(translate('OpenLP.MainWindow', '&Language')) - # Do not use config, options, setup, settings or preferences in menu item name unless it is OpenLP's preferences. - # Qt automatically detects the Preferences entry for the Mac OS X menu based on the name of the menu item. - self.settings_shortcuts_item.setText(translate('OpenLP.MainWindow', '&Shortcuts...')) - self.formatting_tag_item.setText(translate('OpenLP.MainWindow', '&Formatting Tags...')) + self.settings_shortcuts_item.setText(translate('OpenLP.MainWindow', 'Configure &Shortcuts...')) + self.formatting_tag_item.setText(translate('OpenLP.MainWindow', 'Configure &Formatting Tags...')) self.settings_configure_item.setText(translate('OpenLP.MainWindow', '&Configure OpenLP...')) self.settings_export_item.setStatusTip(translate('OpenLP.MainWindow', 'Export OpenLP settings to a specified *.config file')) From c751a208f91d3c8cd74bcc26b08af3737cfb6f57 Mon Sep 17 00:00:00 2001 From: Jonathan Springer Date: Sun, 6 Apr 2014 22:47:41 -0400 Subject: [PATCH 095/155] Change back to using a white list. --- tests/utils/test_bzr_tags.py | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/tests/utils/test_bzr_tags.py b/tests/utils/test_bzr_tags.py index e9b6b35bc..0bcc73b54 100644 --- a/tests/utils/test_bzr_tags.py +++ b/tests/utils/test_bzr_tags.py @@ -34,7 +34,26 @@ from unittest import TestCase from subprocess import Popen, PIPE -BLACK_LISTED_TAGS = '2.2.2', 'help' +TAGS = [ + ['1.9.0', '1'], + ['1.9.1', '775'], + ['1.9.2', '890'], + ['1.9.3', '1063'], + ['1.9.4', '1196'], + ['1.9.5', '1421'], + ['1.9.6', '1657'], + ['1.9.7', '1761'], + ['1.9.8', '1856'], + ['1.9.9', '1917'], + ['1.9.10', '2003'], + ['1.9.11', '2039'], + ['1.9.12', '2063'], + ['2.0', '2118'], + ['2.0.1', '?'], + ['2.0.2', '?'], + ['2.0.3', '?'], + ['2.1.0', '2119'] +] class TestBzrTags(TestCase): @@ -49,11 +68,9 @@ class TestBzrTags(TestCase): tags = [] bzr = Popen(('bzr', 'tags'), stdout=PIPE) stdout = bzr.communicate()[0] - lines = (line.decode('utf-8') for line in stdout.split()) + lines = (line.decode('utf-8') for line in stdout.splitlines()) for line in lines: - tags.append(line) + tags.append(line.split()) - # THEN none of the tags should match the black listed tags - for BLACK_LISTED_TAG in BLACK_LISTED_TAGS: - for tag in tags: - self.assertNotEqual(BLACK_LISTED_TAG, tag, 'Tag should not exist') + # THEN the tags should match the accepted tags + self.assertEqual(TAGS, tags, 'List of tags should match') From d3526c6204ee9a11b77cb4a0f74f2c96c70ed4a0 Mon Sep 17 00:00:00 2001 From: Jonathan Springer Date: Mon, 7 Apr 2014 13:22:12 -0400 Subject: [PATCH 096/155] Simplify code per Andreas Preikschat's comment. --- tests/utils/test_bzr_tags.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/utils/test_bzr_tags.py b/tests/utils/test_bzr_tags.py index 0bcc73b54..7bb38f4ab 100644 --- a/tests/utils/test_bzr_tags.py +++ b/tests/utils/test_bzr_tags.py @@ -65,12 +65,9 @@ class TestBzrTags(TestCase): # GIVEN: A bzr branch # WHEN getting the branches tags - tags = [] bzr = Popen(('bzr', 'tags'), stdout=PIPE) stdout = bzr.communicate()[0] - lines = (line.decode('utf-8') for line in stdout.splitlines()) - for line in lines: - tags.append(line.split()) + tags = [line.decode('utf-8').split() for line in stdout.splitlines()] # THEN the tags should match the accepted tags self.assertEqual(TAGS, tags, 'List of tags should match') From a49854e20b7689b532c28effb2d8981d172882ff Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Tue, 8 Apr 2014 13:07:52 +0200 Subject: [PATCH 097/155] Update verse order when verse names change --- openlp/plugins/songs/lib/xml.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/openlp/plugins/songs/lib/xml.py b/openlp/plugins/songs/lib/xml.py index 667afebdd..d516b5e02 100644 --- a/openlp/plugins/songs/lib/xml.py +++ b/openlp/plugins/songs/lib/xml.py @@ -675,6 +675,7 @@ class OpenLyrics(object): sxml = SongXML() verses = {} verse_def_list = [] + verse_order = self._text(properties.verseOrder).split(' ') if hasattr(properties, 'verseOrder') else [] try: lyrics = song_xml.lyrics except AttributeError: @@ -717,13 +718,17 @@ class OpenLyrics(object): else: verses[(verse_tag, verse_number, lang, translit, verse_part)] = text verse_def_list.append((verse_tag, verse_number, lang, translit, verse_part)) + # Update verse order when the verse name has changed + if verse_def != verse_tag + verse_number + verse_part: + for i in range(len(verse_order)): + if verse_order[i] == verse_def: + verse_order[i] = verse_tag + verse_number + verse_part # We have to use a list to keep the order, as dicts are not sorted. for verse in verse_def_list: sxml.add_verse_to_lyrics(verse[0], verse[1], verses[verse], verse[2]) song_obj.lyrics = str(sxml.extract_xml(), 'utf-8') # Process verse order - if hasattr(properties, 'verseOrder'): - song_obj.verse_order = self._text(properties.verseOrder) + song_obj.verse_order = ' '.join(verse_order) def _process_songbooks(self, properties, song): """ From 2b8586bf99a9168d9e8daf3ef43ceba578a56fdc Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Tue, 8 Apr 2014 13:19:12 +0200 Subject: [PATCH 098/155] Test --- tests/functional/openlp_core_lib/test_ui.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/functional/openlp_core_lib/test_ui.py b/tests/functional/openlp_core_lib/test_ui.py index babc94a81..9ea94719f 100644 --- a/tests/functional/openlp_core_lib/test_ui.py +++ b/tests/functional/openlp_core_lib/test_ui.py @@ -81,3 +81,18 @@ class TestUi(TestCase): self.assertIsInstance(btnbox, QtGui.QDialogButtonBox) self.assertEqual(1, len(btnbox.buttons())) self.assertEqual(QtGui.QDialogButtonBox.HelpRole, btnbox.buttonRole(btnbox.buttons()[0])) + + def test_create_horizontal_adjusting_combo_box(self): + """ + Test creating a horizontal adjusting combo box + """ + # GIVEN: A dialog + dialog = QtGui.QDialog() + + # WHEN: We create the combobox + combo = create_horizontal_adjusting_combo_box(dialog, 'combo1') + + # THEN: We should get a ComboBox + self.assertIsInstance(combo, QtGui.QComboBox) + self.assertEqual('combo1', combo.objectName()) + self.assertEqual(QtGui.QComboBox.AdjustToMinimumContentsLength, combo.sizeAdjustPolicy()) From 1759c72f3649d618916203a380c034fec2224628 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Tue, 8 Apr 2014 16:08:07 +0200 Subject: [PATCH 099/155] Test --- openlp/core/lib/ui.py | 2 +- tests/functional/openlp_core_lib/test_ui.py | 32 +++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/openlp/core/lib/ui.py b/openlp/core/lib/ui.py index 3126d1a56..965adb053 100644 --- a/openlp/core/lib/ui.py +++ b/openlp/core/lib/ui.py @@ -173,7 +173,7 @@ def create_button(parent, name, **kwargs): kwargs.setdefault('tooltip', translate('OpenLP.Ui', 'Move selection down one position.')) else: log.warn('The role "%s" is not defined in create_push_button().', role) - if kwargs.pop('class', '') == 'toolbutton': + if kwargs.pop('btn_class', '') == 'toolbutton': button = QtGui.QToolButton(parent) else: button = QtGui.QPushButton(parent) diff --git a/tests/functional/openlp_core_lib/test_ui.py b/tests/functional/openlp_core_lib/test_ui.py index babc94a81..5efe202cb 100644 --- a/tests/functional/openlp_core_lib/test_ui.py +++ b/tests/functional/openlp_core_lib/test_ui.py @@ -81,3 +81,35 @@ class TestUi(TestCase): self.assertIsInstance(btnbox, QtGui.QDialogButtonBox) self.assertEqual(1, len(btnbox.buttons())) self.assertEqual(QtGui.QDialogButtonBox.HelpRole, btnbox.buttonRole(btnbox.buttons()[0])) + + def test_create_button(self): + """ + Test creating a button + """ + # GIVEN: A dialog + dialog = QtGui.QDialog() + + # WHEN: We create the button + btn = create_button(dialog, 'my_btn') + + # THEN: We should get a button with a name + self.assertIsInstance(btn, QtGui.QPushButton) + self.assertEqual('my_btn', btn.objectName()) + self.assertTrue(btn.isEnabled()) + + # WHEN: We create a button with some attributes + btn = create_button(dialog, 'my_btn', text='Hello', tooltip='How are you?', enabled=False) + + # THEN: We should get a button with those attributes + self.assertIsInstance(btn, QtGui.QPushButton) + self.assertEqual('Hello', btn.text()) + self.assertEqual('How are you?', btn.toolTip()) + self.assertFalse(btn.isEnabled()) + + # WHEN: We create a toolbutton + btn = create_button(dialog, 'my_btn', btn_class='toolbutton') + + # THEN: We should get a toolbutton + self.assertIsInstance(btn, QtGui.QToolButton) + self.assertEqual('my_btn', btn.objectName()) + self.assertTrue(btn.isEnabled()) \ No newline at end of file From 791fdba019ebeb792e1ecd8a492c2c26730dab8a Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Tue, 8 Apr 2014 17:15:46 +0200 Subject: [PATCH 100/155] Fix test --- .../openlp_core_ui/test_maindisplay.py | 37 +++----- .../openlp_core_ui/test_maindisplay.py.moved | 85 ------------------- 2 files changed, 14 insertions(+), 108 deletions(-) delete mode 100644 tests/functional/openlp_core_ui/test_maindisplay.py.moved diff --git a/tests/functional/openlp_core_ui/test_maindisplay.py b/tests/functional/openlp_core_ui/test_maindisplay.py index 48f05978c..fb6b1f0f3 100644 --- a/tests/functional/openlp_core_ui/test_maindisplay.py +++ b/tests/functional/openlp_core_ui/test_maindisplay.py @@ -88,31 +88,22 @@ class TestMainDisplay(TestCase): display = MagicMock() main_display = MainDisplay(display) - # WHEN: MainDisplay.set_transparency is called with a true value" + # WHEN: We enable transparency main_display.set_transparency(True) - # THEN: check MainDisplay.setAutoFillBackground, MainDisplay.setStyleSheet, MainDisplay.setAttribute, - assert main_display.StyleSheet == "QGraphicsView {background: transparent; border: 0px;}", \ - 'MainDisplay instance should be transparent' - assert main_display.getAutoFillBackground == False, \ - 'MainDisplay instance should be without background auto fill' - assert main_display.getAttribute(QtCore.Qt.WA_TranslucentBackground) == True, \ - 'MainDisplay hasnt translusent background' + # THEN: There should be a Stylesheet + self.assertEqual('QGraphicsView {background: transparent; border: 0px;}', main_display.styleSheet(), + 'MainDisplay instance should be transparent') + self.assertFalse(main_display.autoFillBackground(), + 'MainDisplay instance should be without background auto fill') + self.assertTrue(main_display.testAttribute(QtCore.Qt.WA_TranslucentBackground), + 'MainDisplay hasnt translucent background') - def set_transparency_disable_test(self): - """ - Test creating an instance of the MainDisplay class - """ - # GIVEN: get an instance of MainDisplay - display = MagicMock() - main_display = MainDisplay(display) - - # WHEN: MainDisplay.set_transparency is called with a False value" + # WHEN: We disable transparency main_display.set_transparency(False) - # THEN: check MainDisplay.setAutoFillBackground, MainDisplay.setStyleSheet, MainDisplay.setAttribute, - assert main_display.StyleSheet == "QGraphicsView {}", \ - 'MainDisplay instance should not be transparent' - assert main_display.getAutoFillBackground == True, 'MainDisplay instance should be with background auto fill' - assert main_display.getAttribute(QtCore.Qt.WA_TranslucentBackground) == True, \ - 'MainDisplay hasnt translusent background' + # THEN: The Stylesheet should be empty + self.assertEqual('QGraphicsView {}', main_display.styleSheet(), + 'MainDisplay instance should not be transparent') + self.assertFalse(main_display.testAttribute(QtCore.Qt.WA_TranslucentBackground), + 'MainDisplay hasnt translucent background') diff --git a/tests/functional/openlp_core_ui/test_maindisplay.py.moved b/tests/functional/openlp_core_ui/test_maindisplay.py.moved deleted file mode 100644 index 0a1f85d5f..000000000 --- a/tests/functional/openlp_core_ui/test_maindisplay.py.moved +++ /dev/null @@ -1,85 +0,0 @@ -# -*- coding: utf-8 -*- -# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 - -############################################################################### -# OpenLP - Open Source Lyrics Projection # -# --------------------------------------------------------------------------- # -# Copyright (c) 2008-2013 Raoul Snyman # -# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan # -# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # -# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # -# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, # -# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, # -# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, # -# Frode Woldsund, Martin Zibricky, Patrick Zimmermann, Dmitriy Marmyshev # -# --------------------------------------------------------------------------- # -# 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 # -############################################################################### -""" -Package to test the openlp.core.lib.maindisplay package. -""" -from unittest import TestCase - -from tests.functional import MagicMock -from PyQt4 import QtCore - -from openlp.core.ui.maindisplay import MainDisplay - - -class TestMainDisplay(TestCase): - """ - Test the functions in the :mod:`MainDisplay` module. - """ - #TODO: The following classes still need tests written for - # - Display - # - MainDisplay - # - AudioPlayer - - - def set_transparency_enable_test(self): - """ - Test creating an instance of the MainDisplay class - """ - # GIVEN: get an instance of MainDisplay - display = MagicMock() - main_display = MainDisplay(display) - - # WHEN: MainDisplay.set_transparency is called with a true value" - main_display.set_transparency(True) - - # THEN: check MainDisplay.setAutoFillBackground, MainDisplay.setStyleSheet, MainDisplay.setAttribute, - assert main_display.StyleSheet == "QGraphicsView {background: transparent; border: 0px;}", \ - 'MainDisplay instance should be transparent' - assert main_display.getAutoFillBackground == False, \ - 'MainDisplay instance should be without background auto fill' - assert main_display.getAttribute(QtCore.Qt.WA_TranslucentBackground) == True, \ - 'MainDisplay hasnt translusent background' - - def set_transparency_disable_test(self): - """ - Test creating an instance of the MainDisplay class - """ - # GIVEN: get an instance of MainDisplay - display = MagicMock() - main_display = MainDisplay(display) - - # WHEN: MainDispaly.set_transparency is called with a False value" - main_display.set_transparency(False) - - # THEN: check MainDisplay.setAutoFillBackground, MainDisplay.setStyleSheet, MainDisplay.setAttribute, - assert main_display.StyleSheet == "QGraphicsView {}", \ - 'MainDisplay instance should not be transparent' - assert main_display.getAutoFillBackground == True, 'MainDisplay instance should be with background auto fill' - assert main_display.getAttribute(QtCore.Qt.WA_TranslucentBackground) == True, \ - 'MainDisplay hasnt translusent background' From 129f726d4cf297659045cae5ffe7750abe8b879d Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Tue, 8 Apr 2014 17:23:40 +0200 Subject: [PATCH 101/155] Rename method --- tests/functional/openlp_core_ui/test_maindisplay.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/openlp_core_ui/test_maindisplay.py b/tests/functional/openlp_core_ui/test_maindisplay.py index fb6b1f0f3..b1a4dc7f7 100644 --- a/tests/functional/openlp_core_ui/test_maindisplay.py +++ b/tests/functional/openlp_core_ui/test_maindisplay.py @@ -80,7 +80,7 @@ class TestMainDisplay(TestCase): # THEN: The controller should not be a live controller. self.assertEqual(main_display.is_live, True, 'The main display should be a live controller') - def set_transparency_enable_test(self): + def set_transparency_test(self): """ Test creating an instance of the MainDisplay class """ From 5b3a58d1d60fe2986c3a74aafe60a09a347df59c Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Tue, 8 Apr 2014 18:40:06 +0200 Subject: [PATCH 102/155] Add newline --- tests/functional/openlp_core_lib/test_ui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/openlp_core_lib/test_ui.py b/tests/functional/openlp_core_lib/test_ui.py index 5efe202cb..860fed0b1 100644 --- a/tests/functional/openlp_core_lib/test_ui.py +++ b/tests/functional/openlp_core_lib/test_ui.py @@ -112,4 +112,4 @@ class TestUi(TestCase): # THEN: We should get a toolbutton self.assertIsInstance(btn, QtGui.QToolButton) self.assertEqual('my_btn', btn.objectName()) - self.assertTrue(btn.isEnabled()) \ No newline at end of file + self.assertTrue(btn.isEnabled()) From 47767b02c1af747234ea19ee783bffcf76e520b4 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Tue, 8 Apr 2014 22:41:33 +0200 Subject: [PATCH 103/155] Fix debug messages Fixes: https://launchpad.net/bugs/1170231 --- openlp/plugins/bibles/lib/db.py | 74 ++++++++++++++++----------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/openlp/plugins/bibles/lib/db.py b/openlp/plugins/bibles/lib/db.py index 9ffa5d53e..f24005748 100644 --- a/openlp/plugins/bibles/lib/db.py +++ b/openlp/plugins/bibles/lib/db.py @@ -191,7 +191,7 @@ class BibleDB(QtCore.QObject, Manager, RegistryProperties): :param testament: *Defaults to 1.* The testament_reference_id from bibles_resources.sqlite of the testament this book belongs to. """ - log.debug('BibleDB.create_book("%s", "%s")', name, bk_ref_id) + log.debug('BibleDB.create_book("%s", "%s")' % (name, bk_ref_id)) book = Book.populate(name=name, book_reference_id=bk_ref_id, testament_reference_id=testament) self.save_object(book) return book @@ -202,7 +202,7 @@ class BibleDB(QtCore.QObject, Manager, RegistryProperties): :param book: The book object """ - log.debug('BibleDB.update_book("%s")', book.name) + log.debug('BibleDB.update_book("%s")' % book.name) return self.save_object(book) def delete_book(self, db_book): @@ -211,7 +211,7 @@ class BibleDB(QtCore.QObject, Manager, RegistryProperties): :param db_book: The book object. """ - log.debug('BibleDB.delete_book("%s")', db_book.name) + log.debug('BibleDB.delete_book("%s")' % db_book.name) if self.delete_object(Book, db_book.id): return True return False @@ -225,7 +225,7 @@ class BibleDB(QtCore.QObject, Manager, RegistryProperties): :param text_list: A dict of the verses to be inserted. The key is the verse number, and the value is the verse text. """ - log.debug('BibleDBcreate_chapter("%s", "%s")', book_id, chapter) + log.debug('BibleDBcreate_chapter("%s", "%s")' % (book_id, chapter)) # Text list has book and chapter as first two elements of the array. for verse_number, verse_text in text_list.items(): verse = Verse.populate( @@ -267,7 +267,7 @@ class BibleDB(QtCore.QObject, Manager, RegistryProperties): """ if not isinstance(value, str): value = str(value) - log.debug('BibleDB.save_meta("%s/%s")', key, value) + log.debug('BibleDB.save_meta("%s/%s")' % (key, value)) meta = self.get_object(BibleMeta, key) if meta: meta.value = value @@ -281,7 +281,7 @@ class BibleDB(QtCore.QObject, Manager, RegistryProperties): :param book: The name of the book to return. """ - log.debug('BibleDB.get_book("%s")', book) + log.debug('BibleDB.get_book("%s")' % book) return self.get_object_filtered(Book, Book.name.like(book + '%')) def get_books(self): @@ -292,17 +292,17 @@ class BibleDB(QtCore.QObject, Manager, RegistryProperties): log.debug('BibleDB.get_books()') return self.get_all_objects(Book, order_by_ref=Book.id) - def get_book_by_book_ref_id(self, id): + def get_book_by_book_ref_id(self, ref_id): """ Return a book object from the database. - :param id: The reference id of the book to return. + :param ref_id: The reference id of the book to return. """ - log.debug('BibleDB.get_book_by_book_ref_id("%s")', id) - return self.get_object_filtered(Book, Book.book_reference_id.like(id)) + log.debug('BibleDB.get_book_by_book_ref_id("%s")' % ref_id) + return self.get_object_filtered(Book, Book.book_reference_id.like(ref_id)) def get_book_ref_id_by_name(self, book, maxbooks, language_id=None): - log.debug('BibleDB.get_book_ref_id_by_name:("%s", "%s")', book, language_id) + log.debug('BibleDB.get_book_ref_id_by_name:("%s", "%s")' % (book, language_id)) book_id = None if BiblesResourcesDB.get_book(book, True): book_temp = BiblesResourcesDB.get_book(book, True) @@ -328,7 +328,7 @@ class BibleDB(QtCore.QObject, Manager, RegistryProperties): :param book: The name of the book, according to the selected language. :param language_selection: The language selection the user has chosen in the settings section of the Bible. """ - log.debug('get_book_ref_id_by_localised_name("%s", "%s")', book, language_selection) + log.debug('get_book_ref_id_by_localised_name("%s", "%s")' % (book, language_selection)) from openlp.plugins.bibles.lib import LanguageSelection, BibleStrings book_names = BibleStrings().BookNames # escape reserved characters @@ -376,14 +376,14 @@ class BibleDB(QtCore.QObject, Manager, RegistryProperties): [(u'35', 1, 1, 1), (u'35', 2, 2, 3)] :param show_error: """ - log.debug('BibleDB.get_verses("%s")', reference_list) + log.debug('BibleDB.get_verses("%s")' % reference_list) verse_list = [] book_error = False for book_id, chapter, start_verse, end_verse in reference_list: db_book = self.get_book_by_book_ref_id(book_id) if db_book: book_id = db_book.book_reference_id - log.debug('Book name corrected to "%s"', db_book.name) + log.debug('Book name corrected to "%s"' % db_book.name) if end_verse == -1: end_verse = self.get_verse_count(book_id, chapter) verses = self.session.query(Verse) \ @@ -395,7 +395,7 @@ class BibleDB(QtCore.QObject, Manager, RegistryProperties): .all() verse_list.extend(verses) else: - log.debug('OpenLP failed to find book with id "%s"', book_id) + log.debug('OpenLP failed to find book with id "%s"' % book_id) book_error = True if book_error and show_error: critical_error_message_box( @@ -414,7 +414,7 @@ class BibleDB(QtCore.QObject, Manager, RegistryProperties): contains spaces, it will split apart and AND'd on the list of values. """ - log.debug('BibleDB.verse_search("%s")', text) + log.debug('BibleDB.verse_search("%s")' % text) verses = self.session.query(Verse) if text.find(',') > -1: keywords = ['%%%s%%' % keyword.strip() for keyword in text.split(',')] @@ -433,7 +433,7 @@ class BibleDB(QtCore.QObject, Manager, RegistryProperties): :param book: The book object to get the chapter count for. """ - log.debug('BibleDB.get_chapter_count("%s")', book.name) + log.debug('BibleDB.get_chapter_count("%s")' % book.name) count = self.session.query(func.max(Verse.chapter)).join(Book).filter( Book.book_reference_id == book.book_reference_id).scalar() if not count: @@ -447,7 +447,7 @@ class BibleDB(QtCore.QObject, Manager, RegistryProperties): :param book_ref_id: The book reference id. :param chapter: The chapter to get the verse count for. """ - log.debug('BibleDB.get_verse_count("%s", "%s")', book_ref_id, chapter) + log.debug('BibleDB.get_verse_count("%s", "%s")' % (book_ref_id, chapter)) count = self.session.query(func.max(Verse.verse)).join(Book) \ .filter(Book.book_reference_id == book_ref_id) \ .filter(Verse.chapter == chapter) \ @@ -563,7 +563,7 @@ class BiblesResourcesDB(QtCore.QObject, Manager): :param name: The name or abbreviation of the book. :param lower: True if the comparison should be only lowercase """ - log.debug('BiblesResourcesDB.get_book("%s")', name) + log.debug('BiblesResourcesDB.get_book("%s")' % name) if not isinstance(name, str): name = str(name) if lower: @@ -592,7 +592,7 @@ class BiblesResourcesDB(QtCore.QObject, Manager): :param string: The string to search for in the book names or abbreviations. """ - log.debug('BiblesResourcesDB.get_book_like("%s")', string) + log.debug('BiblesResourcesDB.get_book_like("%s")' % string) if not isinstance(string, str): name = str(string) books = BiblesResourcesDB.run_sql( @@ -611,17 +611,17 @@ class BiblesResourcesDB(QtCore.QObject, Manager): return None @staticmethod - def get_book_by_id(id): + def get_book_by_id(book_id): """ Return a book by id. - :param id: The id of the book. + :param book_id: The id of the book. """ - log.debug('BiblesResourcesDB.get_book_by_id("%s")', id) - if not isinstance(id, int): - id = int(id) + log.debug('BiblesResourcesDB.get_book_by_id("%s")' % book_id) + if not isinstance(book_id, int): + book_id = int(book_id) books = BiblesResourcesDB.run_sql( - 'SELECT id, testament_id, name, abbreviation, chapters FROM book_reference WHERE id = ?', (id, )) + 'SELECT id, testament_id, name, abbreviation, chapters FROM book_reference WHERE id = ?', (book_id, )) if books: return { 'id': books[0][0], @@ -641,7 +641,7 @@ class BiblesResourcesDB(QtCore.QObject, Manager): :param book_ref_id: The id of a book. :param chapter: The chapter number. """ - log.debug('BiblesResourcesDB.get_chapter("%s", "%s")', book_ref_id, chapter) + log.debug('BiblesResourcesDB.get_chapter("%s", "%s")' % (book_ref_id, chapter)) if not isinstance(chapter, int): chapter = int(chapter) chapters = BiblesResourcesDB.run_sql( @@ -664,7 +664,7 @@ class BiblesResourcesDB(QtCore.QObject, Manager): :param book_ref_id: The id of the book. """ - log.debug('BiblesResourcesDB.get_chapter_count("%s")', book_ref_id) + log.debug('BiblesResourcesDB.get_chapter_count("%s")' % book_ref_id) details = BiblesResourcesDB.get_book_by_id(book_ref_id) if details: return details['chapters'] @@ -678,7 +678,7 @@ class BiblesResourcesDB(QtCore.QObject, Manager): :param book_ref_id: The id of the book. :param chapter: The number of the chapter. """ - log.debug('BiblesResourcesDB.get_verse_count("%s", "%s")', book_ref_id, chapter) + log.debug('BiblesResourcesDB.get_verse_count("%s", "%s")' % (book_ref_id, chapter)) details = BiblesResourcesDB.get_chapter(book_ref_id, chapter) if details: return details['verse_count'] @@ -691,7 +691,7 @@ class BiblesResourcesDB(QtCore.QObject, Manager): :param source: The name or abbreviation of the book. """ - log.debug('BiblesResourcesDB.get_download_source("%s")', source) + log.debug('BiblesResourcesDB.get_download_source("%s")' % source) if not isinstance(source, str): source = str(source) source = source.title() @@ -712,7 +712,7 @@ class BiblesResourcesDB(QtCore.QObject, Manager): :param source: The source of the web_bible. """ - log.debug('BiblesResourcesDB.get_webbibles("%s")', source) + log.debug('BiblesResourcesDB.get_webbibles("%s")' % source) if not isinstance(source, str): source = str(source) source = BiblesResourcesDB.get_download_source(source) @@ -737,7 +737,7 @@ class BiblesResourcesDB(QtCore.QObject, Manager): :param abbreviation: The abbreviation of the web_bible. :param source: The source of the web_bible. """ - log.debug('BiblesResourcesDB.get_webbibles("%s", "%s")', abbreviation, source) + log.debug('BiblesResourcesDB.get_webbibles("%s", "%s")' % (abbreviation, source)) if not isinstance(abbreviation, str): abbreviation = str(abbreviation) if not isinstance(source, str): @@ -765,7 +765,7 @@ class BiblesResourcesDB(QtCore.QObject, Manager): :param name: The name to search the id. :param language_id: The language_id for which language should be searched """ - log.debug('BiblesResourcesDB.get_alternative_book_name("%s", "%s")', name, language_id) + log.debug('BiblesResourcesDB.get_alternative_book_name("%s", "%s")' % (name, language_id)) if language_id: books = BiblesResourcesDB.run_sql( 'SELECT book_reference_id, name FROM alternative_book_names WHERE language_id = ? ORDER BY id', @@ -784,7 +784,7 @@ class BiblesResourcesDB(QtCore.QObject, Manager): :param name: The name or abbreviation of the language. """ - log.debug('BiblesResourcesDB.get_language("%s")', name) + log.debug('BiblesResourcesDB.get_language("%s")' % name) if not isinstance(name, str): name = str(name) language = BiblesResourcesDB.run_sql( @@ -880,7 +880,7 @@ class AlternativeBookNamesDB(QtCore.QObject, Manager): :param name: The name to search the id. :param language_id: The language_id for which language should be searched """ - log.debug('AlternativeBookNamesDB.get_book_reference_id("%s", "%s")', name, language_id) + log.debug('AlternativeBookNamesDB.get_book_reference_id("%s", "%s")' % (name, language_id)) if language_id: books = AlternativeBookNamesDB.run_sql( 'SELECT book_reference_id, name FROM alternative_book_names WHERE language_id = ?', (language_id, )) @@ -901,8 +901,8 @@ class AlternativeBookNamesDB(QtCore.QObject, Manager): :param book_reference_id: The book_reference_id of the book. :param language_id: The language to which the alternative book name belong. """ - log.debug('AlternativeBookNamesDB.create_alternative_book_name("%s", "%s", "%s")', - name, book_reference_id, language_id) + log.debug('AlternativeBookNamesDB.create_alternative_book_name("%s", "%s", "%s")' % + (name, book_reference_id, language_id)) return AlternativeBookNamesDB.run_sql( 'INSERT INTO alternative_book_names(book_reference_id, language_id, name) ' 'VALUES (?, ?, ?)', (book_reference_id, language_id, name), True) From 25c3503397ab3cdb3bc7c778405717e910fef462 Mon Sep 17 00:00:00 2001 From: Jonathan Springer Date: Tue, 8 Apr 2014 16:45:40 -0400 Subject: [PATCH 104/155] Add test to check for proper handling of service files passed in through the command line. --- .../openlp_core_ui/test_mainwindow.py | 97 ++++++++++++++++++ tests/resources/service/test.osz | Bin 0 -> 8082 bytes 2 files changed, 97 insertions(+) create mode 100644 tests/functional/openlp_core_ui/test_mainwindow.py create mode 100644 tests/resources/service/test.osz diff --git a/tests/functional/openlp_core_ui/test_mainwindow.py b/tests/functional/openlp_core_ui/test_mainwindow.py new file mode 100644 index 000000000..0b17828b9 --- /dev/null +++ b/tests/functional/openlp_core_ui/test_mainwindow.py @@ -0,0 +1,97 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2014 Raoul Snyman # +# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan # +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # +# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, # +# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, # +# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, # +# Frode Woldsund, Martin Zibricky, Patrick Zimmermann # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### +""" +Package to test openlp.core.ui.mainwindow package. +""" +import os + +from unittest import TestCase + +from openlp.core.ui.mainwindow import MainWindow +from openlp.core.common.registry import Registry +from tests.utils.constants import TEST_RESOURCES_PATH +from tests.helpers.testmixin import TestMixin +from tests.functional import MagicMock, patch + + +class TestMainWindow(TestCase, TestMixin): + + def setUp(self): + Registry.create() + self.registry = Registry() + self.get_application() + # Mock cursor busy/normal methods. + self.app.set_busy_cursor = MagicMock() + self.app.set_normal_cursor = MagicMock() + self.app.args = [] + Registry().register('application', self.app) + # Mock classes and methods used by mainwindow. + with patch('openlp.core.ui.mainwindow.SettingsForm') as mocked_settings_form, \ + patch('openlp.core.ui.mainwindow.ImageManager') as mocked_image_manager, \ + patch('openlp.core.ui.mainwindow.LiveController') as mocked_live_controller, \ + patch('openlp.core.ui.mainwindow.PreviewController') as mocked_preview_controller, \ + patch('openlp.core.ui.mainwindow.OpenLPDockWidget') as mocked_dock_widget, \ + patch('openlp.core.ui.mainwindow.QtGui.QToolBox') as mocked_q_tool_box_class, \ + patch('openlp.core.ui.mainwindow.QtGui.QMainWindow.addDockWidget') as mocked_add_dock_method, \ + patch('openlp.core.ui.mainwindow.ThemeManager') as mocked_theme_manager, \ + patch('openlp.core.ui.mainwindow.Renderer') as mocked_renderer: + self.main_window = MainWindow() + + def tearDown(self): + del self.main_window + + def cmd_line_file_test(self): + """ + Test that passing a service file from the command line loads the service. + """ + # GIVEN a service as an argument to openlp + service = os.path.join(TEST_RESOURCES_PATH, 'service', 'test.osz') + self.main_window.arguments = [service] + with patch('openlp.core.ui.servicemanager.ServiceManager.load_file') as mocked_load_path: + + # WHEN the argument is processed + self.main_window.open_cmd_line_files() + + # THEN the service from the arguments is loaded + mocked_load_path.assert_called_with(service), 'load_path should have been called with the service\'s path' + + def cmd_line_arg_test(self): + """ + Test that passing a non service file does nothing. + """ + # GIVEN a non service file as an argument to openlp + service = os.path.join('openlp.py') + self.main_window.arguments = [service] + with patch('openlp.core.ui.servicemanager.ServiceManager.load_file') as mocked_load_path: + + # WHEN the argument is processed + self.main_window.open_cmd_line_files() + + # THEN the file should not be opened + assert not mocked_load_path.called, 'load_path should not have been called' diff --git a/tests/resources/service/test.osz b/tests/resources/service/test.osz new file mode 100644 index 0000000000000000000000000000000000000000..a289c07750289ed1daf256935486aee320d09fc9 GIT binary patch literal 8082 zcmeHMVQ(9`5l!yeqCeoim(_>n3dol2-Ba+Fx7db{FN&ntJS6SWaCRnU{(%e+$JW#xS6^+hk& zL2cAFXr0L1tE;R2O-HmB>?oB%;uj3zpt zm7__E)XvO?(`l**@RwB}7lh$NPqW||;L;&_oOsVJK4yTPHt|RoUgbcLup6@npnTvj z#9ZwPn9-UU3oi@xVrX7vKsbnRl!tq?5f%nbm>2EMv3039m1Bt+$G!97v<`zlqiw7} z8)jk+bvn%`0upO}sPK$|S7Iy!AYBEw9hp1e?%V_J0f!|Z;|0yEqfo`@sk+p;zX>)> zi%n%n81`VvgyO#PX#1!{F6TlA4hA%46dl+R{BsH$@CU>QmN=*V5*B;T5r)nLE#{Cb zZeI#uj{ak7FT~Vo911V$Ljz2J>MR~JL}F?J3ed`*Xt#9eIK~0y z2ctw`3p&rVN`^E?tSg5`L~>vF5@~92%JG#KlF3Wt41{)qZSe$p9QLeUct8>u&*MpD zZ#DD*M|zsY-JzVq*(}!+3dJ|proN9uZ{V{pu=jcDl<8w)-2P{58{Eg0;5bbX0lvWE zb~*NuY50^PE%FpErO`g40K@Fgu#1UChD7F}bLSNDkU_3l-GMdWyR!{XdRN~ewIB#n zM}&(RzC?@IcfLo`LLITQ0@)}OoMZ$6Thj=#11I`_);cn zu(P%G^?vL(G&P* z)?NRy{~kMm)Od`?%IyteGwiSJ2Fv5;49)%?p$qN+IGFe4Hcb;J3o~4g*7wG4fY~=P zJGZY*?rhz~@3m1A#m5obSe`A~i9R}M;CgOsq5Hi-L~w7w;NHfVLm^tU(-jdpkJZlQ zY|=^RY}Lx>Y|%*S+}g=wS}_Vp>7&AV7Tt{0o% zOx$eJ&fRR%OyAs$5N~1dg-HX6-@$yaNW#oU`^36sUQIQV9}$$NM;?V^k`W)$w_1dK zx)Bu6UBn%?#Mg95eEps})-*tVR+xh7vmTgQ%Tcb(G|))I(TObOSVJ9xuJGu5r%$Z@ zRbM>qi?8wfVHk$cMxrT{W)Tb&C*P3NG+Vnd8~5$$U(1zt?|mZMpD&^{q78GXZ?qlz{1Tyl0hLDs$4}$#R))0 zshL7?ywR76_5i95z_1ol7YDKt@4H+it5|V04YUA8sGBq^WQt19$PNMf}kwwDs5%h%RbBw!fy!9lA-Gk~I0= z&M|$jZfWm!Fz(>lq`l*wq4bK8dJm=_vOc3QfoeenL6c8d!{rbw!1F}8iAn<06Bdc+ z0kRNesC^6E`lLhimc#N8FaR08bmBW4LjK?~itZzDpH&oyRhc8` zfd(!&pe`plU7*lV*pD2SF_=#(>0q3Z)Mng)+~S1b?Bq0SIaPE~5aTt6Z%lM)@wb8% z?6%ZMPM46m11?lIWx@Z;z5h|USErtji1GI~J9*66_`T}ZdWX`Oxp?KQAI}#G??*_z z)=a3P2x;aAE_+%jf-Tly3AXDbirT7;E7)WWx?sCD#<)|nt?K0^E!Up@Y7jf=fG5^yue13>>8*cPNtO|*rb^tI^esd-I^0zEK+!|*6hy~?F7IkT^zthYtR7O zbu$5*b&~;`HS)n7EdN9Dq5bgR)(*>==f5Q#+Os2(-O4N$rT&s^==5;D>f)y$89LWQ z5^q(H{QG+0ldrCJcDFwb@cA1v_T@7=4ODwl^_Wt9L*I~tXQ=fhib;oRb&68U2vC9K zD{PtTT&ZXLMu)l<^*fzO{bq=|vXfof(`#|mKK89zhjiUfuhyy6=(P~-jrABsA+7CR zxzBob4Urxd`t}ow=VopGjm?a}?$U4vr{DI)Nj1)OB3@g1->@RVZ(apJcdn(OZ&uPM zqNB-=SJ8`gh%d2<-_mNjPQZ63?;d{k#pliM(dqp<{rT%d`hW3O{p8)xe?i|L;_vV9 Ld5E{}4<7szH}{|L literal 0 HcmV?d00001 From bfc086749b899bbe7a34a9f461c6fdb0fec34b40 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Tue, 8 Apr 2014 23:03:04 +0200 Subject: [PATCH 105/155] Test --- tests/functional/openlp_core_lib/test_ui.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/functional/openlp_core_lib/test_ui.py b/tests/functional/openlp_core_lib/test_ui.py index babc94a81..53bc8a770 100644 --- a/tests/functional/openlp_core_lib/test_ui.py +++ b/tests/functional/openlp_core_lib/test_ui.py @@ -81,3 +81,19 @@ class TestUi(TestCase): self.assertIsInstance(btnbox, QtGui.QDialogButtonBox) self.assertEqual(1, len(btnbox.buttons())) self.assertEqual(QtGui.QDialogButtonBox.HelpRole, btnbox.buttonRole(btnbox.buttons()[0])) + + def test_set_case_insensitive_completer(self): + """ + Test setting a case insensitive text completer for a widget + """ + # GIVEN: A ComboBox and a list of strings + combo = QtGui.QComboBox() + suggestions = ['hello', 'world', 'and', 'others'] + + # WHEN: We set the autocompleter + set_case_insensitive_completer(suggestions, combo) + + # THEN: The Combobox should have the autocompleter. + self.assertIsInstance(combo.completer(), QtGui.QCompleter) + self.assertEqual(QtCore.Qt.CaseInsensitive, combo.completer().caseSensitivity()) + self.assertEqual(suggestions, combo.completer().completionModel().data()) From 11884adbed9a838d178f7752b6aa254dfe241085 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Tue, 8 Apr 2014 23:07:43 +0200 Subject: [PATCH 106/155] Disable two tests for the moment --- tests/functional/openlp_core_lib/test_ui.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/functional/openlp_core_lib/test_ui.py b/tests/functional/openlp_core_lib/test_ui.py index 53bc8a770..0d7315cda 100644 --- a/tests/functional/openlp_core_lib/test_ui.py +++ b/tests/functional/openlp_core_lib/test_ui.py @@ -95,5 +95,5 @@ class TestUi(TestCase): # THEN: The Combobox should have the autocompleter. self.assertIsInstance(combo.completer(), QtGui.QCompleter) - self.assertEqual(QtCore.Qt.CaseInsensitive, combo.completer().caseSensitivity()) - self.assertEqual(suggestions, combo.completer().completionModel().data()) + #self.assertEqual(QtCore.Qt.CaseInsensitive, combo.completer().caseSensitivity()) + #self.assertEqual(suggestions, combo.completer().completionModel().data()) From 31649b0a8e035a4388cab6570801089301051926 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Tue, 8 Apr 2014 23:20:51 +0200 Subject: [PATCH 107/155] Better test --- tests/functional/openlp_core_lib/test_ui.py | 23 +++++++++++---------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/tests/functional/openlp_core_lib/test_ui.py b/tests/functional/openlp_core_lib/test_ui.py index 0d7315cda..03cdd5bb1 100644 --- a/tests/functional/openlp_core_lib/test_ui.py +++ b/tests/functional/openlp_core_lib/test_ui.py @@ -82,18 +82,19 @@ class TestUi(TestCase): self.assertEqual(1, len(btnbox.buttons())) self.assertEqual(QtGui.QDialogButtonBox.HelpRole, btnbox.buttonRole(btnbox.buttons()[0])) - def test_set_case_insensitive_completer(self): + def test_create_valign_selection_widgets(self): """ - Test setting a case insensitive text completer for a widget + Test creating a combo box for valign selection """ - # GIVEN: A ComboBox and a list of strings - combo = QtGui.QComboBox() - suggestions = ['hello', 'world', 'and', 'others'] + # GIVEN: A dialog + dialog = QtGui.QDialog() - # WHEN: We set the autocompleter - set_case_insensitive_completer(suggestions, combo) + # WHEN: We create the widgets + label, combo = create_valign_selection_widgets(dialog) - # THEN: The Combobox should have the autocompleter. - self.assertIsInstance(combo.completer(), QtGui.QCompleter) - #self.assertEqual(QtCore.Qt.CaseInsensitive, combo.completer().caseSensitivity()) - #self.assertEqual(suggestions, combo.completer().completionModel().data()) + # THEN: We should get a label and a combobox. + self.assertEqual(translate('OpenLP.Ui', '&Vertical Align:'), label.text()) + self.assertIsInstance(combo, QtGui.QComboBox) + self.assertEqual(combo, label.buddy()) + for text in [UiStrings().Top, UiStrings().Middle, UiStrings().Bottom]: + self.assertTrue(combo.findText(text) >= 0) From a82a13d2ed42677357ba14c25af9ca6a5f36cabf Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 9 Apr 2014 12:48:12 +0200 Subject: [PATCH 108/155] Fixes for PDF DPI Detection --- openlp/core/ui/slidecontroller.py | 4 ++-- openlp/plugins/presentations/lib/pdfcontroller.py | 14 +++++++------- .../presentations/lib/presentationcontroller.py | 6 ++++-- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index 7faf10ca2..0eef46b4a 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -495,14 +495,14 @@ class SlideController(DisplayController, RegistryProperties): self.on_theme_display(False) self.on_hide_display(False) - def service_previous(self): + def service_previous(self, field=None): """ Live event to select the previous service item from the service manager. """ self.keypress_queue.append(ServiceItemAction.Previous) self._process_queue() - def service_next(self): + def service_next(self, field=None): """ Live event to select the next service item from the service manager. """ diff --git a/openlp/plugins/presentations/lib/pdfcontroller.py b/openlp/plugins/presentations/lib/pdfcontroller.py index 597b7d78b..3dc75ba0b 100644 --- a/openlp/plugins/presentations/lib/pdfcontroller.py +++ b/openlp/plugins/presentations/lib/pdfcontroller.py @@ -204,19 +204,19 @@ class PdfDocument(PresentationDocument): log.debug(' '.join(e.cmd)) log.debug(e.output) # Extract the pdf resolution from output, the format is " Size: x: , y: " - width = 0 - height = 0 + width = 0.0 + height = 0.0 for line in runlog.splitlines(): try: - width = int(re.search('.*Size: x: (\d+\.?\d*), y: \d+.*', line.decode()).group(1)) - height = int(re.search('.*Size: x: \d+\.?\d*, y: (\d+\.?\d*).*', line.decode()).group(1)) + width = float(re.search('.*Size: x: (\d+\.?\d*), y: \d+.*', line.decode()).group(1)) + height = float(re.search('.*Size: x: \d+\.?\d*, y: (\d+\.?\d*).*', line.decode()).group(1)) break except AttributeError: - pass + continue # Calculate the ratio from pdf to screen if width > 0 and height > 0: - width_ratio = size.right() / float(width) - height_ratio = size.bottom() / float(height) + width_ratio = size.right() / width + height_ratio = size.bottom() / height # return the resolution that should be used. 72 is default. if width_ratio > height_ratio: return int(height_ratio * 72) diff --git a/openlp/plugins/presentations/lib/presentationcontroller.py b/openlp/plugins/presentations/lib/presentationcontroller.py index fd636b468..6c8d7fb8c 100644 --- a/openlp/plugins/presentations/lib/presentationcontroller.py +++ b/openlp/plugins/presentations/lib/presentationcontroller.py @@ -122,8 +122,10 @@ class PresentationDocument(object): a file, e.g. thumbnails """ try: - shutil.rmtree(self.get_thumbnail_folder()) - shutil.rmtree(self.get_temp_folder()) + if os.path.exists(self.get_thumbnail_folder()): + shutil.rmtree(self.get_thumbnail_folder()) + if os.path.exists(self.get_temp_folder()): + shutil.rmtree(self.get_temp_folder()) except OSError: log.exception('Failed to delete presentation controller files') From ed250c400d30620b4ab200f19a4b501ff6f7ec30 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 9 Apr 2014 13:10:18 +0200 Subject: [PATCH 109/155] Test --- tests/functional/openlp_core_lib/test_ui.py | 25 +++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/functional/openlp_core_lib/test_ui.py b/tests/functional/openlp_core_lib/test_ui.py index babc94a81..57e70c8df 100644 --- a/tests/functional/openlp_core_lib/test_ui.py +++ b/tests/functional/openlp_core_lib/test_ui.py @@ -81,3 +81,28 @@ class TestUi(TestCase): self.assertIsInstance(btnbox, QtGui.QDialogButtonBox) self.assertEqual(1, len(btnbox.buttons())) self.assertEqual(QtGui.QDialogButtonBox.HelpRole, btnbox.buttonRole(btnbox.buttons()[0])) + + def test_create_action(self): + """ + Test creating an action + """ + # GIVEN: A dialog + dialog = QtGui.QDialog() + + # WHEN: We create an action + action = create_action(dialog, 'my_action') + + # THEN: We should get a QAction + self.assertIsInstance(action, QtGui.QAction) + self.assertEqual('my_action', action.objectName()) + + # WHEN: We create an action with some properties + action = create_action(dialog, 'my_action', text='my text', icon=':/wizards/wizard_firsttime.bmp', + tooltip='my tooltip', statustip='my statustip') + + # THEN: These properties should be set + self.assertIsInstance(action, QtGui.QAction) + self.assertEqual('my text', action.text()) + self.assertIsInstance(action.icon(), QtGui.QIcon) + self.assertEqual('my tooltip', action.toolTip()) + self.assertEqual('my statustip', action.statusTip()) From 04b1d0ec30aaf02839f9e764031cb0af1b0e09e7 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Thu, 10 Apr 2014 09:33:25 +0200 Subject: [PATCH 110/155] Fix songbook search --- openlp/plugins/songs/lib/mediaitem.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index ad981135f..c735f0acf 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -192,7 +192,7 @@ class SongMediaItem(MediaManagerItem): song_number = False if not search_results: search_keywords = search_keywords.rpartition(' ') - search_string = '%' + search_keywords + '%' + search_string = '%' + search_keywords[0] + '%' search_results = self.plugin.manager.get_all_objects(Book, Book.name.like(search_string), Book.name.asc()) song_number = re.sub(r'[^0-9]', '', search_keywords[2]) From 32f6005c98cb3d1d8dcd63411298f3b6c5d99f3a Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Thu, 10 Apr 2014 09:50:30 +0200 Subject: [PATCH 111/155] Test for OpenLyrics Importer --- .../songs/test_openlyricsimport.py | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 tests/functional/openlp_plugins/songs/test_openlyricsimport.py diff --git a/tests/functional/openlp_plugins/songs/test_openlyricsimport.py b/tests/functional/openlp_plugins/songs/test_openlyricsimport.py new file mode 100644 index 000000000..a05f08d61 --- /dev/null +++ b/tests/functional/openlp_plugins/songs/test_openlyricsimport.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2014 Raoul Snyman # +# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan # +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # +# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, # +# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, # +# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, # +# Frode Woldsund, Martin Zibricky, Patrick Zimmermann # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### +""" +This module contains tests for the OpenLyrics song importer. +""" + +from unittest import TestCase + +from tests.functional import MagicMock, patch +from openlp.plugins.songs.lib.openlyricsimport import OpenLyricsImport +from openlp.plugins.songs.lib.songimport import SongImport + + +class TestSongBeamerImport(TestCase): + """ + Test the functions in the :mod:`openlyricsimport` module. + """ + def create_importer_test(self): + """ + Test creating an instance of the OpenLyrics file importer + """ + # GIVEN: A mocked out SongImport class, and a mocked out "manager" + with patch('openlp.plugins.songs.lib.songbeamerimport.SongImport'): + mocked_manager = MagicMock() + + # WHEN: An importer object is created + importer = OpenLyricsImport(mocked_manager, filenames=[]) + + # THEN: The importer should be an instance of SongImport + self.assertIsInstance(importer, SongImport) From 9da5d290cb84140a1807a36bc37f35623e4b3423 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Thu, 10 Apr 2014 18:18:25 +0200 Subject: [PATCH 112/155] Typo --- tests/functional/openlp_plugins/songs/test_openlyricsimport.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/openlp_plugins/songs/test_openlyricsimport.py b/tests/functional/openlp_plugins/songs/test_openlyricsimport.py index a05f08d61..74a291152 100644 --- a/tests/functional/openlp_plugins/songs/test_openlyricsimport.py +++ b/tests/functional/openlp_plugins/songs/test_openlyricsimport.py @@ -37,7 +37,7 @@ from openlp.plugins.songs.lib.openlyricsimport import OpenLyricsImport from openlp.plugins.songs.lib.songimport import SongImport -class TestSongBeamerImport(TestCase): +class TestOpenLyricsImport(TestCase): """ Test the functions in the :mod:`openlyricsimport` module. """ From 056a7b72ed06eec781541a5783b30e0835d27741 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Thu, 10 Apr 2014 21:45:49 +0200 Subject: [PATCH 113/155] Fix bug 1154467 --- openlp/plugins/bibles/lib/db.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/openlp/plugins/bibles/lib/db.py b/openlp/plugins/bibles/lib/db.py index 9ffa5d53e..9ef17bdc7 100644 --- a/openlp/plugins/bibles/lib/db.py +++ b/openlp/plugins/bibles/lib/db.py @@ -32,9 +32,11 @@ import logging import os import re import sqlite3 +import time from PyQt4 import QtCore from sqlalchemy import Column, ForeignKey, Table, or_, types, func +from sqlalchemy.exc import OperationalError from sqlalchemy.orm import class_mapper, mapper, relation from sqlalchemy.orm.exc import UnmappedClassError @@ -235,7 +237,12 @@ class BibleDB(QtCore.QObject, Manager, RegistryProperties): text=verse_text ) self.session.add(verse) - self.session.commit() + try: + self.session.commit() + except OperationalError: + # Wait 10ms and try again. + time.sleep(0.01) + self.session.commit() def create_verse(self, book_id, chapter, verse, text): """ From 93fe4db118adddb2cf1f1fd4807cbf85d38f0d1b Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Thu, 10 Apr 2014 21:46:32 +0200 Subject: [PATCH 114/155] Bug nr --- openlp/plugins/bibles/lib/db.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/plugins/bibles/lib/db.py b/openlp/plugins/bibles/lib/db.py index 9ef17bdc7..0234132b1 100644 --- a/openlp/plugins/bibles/lib/db.py +++ b/openlp/plugins/bibles/lib/db.py @@ -240,7 +240,7 @@ class BibleDB(QtCore.QObject, Manager, RegistryProperties): try: self.session.commit() except OperationalError: - # Wait 10ms and try again. + # Wait 10ms and try again (lp#1154467) time.sleep(0.01) self.session.commit() From 93cd8269f82d44fd73c7f6ce515f3b92818002a0 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Thu, 10 Apr 2014 21:50:30 +0200 Subject: [PATCH 115/155] Test case --- .../songs/test_zionworximport.py | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 tests/functional/openlp_plugins/songs/test_zionworximport.py diff --git a/tests/functional/openlp_plugins/songs/test_zionworximport.py b/tests/functional/openlp_plugins/songs/test_zionworximport.py new file mode 100644 index 000000000..2edc071c7 --- /dev/null +++ b/tests/functional/openlp_plugins/songs/test_zionworximport.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2014 Raoul Snyman # +# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan # +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # +# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, # +# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, # +# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, # +# Frode Woldsund, Martin Zibricky, Patrick Zimmermann # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### +""" +This module contains tests for the ZionWorx song importer. +""" + +from unittest import TestCase + +from tests.functional import MagicMock, patch +from openlp.plugins.songs.lib.zionworximport import ZionWorxImport +from openlp.plugins.songs.lib.songimport import SongImport + + +class TestZionWorxImport(TestCase): + """ + Test the functions in the :mod:`zionworximport` module. + """ + def create_importer_test(self): + """ + Test creating an instance of the ZionWorx file importer + """ + # GIVEN: A mocked out SongImport class, and a mocked out "manager" + with patch('openlp.plugins.songs.lib.songbeamerimport.SongImport'): + mocked_manager = MagicMock() + + # WHEN: An importer object is created + importer = ZionWorxImport(mocked_manager, filenames=[]) + + # THEN: The importer should be an instance of SongImport + self.assertIsInstance(importer, SongImport) From 321fed97e923bc187bfbac43c7cdf2639caaa119 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sat, 12 Apr 2014 18:52:29 +0200 Subject: [PATCH 116/155] more output for the jenkins script --- scripts/jenkins_script.py | 49 +++++----- .../openlp_core_lib/test_image_manager.py | 88 ++++++++++++++++++ tests/resources/church2.jpg | Bin 0 -> 218545 bytes tests/resources/church3.jpg | Bin 0 -> 218545 bytes tests/resources/church4.jpg | Bin 0 -> 218545 bytes 5 files changed, 112 insertions(+), 25 deletions(-) create mode 100644 tests/resources/church2.jpg create mode 100644 tests/resources/church3.jpg create mode 100644 tests/resources/church4.jpg diff --git a/scripts/jenkins_script.py b/scripts/jenkins_script.py index c2920bed1..c42adf09e 100644 --- a/scripts/jenkins_script.py +++ b/scripts/jenkins_script.py @@ -51,6 +51,8 @@ from jenkins import Jenkins JENKINS_URL = 'http://ci.openlp.org/' REPO_REGEX = r'(.*/+)(~.*)' +# Allows us to black list token. So when we change the token, we can display a proper message to the user. +OLD_TOKENS = [] class OpenLPJobs(object): @@ -62,7 +64,6 @@ class OpenLPJobs(object): Branch_Interface = 'Branch-03-Interface-Tests' Branch_Windows = 'Branch-04-Windows_Tests' Branch_PEP = 'Branch-05-Code-Analysis' - PEP_TEST = "sdafajsdklfj lajsldfk jlasdkjf lkajdf lkasjdlfkjalskdjflkajflkadsjkfl jasdlkfj laskdjflka sjdlkfjlaksdjflksajdf" Jobs = [Branch_Pull, Branch_Functional, Branch_Interface, Branch_Windows, Branch_PEP] @@ -82,14 +83,25 @@ class JenkinsTrigger(object): """ Ask our jenkins server to build the "Branch-01-Pull" job. """ - self.jenkins_instance.job(OpenLPJobs.Branch_Pull).build({'BRANCH_NAME': self.repo_name}, token=self.token) + bzr = Popen(('bzr', 'whoami'), stdout=PIPE, stderr=PIPE) + raw_output, error = bzr.communicate() + # We just want the name (not the email). + name = ' '.join(raw_output.decode().split()[:-1]) + cause = 'Build triggered by %s (%s)' % (name, self.repo_name) + self.jenkins_instance.job(OpenLPJobs.Branch_Pull).build( + {'BRANCH_NAME': self.repo_name, 'cause': cause}, token=self.token) def print_output(self): """ Print the status information of the build tirggered. """ - print("Add this to your merge proposal:") - print("--------------------------------") + print('Add this to your merge proposal:') + print('--------------------------------') + bzr = Popen(('bzr', 'revno'), stdout=PIPE, stderr=PIPE) + raw_output, error = bzr.communicate() + revno = raw_output.decode().strip() + print('%s (revision %s)' % (get_repo_name(), revno)) + for job in OpenLPJobs.Jobs: self.__print_build_info(job) @@ -116,10 +128,6 @@ class JenkinsTrigger(object): result_string = build.info['result'] url = build.info['url'] print('[%s] %s' % (result_string, url)) - # On failure open the browser. - #if result_string == "FAILURE": - # url += 'console' - # Popen(('xdg-open', url), stderr=PIPE) def get_repo_name(): @@ -150,41 +158,32 @@ def get_repo_name(): if match: repo_name = 'lp:%s' % match.group(2) break - repo_name = repo_name.strip('/') - - # Did we find the branch name? - if not repo_name: - for line in output_list: - # Check if the branch was pushed. - if 'Shared repository with trees (format: 2a)' in line: - print('Not a branch. cd to a branch.') - return - print('Not a branch. Have you pushed it to launchpad?') - return - return repo_name + return repo_name.strip('/') def main(): usage = 'Usage: python %prog TOKEN [options]' parser = OptionParser(usage=usage) - parser.add_option('-d', '--disable-output', dest='enable_output', action="store_false", default=True, + parser.add_option('-d', '--disable-output', dest='enable_output', action='store_false', default=True, help='Disable output.') - parser.add_option('-b', '--open-browser', dest='open_browser', action="store_true", default=False, + parser.add_option('-b', '--open-browser', dest='open_browser', action='store_true', default=False, help='Opens the jenkins page in your browser.') - #parser.add_option('-e', '--open-browser-on-error', dest='open_browser_on_error', action="store_true", - # default=False, help='Opens the jenkins page in your browser in case a test fails.') options, args = parser.parse_args(sys.argv) if len(args) == 2: if not get_repo_name(): + print('Not a branch. Have you pushed it to launchpad? Did you cd to the branch?') return token = args[-1] + if token in OLD_TOKENS: + print('Your token is not valid anymore. Get the most recent one.') + return jenkins_trigger = JenkinsTrigger(token) try: jenkins_trigger.trigger_build() except HTTPError as e: - print("Wrong token.") + print('Wrong token.') return # Open the browser before printing the output. if options.open_browser: diff --git a/tests/functional/openlp_core_lib/test_image_manager.py b/tests/functional/openlp_core_lib/test_image_manager.py index e9968e1c7..4a73bb40f 100644 --- a/tests/functional/openlp_core_lib/test_image_manager.py +++ b/tests/functional/openlp_core_lib/test_image_manager.py @@ -30,12 +30,16 @@ Package to test the openlp.core.ui package. """ import os +import time +from threading import Lock from unittest import TestCase from PyQt4 import QtGui from openlp.core.common import Registry from openlp.core.lib import ImageManager, ScreenList +from openlp.core.lib.imagemanager import Priority +from tests.functional import patch from tests.helpers.testmixin import TestMixin TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'resources')) @@ -51,6 +55,8 @@ class TestImageManager(TestCase, TestMixin): self.get_application() ScreenList.create(self.app.desktop()) self.image_manager = ImageManager() + self.lock = Lock() + self.sleep_time = 0.1 def tearDown(self): """ @@ -82,3 +88,85 @@ class TestImageManager(TestCase, TestMixin): with self.assertRaises(KeyError) as context: self.image_manager.get_image(TEST_PATH, 'church1.jpg') self.assertNotEquals(context.exception, '', 'KeyError exception should have been thrown for missing image') + + + def process_cache_test(self): + """ + Test the process_cache method + """ + with patch('openlp.core.lib.imagemanager.resize_image') as mocked_resize_image, \ + patch('openlp.core.lib.imagemanager.image_to_byte') as mocked_image_to_byte: + # GIVEN: Mocked functions + mocked_resize_image.side_effect = self.mocked_resize_image + mocked_image_to_byte.side_effect = self.mocked_image_to_byte + image1 = 'church.jpg' + image2 = 'church2.jpg' + image3 = 'church3.jpg' + image4 = 'church4.jpg' + + # WHEN: Add the images. Then get the lock (=queue can not be processed). + self.lock.acquire() + self.image_manager.add_image(TEST_PATH, image1, None) + self.image_manager.add_image(TEST_PATH, image2, None) + + # THEN: All images have been added to the queue, and only the first image is not be in the list anymore, but + # is being processed (see mocked methods/functions). + # Note: Priority.Normal means, that the resize_image() was not completed yet (because afterwards the # + # priority is adjusted to Priority.Lowest). + assert self.get_image_priority(image1) == Priority.Normal, "image1's priority should be 'Priority.Normal'" + assert self.get_image_priority(image2) == Priority.Normal, "image2's priority should be 'Priority.Normal'" + + # WHEN: Add more images. + self.image_manager.add_image(TEST_PATH, image3, None) + self.image_manager.add_image(TEST_PATH, image4, None) + # Allow the queue to process. + self.lock.release() + # Request some "data". + image_bytes = self.image_manager.get_image_bytes(TEST_PATH, image4) + image_object = self.image_manager.get_image(TEST_PATH, image3) + # Now the mocked methods/functions do not have to sleep anymore. + self.sleep_time = 0 + # Wait for the queue to finish. + while not self.image_manager._conversion_queue.empty(): + time.sleep(0.1) + # Because empty() is not reliable, wait a litte; just to make sure. + time.sleep(0.1) + # THEN: The images' priority reflect how they were processed. + assert self.image_manager._conversion_queue.qsize() == 0, "The queue should be empty." + assert self.get_image_priority(image1) == Priority.Lowest, \ + "The image should have not been requested (=Lowest)" + assert self.get_image_priority(image2) == Priority.Lowest, \ + "The image should have not been requested (=Lowest)" + assert self.get_image_priority(image3) == Priority.Low, "Only the QImage should have been requested (=Low)." + assert self.get_image_priority(image4) == Priority.Urgent, \ + "The image bytes should have been requested (=Urgent)." + + def get_image_priority(self, image): + """ + This is a help method to get the priority of the given image out of the image_manager's cache. + + NOTE: This requires, that the image has been added to the image manager using the *TEST_PATH*. + + :param image: The name of the image. E. g. ``image1`` + """ + return self.image_manager._cache[(TEST_PATH, image)].priority + + def mocked_resize_image(self, *args): + """ + This is a mocked method, so that we can control the work flow of the image manager. + """ + self.lock.acquire() + self.lock.release() + # The sleep time is adjusted in the test case. + time.sleep(self.sleep_time) + return QtGui.QImage() + + def mocked_image_to_byte(self, *args): + """ + This is a mocked method, so that we can control the work flow of the image manager. + """ + self.lock.acquire() + self.lock.release() + # The sleep time is adjusted in the test case. + time.sleep(self.sleep_time) + return '' \ No newline at end of file diff --git a/tests/resources/church2.jpg b/tests/resources/church2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..826180870242f78a840d20c20cf5db78311224d2 GIT binary patch literal 218545 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 literal 0 HcmV?d00001 diff --git a/tests/resources/church3.jpg b/tests/resources/church3.jpg new file mode 100644 index 0000000000000000000000000000000000000000..826180870242f78a840d20c20cf5db78311224d2 GIT binary patch literal 218545 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 literal 0 HcmV?d00001 diff --git a/tests/resources/church4.jpg b/tests/resources/church4.jpg new file mode 100644 index 0000000000000000000000000000000000000000..826180870242f78a840d20c20cf5db78311224d2 GIT binary patch literal 218545 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 literal 0 HcmV?d00001 From 487f4279dd6a6073946bfd9a8735693680c2b99e Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sat, 12 Apr 2014 19:11:32 +0200 Subject: [PATCH 117/155] fixed blank line --- tests/functional/openlp_core_lib/test_image_manager.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/functional/openlp_core_lib/test_image_manager.py b/tests/functional/openlp_core_lib/test_image_manager.py index 4a73bb40f..c3036666d 100644 --- a/tests/functional/openlp_core_lib/test_image_manager.py +++ b/tests/functional/openlp_core_lib/test_image_manager.py @@ -89,7 +89,6 @@ class TestImageManager(TestCase, TestMixin): self.image_manager.get_image(TEST_PATH, 'church1.jpg') self.assertNotEquals(context.exception, '', 'KeyError exception should have been thrown for missing image') - def process_cache_test(self): """ Test the process_cache method From 51e43a9a52bcd7645c27688e6df5e3f7a838159b Mon Sep 17 00:00:00 2001 From: Jonathan Springer Date: Sat, 12 Apr 2014 14:21:15 -0400 Subject: [PATCH 118/155] Fix getting urls for first time wirard from config file. --- openlp/core/ui/firsttimeform.py | 18 +++++++++++------- openlp/core/ui/maindisplay.py | 1 - 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/openlp/core/ui/firsttimeform.py b/openlp/core/ui/firsttimeform.py index aa89da6c0..f1b875e6b 100644 --- a/openlp/core/ui/firsttimeform.py +++ b/openlp/core/ui/firsttimeform.py @@ -67,7 +67,7 @@ class ThemeScreenshotThread(QtCore.QThread): title = config.get('theme_%s' % theme, 'title') filename = config.get('theme_%s' % theme, 'filename') screenshot = config.get('theme_%s' % theme, 'screenshot') - urllib.request.urlretrieve('%s%s' % (self.parent().web, screenshot), + urllib.request.urlretrieve('%s%s' % (self.parent().themes_url, screenshot), os.path.join(gettempdir(), 'openlp', screenshot)) item = QtGui.QListWidgetItem(title, self.parent().themes_list_widget) item.setData(QtCore.Qt.UserRole, filename) @@ -96,6 +96,10 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard, RegistryProperties): if self.web_access: files = self.web_access.read() self.config.read_string(files.decode()) + self.web = self.config.get('general', 'base url') + self.songs_url = self.web + self.config.get('songs', 'directory') + '/' + self.bibles_url = self.web + self.config.get('bibles', 'directory') + '/' + self.themes_url = self.web + self.config.get('themes', 'directory') + '/' self.update_screen_list_combo() self.was_download_cancelled = False self.theme_screenshot_thread = None @@ -341,7 +345,7 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard, RegistryProperties): item = self.songs_list_widget.item(i) if item.checkState() == QtCore.Qt.Checked: filename = item.data(QtCore.Qt.UserRole) - size = self._get_file_size('%s%s' % (self.web, filename)) + size = self._get_file_size('%s%s' % (self.songs_url, filename)) self.max_progress += size # Loop through the Bibles list and increase for each selected item iterator = QtGui.QTreeWidgetItemIterator(self.bibles_tree_widget) @@ -350,7 +354,7 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard, RegistryProperties): item = iterator.value() if item.parent() and item.checkState(0) == QtCore.Qt.Checked: filename = item.data(0, QtCore.Qt.UserRole) - size = self._get_file_size('%s%s' % (self.web, filename)) + size = self._get_file_size('%s%s' % (self.bibles_url, filename)) self.max_progress += size iterator += 1 # Loop through the themes list and increase for each selected item @@ -359,7 +363,7 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard, RegistryProperties): item = self.themes_list_widget.item(i) if item.checkState() == QtCore.Qt.Checked: filename = item.data(QtCore.Qt.UserRole) - size = self._get_file_size('%s%s' % (self.web, filename)) + size = self._get_file_size('%s%s' % (self.themes_url, filename)) self.max_progress += size if self.max_progress: # Add on 2 for plugins status setting plus a "finished" point. @@ -435,7 +439,7 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard, RegistryProperties): self._increment_progress_bar(self.downloading % filename, 0) self.previous_size = 0 destination = os.path.join(songs_destination, str(filename)) - self.url_get_file('%s%s' % (self.web, filename), destination) + self.url_get_file('%s%s' % (self.songs_url, filename), destination) # Download Bibles bibles_iterator = QtGui.QTreeWidgetItemIterator( self.bibles_tree_widget) @@ -445,7 +449,7 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard, RegistryProperties): bible = item.data(0, QtCore.Qt.UserRole) self._increment_progress_bar(self.downloading % bible, 0) self.previous_size = 0 - self.url_get_file('%s%s' % (self.web, bible), os.path.join(bibles_destination, bible)) + self.url_get_file('%s%s' % (self.bibles_url, bible), os.path.join(bibles_destination, bible)) bibles_iterator += 1 # Download themes for i in range(self.themes_list_widget.count()): @@ -454,7 +458,7 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard, RegistryProperties): theme = item.data(QtCore.Qt.UserRole) self._increment_progress_bar(self.downloading % theme, 0) self.previous_size = 0 - self.url_get_file('%s%s' % (self.web, theme), os.path.join(themes_destination, theme)) + self.url_get_file('%s%s' % (self.themes_url, theme), os.path.join(themes_destination, theme)) # Set Default Display if self.display_combo_box.currentIndex() != -1: Settings().setValue('core/monitor', self.display_combo_box.currentIndex()) diff --git a/openlp/core/ui/maindisplay.py b/openlp/core/ui/maindisplay.py index b9423046e..6f873a10a 100644 --- a/openlp/core/ui/maindisplay.py +++ b/openlp/core/ui/maindisplay.py @@ -350,7 +350,6 @@ class MainDisplay(OpenLPMixin, Display, RegistryProperties): self.hide_display(self.hide_mode) # Only continue if the visibility wasn't changed during method call. elif was_visible == self.isVisible(): - # Single screen active if self.screens.display_count == 1: # Only make visible if setting enabled. From b9ba2c68a6e901c422769527f19c3aea9cb29745 Mon Sep 17 00:00:00 2001 From: Raoul Snyman Date: Sat, 12 Apr 2014 22:19:22 +0200 Subject: [PATCH 119/155] Fix up PEP8 issues. --- openlp/core/common/__init__.py | 2 +- openlp/core/lib/__init__.py | 2 +- openlp/core/lib/mediamanageritem.py | 16 +++---- openlp/core/lib/plugin.py | 22 +++++----- openlp/core/lib/pluginmanager.py | 2 +- openlp/core/lib/serviceitem.py | 2 +- openlp/core/ui/exceptiondialog.py | 18 ++++---- openlp/core/ui/firsttimewizard.py | 6 +-- openlp/core/ui/formattingtagform.py | 2 - openlp/core/ui/mainwindow.py | 12 ++--- openlp/core/ui/media/__init__.py | 2 +- openlp/core/ui/media/mediacontroller.py | 6 +-- openlp/core/ui/media/webkitplayer.py | 44 +++++-------------- openlp/core/ui/servicemanager.py | 9 ++-- openlp/core/ui/settingsform.py | 2 +- openlp/core/ui/themelayoutdialog.py | 1 - openlp/core/ui/themestab.py | 20 +++++---- openlp/core/utils/__init__.py | 2 +- openlp/plugins/alerts/alertsplugin.py | 4 +- openlp/plugins/bibles/bibleplugin.py | 23 ++++++---- openlp/plugins/bibles/lib/db.py | 12 ++--- openlp/plugins/bibles/lib/http.py | 8 ++-- openlp/plugins/bibles/lib/mediaitem.py | 11 +++-- .../plugins/bibles/lib/versereferencelist.py | 2 +- openlp/plugins/custom/customplugin.py | 6 +-- .../custom/forms/editcustomslideform.py | 1 - openlp/plugins/custom/lib/customxmlhandler.py | 2 +- openlp/plugins/images/imageplugin.py | 4 +- openlp/plugins/media/lib/mediaitem.py | 3 +- openlp/plugins/media/mediaplugin.py | 4 +- .../presentations/lib/impresscontroller.py | 2 - .../presentations/lib/pdfcontroller.py | 6 +-- .../presentations/lib/powerpointcontroller.py | 2 +- .../presentations/lib/presentationtab.py | 4 +- .../presentations/presentationplugin.py | 4 +- openlp/plugins/remotes/remoteplugin.py | 4 +- .../songs/forms/duplicatesongremovalform.py | 6 +-- openlp/plugins/songs/forms/editsongform.py | 3 +- openlp/plugins/songs/forms/editverseform.py | 2 +- .../plugins/songs/forms/mediafilesdialog.py | 8 ++-- openlp/plugins/songs/forms/songexportform.py | 6 +-- openlp/plugins/songs/lib/cclifileimport.py | 12 ++--- openlp/plugins/songs/lib/ewimport.py | 2 +- openlp/plugins/songs/lib/mediaitem.py | 4 +- openlp/plugins/songs/songsplugin.py | 6 +-- openlp/plugins/songusage/songusageplugin.py | 4 +- .../openlp_core_lib/test_pluginmanager.py | 12 ++--- 47 files changed, 161 insertions(+), 176 deletions(-) diff --git a/openlp/core/common/__init__.py b/openlp/core/common/__init__.py index 9a0648909..22207dec4 100644 --- a/openlp/core/common/__init__.py +++ b/openlp/core/common/__init__.py @@ -38,7 +38,7 @@ import traceback from PyQt4 import QtCore -log = logging.getLogger(__name__+'.__init__') +log = logging.getLogger(__name__ + '.__init__') FIRST_CAMEL_REGEX = re.compile('(.)([A-Z][a-z]+)') diff --git a/openlp/core/lib/__init__.py b/openlp/core/lib/__init__.py index e6b7f4fe3..9561baff4 100644 --- a/openlp/core/lib/__init__.py +++ b/openlp/core/lib/__init__.py @@ -39,7 +39,7 @@ from PyQt4 import QtCore, QtGui, Qt from openlp.core.common import translate -log = logging.getLogger(__name__+'.__init__') +log = logging.getLogger(__name__ + '.__init__') class ServiceItemContext(object): diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py index 13d4b0bbb..4d7676ad6 100644 --- a/openlp/core/lib/mediamanageritem.py +++ b/openlp/core/lib/mediamanageritem.py @@ -168,29 +168,29 @@ class MediaManagerItem(QtGui.QWidget, RegistryProperties): Create buttons for the media item toolbar """ toolbar_actions = [] - ## Import Button ## + # Import Button if self.has_import_icon: toolbar_actions.append(['Import', StringContent.Import, ':/general/general_import.png', self.on_import_click]) - ## Load Button ## + # Load Button if self.has_file_icon: toolbar_actions.append(['Load', StringContent.Load, ':/general/general_open.png', self.on_file_click]) - ## New Button ## + # New Button if self.has_new_icon: toolbar_actions.append(['New', StringContent.New, ':/general/general_new.png', self.on_new_click]) - ## Edit Button ## + # Edit Button if self.has_edit_icon: toolbar_actions.append(['Edit', StringContent.Edit, ':/general/general_edit.png', self.on_edit_click]) - ## Delete Button ## + # Delete Button if self.has_delete_icon: toolbar_actions.append(['Delete', StringContent.Delete, ':/general/general_delete.png', self.on_delete_click]) - ## Preview ## + # Preview toolbar_actions.append(['Preview', StringContent.Preview, ':/general/general_preview.png', self.on_preview_click]) - ## Live Button ## + # Live Button toolbar_actions.append(['Live', StringContent.Live, ':/general/general_live.png', self.on_live_click]) - ## Add to service Button ## + # Add to service Button toolbar_actions.append(['Service', StringContent.Service, ':/general/general_add.png', self.on_add_click]) for action in toolbar_actions: if action[0] == StringContent.Preview: diff --git a/openlp/core/lib/plugin.py b/openlp/core/lib/plugin.py index e14fe8bb0..1f459524c 100644 --- a/openlp/core/lib/plugin.py +++ b/openlp/core/lib/plugin.py @@ -101,7 +101,7 @@ class Plugin(QtCore.QObject, RegistryProperties): ``add_import_menu_item(import_menu)`` Add an item to the Import menu. - ``add_export_menu_Item(export_menu)`` + ``add_export_menu_item(export_menu)`` Add an item to the Export menu. ``create_settings_tab()`` @@ -226,7 +226,7 @@ class Plugin(QtCore.QObject, RegistryProperties): """ pass - def add_export_menu_Item(self, export_menu): + def add_export_menu_item(self, export_menu): """ Create a menu item and add it to the "Export" menu. @@ -329,22 +329,24 @@ class Plugin(QtCore.QObject, RegistryProperties): def set_plugin_ui_text_strings(self, tooltips): """ Called to define all translatable texts of the plugin + + :param tooltips: """ - ## Load Action ## + # Load Action self.__set_name_text_string(StringContent.Load, UiStrings().Load, tooltips['load']) - ## Import Action ## + # Import Action self.__set_name_text_string(StringContent.Import, UiStrings().Import, tooltips['import']) - ## New Action ## + # New Action self.__set_name_text_string(StringContent.New, UiStrings().Add, tooltips['new']) - ## Edit Action ## + # Edit Action self.__set_name_text_string(StringContent.Edit, UiStrings().Edit, tooltips['edit']) - ## Delete Action ## + # Delete Action self.__set_name_text_string(StringContent.Delete, UiStrings().Delete, tooltips['delete']) - ## Preview Action ## + # Preview Action self.__set_name_text_string(StringContent.Preview, UiStrings().Preview, tooltips['preview']) - ## Send Live Action ## + # Send Live Action self.__set_name_text_string(StringContent.Live, UiStrings().Live, tooltips['live']) - ## Add to Service Action ## + # Add to Service Action self.__set_name_text_string(StringContent.Service, UiStrings().Service, tooltips['service']) def __set_name_text_string(self, name, title, tooltip): diff --git a/openlp/core/lib/pluginmanager.py b/openlp/core/lib/pluginmanager.py index d24f07eac..474113c98 100644 --- a/openlp/core/lib/pluginmanager.py +++ b/openlp/core/lib/pluginmanager.py @@ -161,7 +161,7 @@ class PluginManager(RegistryMixin, OpenLPMixin, RegistryProperties): """ for plugin in self.plugins: if plugin.status is not PluginStatus.Disabled: - plugin.add_export_menu_Item(self.main_window.file_export_menu) + plugin.add_export_menu_item(self.main_window.file_export_menu) def hook_tools_menu(self): """ diff --git a/openlp/core/lib/serviceitem.py b/openlp/core/lib/serviceitem.py index 6d07e1c34..bb59938d4 100644 --- a/openlp/core/lib/serviceitem.py +++ b/openlp/core/lib/serviceitem.py @@ -383,7 +383,7 @@ class ServiceItem(RegistryProperties): self.will_auto_start = header.get('will_auto_start', False) self.processor = header.get('processor', None) self.has_original_files = True - #TODO Remove me in 2,3 build phase + # TODO: Remove me in 2,3 build phase if self.is_capable(ItemCapabilities.HasDetailedTitleDisplay): self.capabilities.remove(ItemCapabilities.HasDetailedTitleDisplay) self.processor = self.title diff --git a/openlp/core/ui/exceptiondialog.py b/openlp/core/ui/exceptiondialog.py index 4d7abe708..212fee4cd 100644 --- a/openlp/core/ui/exceptiondialog.py +++ b/openlp/core/ui/exceptiondialog.py @@ -95,15 +95,15 @@ class Ui_ExceptionDialog(object): Translate the widgets on the fly. """ exception_dialog.setWindowTitle(translate('OpenLP.ExceptionDialog', 'Error Occurred')) - self.description_explanation.setText(translate('OpenLP.ExceptionDialog', - 'Please enter a description of what you were doing to cause this error ' - '\n(Minimum 20 characters)')) - self.message_label.setText(translate('OpenLP.ExceptionDialog', 'Oops! ' - 'OpenLP hit a problem, and couldn\'t recover. The text in the box ' - 'below contains information that might be helpful to the OpenLP ' - 'developers, so please e-mail it to bugs@openlp.org, along with a ' - 'detailed description of what you were doing when the problem ' - 'occurred.')) + self.description_explanation.setText( + translate('OpenLP.ExceptionDialog', 'Please enter a description of what you were doing to cause this error ' + '\n(Minimum 20 characters)')) + self.message_label.setText( + translate('OpenLP.ExceptionDialog', 'Oops! OpenLP hit a problem, and couldn\'t recover. The text in the ' + 'box below contains information that might be helpful to the OpenLP ' + 'developers, so please e-mail it to bugs@openlp.org, along with a ' + 'detailed description of what you were doing when the problem ' + 'occurred.')) self.send_report_button.setText(translate('OpenLP.ExceptionDialog', 'Send E-Mail')) self.save_report_button.setText(translate('OpenLP.ExceptionDialog', 'Save to File')) self.attach_tile_button.setText(translate('OpenLP.ExceptionDialog', 'Attach File')) diff --git a/openlp/core/ui/firsttimewizard.py b/openlp/core/ui/firsttimewizard.py index 407842c48..1a270f931 100644 --- a/openlp/core/ui/firsttimewizard.py +++ b/openlp/core/ui/firsttimewizard.py @@ -211,9 +211,9 @@ class Ui_FirstTimeWizard(object): first_time_wizard.setWindowTitle(translate('OpenLP.FirstTimeWizard', 'First Time Wizard')) self.title_label.setText('%s' % translate('OpenLP.FirstTimeWizard', 'Welcome to the First Time Wizard')) - self.information_label.setText(translate('OpenLP.FirstTimeWizard', - 'This wizard will help you to configure OpenLP for initial use. ' - 'Click the next button below to start.')) + self.information_label.setText( + translate('OpenLP.FirstTimeWizard', 'This wizard will help you to configure OpenLP for initial use. ' + 'Click the next button below to start.')) self.plugin_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Activate required Plugins')) self.plugin_page.setSubTitle(translate('OpenLP.FirstTimeWizard', 'Select the Plugins you wish to use. ')) self.songs_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Songs')) diff --git a/openlp/core/ui/formattingtagform.py b/openlp/core/ui/formattingtagform.py index b7153429d..be4247bc1 100644 --- a/openlp/core/ui/formattingtagform.py +++ b/openlp/core/ui/formattingtagform.py @@ -63,7 +63,6 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog, FormattingTagCont self.services = FormattingTagController() self.tag_table_widget.itemSelectionChanged.connect(self.on_row_selected) self.new_button.clicked.connect(self.on_new_clicked) - #self.save_button.clicked.connect(self.on_saved_clicked) self.delete_button.clicked.connect(self.on_delete_clicked) self.tag_table_widget.currentCellChanged.connect(self.on_current_cell_changed) self.button_box.rejected.connect(self.close) @@ -202,5 +201,4 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog, FormattingTagCont if errors: QtGui.QMessageBox.warning(self, translate('OpenLP.FormattingTagForm', 'Validation Error'), errors, QtGui.QMessageBox.Ok) - #self.tag_table_widget.selectRow(pre_row - 1) self.tag_table_widget.resizeRowsToContents() diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index c61526586..81e822c16 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -394,12 +394,12 @@ class Ui_MainWindow(object): self.settings_shortcuts_item.setText(translate('OpenLP.MainWindow', 'Configure &Shortcuts...')) self.formatting_tag_item.setText(translate('OpenLP.MainWindow', 'Configure &Formatting Tags...')) self.settings_configure_item.setText(translate('OpenLP.MainWindow', '&Configure OpenLP...')) - self.settings_export_item.setStatusTip(translate('OpenLP.MainWindow', - 'Export OpenLP settings to a specified *.config file')) + self.settings_export_item.setStatusTip( + translate('OpenLP.MainWindow', 'Export OpenLP settings to a specified *.config file')) self.settings_export_item.setText(translate('OpenLP.MainWindow', 'Settings')) - self.settings_import_item.setStatusTip(translate('OpenLP.MainWindow', - 'Import OpenLP settings from a specified *.config file previously ' - 'exported on this or another machine')) + self.settings_import_item.setStatusTip( + translate('OpenLP.MainWindow', 'Import OpenLP settings from a specified *.config file previously ' + 'exported on this or another machine')) self.settings_import_item.setText(translate('OpenLP.MainWindow', 'Settings')) self.view_media_manager_item.setText(translate('OpenLP.MainWindow', '&Media Manager')) self.view_media_manager_item.setToolTip(translate('OpenLP.MainWindow', 'Toggle Media Manager')) @@ -860,7 +860,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow, RegistryProperties): section = 'general' section_key = section + "/" + key # Make sure it's a valid section for us. - if not section in setting_sections: + if section not in setting_sections: continue # We have a good file, import it. for section_key in import_keys: diff --git a/openlp/core/ui/media/__init__.py b/openlp/core/ui/media/__init__.py index 1276c8662..8cc10b6a1 100644 --- a/openlp/core/ui/media/__init__.py +++ b/openlp/core/ui/media/__init__.py @@ -35,7 +35,7 @@ from openlp.core.common import Settings from PyQt4 import QtCore -log = logging.getLogger(__name__+'.__init__') +log = logging.getLogger(__name__ + '.__init__') class MediaState(object): diff --git a/openlp/core/ui/media/mediacontroller.py b/openlp/core/ui/media/mediacontroller.py index d846af0e4..0b065c131 100644 --- a/openlp/core/ui/media/mediacontroller.py +++ b/openlp/core/ui/media/mediacontroller.py @@ -137,7 +137,7 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties): for player in list(self.media_players.values()): if player.is_active: for item in player.audio_extensions_list: - if not item in self.audio_extensions_list: + if item not in self.audio_extensions_list: self.audio_extensions_list.append(item) suffix_list.append(item[2:]) self.video_extensions_list = [] @@ -184,8 +184,8 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties): return False saved_players, overridden_player = get_media_players() invalid_media_players = \ - [mediaPlayer for mediaPlayer in saved_players if not mediaPlayer in self.media_players or - not self.media_players[mediaPlayer].check_available()] + [media_player for media_player in saved_players if media_player not in self.media_players or + not self.media_players[media_player].check_available()] if invalid_media_players: for invalidPlayer in invalid_media_players: saved_players.remove(invalidPlayer) diff --git a/openlp/core/ui/media/webkitplayer.py b/openlp/core/ui/media/webkitplayer.py index e3a522140..38e691494 100644 --- a/openlp/core/ui/media/webkitplayer.py +++ b/openlp/core/ui/media/webkitplayer.py @@ -174,34 +174,11 @@ FLASH_HTML = """ """ -VIDEO_EXT = [ - '*.3gp', - '*.3gpp', - '*.3g2', - '*.3gpp2', - '*.aac', - '*.flv', - '*.f4a', - '*.f4b', - '*.f4p', - '*.f4v', - '*.mov', - '*.m4a', - '*.m4b', - '*.m4p', - '*.m4v', - '*.mkv', - '*.mp4', - '*.ogv', - '*.webm', - '*.mpg', '*.wmv', '*.mpeg', '*.avi', - '*.swf' -] +VIDEO_EXT = ['*.3gp', '*.3gpp', '*.3g2', '*.3gpp2', '*.aac', '*.flv', '*.f4a', '*.f4b', '*.f4p', '*.f4v', '*.mov', + '*.m4a', '*.m4b', '*.m4p', '*.m4v', '*.mkv', '*.mp4', '*.ogv', '*.webm', '*.mpg', '*.wmv', '*.mpeg', + '*.avi', '*.swf'] -AUDIO_EXT = [ - '*.mp3', - '*.ogg' -] +AUDIO_EXT = ['*.mp3', '*.ogg'] class WebkitPlayer(MediaPlayer): @@ -411,10 +388,9 @@ class WebkitPlayer(MediaPlayer): """ Return some information about this player """ - return(translate('Media.player', 'Webkit is a media player which runs ' - 'inside a web browser. This player allows text over video to be ' - 'rendered.') + - '
' + translate('Media.player', 'Audio') + - '
' + str(AUDIO_EXT) + '
' + - translate('Media.player', 'Video') + '
' + - str(VIDEO_EXT) + '
') + part1 = translate('Media.player', 'Webkit is a media player which runs inside a web browser. This player ' + 'allows text over video to be rendered.') + part2 = translate('Media.player', 'Audio') + part3 = translate('Media.player', 'Video') + return part1 + '
' + part2 + '
' + str(AUDIO_EXT) + '
' + part3 + \ + '
' + str(VIDEO_EXT) + '
' diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index c2b36551a..4d2ff2ee9 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -399,7 +399,7 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ServiceManage :param suffix_list: New Suffix's to be supported """ for suffix in suffix_list: - if not suffix in self.suffixes: + if suffix not in self.suffixes: self.suffixes.append(suffix) def on_new_service_clicked(self, field=None): @@ -629,7 +629,7 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ServiceManage for item in self.service_items: self.main_window.increment_progress_bar() service_item = item['service_item'].get_service_repr(self._save_lite) - #TODO: check for file item on save. + # TODO: check for file item on save. service.append({'serviceitem': service_item}) self.main_window.increment_progress_bar() service_content = json.dumps(service) @@ -754,8 +754,9 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ServiceManage items = json.load(file_to) else: critical_error_message_box(message=translate('OpenLP.ServiceManager', - 'The service file you are trying to open is in an old format.\n ' - 'Please save it using OpenLP 2.0.2 or greater.')) + 'The service file you are trying to open is in an old ' + 'format.\n Please save it using OpenLP 2.0.2 or ' + 'greater.')) return file_to.close() self.new_file() diff --git a/openlp/core/ui/settingsform.py b/openlp/core/ui/settingsform.py index 5aba66ef2..a1077e1f4 100644 --- a/openlp/core/ui/settingsform.py +++ b/openlp/core/ui/settingsform.py @@ -150,5 +150,5 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog, RegistryProperties): :param function: The function to be called """ - if not function in self.processes: + if function not in self.processes: self.processes.append(function) diff --git a/openlp/core/ui/themelayoutdialog.py b/openlp/core/ui/themelayoutdialog.py index bf6c1069a..023d6259c 100644 --- a/openlp/core/ui/themelayoutdialog.py +++ b/openlp/core/ui/themelayoutdialog.py @@ -44,7 +44,6 @@ class Ui_ThemeLayoutDialog(object): Set up the UI """ themeLayoutDialog.setObjectName('themeLayoutDialogDialog') - #themeLayoutDialog.resize(300, 200) self.preview_layout = QtGui.QVBoxLayout(themeLayoutDialog) self.preview_layout.setObjectName('preview_layout') self.preview_area = QtGui.QWidget(themeLayoutDialog) diff --git a/openlp/core/ui/themestab.py b/openlp/core/ui/themestab.py index 32ed30303..1f54f984b 100644 --- a/openlp/core/ui/themestab.py +++ b/openlp/core/ui/themestab.py @@ -114,17 +114,19 @@ class ThemesTab(SettingsTab): self.global_group_box.setTitle(translate('OpenLP.ThemesTab', 'Global Theme')) self.level_group_box.setTitle(translate('OpenLP.ThemesTab', 'Theme Level')) self.song_level_radio_button.setText(translate('OpenLP.ThemesTab', 'S&ong Level')) - self.song_level_label.setText(translate('OpenLP.ThemesTab', 'Use the theme from each song ' - 'in the database. If a song doesn\'t have a theme associated with ' - 'it, then use the service\'s theme. If the service doesn\'t have ' - 'a theme, then use the global theme.')) + self.song_level_label.setText( + translate('OpenLP.ThemesTab', 'Use the theme from each song in the database. If a song doesn\'t have a ' + 'theme associated with it, then use the service\'s theme. If the service ' + 'doesn\'t have a theme, then use the global theme.')) self.service_level_radio_button.setText(translate('OpenLP.ThemesTab', '&Service Level')) - self.service_level_label.setText(translate('OpenLP.ThemesTab', 'Use the theme from the service, ' - 'overriding any of the individual songs\' themes. If the ' - 'service doesn\'t have a theme, then use the global theme.')) + self.service_level_label.setText( + translate('OpenLP.ThemesTab', 'Use the theme from the service, overriding any of the individual ' + 'songs\' themes. If the service doesn\'t have a theme, then use the global ' + 'theme.')) self.global_level_radio_button.setText(translate('OpenLP.ThemesTab', '&Global Level')) - self.global_level_label.setText(translate('OpenLP.ThemesTab', 'Use the global theme, overriding ' - 'any themes associated with either the service or the songs.')) + self.global_level_label.setText(translate('OpenLP.ThemesTab', 'Use the global theme, overriding any themes ' + 'associated with either the service or the ' + 'songs.')) def load(self): """ diff --git a/openlp/core/utils/__init__.py b/openlp/core/utils/__init__.py index 746c50d71..a5b5f356a 100644 --- a/openlp/core/utils/__init__.py +++ b/openlp/core/utils/__init__.py @@ -56,7 +56,7 @@ if sys.platform != 'win32' and sys.platform != 'darwin': from openlp.core.common import translate -log = logging.getLogger(__name__+'.__init__') +log = logging.getLogger(__name__ + '.__init__') APPLICATION_VERSION = {} IMAGES_FILTER = None diff --git a/openlp/plugins/alerts/alertsplugin.py b/openlp/plugins/alerts/alertsplugin.py index cf3b7d0ce..76efadf32 100644 --- a/openlp/plugins/alerts/alertsplugin.py +++ b/openlp/plugins/alerts/alertsplugin.py @@ -207,12 +207,12 @@ class AlertsPlugin(Plugin): """ Called to define all translatable texts of the plugin """ - ## Name PluginList ## + # Name PluginList self.text_strings[StringContent.Name] = { 'singular': translate('AlertsPlugin', 'Alert', 'name singular'), 'plural': translate('AlertsPlugin', 'Alerts', 'name plural') } - ## Name for MediaDockManager, SettingsManager ## + # Name for MediaDockManager, SettingsManager self.text_strings[StringContent.VisibleName] = { 'title': translate('AlertsPlugin', 'Alerts', 'container title') } diff --git a/openlp/plugins/bibles/bibleplugin.py b/openlp/plugins/bibles/bibleplugin.py index 5a51be163..e7f1fdd56 100644 --- a/openlp/plugins/bibles/bibleplugin.py +++ b/openlp/plugins/bibles/bibleplugin.py @@ -88,8 +88,6 @@ class BiblePlugin(Plugin): self.import_bible_item.setVisible(True) action_list = ActionList.get_instance() action_list.add_action(self.import_bible_item, UiStrings().Import) - # Do not add the action to the list yet. - #action_list.add_action(self.export_bible_item, UiStrings().Export) # Set to invisible until we can export bibles self.export_bible_item.setVisible(False) self.tools_upgrade_item.setVisible(bool(self.manager.old_bible_databases)) @@ -104,7 +102,6 @@ class BiblePlugin(Plugin): action_list = ActionList.get_instance() action_list.remove_action(self.import_bible_item, UiStrings().Import) self.import_bible_item.setVisible(False) - #action_list.remove_action(self.export_bible_item, UiStrings().Export) self.export_bible_item.setVisible(False) def app_startup(self): @@ -115,19 +112,27 @@ class BiblePlugin(Plugin): if self.manager.old_bible_databases: if QtGui.QMessageBox.information( self.main_window, translate('OpenLP', 'Information'), - translate('OpenLP', 'Bible format has changed.\nYou have to upgrade your existing Bibles.\n' - 'Should OpenLP upgrade now?'), - QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No)) == \ + translate('OpenLP', 'Bible format has changed.\nYou have to upgrade your ' + 'existing Bibles.\nShould OpenLP upgrade now?'), + QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No)) == \ QtGui.QMessageBox.Yes: self.on_tools_upgrade_Item_triggered() def add_import_menu_item(self, import_menu): + """ + + :param import_menu: + """ self.import_bible_item = create_action(import_menu, 'importBibleItem', text=translate('BiblesPlugin', '&Bible'), visible=False, triggers=self.on_bible_import_click) import_menu.addAction(self.import_bible_item) - def add_export_menu_Item(self, export_menu): + def add_export_menu_item(self, export_menu): + """ + + :param export_menu: + """ self.export_bible_item = create_action(export_menu, 'exportBibleItem', text=translate('BiblesPlugin', '&Bible'), visible=False) export_menu.addAction(self.export_bible_item) @@ -190,12 +195,12 @@ class BiblePlugin(Plugin): """ Called to define all translatable texts of the plugin """ - ## Name PluginList ## + # Name PluginList self.text_strings[StringContent.Name] = { 'singular': translate('BiblesPlugin', 'Bible', 'name singular'), 'plural': translate('BiblesPlugin', 'Bibles', 'name plural') } - ## Name for MediaDockManager, SettingsManager ## + # Name for MediaDockManager, SettingsManager self.text_strings[StringContent.VisibleName] = { 'title': translate('BiblesPlugin', 'Bibles', 'container title') } diff --git a/openlp/plugins/bibles/lib/db.py b/openlp/plugins/bibles/lib/db.py index 8011c97c1..c94f6ec8c 100644 --- a/openlp/plugins/bibles/lib/db.py +++ b/openlp/plugins/bibles/lib/db.py @@ -649,10 +649,10 @@ class BiblesResourcesDB(QtCore.QObject, Manager): 'chapter, verse_count FROM chapters WHERE book_reference_id = ?', (book_ref_id,)) try: return { - 'id': chapters[chapter-1][0], - 'book_reference_id': chapters[chapter-1][1], - 'chapter': chapters[chapter-1][2], - 'verse_count': chapters[chapter-1][3] + 'id': chapters[chapter - 1][0], + 'book_reference_id': chapters[chapter - 1][1], + 'chapter': chapters[chapter - 1][2], + 'verse_count': chapters[chapter - 1][3] } except (IndexError, TypeError): return None @@ -846,13 +846,13 @@ class AlternativeBookNamesDB(QtCore.QObject, Manager): file_path = os.path.join( AppLocation.get_directory(AppLocation.DataDir), 'bibles', 'alternative_book_names.sqlite') if not os.path.exists(file_path): - #create new DB, create table alternative_book_names + # create new DB, create table alternative_book_names AlternativeBookNamesDB.conn = sqlite3.connect(file_path) AlternativeBookNamesDB.conn.execute( 'CREATE TABLE alternative_book_names(id INTEGER NOT NULL, ' 'book_reference_id INTEGER, language_id INTEGER, name VARCHAR(50), PRIMARY KEY (id))') else: - #use existing DB + # use existing DB AlternativeBookNamesDB.conn = sqlite3.connect(file_path) AlternativeBookNamesDB.cursor = AlternativeBookNamesDB.conn.cursor() return AlternativeBookNamesDB.cursor diff --git a/openlp/plugins/bibles/lib/http.py b/openlp/plugins/bibles/lib/http.py index 340d8ef92..b15e11738 100644 --- a/openlp/plugins/bibles/lib/http.py +++ b/openlp/plugins/bibles/lib/http.py @@ -552,10 +552,10 @@ class HTTPBible(BibleDB, RegistryProperties): self.application.set_busy_cursor() search_results = self.get_chapter(book, reference[1]) if search_results and search_results.has_verse_list(): - ## We have found a book of the bible lets check to see - ## if it was there. By reusing the returned book name - ## we get a correct book. For example it is possible - ## to request ac and get Acts back. + # We have found a book of the bible lets check to see + # if it was there. By reusing the returned book name + # we get a correct book. For example it is possible + # to request ac and get Acts back. book_name = search_results.book self.application.process_events() # Check to see if book/chapter exists. diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index 10fef8e31..3b3a0b048 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -480,6 +480,10 @@ class BibleMediaItem(MediaManagerItem): self.reload_bibles() def on_delete_click(self): + """ + When the delete button is pressed + """ + bible = None if self.quickTab.isVisible(): bible = self.quickVersionComboBox.currentText() elif self.advancedTab.isVisible(): @@ -488,8 +492,9 @@ class BibleMediaItem(MediaManagerItem): if QtGui.QMessageBox.question( self, UiStrings().ConfirmDelete, translate('BiblesPlugin.MediaItem', 'Are you sure you want to completely delete "%s" Bible from ' - 'OpenLP?\n\nYou will need to re-import this Bible to use it again.') % bible, - QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No), + 'OpenLP?\n\nYou will need to re-import this Bible to use it ' + 'again.') % bible, + QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No), QtGui.QMessageBox.Yes) == QtGui.QMessageBox.No: return self.plugin.manager.delete_bible(bible) @@ -752,7 +757,7 @@ class BibleMediaItem(MediaManagerItem): log.exception('The second_search_results does not have as many verses as the search_results.') break bible_text = '%s %d%s%d (%s, %s)' % (book, verse.chapter, verse_separator, verse.verse, version, - second_version) + second_version) else: bible_text = '%s %d%s%d (%s)' % (book, verse.chapter, verse_separator, verse.verse, version) bible_verse = QtGui.QListWidgetItem(bible_text) diff --git a/openlp/plugins/bibles/lib/versereferencelist.py b/openlp/plugins/bibles/lib/versereferencelist.py index 1d3877d1d..7cbba68fb 100644 --- a/openlp/plugins/bibles/lib/versereferencelist.py +++ b/openlp/plugins/bibles/lib/versereferencelist.py @@ -94,5 +94,5 @@ class VerseReferenceList(object): result = result + ', ' + version['permission'] result = result.rstrip() if result.endswith(','): - return result[:len(result)-1] + return result[:len(result) - 1] return result diff --git a/openlp/plugins/custom/customplugin.py b/openlp/plugins/custom/customplugin.py index eb7de530a..2065717cb 100644 --- a/openlp/plugins/custom/customplugin.py +++ b/openlp/plugins/custom/customplugin.py @@ -43,7 +43,7 @@ log = logging.getLogger(__name__) __default_settings__ = { 'custom/db type': 'sqlite', - 'custom/last search type': CustomSearch.Titles, + 'custom/last search type': CustomSearch.Titles, 'custom/display footer': True, 'custom/add custom from service': True } @@ -97,12 +97,12 @@ class CustomPlugin(Plugin): """ Called to define all translatable texts of the plugin """ - ## Name PluginList ## + # Name PluginList self.text_strings[StringContent.Name] = { 'singular': translate('CustomPlugin', 'Custom Slide', 'name singular'), 'plural': translate('CustomPlugin', 'Custom Slides', 'name plural') } - ## Name for MediaDockManager, SettingsManager ## + # Name for MediaDockManager, SettingsManager self.text_strings[StringContent.VisibleName] = { 'title': translate('CustomPlugin', 'Custom Slides', 'container title') } diff --git a/openlp/plugins/custom/forms/editcustomslideform.py b/openlp/plugins/custom/forms/editcustomslideform.py index afce4887b..e6834dafb 100644 --- a/openlp/plugins/custom/forms/editcustomslideform.py +++ b/openlp/plugins/custom/forms/editcustomslideform.py @@ -1,4 +1,3 @@ -#lint:disable # -*- coding: utf-8 -*- # vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 diff --git a/openlp/plugins/custom/lib/customxmlhandler.py b/openlp/plugins/custom/lib/customxmlhandler.py index ca42a705b..3e1880055 100644 --- a/openlp/plugins/custom/lib/customxmlhandler.py +++ b/openlp/plugins/custom/lib/customxmlhandler.py @@ -51,7 +51,7 @@ from lxml import etree, objectify log = logging.getLogger(__name__) -#TODO: These classes need to be refactored into a single class. +# TODO: These classes need to be refactored into a single class. class CustomXMLBuilder(object): """ This class builds the XML used to describe songs. diff --git a/openlp/plugins/images/imageplugin.py b/openlp/plugins/images/imageplugin.py index 693449530..08c31f53c 100644 --- a/openlp/plugins/images/imageplugin.py +++ b/openlp/plugins/images/imageplugin.py @@ -95,12 +95,12 @@ class ImagePlugin(Plugin): """ Called to define all translatable texts of the plugin. """ - ## Name PluginList ## + # Name PluginList self.text_strings[StringContent.Name] = { 'singular': translate('ImagePlugin', 'Image', 'name singular'), 'plural': translate('ImagePlugin', 'Images', 'name plural') } - ## Name for MediaDockManager, SettingsManager ## + # Name for MediaDockManager, SettingsManager self.text_strings[StringContent.VisibleName] = {'title': translate('ImagePlugin', 'Images', 'container title')} # Middle Header Bar tooltips = { diff --git a/openlp/plugins/media/lib/mediaitem.py b/openlp/plugins/media/lib/mediaitem.py index 56d3a49f4..a54b37deb 100644 --- a/openlp/plugins/media/lib/mediaitem.py +++ b/openlp/plugins/media/lib/mediaitem.py @@ -95,7 +95,6 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties): self.reset_action.setToolTip(UiStrings().ResetLiveBG) self.automatic = UiStrings().Automatic self.display_type_label.setText(translate('MediaPlugin.MediaItem', 'Use Player:')) - #self.rebuild_players() def required_icons(self): """ @@ -141,7 +140,7 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties): if index == 0: set_media_players(player) else: - set_media_players(player, player[index-1]) + set_media_players(player, player[index - 1]) def on_reset_click(self): """ diff --git a/openlp/plugins/media/mediaplugin.py b/openlp/plugins/media/mediaplugin.py index 6cf241d1d..d0e082f53 100644 --- a/openlp/plugins/media/mediaplugin.py +++ b/openlp/plugins/media/mediaplugin.py @@ -73,12 +73,12 @@ class MediaPlugin(Plugin): """ Called to define all translatable texts of the plugin """ - ## Name PluginList ## + # Name PluginList self.text_strings[StringContent.Name] = { 'singular': translate('MediaPlugin', 'Media', 'name singular'), 'plural': translate('MediaPlugin', 'Media', 'name plural') } - ## Name for MediaDockManager, SettingsManager ## + # Name for MediaDockManager, SettingsManager self.text_strings[StringContent.VisibleName] = { 'title': translate('MediaPlugin', 'Media', 'container title') } diff --git a/openlp/plugins/presentations/lib/impresscontroller.py b/openlp/plugins/presentations/lib/impresscontroller.py index c55873c5f..584c1401f 100644 --- a/openlp/plugins/presentations/lib/impresscontroller.py +++ b/openlp/plugins/presentations/lib/impresscontroller.py @@ -377,8 +377,6 @@ class ImpressDocument(PresentationDocument): Stop the presentation, remove from screen. """ log.debug('stop presentation OpenOffice') - # deactivate should hide the screen according to docs, but doesn't - #self.control.deactivate() self.presentation.end() self.control = None diff --git a/openlp/plugins/presentations/lib/pdfcontroller.py b/openlp/plugins/presentations/lib/pdfcontroller.py index 597b7d78b..1365e59a9 100644 --- a/openlp/plugins/presentations/lib/pdfcontroller.py +++ b/openlp/plugins/presentations/lib/pdfcontroller.py @@ -74,7 +74,7 @@ class PdfController(PresentationController): runlog = '' log.debug('testing program_path: %s', program_path) try: - runlog = check_output([program_path, '--help'], stderr=STDOUT) + runlog = check_output([program_path, '--help'], stderr=STDOUT) except CalledProcessError as e: runlog = e.output except Exception: @@ -183,7 +183,7 @@ class PdfDocument(PresentationDocument): self.image_files = [] self.num_pages = -1 - def gs_get_resolution(self, size): + def gs_get_resolution(self, size): """ Only used when using ghostscript Ghostscript can't scale automatically while keeping aspect like mupdf, so we need @@ -236,7 +236,7 @@ class PdfDocument(PresentationDocument): if os.path.isfile(os.path.join(self.get_temp_folder(), 'mainslide001.png')): created_files = sorted(os.listdir(self.get_temp_folder())) for fn in created_files: - if os.path.isfile(os.path.join(self.get_temp_folder(), fn)): + if os.path.isfile(os.path.join(self.get_temp_folder(), fn)): self.image_files.append(os.path.join(self.get_temp_folder(), fn)) self.num_pages = len(self.image_files) return True diff --git a/openlp/plugins/presentations/lib/powerpointcontroller.py b/openlp/plugins/presentations/lib/powerpointcontroller.py index d6447adce..09fdf2fcc 100644 --- a/openlp/plugins/presentations/lib/powerpointcontroller.py +++ b/openlp/plugins/presentations/lib/powerpointcontroller.py @@ -247,7 +247,7 @@ class PowerpointDocument(PresentationDocument): Starts a presentation from the beginning. """ log.debug('start_presentation') - #SlideShowWindow measures its size/position by points, not pixels + # SlideShowWindow measures its size/position by points, not pixels try: dpi = win32ui.GetActiveWindow().GetDC().GetDeviceCaps(88) except win32ui.error: diff --git a/openlp/plugins/presentations/lib/presentationtab.py b/openlp/plugins/presentations/lib/presentationtab.py index 7d5c81366..590289014 100644 --- a/openlp/plugins/presentations/lib/presentationtab.py +++ b/openlp/plugins/presentations/lib/presentationtab.py @@ -175,10 +175,10 @@ class PresentationTab(SettingsTab): if pdf_program == '': enable_pdf_program = 0 if pdf_program != Settings().value(self.settings_section + '/pdf_program'): - Settings().setValue(self.settings_section + '/pdf_program', pdf_program) + Settings().setValue(self.settings_section + '/pdf_program', pdf_program) changed = True if enable_pdf_program != Settings().value(self.settings_section + '/enable_pdf_program'): - Settings().setValue(self.settings_section + '/enable_pdf_program', enable_pdf_program) + Settings().setValue(self.settings_section + '/enable_pdf_program', enable_pdf_program) changed = True if changed: self.settings_form.register_post_process('mediaitem_suffix_reset') diff --git a/openlp/plugins/presentations/presentationplugin.py b/openlp/plugins/presentations/presentationplugin.py index de0a0c5a5..5e0d7395d 100644 --- a/openlp/plugins/presentations/presentationplugin.py +++ b/openlp/plugins/presentations/presentationplugin.py @@ -156,12 +156,12 @@ class PresentationPlugin(Plugin): """ Called to define all translatable texts of the plugin. """ - ## Name PluginList ## + # Name PluginList self.text_strings[StringContent.Name] = { 'singular': translate('PresentationPlugin', 'Presentation', 'name singular'), 'plural': translate('PresentationPlugin', 'Presentations', 'name plural') } - ## Name for MediaDockManager, SettingsManager ## + # Name for MediaDockManager, SettingsManager self.text_strings[StringContent.VisibleName] = { 'title': translate('PresentationPlugin', 'Presentations', 'container title') } diff --git a/openlp/plugins/remotes/remoteplugin.py b/openlp/plugins/remotes/remoteplugin.py index 393f08dd9..d3dc6e58a 100644 --- a/openlp/plugins/remotes/remoteplugin.py +++ b/openlp/plugins/remotes/remoteplugin.py @@ -92,12 +92,12 @@ class RemotesPlugin(Plugin): """ Called to define all translatable texts of the plugin """ - ## Name PluginList ## + # Name PluginList self.text_strings[StringContent.Name] = { 'singular': translate('RemotePlugin', 'Remote', 'name singular'), 'plural': translate('RemotePlugin', 'Remotes', 'name plural') } - ## Name for MediaDockManager, SettingsManager ## + # Name for MediaDockManager, SettingsManager self.text_strings[StringContent.VisibleName] = { 'title': translate('RemotePlugin', 'Remote', 'container title') } diff --git a/openlp/plugins/songs/forms/duplicatesongremovalform.py b/openlp/plugins/songs/forms/duplicatesongremovalform.py index 22299cde5..d85197223 100644 --- a/openlp/plugins/songs/forms/duplicatesongremovalform.py +++ b/openlp/plugins/songs/forms/duplicatesongremovalform.py @@ -114,7 +114,7 @@ class DuplicateSongRemovalForm(OpenLPWizard, RegistryProperties): self.review_layout.addWidget(self.review_scroll_area) self.review_page_id = self.addPage(self.review_page) # Add a dummy page to the end, to prevent the finish button to appear and the next button do disappear on the - #review page. + # review page. self.dummy_page = QtGui.QWizardPage() self.dummy_page_id = self.addPage(self.dummy_page) @@ -217,12 +217,12 @@ class DuplicateSongRemovalForm(OpenLPWizard, RegistryProperties): duplicate_added = False for duplicate_group in self.duplicate_song_list: # Skip the first song in the duplicate lists, since the first one has to be an earlier song. - if search_song in duplicate_group and not duplicate_song in duplicate_group: + if search_song in duplicate_group and duplicate_song not in duplicate_group: duplicate_group.append(duplicate_song) duplicate_group_found = True duplicate_added = True break - elif not search_song in duplicate_group and duplicate_song in duplicate_group: + elif search_song not in duplicate_group and duplicate_song in duplicate_group: duplicate_group.append(search_song) duplicate_group_found = True duplicate_added = True diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index b655c0f73..60c6eae78 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -675,14 +675,13 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): separator = parts.find(':') if separator >= 0: verse_name = parts[0:separator].strip() - verse_num = parts[separator+1:].strip() + verse_num = parts[separator + 1:].strip() else: verse_name = parts verse_num = '1' verse_index = VerseType.from_loose_input(verse_name) verse_tag = VerseType.tags[verse_index] # Later we need to handle v1a as well. - #regex = re.compile(r'(\d+\w.)') regex = re.compile(r'\D*(\d+)\D*') match = regex.match(verse_num) if match: diff --git a/openlp/plugins/songs/forms/editverseform.py b/openlp/plugins/songs/forms/editverseform.py index 038cff539..79a69a015 100644 --- a/openlp/plugins/songs/forms/editverseform.py +++ b/openlp/plugins/songs/forms/editverseform.py @@ -75,7 +75,7 @@ class EditVerseForm(QtGui.QDialog, Ui_EditVerseDialog): text = self.verse_text_edit.toPlainText() position = self.verse_text_edit.textCursor().position() insert_string = '[---]' - if position and text[position-1] != '\n': + if position and text[position - 1] != '\n': insert_string = '\n' + insert_string if position == len(text) or text[position] != '\n': insert_string += '\n' diff --git a/openlp/plugins/songs/forms/mediafilesdialog.py b/openlp/plugins/songs/forms/mediafilesdialog.py index a0392e3db..3a1db39ed 100644 --- a/openlp/plugins/songs/forms/mediafilesdialog.py +++ b/openlp/plugins/songs/forms/mediafilesdialog.py @@ -66,8 +66,10 @@ class Ui_MediaFilesDialog(object): def retranslateUi(self, media_files_dialog): """ Translate the UI on the fly. + + :param media_files_dialog: """ media_files_dialog.setWindowTitle(translate('SongsPlugin.MediaFilesForm', 'Select Media File(s)')) - self.select_label.setText(translate('SongsPlugin.MediaFilesForm', - 'Select one or more audio files from the list below, and click OK to import them ' - 'into this song.')) + self.select_label.setText(translate('SongsPlugin.MediaFilesForm', 'Select one or more audio files from the ' + 'list below, and click OK to import them ' + 'into this song.')) diff --git a/openlp/plugins/songs/forms/songexportform.py b/openlp/plugins/songs/forms/songexportform.py index 4e345b750..589da4d33 100644 --- a/openlp/plugins/songs/forms/songexportform.py +++ b/openlp/plugins/songs/forms/songexportform.py @@ -152,9 +152,9 @@ class SongExportForm(OpenLPWizard): self.setWindowTitle(translate('SongsPlugin.ExportWizardForm', 'Song Export Wizard')) self.title_label.setText(WizardStrings.HeaderStyle % translate('OpenLP.Ui', 'Welcome to the Song Export Wizard')) - self.information_label.setText(translate('SongsPlugin.ExportWizardForm', 'This wizard will help to' - ' export your songs to the open and free OpenLyrics worship ' - 'song format.')) + self.information_label.setText( + translate('SongsPlugin.ExportWizardForm', 'This wizard will help to export your songs to the open and free ' + 'OpenLyrics worship song format.')) self.available_songs_page.setTitle(translate('SongsPlugin.ExportWizardForm', 'Select Songs')) self.available_songs_page.setSubTitle(translate('SongsPlugin.ExportWizardForm', 'Check the songs you want to export.')) diff --git a/openlp/plugins/songs/lib/cclifileimport.py b/openlp/plugins/songs/lib/cclifileimport.py index 0866a1cc7..5c1fbdcb2 100644 --- a/openlp/plugins/songs/lib/cclifileimport.py +++ b/openlp/plugins/songs/lib/cclifileimport.py @@ -270,13 +270,13 @@ class CCLIFileImport(SongImport): verse_text = '' verse_start = False else: - #line_number=0, song title + # line_number=0, song title if line_number == 0: self.title = clean_line line_number += 1 - #line_number=1, verses + # line_number=1, verses elif line_number == 1: - #line_number=1, ccli number, first line after verses + # line_number=1, ccli number, first line after verses if clean_line.startswith('CCLI'): line_number += 1 ccli_parts = clean_line.split(' ') @@ -319,21 +319,21 @@ class CCLIFileImport(SongImport): # last part. Add l so as to keep the CRLF verse_text = verse_text + line else: - #line_number=2, copyright + # line_number=2, copyright if line_number == 2: line_number += 1 if clean_line.startswith('©'): self.copyright = clean_line else: song_author = clean_line - #n=3, authors + # n=3, authors elif line_number == 3: line_number += 1 if song_author: self.copyright = clean_line else: song_author = clean_line - #line_number=4, comments lines before last line + # line_number=4, comments lines before last line elif line_number == 4 and not clean_line.startswith('CCL'): self.comments += clean_line # split on known separators diff --git a/openlp/plugins/songs/lib/ewimport.py b/openlp/plugins/songs/lib/ewimport.py index cde0b1692..ca201279a 100644 --- a/openlp/plugins/songs/lib/ewimport.py +++ b/openlp/plugins/songs/lib/ewimport.py @@ -305,7 +305,7 @@ class EasyWorshipSongImport(SongImport): elif field_desc.field_type == FieldType.Logical: return field ^ 0x80 == 1 elif field_desc.field_type == FieldType.Memo or field_desc.field_type == FieldType.Blob: - block_start, blob_size = struct.unpack_from(' Date: Sat, 12 Apr 2014 22:22:31 +0200 Subject: [PATCH 120/155] Fix an import issue --- openlp/__init__.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openlp/__init__.py b/openlp/__init__.py index bf7cbc680..98e847979 100644 --- a/openlp/__init__.py +++ b/openlp/__init__.py @@ -30,7 +30,6 @@ The :mod:`openlp` module contains all the project produced OpenLP functionality """ -import core -import plugins +from openlp import core, plugins __all__ = ['core', 'plugins'] From 49ff37c530dd6ab7875a7e4fd37e09ffab8a3d1d Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sun, 13 Apr 2014 09:54:34 +0200 Subject: [PATCH 121/155] fixed long line; use generator --- openlp/core/ui/servicemanager.py | 3 ++- .../songs/forms/duplicatesongremovalform.py | 21 ++++++++----------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 4489360da..09d7908c1 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -235,7 +235,8 @@ class Ui_ServiceManager(object): self.edit_action = create_widget_action(self.menu, text=translate('OpenLP.ServiceManager', '&Edit Item'), icon=':/general/general_edit.png', triggers=self.remote_edit) self.rename_action = create_widget_action(self.menu, text=translate('OpenLP.ServiceManager', '&Rename...'), - icon=':/general/general_edit.png', triggers=self.on_service_item_rename) + icon=':/general/general_edit.png', + triggers=self.on_service_item_rename) self.maintain_action = create_widget_action(self.menu, text=translate('OpenLP.ServiceManager', '&Reorder Item'), icon=':/general/general_edit.png', triggers=self.on_service_item_edit_form) diff --git a/openlp/plugins/songs/forms/duplicatesongremovalform.py b/openlp/plugins/songs/forms/duplicatesongremovalform.py index 1fd81ef07..35eccaeb4 100644 --- a/openlp/plugins/songs/forms/duplicatesongremovalform.py +++ b/openlp/plugins/songs/forms/duplicatesongremovalform.py @@ -46,18 +46,16 @@ from openlp.plugins.songs.lib.songcompare import songs_probably_equal log = logging.getLogger(__name__) -class SongIterator(object): +def song_generator(songs): """ - This class implements an iterator for the song duplicate finder. The iterator returns a tuple of two songs. When - completely iterated then all songs have once been returned combined with any other songs. - """ - def __init__(self, songs): - self.songs = songs + This is a generator function to return tuples of two songs. When completed then all songs have once been returned + combined with any other songs. - def __iter__(self): - for outer_song_counter in range(len(self.songs) - 1): - for inner_song_counter in range(outer_song_counter + 1, len(self.songs)): - yield (self.songs[outer_song_counter], self.songs[inner_song_counter]) + :param songs: All songs in the database. + """ + for outer_song_counter in range(len(songs) - 1): + for inner_song_counter in range(outer_song_counter + 1, len(songs)): + yield (songs[outer_song_counter], songs[inner_song_counter]) class DuplicateSongRemovalForm(OpenLPWizard, RegistryProperties): @@ -185,8 +183,7 @@ class DuplicateSongRemovalForm(OpenLPWizard, RegistryProperties): # Create a worker/process pool to check the songs. process_number = max(1, multiprocessing.cpu_count() - 1) pool = multiprocessing.Pool(process_number) - song_list = SongIterator(songs) - result = pool.imap_unordered(songs_probably_equal, song_list, 30) + result = pool.imap_unordered(songs_probably_equal, song_generator(songs), 30) # Do not accept any further tasks. Also this closes the processes if all tasks are done. pool.close() # While the processes are still working, start to look at the results. From 921f238b86b2e30b8550e347a7662c66d999fcf0 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sun, 13 Apr 2014 10:15:53 +0200 Subject: [PATCH 122/155] use colours to indicate failure/success --- scripts/jenkins_script.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/scripts/jenkins_script.py b/scripts/jenkins_script.py index c42adf09e..7dd84b04a 100644 --- a/scripts/jenkins_script.py +++ b/scripts/jenkins_script.py @@ -68,6 +68,16 @@ class OpenLPJobs(object): Jobs = [Branch_Pull, Branch_Functional, Branch_Interface, Branch_Windows, Branch_PEP] +class Colour(object): + """ + This class holds values which can be used to print coloured text. + """ + RED_START = '\033[1;31m' + RED_END = '\033[1;m' + GREEN_START = '\033[1;32m' + GREEN_END = '\033[1;m' + + class JenkinsTrigger(object): def __init__(self, token): """ @@ -125,7 +135,12 @@ class JenkinsTrigger(object): time.sleep(1) build = job.last_build build.wait() - result_string = build.info['result'] + if build.info['result'] == 'SUCCESS': + # Make 'SUCCESS' green. + result_string = '%s%s%s' % (Colour.GREEN_START, build.info['result'], Colour.GREEN_END) + else: + # Make 'FAILURE' red. + result_string = '%s%s%s' % (Colour.RED_START, build.info['result'], Colour.RED_END) url = build.info['url'] print('[%s] %s' % (result_string, url)) From 7e064246fbfa7c106e7a518f0519bf712325128a Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sun, 13 Apr 2014 10:21:30 +0200 Subject: [PATCH 123/155] fixed counter --- openlp/plugins/songs/forms/duplicatesongremovalform.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openlp/plugins/songs/forms/duplicatesongremovalform.py b/openlp/plugins/songs/forms/duplicatesongremovalform.py index 35eccaeb4..930e8cf37 100644 --- a/openlp/plugins/songs/forms/duplicatesongremovalform.py +++ b/openlp/plugins/songs/forms/duplicatesongremovalform.py @@ -200,6 +200,7 @@ class DuplicateSongRemovalForm(OpenLPWizard, RegistryProperties): duplicate_added = self.add_duplicates_to_song_list(song1, song2) if duplicate_added: self.found_duplicates_edit.appendPlainText(song1.title + " = " + song2.title) + self.review_total_count = len(self.duplicate_song_list) if self.duplicate_song_list: self.button(QtGui.QWizard.NextButton).show() else: From e0a85a668b2980ffd2ebfa0b9e701305243427a6 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Sun, 13 Apr 2014 12:45:53 +0200 Subject: [PATCH 124/155] Leading whitespace --- tests/functional/openlp_core_lib/test_ui.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/functional/openlp_core_lib/test_ui.py b/tests/functional/openlp_core_lib/test_ui.py index db926b323..8f7770f1c 100644 --- a/tests/functional/openlp_core_lib/test_ui.py +++ b/tests/functional/openlp_core_lib/test_ui.py @@ -88,7 +88,7 @@ class TestUi(TestCase): """ # GIVEN: A dialog dialog = QtGui.QDialog() - + # WHEN: We create the button btn = create_button(dialog, 'my_btn') @@ -120,7 +120,7 @@ class TestUi(TestCase): """ # GIVEN: A dialog dialog = QtGui.QDialog() - + # WHEN: We create the widgets label, combo = create_valign_selection_widgets(dialog) From 69500907c076344417e2bd231c40a965f1f672c5 Mon Sep 17 00:00:00 2001 From: Stefan Strasser Date: Sun, 13 Apr 2014 19:03:28 +0200 Subject: [PATCH 125/155] Songbeamer-import: added additional verse-marks for recognition (Misc,Part,Teil,$$M=), changed check_verse_marks to handle the special mark $$M=, page-break-recognition handles now also -- --- openlp/plugins/songs/lib/songbeamerimport.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/openlp/plugins/songs/lib/songbeamerimport.py b/openlp/plugins/songs/lib/songbeamerimport.py index 0106ca687..5b86591e8 100644 --- a/openlp/plugins/songs/lib/songbeamerimport.py +++ b/openlp/plugins/songs/lib/songbeamerimport.py @@ -56,11 +56,15 @@ class SongBeamerTypes(object): 'Zwischenspiel': VerseType.tags[VerseType.Bridge], 'Pre-Chorus': VerseType.tags[VerseType.PreChorus], 'Pre-Refrain': VerseType.tags[VerseType.PreChorus], + 'Misc': VerseType.tags[VerseType.Other], 'Pre-Bridge': VerseType.tags[VerseType.Other], 'Pre-Coda': VerseType.tags[VerseType.Other], + 'Part': VerseType.tags[VerseType.Other], + 'Teil': VerseType.tags[VerseType.Other], 'Unbekannt': VerseType.tags[VerseType.Other], 'Unknown': VerseType.tags[VerseType.Other], - 'Unbenannt': VerseType.tags[VerseType.Other] + 'Unbenannt': VerseType.tags[VerseType.Other], + '$$M=': VerseType.tags[VerseType.Other] } @@ -132,7 +136,8 @@ class SongBeamerImport(SongImport): line = str(line).strip() if line.startswith('#') and not read_verses: self.parseTags(line) - elif line.startswith('---'): + elif line.startswith('--'): + # --- and -- allowed for page-breaks (difference in Songbeamer only in printout) if self.current_verse: self.replace_html_tags() self.add_verse(self.current_verse, self.current_verse_type) @@ -282,4 +287,7 @@ class SongBeamerImport(SongImport): if marks[1].isdigit(): self.current_verse_type += marks[1] return True + elif marks[0].startswith('$$M='): # this verse-mark cannot be numbered + self.current_verse_type = SongBeamerTypes.MarkTypes['$$M='] + return True return False From 029381513568a420c6e56071bc565ff18244e17c Mon Sep 17 00:00:00 2001 From: Stefan Strasser Date: Sun, 13 Apr 2014 19:08:38 +0200 Subject: [PATCH 126/155] Songbeamer-import: added check_verse_marks_test to test the correct detection of different lines that may occur --- .../songs/test_songbeamerimport.py | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/tests/functional/openlp_plugins/songs/test_songbeamerimport.py b/tests/functional/openlp_plugins/songs/test_songbeamerimport.py index e7bd891d3..dbf72b068 100644 --- a/tests/functional/openlp_plugins/songs/test_songbeamerimport.py +++ b/tests/functional/openlp_plugins/songs/test_songbeamerimport.py @@ -35,6 +35,7 @@ from unittest import TestCase from tests.functional import MagicMock, patch from openlp.plugins.songs.lib.songbeamerimport import SongBeamerImport +from openlp.plugins.songs.lib import VerseType TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..', 'resources', 'songbeamersongs')) @@ -153,3 +154,80 @@ class TestSongBeamerImport(TestCase): self.assertEquals(importer.song_number, song_number, 'song_number for %s should be %s' % (song_file, song_number)) mocked_finish.assert_called_with() + + def check_verse_marks_test(self): + """ + Tests different lines to see if a verse mark is detected or not + """ + + # GIVEN: line with unnumbered verse-type + line = 'Refrain' + self.current_verse_type = None + # WHEN: line is being checked for verse marks + result = SongBeamerImport.check_verse_marks(self, line) + # THEN: we should get back true and c as self.current_verse_type + assert result is True, u'Versemark for should be found, value true' + assert self.current_verse_type == 'c', u' should be interpreted as ' + + # GIVEN: line with unnumbered verse-type and trailing space + line = 'Refrain ' + self.current_verse_type = None + # WHEN: line is being checked for verse marks + result = SongBeamerImport.check_verse_marks(self, line) + # THEN: we should get back true and c as self.current_verse_type + assert result is True, u'Versemark for should be found, value true' + assert self.current_verse_type == 'c', u' should be interpreted as ' + + # GIVEN: line with numbered verse-type + line = 'Verse 1' + self.current_verse_type = None + # WHEN: line is being checked for verse marks + result = SongBeamerImport.check_verse_marks(self, line) + # THEN: we should get back true and v1 as self.current_verse_type + assert result is True, u'Versemark for should be found, value true' + assert self.current_verse_type == 'v1', u' should be interpreted as ' + + # GIVEN: line with special unnumbered verse-mark (used in Songbeamer to allow usage of non-supported tags) + line = '$$M=special' + self.current_verse_type = None + # WHEN: line is being checked for verse marks + result = SongBeamerImport.check_verse_marks(self, line) + # THEN: we should get back true and o as self.current_verse_type + assert result is True, u'Versemark for <$$M=special> should be found, value true' + assert self.current_verse_type == 'o', u'<$$M=special> should be interpreted as ' + + # GIVEN: line with song-text with 3 words + line = 'Jesus my saviour' + self.current_verse_type = None + # WHEN: line is being checked for verse marks + result = SongBeamerImport.check_verse_marks(self, line) + # THEN: we should get back false and none as self.current_verse_type + assert result is False, u'No versemark for should be found, value false' + assert self.current_verse_type is None, u' should be interpreted as none versemark' + + # GIVEN: line with song-text with 2 words + line = 'Praise him' + self.current_verse_type = None + # WHEN: line is being checked for verse marks + result = SongBeamerImport.check_verse_marks(self, line) + # THEN: we should get back false and none as self.current_verse_type + assert result is False, u'No versemark for should be found, value false' + assert self.current_verse_type is None, u' should be interpreted as none versemark' + + # GIVEN: line with only a space (could occur, nothing regular) + line = ' ' + self.current_verse_type = None + # WHEN: line is being checked for verse marks + result = SongBeamerImport.check_verse_marks(self, line) + # THEN: we should get back false and none as self.current_verse_type + assert result is False, u'No versemark for < > should be found, value false' + assert self.current_verse_type is None, u'< > should be interpreted as none versemark' + + # GIVEN: blank line (could occur, nothing regular) + line = '' + self.current_verse_type = None + # WHEN: line is being checked for verse marks + result = SongBeamerImport.check_verse_marks(self, line) + # THEN: we should get back false and none as self.current_verse_type + assert result is False, u'No versemark for <> should be found, value false' + assert self.current_verse_type is None, u'<> should be interpreted as none versemark' From 4a4f8c060b2f68f08469c1a12a8e41e8cca03cd0 Mon Sep 17 00:00:00 2001 From: Stefan Strasser Date: Mon, 14 Apr 2014 20:33:34 +0200 Subject: [PATCH 127/155] Songbeamer-import-test: replaced usage of assert with the testcase-functions assertTrue, assertEqual, assertIsNone; replaced all occurrences of assertEquals() with assertEqual() (deprecated alias) --- .../songs/test_songbeamerimport.py | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/tests/functional/openlp_plugins/songs/test_songbeamerimport.py b/tests/functional/openlp_plugins/songs/test_songbeamerimport.py index dbf72b068..f08cedec5 100644 --- a/tests/functional/openlp_plugins/songs/test_songbeamerimport.py +++ b/tests/functional/openlp_plugins/songs/test_songbeamerimport.py @@ -90,7 +90,7 @@ class TestSongBeamerImport(TestCase): # THEN: do_import should return none and the progress bar maximum should not be set. self.assertIsNone(importer.do_import(), 'do_import should return None when import_source is not a list') - self.assertEquals(mocked_import_wizard.progress_bar.setMaximum.called, False, + self.assertEqual(mocked_import_wizard.progress_bar.setMaximum.called, False, 'setMaxium on import_wizard.progress_bar should not have been called') def valid_import_source_test(self): @@ -144,14 +144,14 @@ class TestSongBeamerImport(TestCase): # THEN: do_import should return none, the song data should be as expected, and finish should have been # called. self.assertIsNone(importer.do_import(), 'do_import should return None when it has completed') - self.assertEquals(importer.title, title, 'title for %s should be "%s"' % (song_file, title)) + self.assertEqual(importer.title, title, 'title for %s should be "%s"' % (song_file, title)) for verse_text, verse_tag in add_verse_calls: mocked_add_verse.assert_any_call(verse_text, verse_tag) if song_book_name: - self.assertEquals(importer.song_book_name, song_book_name, 'song_book_name for %s should be "%s"' % + self.assertEqual(importer.song_book_name, song_book_name, 'song_book_name for %s should be "%s"' % (song_file, song_book_name)) if song_number: - self.assertEquals(importer.song_number, song_number, 'song_number for %s should be %s' % + self.assertEqual(importer.song_number, song_number, 'song_number for %s should be %s' % (song_file, song_number)) mocked_finish.assert_called_with() @@ -166,8 +166,8 @@ class TestSongBeamerImport(TestCase): # WHEN: line is being checked for verse marks result = SongBeamerImport.check_verse_marks(self, line) # THEN: we should get back true and c as self.current_verse_type - assert result is True, u'Versemark for should be found, value true' - assert self.current_verse_type == 'c', u' should be interpreted as ' + self.assertTrue(result, 'Versemark for should be found, value true') + self.assertEqual(self.current_verse_type, 'c', ' should be interpreted as ') # GIVEN: line with unnumbered verse-type and trailing space line = 'Refrain ' @@ -175,8 +175,8 @@ class TestSongBeamerImport(TestCase): # WHEN: line is being checked for verse marks result = SongBeamerImport.check_verse_marks(self, line) # THEN: we should get back true and c as self.current_verse_type - assert result is True, u'Versemark for should be found, value true' - assert self.current_verse_type == 'c', u' should be interpreted as ' + self.assertTrue(result, 'Versemark for should be found, value true') + self.assertEqual(self.current_verse_type, 'c', ' should be interpreted as ') # GIVEN: line with numbered verse-type line = 'Verse 1' @@ -184,8 +184,8 @@ class TestSongBeamerImport(TestCase): # WHEN: line is being checked for verse marks result = SongBeamerImport.check_verse_marks(self, line) # THEN: we should get back true and v1 as self.current_verse_type - assert result is True, u'Versemark for should be found, value true' - assert self.current_verse_type == 'v1', u' should be interpreted as ' + self.assertTrue(result, 'Versemark for should be found, value true') + self.assertEqual(self.current_verse_type, 'v1', u' should be interpreted as ') # GIVEN: line with special unnumbered verse-mark (used in Songbeamer to allow usage of non-supported tags) line = '$$M=special' @@ -193,8 +193,8 @@ class TestSongBeamerImport(TestCase): # WHEN: line is being checked for verse marks result = SongBeamerImport.check_verse_marks(self, line) # THEN: we should get back true and o as self.current_verse_type - assert result is True, u'Versemark for <$$M=special> should be found, value true' - assert self.current_verse_type == 'o', u'<$$M=special> should be interpreted as ' + self.assertTrue(result, 'Versemark for <$$M=special> should be found, value true') + self.assertEqual(self.current_verse_type, 'o', u'<$$M=special> should be interpreted as ') # GIVEN: line with song-text with 3 words line = 'Jesus my saviour' @@ -202,8 +202,8 @@ class TestSongBeamerImport(TestCase): # WHEN: line is being checked for verse marks result = SongBeamerImport.check_verse_marks(self, line) # THEN: we should get back false and none as self.current_verse_type - assert result is False, u'No versemark for should be found, value false' - assert self.current_verse_type is None, u' should be interpreted as none versemark' + self.assertFalse(result, 'No versemark for should be found, value false') + self.assertIsNone(self.current_verse_type, ' should be interpreted as none versemark') # GIVEN: line with song-text with 2 words line = 'Praise him' @@ -211,8 +211,8 @@ class TestSongBeamerImport(TestCase): # WHEN: line is being checked for verse marks result = SongBeamerImport.check_verse_marks(self, line) # THEN: we should get back false and none as self.current_verse_type - assert result is False, u'No versemark for should be found, value false' - assert self.current_verse_type is None, u' should be interpreted as none versemark' + self.assertFalse(result, 'No versemark for should be found, value false') + self.assertIsNone(self.current_verse_type, ' should be interpreted as none versemark') # GIVEN: line with only a space (could occur, nothing regular) line = ' ' @@ -220,8 +220,8 @@ class TestSongBeamerImport(TestCase): # WHEN: line is being checked for verse marks result = SongBeamerImport.check_verse_marks(self, line) # THEN: we should get back false and none as self.current_verse_type - assert result is False, u'No versemark for < > should be found, value false' - assert self.current_verse_type is None, u'< > should be interpreted as none versemark' + self.assertFalse(result, 'No versemark for < > should be found, value false') + self.assertIsNone(self.current_verse_type, '< > should be interpreted as none versemark') # GIVEN: blank line (could occur, nothing regular) line = '' @@ -229,5 +229,5 @@ class TestSongBeamerImport(TestCase): # WHEN: line is being checked for verse marks result = SongBeamerImport.check_verse_marks(self, line) # THEN: we should get back false and none as self.current_verse_type - assert result is False, u'No versemark for <> should be found, value false' - assert self.current_verse_type is None, u'<> should be interpreted as none versemark' + self.assertFalse(result, 'No versemark for <> should be found, value false') + self.assertIsNone(self.current_verse_type, '<> should be interpreted as none versemark') From 2c8d163b09bf84f4db5c59f7508d45baf379841f Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 14 Apr 2014 20:59:28 +0200 Subject: [PATCH 128/155] fixed assertion message --- tests/functional/openlp_core_lib/test_renderer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/functional/openlp_core_lib/test_renderer.py b/tests/functional/openlp_core_lib/test_renderer.py index 22b7c4a7b..8814a21a0 100644 --- a/tests/functional/openlp_core_lib/test_renderer.py +++ b/tests/functional/openlp_core_lib/test_renderer.py @@ -102,8 +102,8 @@ class TestRenderer(TestCase): result = renderer._get_start_tags(given_raw_text) # THEN: Check if the correct tuple is returned. - self.assertEqual(result, expected_tuple), 'A tuple should be returned ' - '(fixed-text, opening tags, html opening tags).' + self.assertEqual(result, expected_tuple), 'A tuple should be returned containing the text with correct ' \ + 'tags, the opening tags, and the opening html tags.' def _word_split_test(self): """ From ba5320920a173ff47bbf6ff170690ce516ed0a57 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 14 Apr 2014 21:01:48 +0200 Subject: [PATCH 129/155] added more jenkins jobs --- scripts/jenkins_script.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/jenkins_script.py b/scripts/jenkins_script.py index 7dd84b04a..a1649a987 100644 --- a/scripts/jenkins_script.py +++ b/scripts/jenkins_script.py @@ -63,9 +63,10 @@ class OpenLPJobs(object): Branch_Functional = 'Branch-02-Functional-Tests' Branch_Interface = 'Branch-03-Interface-Tests' Branch_Windows = 'Branch-04-Windows_Tests' - Branch_PEP = 'Branch-05-Code-Analysis' + Branch_PEP = 'Branch-05a-Code_Analysis' + Branch_Coverage = 'Branch-05b-Test-Coverage' - Jobs = [Branch_Pull, Branch_Functional, Branch_Interface, Branch_Windows, Branch_PEP] + Jobs = [Branch_Pull, Branch_Functional, Branch_Interface, Branch_Windows, Branch_PEP, Branch_Coverage] class Colour(object): From a07a97f78b1924a62772f6738bacc4d0f0ef73b3 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 14 Apr 2014 21:07:01 +0200 Subject: [PATCH 130/155] strange pyqt fix --- openlp/core/ui/servicemanager.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 1796ddc11..70cbd6141 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -1489,9 +1489,11 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ServiceManage if new_item: self.add_service_item(new_item, replace=True) - def on_service_item_rename(self): + def on_service_item_rename(self, field=None): """ Opens a dialog to rename the service item. + + :param field: Not used, but PyQt needs this. """ item = self.find_service_item()[0] if not self.service_items[item]['service_item'].is_capable(ItemCapabilities.CanEditTitle): From 9950a06b1f08d69420c3296ca68682462b0aa06e Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 14 Apr 2014 21:16:46 +0200 Subject: [PATCH 131/155] fix name --- scripts/jenkins_script.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/jenkins_script.py b/scripts/jenkins_script.py index a1649a987..aaee9a71b 100644 --- a/scripts/jenkins_script.py +++ b/scripts/jenkins_script.py @@ -64,7 +64,7 @@ class OpenLPJobs(object): Branch_Interface = 'Branch-03-Interface-Tests' Branch_Windows = 'Branch-04-Windows_Tests' Branch_PEP = 'Branch-05a-Code_Analysis' - Branch_Coverage = 'Branch-05b-Test-Coverage' + Branch_Coverage = 'Branch-05b-Test_Coverage' Jobs = [Branch_Pull, Branch_Functional, Branch_Interface, Branch_Windows, Branch_PEP, Branch_Coverage] From 0c2112091d0819f90d5f7a00f785aeaa0d63ee2c Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 14 Apr 2014 21:36:07 +0200 Subject: [PATCH 132/155] fixed spelling --- tests/functional/openlp_plugins/songs/test_lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/openlp_plugins/songs/test_lib.py b/tests/functional/openlp_plugins/songs/test_lib.py index 2b7f107e8..2ab808bc9 100644 --- a/tests/functional/openlp_plugins/songs/test_lib.py +++ b/tests/functional/openlp_plugins/songs/test_lib.py @@ -141,7 +141,7 @@ class TestLib(TestCase): # WHEN: We compare those songs for equality. result = songs_probably_equal((self.song1, self.song2)) - # THEN: The result should be Nonw. + # THEN: The result should be None. assert result is None, 'The result should be None' def remove_typos_beginning_test(self): From 97ecc3442f918bd43dd6043180966e8b56d0b141 Mon Sep 17 00:00:00 2001 From: Jonathan Springer Date: Mon, 14 Apr 2014 15:45:41 -0400 Subject: [PATCH 133/155] Fix bzt tag test to handle being run from a different directory --- tests/utils/test_bzr_tags.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/utils/test_bzr_tags.py b/tests/utils/test_bzr_tags.py index 7bb38f4ab..73817e883 100644 --- a/tests/utils/test_bzr_tags.py +++ b/tests/utils/test_bzr_tags.py @@ -29,6 +29,7 @@ """ Package to test for proper bzr tags. """ +import os from unittest import TestCase @@ -63,11 +64,12 @@ class TestBzrTags(TestCase): Test for proper bzr tags """ # GIVEN: A bzr branch + path = os.path.dirname(__file__) # WHEN getting the branches tags - bzr = Popen(('bzr', 'tags'), stdout=PIPE) + bzr = Popen(('bzr', 'tags', '--directory=' + path), stdout=PIPE) stdout = bzr.communicate()[0] tags = [line.decode('utf-8').split() for line in stdout.splitlines()] # THEN the tags should match the accepted tags - self.assertEqual(TAGS, tags, 'List of tags should match') + self.assertEqual(TAGS, tags, path) From 6c86c9768db373988b1dda70cbd1a34ebeca2891 Mon Sep 17 00:00:00 2001 From: Jonathan Springer Date: Mon, 14 Apr 2014 16:01:13 -0400 Subject: [PATCH 134/155] Fix typo --- tests/utils/test_bzr_tags.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/utils/test_bzr_tags.py b/tests/utils/test_bzr_tags.py index 73817e883..95017e94f 100644 --- a/tests/utils/test_bzr_tags.py +++ b/tests/utils/test_bzr_tags.py @@ -72,4 +72,4 @@ class TestBzrTags(TestCase): tags = [line.decode('utf-8').split() for line in stdout.splitlines()] # THEN the tags should match the accepted tags - self.assertEqual(TAGS, tags, path) + self.assertEqual(TAGS, tags, 'List of tags should match') From 5e1a7ce767d823051768fa53f2f483681d809d8f Mon Sep 17 00:00:00 2001 From: Stefan Strasser Date: Tue, 15 Apr 2014 07:28:51 +0200 Subject: [PATCH 135/155] replaced all occurrences of the deprecated alias assertEquals() with assertEqual() --- .../test_registryproperties.py | 4 ++-- .../openlp_plugins/bibles/test_http.py | 2 +- .../openlp_plugins/songs/test_ewimport.py | 16 ++++++++-------- .../songs/test_songshowplusimport.py | 6 +++--- tests/helpers/songfileimport.py | 14 +++++++------- .../bibles/test_lib_parse_reference.py | 6 +++--- 6 files changed, 24 insertions(+), 24 deletions(-) diff --git a/tests/functional/openlp_core_common/test_registryproperties.py b/tests/functional/openlp_core_common/test_registryproperties.py index fa8a2b540..c2e430a95 100644 --- a/tests/functional/openlp_core_common/test_registryproperties.py +++ b/tests/functional/openlp_core_common/test_registryproperties.py @@ -52,7 +52,7 @@ class TestRegistryProperties(TestCase, RegistryProperties): # GIVEN an Empty Registry # WHEN there is no Application # THEN the application should be none - self.assertEquals(self.application, None, 'The application value should be None') + self.assertEqual(self.application, None, 'The application value should be None') def application_test(self): """ @@ -63,4 +63,4 @@ class TestRegistryProperties(TestCase, RegistryProperties): # WHEN the application is registered Registry().register('application', application) # THEN the application should be none - self.assertEquals(self.application, application, 'The application value should match') + self.assertEqual(self.application, application, 'The application value should match') diff --git a/tests/functional/openlp_plugins/bibles/test_http.py b/tests/functional/openlp_plugins/bibles/test_http.py index b9bb8f11c..05a59a509 100644 --- a/tests/functional/openlp_plugins/bibles/test_http.py +++ b/tests/functional/openlp_plugins/bibles/test_http.py @@ -180,4 +180,4 @@ class TestBSExtract(TestCase): 'http://m.bibleserver.com/overlay/selectBook?translation=NIV') self.assertFalse(self.mock_log.error.called, 'log.error should not have been called') self.assertFalse(self.mock_send_error_message.called, 'send_error_message should not have been called') - self.assertEquals(result, ['Genesis', 'Leviticus']) + self.assertEqual(result, ['Genesis', 'Leviticus']) diff --git a/tests/functional/openlp_plugins/songs/test_ewimport.py b/tests/functional/openlp_plugins/songs/test_ewimport.py index 9e327517c..c1b9db52d 100644 --- a/tests/functional/openlp_plugins/songs/test_ewimport.py +++ b/tests/functional/openlp_plugins/songs/test_ewimport.py @@ -139,10 +139,10 @@ class TestEasyWorshipSongImport(TestCase): # THEN: self.assertIsNotNone(field_desc_entry, 'Import should not be none') - self.assertEquals(field_desc_entry.name, name, 'FieldDescEntry.name should be the same as the name argument') - self.assertEquals(field_desc_entry.field_type, field_type, + self.assertEqual(field_desc_entry.name, name, 'FieldDescEntry.name should be the same as the name argument') + self.assertEqual(field_desc_entry.field_type, field_type, 'FieldDescEntry.type should be the same as the type argument') - self.assertEquals(field_desc_entry.size, size, 'FieldDescEntry.size should be the same as the size argument') + self.assertEqual(field_desc_entry.size, size, 'FieldDescEntry.size should be the same as the size argument') def create_importer_test(self): """ @@ -174,7 +174,7 @@ class TestEasyWorshipSongImport(TestCase): for field_name in existing_fields: # THEN: The item corresponding the index returned should have the same name attribute - self.assertEquals(importer.field_descriptions[importer.find_field(field_name)].name, field_name) + self.assertEqual(importer.field_descriptions[importer.find_field(field_name)].name, field_name) def find_non_existing_field_test(self): """ @@ -230,7 +230,7 @@ class TestEasyWorshipSongImport(TestCase): return_value = importer.get_field(field_index) # THEN: get_field should return the known results - self.assertEquals(return_value, result, + self.assertEqual(return_value, result, 'get_field should return "%s" when called with "%s"' % (result, TEST_FIELDS[field_index])) @@ -257,7 +257,7 @@ class TestEasyWorshipSongImport(TestCase): get_field_seek_calls = test_results[2]['seek'] # THEN: get_field should return the appropriate value with the appropriate mocked objects being called - self.assertEquals(importer.get_field(field_index), get_field_result) + self.assertEqual(importer.get_field(field_index), get_field_result) for call in get_field_read_calls: mocked_memo_file.read.assert_any_call(call) for call in get_field_seek_calls: @@ -403,11 +403,11 @@ class TestEasyWorshipSongImport(TestCase): if song_copyright: self.assertEqual(importer.copyright, song_copyright) if ccli_number: - self.assertEquals(importer.ccli_number, ccli_number, 'ccli_number for %s should be %s' + self.assertEqual(importer.ccli_number, ccli_number, 'ccli_number for %s should be %s' % (title, ccli_number)) for verse_text, verse_tag in add_verse_calls: mocked_add_verse.assert_any_call(verse_text, verse_tag) if verse_order_list: - self.assertEquals(importer.verse_order_list, verse_order_list, + self.assertEqual(importer.verse_order_list, verse_order_list, 'verse_order_list for %s should be %s' % (title, verse_order_list)) mocked_finish.assert_called_with() diff --git a/tests/functional/openlp_plugins/songs/test_songshowplusimport.py b/tests/functional/openlp_plugins/songs/test_songshowplusimport.py index 7876558e9..f2839c332 100644 --- a/tests/functional/openlp_plugins/songs/test_songshowplusimport.py +++ b/tests/functional/openlp_plugins/songs/test_songshowplusimport.py @@ -95,7 +95,7 @@ class TestSongShowPlusImport(TestCase): # THEN: do_import should return none and the progress bar maximum should not be set. self.assertIsNone(importer.do_import(), 'do_import should return None when import_source is not a list') - self.assertEquals(mocked_import_wizard.progress_bar.setMaximum.called, False, + self.assertEqual(mocked_import_wizard.progress_bar.setMaximum.called, False, 'setMaximum on import_wizard.progress_bar should not have been called') def valid_import_source_test(self): @@ -143,7 +143,7 @@ class TestSongShowPlusImport(TestCase): # THEN: The returned value should should correlate with the input arguments for original_tag, openlp_tag in test_values: - self.assertEquals(importer.to_openlp_verse_tag(original_tag), openlp_tag, + self.assertEqual(importer.to_openlp_verse_tag(original_tag), openlp_tag, 'SongShowPlusImport.to_openlp_verse_tag should return "%s" when called with "%s"' % (openlp_tag, original_tag)) @@ -172,6 +172,6 @@ class TestSongShowPlusImport(TestCase): # THEN: The returned value should should correlate with the input arguments for original_tag, openlp_tag in test_values: - self.assertEquals(importer.to_openlp_verse_tag(original_tag, ignore_unique=True), openlp_tag, + self.assertEqual(importer.to_openlp_verse_tag(original_tag, ignore_unique=True), openlp_tag, 'SongShowPlusImport.to_openlp_verse_tag should return "%s" when called with "%s"' % (openlp_tag, original_tag)) diff --git a/tests/helpers/songfileimport.py b/tests/helpers/songfileimport.py index cc67770c1..49a09528c 100644 --- a/tests/helpers/songfileimport.py +++ b/tests/helpers/songfileimport.py @@ -110,29 +110,29 @@ class SongImportTestHelper(TestCase): # THEN: do_import should return none, the song data should be as expected, and finish should have been # called. self.assertIsNone(importer.do_import(), 'do_import should return None when it has completed') - self.assertEquals(importer.title, title, 'title for %s should be "%s"' % (source_file_name, title)) + self.assertEqual(importer.title, title, 'title for %s should be "%s"' % (source_file_name, title)) for author in author_calls: self.mocked_parse_author.assert_any_call(author) if song_copyright: self.mocked_add_copyright.assert_called_with(song_copyright) if ccli_number: - self.assertEquals(importer.ccli_number, ccli_number, 'ccli_number for %s should be %s' % + self.assertEqual(importer.ccli_number, ccli_number, 'ccli_number for %s should be %s' % (source_file_name, ccli_number)) for verse_text, verse_tag in add_verse_calls: self.mocked_add_verse.assert_any_call(verse_text, verse_tag) if topics: - self.assertEquals(importer.topics, topics, 'topics for %s should be %s' % (source_file_name, topics)) + self.assertEqual(importer.topics, topics, 'topics for %s should be %s' % (source_file_name, topics)) if comments: - self.assertEquals(importer.comments, comments, 'comments for %s should be "%s"' % + self.assertEqual(importer.comments, comments, 'comments for %s should be "%s"' % (source_file_name, comments)) if song_book_name: - self.assertEquals(importer.song_book_name, song_book_name, 'song_book_name for %s should be "%s"' % + self.assertEqual(importer.song_book_name, song_book_name, 'song_book_name for %s should be "%s"' % (source_file_name, song_book_name)) if song_number: - self.assertEquals(importer.song_number, song_number, 'song_number for %s should be %s' % + self.assertEqual(importer.song_number, song_number, 'song_number for %s should be %s' % (source_file_name, song_number)) if verse_order_list: - self.assertEquals(importer.verse_order_list, [], 'verse_order_list for %s should be %s' % + self.assertEqual(importer.verse_order_list, [], 'verse_order_list for %s should be %s' % (source_file_name, verse_order_list)) self.mocked_finish.assert_called_with() diff --git a/tests/interfaces/openlp_plugins/bibles/test_lib_parse_reference.py b/tests/interfaces/openlp_plugins/bibles/test_lib_parse_reference.py index b085bd1df..84f80e7ed 100644 --- a/tests/interfaces/openlp_plugins/bibles/test_lib_parse_reference.py +++ b/tests/interfaces/openlp_plugins/bibles/test_lib_parse_reference.py @@ -83,7 +83,7 @@ class TestBibleManager(TestCase, TestMixin): # WHEN asking to parse the bible reference results = parse_reference('1 Timothy 1', self.manager.db_cache['tests'], MagicMock(), 54) # THEN a verse array should be returned - self.assertEquals([(54, 1, 1, -1)], results, "The bible verses should matches the expected results") + self.assertEqual([(54, 1, 1, -1)], results, "The bible verses should matches the expected results") def parse_reference_two_test(self): """ @@ -93,7 +93,7 @@ class TestBibleManager(TestCase, TestMixin): # WHEN asking to parse the bible reference results = parse_reference('1 Timothy 1:1-2', self.manager.db_cache['tests'], MagicMock(), 54) # THEN a verse array should be returned - self.assertEquals([(54, 1, 1, 2)], results, "The bible verses should matches the expected results") + self.assertEqual([(54, 1, 1, 2)], results, "The bible verses should matches the expected results") def parse_reference_three_test(self): """ @@ -103,5 +103,5 @@ class TestBibleManager(TestCase, TestMixin): # WHEN asking to parse the bible reference results = parse_reference('1 Timothy 1:1-2:1', self.manager.db_cache['tests'], MagicMock(), 54) # THEN a verse array should be returned - self.assertEquals([(54, 1, 1, -1), (54, 2, 1, 1)], results, "The bible verses should matches the expected " + self.assertEqual([(54, 1, 1, -1), (54, 2, 1, 1)], results, "The bible verses should matches the expected " "results") From c204967304333074115d7bc2eeba847e617d6a2a Mon Sep 17 00:00:00 2001 From: Jonathan Springer Date: Tue, 15 Apr 2014 14:42:08 -0400 Subject: [PATCH 136/155] Fixes for PEP8 --- openlp/core/ui/servicemanager.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 9bc63eae6..1796ddc11 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -235,7 +235,8 @@ class Ui_ServiceManager(object): self.edit_action = create_widget_action(self.menu, text=translate('OpenLP.ServiceManager', '&Edit Item'), icon=':/general/general_edit.png', triggers=self.remote_edit) self.rename_action = create_widget_action(self.menu, text=translate('OpenLP.ServiceManager', '&Rename...'), - icon=':/general/general_edit.png', triggers=self.on_service_item_rename) + icon=':/general/general_edit.png', + triggers=self.on_service_item_rename) self.maintain_action = create_widget_action(self.menu, text=translate('OpenLP.ServiceManager', '&Reorder Item'), icon=':/general/general_edit.png', triggers=self.on_service_item_edit_form) From 87e1c5d986884bdcf75502f3fbd6a30c29975631 Mon Sep 17 00:00:00 2001 From: Jonathan Springer Date: Tue, 15 Apr 2014 14:44:33 -0400 Subject: [PATCH 137/155] Add tests for First Time Wizards web config file --- .../openlp_core_ui/test_firsttimeform.py | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 tests/functional/openlp_core_ui/test_firsttimeform.py diff --git a/tests/functional/openlp_core_ui/test_firsttimeform.py b/tests/functional/openlp_core_ui/test_firsttimeform.py new file mode 100644 index 000000000..9fc6f5137 --- /dev/null +++ b/tests/functional/openlp_core_ui/test_firsttimeform.py @@ -0,0 +1,72 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2014 Raoul Snyman # +# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan # +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # +# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, # +# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, # +# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, # +# Frode Woldsund, Martin Zibricky, Patrick Zimmermann # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### +""" +Package to test the openlp.core.ui.firsttimeform package. +""" +from unittest import TestCase + +from tests.functional import MagicMock + +from tests.helpers.testmixin import TestMixin +from openlp.core.common import Registry +from openlp.core.ui.firsttimeform import FirstTimeForm + + +class TestFirstTimeForm(TestCase, TestMixin): + + def setUp(self): + screens = MagicMock() + self.get_application() + Registry.create() + Registry().register('application', self.app) + self.first_time_form = FirstTimeForm(screens) + + def test_access_to_config(self): + """ + Test if we can access the First Time Form's config file + """ + # GIVEN A new First Time Form instance. + + # WHEN The default First Time Form is built. + + # THEN The First Time Form web configuration file should be accessable. + self.assertTrue(self.first_time_form.web_access, + 'First Time Wizard\'s web configuration file should be available') + + def test_parsable_config(self): + """ + Test if the First Time Form's config file is parsable + """ + # GIVEN A new First Time Form instance. + + # WHEN The default First Time Form is built. + + # THEN The First Time Form web configuration file should be parsable + self.assertTrue(self.first_time_form.songs_url, + 'First Time Wizard\'s web configuration file should be parsable') From 5090a014ae40d85a202a67c54f6ee4ae14f2f48d Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Tue, 15 Apr 2014 22:27:32 +0200 Subject: [PATCH 138/155] use self.assert and friends --- .../openlp_core_lib/test_image_manager.py | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/tests/functional/openlp_core_lib/test_image_manager.py b/tests/functional/openlp_core_lib/test_image_manager.py index c3036666d..6cb4a445a 100644 --- a/tests/functional/openlp_core_lib/test_image_manager.py +++ b/tests/functional/openlp_core_lib/test_image_manager.py @@ -112,8 +112,10 @@ class TestImageManager(TestCase, TestMixin): # is being processed (see mocked methods/functions). # Note: Priority.Normal means, that the resize_image() was not completed yet (because afterwards the # # priority is adjusted to Priority.Lowest). - assert self.get_image_priority(image1) == Priority.Normal, "image1's priority should be 'Priority.Normal'" - assert self.get_image_priority(image2) == Priority.Normal, "image2's priority should be 'Priority.Normal'" + self.assertEqual(self.get_image_priority(image1), Priority.Normal, + "image1's priority should be 'Priority.Normal'") + self.assertEqual(self.get_image_priority(image2), Priority.Normal, + "image2's priority should be 'Priority.Normal'") # WHEN: Add more images. self.image_manager.add_image(TEST_PATH, image3, None) @@ -131,14 +133,16 @@ class TestImageManager(TestCase, TestMixin): # Because empty() is not reliable, wait a litte; just to make sure. time.sleep(0.1) # THEN: The images' priority reflect how they were processed. - assert self.image_manager._conversion_queue.qsize() == 0, "The queue should be empty." - assert self.get_image_priority(image1) == Priority.Lowest, \ - "The image should have not been requested (=Lowest)" - assert self.get_image_priority(image2) == Priority.Lowest, \ - "The image should have not been requested (=Lowest)" - assert self.get_image_priority(image3) == Priority.Low, "Only the QImage should have been requested (=Low)." - assert self.get_image_priority(image4) == Priority.Urgent, \ - "The image bytes should have been requested (=Urgent)." + assert self.image_manager._conversion_queue.qsize() == 0, + self.assertEqual(self.image_manager._conversion_queue.qsize(), 0, "The queue should be empty.") + self.assertEqual(self.get_image_priority(image1), Priority.Lowest, + "The image should have not been requested (=Lowest)") + self.assertEqual(self.get_image_priority(image2), Priority.Lowest, + "The image should have not been requested (=Lowest)") + self.assertEqual(self.get_image_priority(image3), Priority.Low, + "Only the QImage should have been requested (=Low).") + self.assertEqual(self.get_image_priority(image4), Priority.Urgent, + "The image bytes should have been requested (=Urgent).") def get_image_priority(self, image): """ From dbd6d74c8ddca97dc0c10e8c61654dd5486d4c96 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Tue, 15 Apr 2014 22:30:20 +0200 Subject: [PATCH 139/155] removed not wanted code --- tests/functional/openlp_core_lib/test_image_manager.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/functional/openlp_core_lib/test_image_manager.py b/tests/functional/openlp_core_lib/test_image_manager.py index 6cb4a445a..37b6d6fdd 100644 --- a/tests/functional/openlp_core_lib/test_image_manager.py +++ b/tests/functional/openlp_core_lib/test_image_manager.py @@ -133,7 +133,6 @@ class TestImageManager(TestCase, TestMixin): # Because empty() is not reliable, wait a litte; just to make sure. time.sleep(0.1) # THEN: The images' priority reflect how they were processed. - assert self.image_manager._conversion_queue.qsize() == 0, self.assertEqual(self.image_manager._conversion_queue.qsize(), 0, "The queue should be empty.") self.assertEqual(self.get_image_priority(image1), Priority.Lowest, "The image should have not been requested (=Lowest)") From be4a65429879a0e15fa6ff064ce8dc2da86d50aa Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 16 Apr 2014 00:23:29 +0200 Subject: [PATCH 142/155] More tests --- openlp/plugins/songs/lib/openlyricsimport.py | 4 +- .../songs/test_openlyricsimport.py | 40 ++++++++++++++ .../Mám zde přítele, Pána Ježíše.xml | 53 +++++++++++++++++++ 3 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 tests/resources/openlyricssongs/Mám zde přítele, Pána Ježíše.xml diff --git a/openlp/plugins/songs/lib/openlyricsimport.py b/openlp/plugins/songs/lib/openlyricsimport.py index 675c0d98a..6e6cc8efa 100644 --- a/openlp/plugins/songs/lib/openlyricsimport.py +++ b/openlp/plugins/songs/lib/openlyricsimport.py @@ -53,7 +53,7 @@ class OpenLyricsImport(SongImport): Initialise the Open Lyrics importer. """ log.debug('initialise OpenLyricsImport') - SongImport.__init__(self, manager, **kwargs) + super(OpenLyricsImport, self).__init__(manager, **kwargs) self.open_lyrics = OpenLyrics(self.manager) def do_import(self): @@ -71,7 +71,7 @@ class OpenLyricsImport(SongImport): # special characters in the path (see lp:757673 and lp:744337). parsed_file = etree.parse(open(file_path, 'r'), parser) xml = etree.tostring(parsed_file).decode() - self.open_lyrics.xml_to_song(xml) + return self.open_lyrics.xml_to_song(xml) except etree.XMLSyntaxError: log.exception('XML syntax error in file %s' % file_path) self.log_error(file_path, SongStrings.XMLSyntaxError) diff --git a/tests/functional/openlp_plugins/songs/test_openlyricsimport.py b/tests/functional/openlp_plugins/songs/test_openlyricsimport.py index 74a291152..5465a03e4 100644 --- a/tests/functional/openlp_plugins/songs/test_openlyricsimport.py +++ b/tests/functional/openlp_plugins/songs/test_openlyricsimport.py @@ -30,12 +30,30 @@ This module contains tests for the OpenLyrics song importer. """ +import os from unittest import TestCase from tests.functional import MagicMock, patch from openlp.plugins.songs.lib.openlyricsimport import OpenLyricsImport from openlp.plugins.songs.lib.songimport import SongImport +TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), + '..', '..', '..', 'resources', 'openlyricssongs')) +SONG_TEST_DATA = { + 'Mám zde přítele, Pána Ježíše.xml': { + 'title': 'Mám zde přítele', + 'verses': [ + ('Mám zde přítele,\nPána Ježíše,\na na rámě jeho spoléhám;\nv něm své stěstí mám,\n\ + pokoj nalézám,\nkdyž na rámě jeho spoléhám!', 'v1'), + ('Boží rámě\nje v soužení náš pevný hrad;\nBoží rámě,\nuč se na ně vždycky spoléhat!', 'c'), + ('Jak je sladké být,\nv jeho družině,\nkdyž na rámě jeho spoléhám,\njak se života\ncesta zjasňuje\n\ + když na rámě Boží spoléhám!', 'v2'), + ('Čeho bych se bál,\nčeho strachoval,\nkdyž na rámě Boží spoléhám?\nMír je v duši mé,\n\ + když On blízko je,\nkdyž na rámě jeho spoléhám.', 'v') + ] + } +} + class TestOpenLyricsImport(TestCase): """ @@ -54,3 +72,25 @@ class TestOpenLyricsImport(TestCase): # THEN: The importer should be an instance of SongImport self.assertIsInstance(importer, SongImport) + + @patch('openlp.plugins.songs.lib.db.Song') + @patch('openlp.plugins.songs.lib.songbeamerimport.SongImport') + def file_import_test(self, mock_songimport, mock_song): + """ + Test the actual import of real song files and check that the imported data is correct. + """ + + # GIVEN: Test files with a mocked out "manager" and a mocked out "import_wizard" + for song_file in SONG_TEST_DATA: + mocked_manager = MagicMock() + mocked_import_wizard = MagicMock() + importer = OpenLyricsImport(mocked_manager, filenames=[]) + importer.import_wizard = mocked_import_wizard + + # WHEN: Importing each file + importer.import_source = [os.path.join(TEST_PATH, song_file)] + song = importer.do_import() + + # THEN: the song title should be as expected + self.assertEqual(song.title, SONG_TEST_DATA[song_file]['title'], + 'title for %s should be "%s"' % (song_file, SONG_TEST_DATA[song_file]['title'])) diff --git a/tests/resources/openlyricssongs/Mám zde přítele, Pána Ježíše.xml b/tests/resources/openlyricssongs/Mám zde přítele, Pána Ježíše.xml new file mode 100644 index 000000000..0914b4dfe --- /dev/null +++ b/tests/resources/openlyricssongs/Mám zde přítele, Pána Ježíše.xml @@ -0,0 +1,53 @@ + + + + + Mám zde přítele + + + A.J. Showalter + E.A. Hoffman + + v1 c v2 c v3 c + + + + + Mám zde přítele,
+ Pána Ježíše,
+ a na rámě jeho spoléhám;
+ v něm své stěstí mám,
+ pokoj nalézám,
+ když na rámě jeho spoléhám! +
+
+ + + Boží rámě
+ je v soužení náš pevný hrad;
+ Boží rámě,
+ uč se na ně vždycky spoléhat! +
+
+ + + Jak je sladké být,
+ v jeho družině,
+ když na rámě jeho spoléhám,
+ jak se života
+ cesta zjasňuje
+ když na rámě Boží spoléhám! +
+
+ + + Čeho bych se bál,
+ čeho strachoval,
+ když na rámě Boží spoléhám?
+ Mír je v duši mé,
+ když On blízko je,
+ když na rámě jeho spoléhám. +
+
+
+
From c3ed2c02ee6e6b46d5a2db92583f2c896b9a0d3a Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 16 Apr 2014 09:56:37 +0200 Subject: [PATCH 143/155] Fix bug 1247493 Fixes: https://launchpad.net/bugs/1247493 --- openlp/plugins/bibles/lib/opensong.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/openlp/plugins/bibles/lib/opensong.py b/openlp/plugins/bibles/lib/opensong.py index a522c1c57..e35aa2e3b 100644 --- a/openlp/plugins/bibles/lib/opensong.py +++ b/openlp/plugins/bibles/lib/opensong.py @@ -73,13 +73,13 @@ class OpenSongBible(BibleDB): log.debug('Starting OpenSong import from "%s"' % self.filename) if not isinstance(self.filename, str): self.filename = str(self.filename, 'utf8') - file = None + import_file = None success = True try: # NOTE: We don't need to do any of the normal encoding detection here, because lxml does it's own encoding # detection, and the two mechanisms together interfere with each other. - file = open(self.filename, 'r') - opensong = objectify.parse(file) + import_file = open(self.filename, 'rb') + opensong = objectify.parse(import_file) bible = opensong.getroot() language_id = self.get_language(bible_name) if not language_id: @@ -93,7 +93,7 @@ class OpenSongBible(BibleDB): log.error('Importing books from "%s" failed' % self.filename) return False book_details = BiblesResourcesDB.get_book_by_id(book_ref_id) - db_book = self.create_book(str(book.attrib['n']), book_ref_id, book_details['testament_id']) + db_book = self.create_book(book.attrib['n'], book_ref_id, book_details['testament_id']) chapter_number = 0 for chapter in book.c: if self.stop_import_flag: @@ -122,8 +122,8 @@ class OpenSongBible(BibleDB): verse_number += 1 self.create_verse(db_book.id, chapter_number, verse_number, self.get_text(verse)) self.wizard.increment_progress_bar( - translate('BiblesPlugin.Opensong', 'Importing %s %s...', - 'Importing ...')) % (db_book.name, chapter_number) + translate('BiblesPlugin.Opensong', 'Importing %(bookname)s %(chapter)s...' % + {'bookname':db_book.name, 'chapter': chapter_number})) self.session.commit() self.application.process_events() except etree.XMLSyntaxError as inst: @@ -137,8 +137,8 @@ class OpenSongBible(BibleDB): log.exception('Loading Bible from OpenSong file failed') success = False finally: - if file: - file.close() + if import_file: + import_file.close() if self.stop_import_flag: return False else: From 5929b0258675a169b696b59646b3ce3b84869fc2 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 16 Apr 2014 10:25:17 +0200 Subject: [PATCH 144/155] Test: Songbeamer Authors import --- .../openlp_plugins/songs/test_songbeamerimport.py | 7 ++++++- tests/resources/songbeamersongs/Lobsinget dem Herrn.sng | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/functional/openlp_plugins/songs/test_songbeamerimport.py b/tests/functional/openlp_plugins/songs/test_songbeamerimport.py index f08cedec5..c408b4dbe 100644 --- a/tests/functional/openlp_plugins/songs/test_songbeamerimport.py +++ b/tests/functional/openlp_plugins/songs/test_songbeamerimport.py @@ -49,7 +49,8 @@ SONG_TEST_DATA = { ('4. Lobsingt seiner Treu´,\ndie immerdar neu,\nbis Er uns zur Herrlichket führet!\n\n', 'v') ], 'song_book_name': 'Glaubenslieder I', - 'song_number': "1" + 'song_number': "1", + 'authors': ['Carl Brockhaus', 'Johann Jakob Vetter'] } } @@ -140,6 +141,7 @@ class TestSongBeamerImport(TestCase): add_verse_calls = SONG_TEST_DATA[song_file]['verses'] song_book_name = SONG_TEST_DATA[song_file]['song_book_name'] song_number = SONG_TEST_DATA[song_file]['song_number'] + song_authors = SONG_TEST_DATA[song_file]['authors'] # THEN: do_import should return none, the song data should be as expected, and finish should have been # called. @@ -153,6 +155,9 @@ class TestSongBeamerImport(TestCase): if song_number: self.assertEqual(importer.song_number, song_number, 'song_number for %s should be %s' % (song_file, song_number)) + if song_authors: + for author in importer.authors: + self.assertIn(author, song_authors) mocked_finish.assert_called_with() def check_verse_marks_test(self): diff --git a/tests/resources/songbeamersongs/Lobsinget dem Herrn.sng b/tests/resources/songbeamersongs/Lobsinget dem Herrn.sng index fbc9aa9fc..c93a143fa 100644 --- a/tests/resources/songbeamersongs/Lobsinget dem Herrn.sng +++ b/tests/resources/songbeamersongs/Lobsinget dem Herrn.sng @@ -1,5 +1,7 @@ #LangCount=1 #Title=GL 1 - Lobsinget dem Herrn +#Author=Carl Brockhaus +#Melody=Johann Jakob Vetter #Editor=SongBeamer 4.20 #Version=3 #Format=F/K// From bb1af7ed00a01fd8e8cef3b438d4d4d3214256c8 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 16 Apr 2014 21:56:54 +0200 Subject: [PATCH 145/155] PEP8 --- openlp/plugins/bibles/lib/opensong.py | 2 +- openlp/plugins/songs/lib/songbeamerimport.py | 2 +- scripts/translation_utils.py | 2 +- .../openlp_core_common/test_common.py | 4 ++-- .../openlp_core_lib/test_file_dialog.py | 2 +- tests/functional/openlp_core_lib/test_ui.py | 2 +- .../openlp_core_ui/test_maindisplay.py | 2 +- .../openlp_plugins/songs/test_ewimport.py | 14 ++++++------- .../songs/test_foilpresenterimport.py | 2 +- .../openlp_plugins/songs/test_lib.py | 1 - .../songs/test_songbeamerimport.py | 10 +++++----- .../songs/test_songshowplusimport.py | 12 +++++------ tests/helpers/songfileimport.py | 20 +++++++++---------- .../bibles/test_lib_parse_reference.py | 4 ++-- 14 files changed, 39 insertions(+), 40 deletions(-) diff --git a/openlp/plugins/bibles/lib/opensong.py b/openlp/plugins/bibles/lib/opensong.py index e35aa2e3b..c7bfa01a2 100644 --- a/openlp/plugins/bibles/lib/opensong.py +++ b/openlp/plugins/bibles/lib/opensong.py @@ -123,7 +123,7 @@ class OpenSongBible(BibleDB): self.create_verse(db_book.id, chapter_number, verse_number, self.get_text(verse)) self.wizard.increment_progress_bar( translate('BiblesPlugin.Opensong', 'Importing %(bookname)s %(chapter)s...' % - {'bookname':db_book.name, 'chapter': chapter_number})) + {'bookname': db_book.name, 'chapter': chapter_number})) self.session.commit() self.application.process_events() except etree.XMLSyntaxError as inst: diff --git a/openlp/plugins/songs/lib/songbeamerimport.py b/openlp/plugins/songs/lib/songbeamerimport.py index 5b86591e8..a0b166ded 100644 --- a/openlp/plugins/songs/lib/songbeamerimport.py +++ b/openlp/plugins/songs/lib/songbeamerimport.py @@ -137,7 +137,7 @@ class SongBeamerImport(SongImport): if line.startswith('#') and not read_verses: self.parseTags(line) elif line.startswith('--'): - # --- and -- allowed for page-breaks (difference in Songbeamer only in printout) + # --- and -- allowed for page-breaks (difference in Songbeamer only in printout) if self.current_verse: self.replace_html_tags() self.add_verse(self.current_verse, self.current_verse_type) diff --git a/scripts/translation_utils.py b/scripts/translation_utils.py index 5aa320806..ad3edcaa3 100755 --- a/scripts/translation_utils.py +++ b/scripts/translation_utils.py @@ -96,7 +96,7 @@ class CommandStack(object): return len(self.data) def __getitem__(self, index): - if not index in self.data: + if index not in self.data: return None elif self.data[index].get('arguments'): return self.data[index]['command'], self.data[index]['arguments'] diff --git a/tests/functional/openlp_core_common/test_common.py b/tests/functional/openlp_core_common/test_common.py index 90b7d0520..ab2d11b3a 100644 --- a/tests/functional/openlp_core_common/test_common.py +++ b/tests/functional/openlp_core_common/test_common.py @@ -79,5 +79,5 @@ class TestCommonFunctions(TestCase): trace_error_handler(mocked_logger) # THEN: The mocked_logger.error() method should have been called with the correct parameters - mocked_logger.error.assert_called_with('OpenLP Error trace\n File openlp.fake at line 56 \n\t called trace_error_handler_test') - + mocked_logger.error.assert_called_with( + 'OpenLP Error trace\n File openlp.fake at line 56 \n\t called trace_error_handler_test') diff --git a/tests/functional/openlp_core_lib/test_file_dialog.py b/tests/functional/openlp_core_lib/test_file_dialog.py index b2c2178a9..3120f48fa 100644 --- a/tests/functional/openlp_core_lib/test_file_dialog.py +++ b/tests/functional/openlp_core_lib/test_file_dialog.py @@ -54,7 +54,7 @@ class TestFileDialog(TestCase): self.mocked_qt_gui.reset() # GIVEN: A List of known values as a return value from QFileDialog.getOpenFileNames and a list of valid - # file names. + # file names. self.mocked_qt_gui.QFileDialog.getOpenFileNames.return_value = [ '/Valid File', '/url%20encoded%20file%20%231', '/non-existing'] self.mocked_os.path.exists.side_effect = lambda file_name: file_name in [ diff --git a/tests/functional/openlp_core_lib/test_ui.py b/tests/functional/openlp_core_lib/test_ui.py index 91d59ab5a..025b1a638 100644 --- a/tests/functional/openlp_core_lib/test_ui.py +++ b/tests/functional/openlp_core_lib/test_ui.py @@ -162,7 +162,7 @@ class TestUi(TestCase): # WHEN: We create an action with some properties action = create_action(dialog, 'my_action', text='my text', icon=':/wizards/wizard_firsttime.bmp', - tooltip='my tooltip', statustip='my statustip') + tooltip='my tooltip', statustip='my statustip') # THEN: These properties should be set self.assertIsInstance(action, QtGui.QAction) diff --git a/tests/functional/openlp_core_ui/test_maindisplay.py b/tests/functional/openlp_core_ui/test_maindisplay.py index b1a4dc7f7..6d67a3b67 100644 --- a/tests/functional/openlp_core_ui/test_maindisplay.py +++ b/tests/functional/openlp_core_ui/test_maindisplay.py @@ -106,4 +106,4 @@ class TestMainDisplay(TestCase): self.assertEqual('QGraphicsView {}', main_display.styleSheet(), 'MainDisplay instance should not be transparent') self.assertFalse(main_display.testAttribute(QtCore.Qt.WA_TranslucentBackground), - 'MainDisplay hasnt translucent background') + 'MainDisplay hasnt translucent background') diff --git a/tests/functional/openlp_plugins/songs/test_ewimport.py b/tests/functional/openlp_plugins/songs/test_ewimport.py index c1b9db52d..182a6b04a 100644 --- a/tests/functional/openlp_plugins/songs/test_ewimport.py +++ b/tests/functional/openlp_plugins/songs/test_ewimport.py @@ -141,7 +141,7 @@ class TestEasyWorshipSongImport(TestCase): self.assertIsNotNone(field_desc_entry, 'Import should not be none') self.assertEqual(field_desc_entry.name, name, 'FieldDescEntry.name should be the same as the name argument') self.assertEqual(field_desc_entry.field_type, field_type, - 'FieldDescEntry.type should be the same as the type argument') + 'FieldDescEntry.type should be the same as the type argument') self.assertEqual(field_desc_entry.size, size, 'FieldDescEntry.size should be the same as the size argument') def create_importer_test(self): @@ -229,10 +229,10 @@ class TestEasyWorshipSongImport(TestCase): for field_index, result in field_results: return_value = importer.get_field(field_index) - # THEN: get_field should return the known results + # THEN: get_field should return the known results self.assertEqual(return_value, result, - 'get_field should return "%s" when called with "%s"' % - (result, TEST_FIELDS[field_index])) + 'get_field should return "%s" when called with "%s"' % + (result, TEST_FIELDS[field_index])) def get_memo_field_test(self): """ @@ -403,11 +403,11 @@ class TestEasyWorshipSongImport(TestCase): if song_copyright: self.assertEqual(importer.copyright, song_copyright) if ccli_number: - self.assertEqual(importer.ccli_number, ccli_number, 'ccli_number for %s should be %s' - % (title, ccli_number)) + self.assertEqual(importer.ccli_number, ccli_number, + 'ccli_number for %s should be %s' % (title, ccli_number)) for verse_text, verse_tag in add_verse_calls: mocked_add_verse.assert_any_call(verse_text, verse_tag) if verse_order_list: self.assertEqual(importer.verse_order_list, verse_order_list, - 'verse_order_list for %s should be %s' % (title, verse_order_list)) + 'verse_order_list for %s should be %s' % (title, verse_order_list)) mocked_finish.assert_called_with() diff --git a/tests/functional/openlp_plugins/songs/test_foilpresenterimport.py b/tests/functional/openlp_plugins/songs/test_foilpresenterimport.py index fbd339cf3..61206b9fa 100644 --- a/tests/functional/openlp_plugins/songs/test_foilpresenterimport.py +++ b/tests/functional/openlp_plugins/songs/test_foilpresenterimport.py @@ -44,7 +44,7 @@ class TestFoilPresenter(TestCase): """ Test the functions in the :mod:`foilpresenterimport` module. """ - #TODO: The following modules still need tests written for + # TODO: The following modules still need tests written for # xml_to_song # _child # _process_authors diff --git a/tests/functional/openlp_plugins/songs/test_lib.py b/tests/functional/openlp_plugins/songs/test_lib.py index 2ab808bc9..b67c1a4be 100644 --- a/tests/functional/openlp_plugins/songs/test_lib.py +++ b/tests/functional/openlp_plugins/songs/test_lib.py @@ -129,7 +129,6 @@ class TestLib(TestCase): # THEN: The result should be a tuple of songs.. assert result == (self.song1, self.song2), 'The result should be the tuble of songs' - def songs_probably_equal_different_song_test(self): """ Test the songs_probably_equal function with two different songs. diff --git a/tests/functional/openlp_plugins/songs/test_songbeamerimport.py b/tests/functional/openlp_plugins/songs/test_songbeamerimport.py index c408b4dbe..a69d4a86c 100644 --- a/tests/functional/openlp_plugins/songs/test_songbeamerimport.py +++ b/tests/functional/openlp_plugins/songs/test_songbeamerimport.py @@ -92,7 +92,7 @@ class TestSongBeamerImport(TestCase): # THEN: do_import should return none and the progress bar maximum should not be set. self.assertIsNone(importer.do_import(), 'do_import should return None when import_source is not a list') self.assertEqual(mocked_import_wizard.progress_bar.setMaximum.called, False, - 'setMaxium on import_wizard.progress_bar should not have been called') + 'setMaxium on import_wizard.progress_bar should not have been called') def valid_import_source_test(self): """ @@ -150,11 +150,11 @@ class TestSongBeamerImport(TestCase): for verse_text, verse_tag in add_verse_calls: mocked_add_verse.assert_any_call(verse_text, verse_tag) if song_book_name: - self.assertEqual(importer.song_book_name, song_book_name, 'song_book_name for %s should be "%s"' % - (song_file, song_book_name)) + self.assertEqual(importer.song_book_name, song_book_name, + 'song_book_name for %s should be "%s"' % (song_file, song_book_name)) if song_number: - self.assertEqual(importer.song_number, song_number, 'song_number for %s should be %s' % - (song_file, song_number)) + self.assertEqual(importer.song_number, song_number, + 'song_number for %s should be %s' % (song_file, song_number)) if song_authors: for author in importer.authors: self.assertIn(author, song_authors) diff --git a/tests/functional/openlp_plugins/songs/test_songshowplusimport.py b/tests/functional/openlp_plugins/songs/test_songshowplusimport.py index f2839c332..7292bb2b0 100644 --- a/tests/functional/openlp_plugins/songs/test_songshowplusimport.py +++ b/tests/functional/openlp_plugins/songs/test_songshowplusimport.py @@ -96,7 +96,7 @@ class TestSongShowPlusImport(TestCase): # THEN: do_import should return none and the progress bar maximum should not be set. self.assertIsNone(importer.do_import(), 'do_import should return None when import_source is not a list') self.assertEqual(mocked_import_wizard.progress_bar.setMaximum.called, False, - 'setMaximum on import_wizard.progress_bar should not have been called') + 'setMaximum on import_wizard.progress_bar should not have been called') def valid_import_source_test(self): """ @@ -116,7 +116,7 @@ class TestSongShowPlusImport(TestCase): # THEN: do_import should return none and the progress bar setMaximum should be called with the length of # import_source. self.assertIsNone(importer.do_import(), 'do_import should return None when import_source is a list ' - 'and stop_import_flag is True') + 'and stop_import_flag is True') mocked_import_wizard.progress_bar.setMaximum.assert_called_with(len(importer.import_source)) def to_openlp_verse_tag_test(self): @@ -144,8 +144,8 @@ class TestSongShowPlusImport(TestCase): # THEN: The returned value should should correlate with the input arguments for original_tag, openlp_tag in test_values: self.assertEqual(importer.to_openlp_verse_tag(original_tag), openlp_tag, - 'SongShowPlusImport.to_openlp_verse_tag should return "%s" when called with "%s"' % - (openlp_tag, original_tag)) + 'SongShowPlusImport.to_openlp_verse_tag should return "%s" when called with "%s"' % + (openlp_tag, original_tag)) def to_openlp_verse_tag_verse_order_test(self): """ @@ -173,5 +173,5 @@ class TestSongShowPlusImport(TestCase): # THEN: The returned value should should correlate with the input arguments for original_tag, openlp_tag in test_values: self.assertEqual(importer.to_openlp_verse_tag(original_tag, ignore_unique=True), openlp_tag, - 'SongShowPlusImport.to_openlp_verse_tag should return "%s" when called with "%s"' % - (openlp_tag, original_tag)) + 'SongShowPlusImport.to_openlp_verse_tag should return "%s" when called with "%s"' % + (openlp_tag, original_tag)) diff --git a/tests/helpers/songfileimport.py b/tests/helpers/songfileimport.py index 49a09528c..5364c2c3b 100644 --- a/tests/helpers/songfileimport.py +++ b/tests/helpers/songfileimport.py @@ -116,24 +116,24 @@ class SongImportTestHelper(TestCase): if song_copyright: self.mocked_add_copyright.assert_called_with(song_copyright) if ccli_number: - self.assertEqual(importer.ccli_number, ccli_number, 'ccli_number for %s should be %s' % - (source_file_name, ccli_number)) + self.assertEqual(importer.ccli_number, ccli_number, + 'ccli_number for %s should be %s' % (source_file_name, ccli_number)) for verse_text, verse_tag in add_verse_calls: self.mocked_add_verse.assert_any_call(verse_text, verse_tag) if topics: self.assertEqual(importer.topics, topics, 'topics for %s should be %s' % (source_file_name, topics)) if comments: - self.assertEqual(importer.comments, comments, 'comments for %s should be "%s"' % - (source_file_name, comments)) + self.assertEqual(importer.comments, comments, + 'comments for %s should be "%s"' % (source_file_name, comments)) if song_book_name: - self.assertEqual(importer.song_book_name, song_book_name, 'song_book_name for %s should be "%s"' % - (source_file_name, song_book_name)) + self.assertEqual(importer.song_book_name, song_book_name, + 'song_book_name for %s should be "%s"' % (source_file_name, song_book_name)) if song_number: - self.assertEqual(importer.song_number, song_number, 'song_number for %s should be %s' % - (source_file_name, song_number)) + self.assertEqual(importer.song_number, song_number, + 'song_number for %s should be %s' % (source_file_name, song_number)) if verse_order_list: - self.assertEqual(importer.verse_order_list, [], 'verse_order_list for %s should be %s' % - (source_file_name, verse_order_list)) + self.assertEqual(importer.verse_order_list, [], + 'verse_order_list for %s should be %s' % (source_file_name, verse_order_list)) self.mocked_finish.assert_called_with() def _get_data(self, data, key): diff --git a/tests/interfaces/openlp_plugins/bibles/test_lib_parse_reference.py b/tests/interfaces/openlp_plugins/bibles/test_lib_parse_reference.py index 84f80e7ed..e20105ea1 100644 --- a/tests/interfaces/openlp_plugins/bibles/test_lib_parse_reference.py +++ b/tests/interfaces/openlp_plugins/bibles/test_lib_parse_reference.py @@ -103,5 +103,5 @@ class TestBibleManager(TestCase, TestMixin): # WHEN asking to parse the bible reference results = parse_reference('1 Timothy 1:1-2:1', self.manager.db_cache['tests'], MagicMock(), 54) # THEN a verse array should be returned - self.assertEqual([(54, 1, 1, -1), (54, 2, 1, 1)], results, "The bible verses should matches the expected " - "results") + self.assertEqual([(54, 1, 1, -1), (54, 2, 1, 1)], results, + "The bible verses should match the expected results") From dd4e2c8d6151c3b898d8caf2176a78b25a673011 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 16 Apr 2014 21:59:34 +0200 Subject: [PATCH 146/155] More PEP8 --- tests/functional/openlp_core_lib/test_lib.py | 2 +- tests/functional/openlp_core_ui/test_formattingtagsform.py | 4 ++-- tests/functional/openlp_plugins/bibles/test_http.py | 4 ++-- .../openlp_plugins/presentations/test_pptviewcontroller.py | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/functional/openlp_core_lib/test_lib.py b/tests/functional/openlp_core_lib/test_lib.py index bb3a17ebb..b4334a728 100644 --- a/tests/functional/openlp_core_lib/test_lib.py +++ b/tests/functional/openlp_core_lib/test_lib.py @@ -482,7 +482,7 @@ class TestLib(TestCase): # WHEN: we run the validate_thumb() function # THEN: we should have called a few functions, and the result should be True - #mocked_os.path.exists.assert_called_with(thumb_path) + # mocked_os.path.exists.assert_called_with(thumb_path) def validate_thumb_file_exists_and_older_test(self): """ diff --git a/tests/functional/openlp_core_ui/test_formattingtagsform.py b/tests/functional/openlp_core_ui/test_formattingtagsform.py index 05b5fed74..e71a75651 100644 --- a/tests/functional/openlp_core_ui/test_formattingtagsform.py +++ b/tests/functional/openlp_core_ui/test_formattingtagsform.py @@ -70,7 +70,7 @@ class TestFormattingTagForm(TestCase): form.save_button = MagicMock() # WHEN: on_text_edited is called with an arbitrary value - #form.on_text_edited('text') + # form.on_text_edited('text') # THEN: setEnabled and setDefault should have been called on save_push_button - #form.save_button.setEnabled.assert_called_with(True) + # form.save_button.setEnabled.assert_called_with(True) diff --git a/tests/functional/openlp_plugins/bibles/test_http.py b/tests/functional/openlp_plugins/bibles/test_http.py index 05a59a509..060c00d02 100644 --- a/tests/functional/openlp_plugins/bibles/test_http.py +++ b/tests/functional/openlp_plugins/bibles/test_http.py @@ -35,7 +35,7 @@ from bs4 import BeautifulSoup from tests.functional import patch, MagicMock from openlp.plugins.bibles.lib.http import BSExtract -#TODO: Items left to test +# TODO: Items left to test # BGExtract # __init__ # _remove_elements @@ -68,7 +68,7 @@ class TestBSExtract(TestCase): """ Test the BSExtractClass """ - #TODO: Items left to test + # TODO: Items left to test # BSExtract # __init__ # get_bible_chapter diff --git a/tests/functional/openlp_plugins/presentations/test_pptviewcontroller.py b/tests/functional/openlp_plugins/presentations/test_pptviewcontroller.py index 8a8897cec..c3d0912c0 100644 --- a/tests/functional/openlp_plugins/presentations/test_pptviewcontroller.py +++ b/tests/functional/openlp_plugins/presentations/test_pptviewcontroller.py @@ -47,7 +47,7 @@ class TestPptviewController(TestCase, TestMixin): """ Test the PptviewController Class """ -#TODO: Items left to test +# TODO: Items left to test # PptviewController # start_process(self) # kill @@ -108,7 +108,7 @@ class TestPptviewDocument(TestCase): """ Test the PptviewDocument Class """ - #TODO: Items left to test + # TODO: Items left to test # PptviewDocument # __init__ # create_thumbnails From 2c6da0603b779856b1a3ae98af2af0ed94c5577b Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 16 Apr 2014 22:33:06 +0200 Subject: [PATCH 147/155] Fix tests --- openlp/plugins/songs/lib/openlyricsimport.py | 2 +- .../songs/test_openlyricsimport.py | 16 +++++++--------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/openlp/plugins/songs/lib/openlyricsimport.py b/openlp/plugins/songs/lib/openlyricsimport.py index 6e6cc8efa..cdb017bae 100644 --- a/openlp/plugins/songs/lib/openlyricsimport.py +++ b/openlp/plugins/songs/lib/openlyricsimport.py @@ -71,7 +71,7 @@ class OpenLyricsImport(SongImport): # special characters in the path (see lp:757673 and lp:744337). parsed_file = etree.parse(open(file_path, 'r'), parser) xml = etree.tostring(parsed_file).decode() - return self.open_lyrics.xml_to_song(xml) + self.open_lyrics.xml_to_song(xml) except etree.XMLSyntaxError: log.exception('XML syntax error in file %s' % file_path) self.log_error(file_path, SongStrings.XMLSyntaxError) diff --git a/tests/functional/openlp_plugins/songs/test_openlyricsimport.py b/tests/functional/openlp_plugins/songs/test_openlyricsimport.py index 5465a03e4..5f005e10e 100644 --- a/tests/functional/openlp_plugins/songs/test_openlyricsimport.py +++ b/tests/functional/openlp_plugins/songs/test_openlyricsimport.py @@ -73,24 +73,22 @@ class TestOpenLyricsImport(TestCase): # THEN: The importer should be an instance of SongImport self.assertIsInstance(importer, SongImport) - @patch('openlp.plugins.songs.lib.db.Song') - @patch('openlp.plugins.songs.lib.songbeamerimport.SongImport') - def file_import_test(self, mock_songimport, mock_song): + def file_import_test(self): """ - Test the actual import of real song files and check that the imported data is correct. + Test the actual import of real song files and check that the importer is called. """ - # GIVEN: Test files with a mocked out "manager" and a mocked out "import_wizard" for song_file in SONG_TEST_DATA: mocked_manager = MagicMock() mocked_import_wizard = MagicMock() importer = OpenLyricsImport(mocked_manager, filenames=[]) importer.import_wizard = mocked_import_wizard + importer.open_lyrics = MagicMock() + importer.open_lyrics.xml_to_song = MagicMock() # WHEN: Importing each file importer.import_source = [os.path.join(TEST_PATH, song_file)] - song = importer.do_import() + importer.do_import() - # THEN: the song title should be as expected - self.assertEqual(song.title, SONG_TEST_DATA[song_file]['title'], - 'title for %s should be "%s"' % (song_file, SONG_TEST_DATA[song_file]['title'])) + # THEN: The xml_to_song() method should have been called + self.assertTrue(importer.open_lyrics.xml_to_song.called) From 655ebc009d7e93c1c55a9c154e364a1d55dcf142 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 16 Apr 2014 22:37:15 +0200 Subject: [PATCH 148/155] binary open --- openlp/plugins/songs/lib/openlyricsimport.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/plugins/songs/lib/openlyricsimport.py b/openlp/plugins/songs/lib/openlyricsimport.py index cdb017bae..031c5ba72 100644 --- a/openlp/plugins/songs/lib/openlyricsimport.py +++ b/openlp/plugins/songs/lib/openlyricsimport.py @@ -69,7 +69,7 @@ class OpenLyricsImport(SongImport): try: # Pass a file object, because lxml does not cope with some # special characters in the path (see lp:757673 and lp:744337). - parsed_file = etree.parse(open(file_path, 'r'), parser) + parsed_file = etree.parse(open(file_path, 'rb'), parser) xml = etree.tostring(parsed_file).decode() self.open_lyrics.xml_to_song(xml) except etree.XMLSyntaxError: From 9010e05430cfbc3bc4f392a1e8dd0c4c1992ba99 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 16 Apr 2014 22:42:44 +0200 Subject: [PATCH 149/155] add new openlyrics file --- .../songs/test_openlyricsimport.py | 18 ++++++-- .../What a friend we have in Jesus.xml | 44 +++++++++++++++++++ 2 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 tests/resources/openlyricssongs/What a friend we have in Jesus.xml diff --git a/tests/functional/openlp_plugins/songs/test_openlyricsimport.py b/tests/functional/openlp_plugins/songs/test_openlyricsimport.py index 5f005e10e..c7a176d07 100644 --- a/tests/functional/openlp_plugins/songs/test_openlyricsimport.py +++ b/tests/functional/openlp_plugins/songs/test_openlyricsimport.py @@ -40,8 +40,20 @@ from openlp.plugins.songs.lib.songimport import SongImport TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..', 'resources', 'openlyricssongs')) SONG_TEST_DATA = { - 'Mám zde přítele, Pána Ježíše.xml': { - 'title': 'Mám zde přítele', + #'Mám zde přítele, Pána Ježíše.xml': { + # 'title': 'Mám zde přítele', + # 'verses': [ + # ('Mám zde přítele,\nPána Ježíše,\na na rámě jeho spoléhám;\nv něm své stěstí mám,\n\ + # pokoj nalézám,\nkdyž na rámě jeho spoléhám!', 'v1'), + # ('Boží rámě\nje v soužení náš pevný hrad;\nBoží rámě,\nuč se na ně vždycky spoléhat!', 'c'), + # ('Jak je sladké být,\nv jeho družině,\nkdyž na rámě jeho spoléhám,\njak se života\ncesta zjasňuje\n\ + # když na rámě Boží spoléhám!', 'v2'), + # ('Čeho bych se bál,\nčeho strachoval,\nkdyž na rámě Boží spoléhám?\nMír je v duši mé,\n\ + # když On blízko je,\nkdyž na rámě jeho spoléhám.', 'v') + # ] + #}, + 'What a friend we have in Jesus.xml': { + 'title': 'What A Friend We Have In Jesus', 'verses': [ ('Mám zde přítele,\nPána Ježíše,\na na rámě jeho spoléhám;\nv něm své stěstí mám,\n\ pokoj nalézám,\nkdyž na rámě jeho spoléhám!', 'v1'), @@ -75,7 +87,7 @@ class TestOpenLyricsImport(TestCase): def file_import_test(self): """ - Test the actual import of real song files and check that the importer is called. + Test the actual import of real song files """ # GIVEN: Test files with a mocked out "manager" and a mocked out "import_wizard" for song_file in SONG_TEST_DATA: diff --git a/tests/resources/openlyricssongs/What a friend we have in Jesus.xml b/tests/resources/openlyricssongs/What a friend we have in Jesus.xml new file mode 100644 index 000000000..a111c853c --- /dev/null +++ b/tests/resources/openlyricssongs/What a friend we have in Jesus.xml @@ -0,0 +1,44 @@ + + + + + What A Friend We Have In Jesus + + + Joseph M. Scriven + Charles C. Convers + + Public Domain + 27714 + + Christ: Love/Mercy + Fruit: Peace/Comfort + + + + + + What a friend we have in Jesus, All ours sins and griefs to bear;
+ What a privilege to carry, Everything to God in prayer!
+ O what peace we often forfeit, O what needless pain we bear;
+ All because we do not carry, Everything to God in prayer! +
+
+ + + Have we trials and temptations? Is there trouble anywhere?
+ We should never be discouraged, Take it to the Lord in prayer.
+ Can we find a friend so faithful? Who will all our sorrows share?
+ Jesus knows our every weakness; Take it to the Lord in prayer. +
+
+ + + Are we weak and heavy laden, Cumbered with a load of care?
+ Precious Saviour still our refuge; Take it to the Lord in prayer.
+ Do thy friends despise forsake thee? Take it to the Lord in prayer!
+ In His arms He’ll take and shield thee; Thou wilt find a solace there. +
+
+
+
From 5f86811f8251edadd0202bfe233743626034d97d Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 16 Apr 2014 23:01:57 +0200 Subject: [PATCH 150/155] Fix tests --- .../songs/test_openlyricsimport.py | 35 +++++++++++-------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/tests/functional/openlp_plugins/songs/test_openlyricsimport.py b/tests/functional/openlp_plugins/songs/test_openlyricsimport.py index c7a176d07..8ac0637be 100644 --- a/tests/functional/openlp_plugins/songs/test_openlyricsimport.py +++ b/tests/functional/openlp_plugins/songs/test_openlyricsimport.py @@ -40,20 +40,8 @@ from openlp.plugins.songs.lib.songimport import SongImport TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..', 'resources', 'openlyricssongs')) SONG_TEST_DATA = { - #'Mám zde přítele, Pána Ježíše.xml': { - # 'title': 'Mám zde přítele', - # 'verses': [ - # ('Mám zde přítele,\nPána Ježíše,\na na rámě jeho spoléhám;\nv něm své stěstí mám,\n\ - # pokoj nalézám,\nkdyž na rámě jeho spoléhám!', 'v1'), - # ('Boží rámě\nje v soužení náš pevný hrad;\nBoží rámě,\nuč se na ně vždycky spoléhat!', 'c'), - # ('Jak je sladké být,\nv jeho družině,\nkdyž na rámě jeho spoléhám,\njak se života\ncesta zjasňuje\n\ - # když na rámě Boží spoléhám!', 'v2'), - # ('Čeho bych se bál,\nčeho strachoval,\nkdyž na rámě Boží spoléhám?\nMír je v duši mé,\n\ - # když On blízko je,\nkdyž na rámě jeho spoléhám.', 'v') - # ] - #}, - 'What a friend we have in Jesus.xml': { - 'title': 'What A Friend We Have In Jesus', + 'Mám zde přítele, Pána Ježíše.xml': { + 'title': 'Mám zde přítele', 'verses': [ ('Mám zde přítele,\nPána Ježíše,\na na rámě jeho spoléhám;\nv něm své stěstí mám,\n\ pokoj nalézám,\nkdyž na rámě jeho spoléhám!', 'v1'), @@ -61,7 +49,24 @@ SONG_TEST_DATA = { ('Jak je sladké být,\nv jeho družině,\nkdyž na rámě jeho spoléhám,\njak se života\ncesta zjasňuje\n\ když na rámě Boží spoléhám!', 'v2'), ('Čeho bych se bál,\nčeho strachoval,\nkdyž na rámě Boží spoléhám?\nMír je v duši mé,\n\ - když On blízko je,\nkdyž na rámě jeho spoléhám.', 'v') + když On blízko je,\nkdyž na rámě jeho spoléhám.', 'v3') + ] + }, + 'What a friend we have in Jesus.xml': { + 'title': 'What A Friend We Have In Jesus', + 'verses': [ + ('What a friend we have in Jesus, All ours sins and griefs to bear;\n\ + What a privilege to carry, Everything to God in prayer!\n\ + O what peace we often forfeit, O what needless pain we bear;\n\ + All because we do not carry, Everything to God in prayer!', 'v1'), + ('Have we trials and temptations? Is there trouble anywhere?\n\ + We should never be discouraged, Take it to the Lord in prayer.\n\ + Can we find a friend so faithful? Who will all our sorrows share?\n\ + Jesus knows our every weakness; Take it to the Lord in prayer.', 'v2'), + ('Are we weak and heavy laden, Cumbered with a load of care?\n\ + Precious Saviour still our refuge; Take it to the Lord in prayer.\n\ + Do thy friends despise forsake thee? Take it to the Lord in prayer!\n\ + In His arms He’ll take and shield thee; Thou wilt find a solace there.', 'v3') ] } } From d3443d12c2a35e6ac14a81e3f08714f53ce79e62 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Thu, 17 Apr 2014 16:32:56 +0200 Subject: [PATCH 151/155] Remove one test for now --- .../openlp_plugins/songs/test_openlyricsimport.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/tests/functional/openlp_plugins/songs/test_openlyricsimport.py b/tests/functional/openlp_plugins/songs/test_openlyricsimport.py index 8ac0637be..93ecafb78 100644 --- a/tests/functional/openlp_plugins/songs/test_openlyricsimport.py +++ b/tests/functional/openlp_plugins/songs/test_openlyricsimport.py @@ -40,18 +40,6 @@ from openlp.plugins.songs.lib.songimport import SongImport TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..', 'resources', 'openlyricssongs')) SONG_TEST_DATA = { - 'Mám zde přítele, Pána Ježíše.xml': { - 'title': 'Mám zde přítele', - 'verses': [ - ('Mám zde přítele,\nPána Ježíše,\na na rámě jeho spoléhám;\nv něm své stěstí mám,\n\ - pokoj nalézám,\nkdyž na rámě jeho spoléhám!', 'v1'), - ('Boží rámě\nje v soužení náš pevný hrad;\nBoží rámě,\nuč se na ně vždycky spoléhat!', 'c'), - ('Jak je sladké být,\nv jeho družině,\nkdyž na rámě jeho spoléhám,\njak se života\ncesta zjasňuje\n\ - když na rámě Boží spoléhám!', 'v2'), - ('Čeho bych se bál,\nčeho strachoval,\nkdyž na rámě Boží spoléhám?\nMír je v duši mé,\n\ - když On blízko je,\nkdyž na rámě jeho spoléhám.', 'v3') - ] - }, 'What a friend we have in Jesus.xml': { 'title': 'What A Friend We Have In Jesus', 'verses': [ From 9460b39a0150a5c35b85ed37d72fd44bc1add8a7 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Thu, 17 Apr 2014 17:00:28 +0200 Subject: [PATCH 152/155] Remove utf8 file --- .../Mám zde přítele, Pána Ježíše.xml | 53 ------------------- 1 file changed, 53 deletions(-) delete mode 100644 tests/resources/openlyricssongs/Mám zde přítele, Pána Ježíše.xml diff --git a/tests/resources/openlyricssongs/Mám zde přítele, Pána Ježíše.xml b/tests/resources/openlyricssongs/Mám zde přítele, Pána Ježíše.xml deleted file mode 100644 index 0914b4dfe..000000000 --- a/tests/resources/openlyricssongs/Mám zde přítele, Pána Ježíše.xml +++ /dev/null @@ -1,53 +0,0 @@ - - - - - Mám zde přítele - - - A.J. Showalter - E.A. Hoffman - - v1 c v2 c v3 c - - - - - Mám zde přítele,
- Pána Ježíše,
- a na rámě jeho spoléhám;
- v něm své stěstí mám,
- pokoj nalézám,
- když na rámě jeho spoléhám! -
-
- - - Boží rámě
- je v soužení náš pevný hrad;
- Boží rámě,
- uč se na ně vždycky spoléhat! -
-
- - - Jak je sladké být,
- v jeho družině,
- když na rámě jeho spoléhám,
- jak se života
- cesta zjasňuje
- když na rámě Boží spoléhám! -
-
- - - Čeho bych se bál,
- čeho strachoval,
- když na rámě Boží spoléhám?
- Mír je v duši mé,
- když On blízko je,
- když na rámě jeho spoléhám. -
-
-
-
From cec47fc950ae09cde0ce5680084c60fc0795cba9 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sun, 20 Apr 2014 14:06:02 +0200 Subject: [PATCH 153/155] fixed bug 1146964 Fixes: https://launchpad.net/bugs/1146964 --- openlp/core/ui/advancedtab.py | 2 +- openlp/core/ui/exceptionform.py | 2 +- openlp/core/ui/themeform.py | 2 +- openlp/plugins/images/lib/mediaitem.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/openlp/core/ui/advancedtab.py b/openlp/core/ui/advancedtab.py index b2c8cd14b..a5ce09bdf 100644 --- a/openlp/core/ui/advancedtab.py +++ b/openlp/core/ui/advancedtab.py @@ -511,7 +511,7 @@ class AdvancedTab(SettingsTab): """ Select an image for the default display screen. """ - file_filters = '%s;;%s (*.*) (*)' % (get_images_filter(), UiStrings().AllFiles) + file_filters = '%s;;%s (*.*)' % (get_images_filter(), UiStrings().AllFiles) filename = QtGui.QFileDialog.getOpenFileName(self, translate('OpenLP.AdvancedTab', 'Open File'), '', file_filters) if filename: diff --git a/openlp/core/ui/exceptionform.py b/openlp/core/ui/exceptionform.py index ae3bc9db0..e0228a43b 100644 --- a/openlp/core/ui/exceptionform.py +++ b/openlp/core/ui/exceptionform.py @@ -228,7 +228,7 @@ class ExceptionForm(QtGui.QDialog, Ui_ExceptionDialog, RegistryProperties): """ files = QtGui.QFileDialog.getOpenFileName(self, translate('ImagePlugin.ExceptionDialog', 'Select Attachment'), Settings().value(self.settings_section + '/last directory'), - '%s (*.*) (*)' % UiStrings().AllFiles) + '%s (*)' % UiStrings().AllFiles) log.info('New files(s) %s', str(files)) if files: self.file_attachment = str(files) diff --git a/openlp/core/ui/themeform.py b/openlp/core/ui/themeform.py index fbfc1035c..d9b61e117 100644 --- a/openlp/core/ui/themeform.py +++ b/openlp/core/ui/themeform.py @@ -432,7 +432,7 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard, RegistryProperties): Background Image button pushed. """ images_filter = get_images_filter() - images_filter = '%s;;%s (*.*) (*)' % (images_filter, UiStrings().AllFiles) + images_filter = '%s;;%s (*.*)' % (images_filter, UiStrings().AllFiles) filename = QtGui.QFileDialog.getOpenFileName(self, translate('OpenLP.ThemeWizard', 'Select Image'), '', images_filter) if filename: diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index d24a18434..c28f1e834 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -75,7 +75,7 @@ class ImageMediaItem(MediaManagerItem): def retranslateUi(self): self.on_new_prompt = translate('ImagePlugin.MediaItem', 'Select Image(s)') file_formats = get_images_filter() - self.on_new_file_masks = '%s;;%s (*.*) (*)' % (file_formats, UiStrings().AllFiles) + self.on_new_file_masks = '%s;;%s (*)' % (file_formats, UiStrings().AllFiles) self.add_group_action.setText(UiStrings().AddGroup) self.add_group_action.setToolTip(UiStrings().AddGroup) self.replace_action.setText(UiStrings().ReplaceBG) From c691fc55d0f0d287cbf24e96f6a5b13ef32017a6 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sun, 20 Apr 2014 15:00:14 +0200 Subject: [PATCH 154/155] added a test --- tests/functional/test_init.py | 70 +++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 tests/functional/test_init.py diff --git a/tests/functional/test_init.py b/tests/functional/test_init.py new file mode 100644 index 000000000..6fc7d7f70 --- /dev/null +++ b/tests/functional/test_init.py @@ -0,0 +1,70 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2014 Raoul Snyman # +# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan # +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # +# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, # +# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, # +# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, # +# Frode Woldsund, Martin Zibricky, Patrick Zimmermann # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### +""" +Package to test the openlp.core.__init__ package. +""" +import os + +from unittest import TestCase +from unittest.mock import MagicMock, patch +from PyQt4 import QtCore + +from openlp.core import OpenLP +from tests.helpers.testmixin import TestMixin + + +TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'resources')) + + +class TestInit(TestCase, TestMixin): + def setUp(self): + with patch('openlp.core.common.OpenLPMixin.__init__') as constructor: + constructor.return_value = None + self.openlp = OpenLP(list()) + + def tearDown(self): + del self.openlp + + def event_test(self): + """ + Test the reimplemented event method + """ + # GIVEN: A file path and a QEvent. + file_path = os.path.join(TEST_PATH, 'church.jpg') + mocked_file_method = MagicMock(return_value=file_path) + event = QtCore.QEvent(QtCore.QEvent.FileOpen) + event.file = mocked_file_method + + # WHEN: Call the vent method. + result = self.openlp.event(event) + + # THEN: The path should be inserted. + self.assertTrue(result, "The method should have returned True.") + mocked_file_method.assert_called_once_with() + self.assertEqual(self.openlp.args[0], file_path, "The path should be in args.") From dee8ed285035e77c56abfada996f635bc795f861 Mon Sep 17 00:00:00 2001 From: Raoul Snyman Date: Mon, 21 Apr 2014 00:23:26 +0200 Subject: [PATCH 155/155] Fix an exception raised when loading media players if the configuration is blank --- openlp/core/ui/media/__init__.py | 2 +- tests/functional/openlp_core_ui/test_media.py | 128 ++++++++++++++++++ 2 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 tests/functional/openlp_core_ui/test_media.py diff --git a/openlp/core/ui/media/__init__.py b/openlp/core/ui/media/__init__.py index 8cc10b6a1..b2b6ab0b8 100644 --- a/openlp/core/ui/media/__init__.py +++ b/openlp/core/ui/media/__init__.py @@ -90,7 +90,7 @@ def get_media_players(): overridden_player = 'auto' else: overridden_player = '' - saved_players_list = saved_players.replace('[', '').replace(']', '').split(',') + saved_players_list = saved_players.replace('[', '').replace(']', '').split(',') if saved_players else [] return saved_players_list, overridden_player diff --git a/tests/functional/openlp_core_ui/test_media.py b/tests/functional/openlp_core_ui/test_media.py new file mode 100644 index 000000000..d59690949 --- /dev/null +++ b/tests/functional/openlp_core_ui/test_media.py @@ -0,0 +1,128 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2014 Raoul Snyman # +# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan # +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # +# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, # +# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, # +# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, # +# Frode Woldsund, Martin Zibricky, Patrick Zimmermann # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### +""" +Package to test the openlp.core.ui package. +""" +from PyQt4 import QtCore +from unittest import TestCase + +from openlp.core.ui.media import get_media_players + +from tests.functional import MagicMock, patch +from tests.helpers.testmixin import TestMixin + + +class TestMedia(TestCase, TestMixin): + + def setUp(self): + pass + + def test_get_media_players_no_config(self): + """ + Test that when there's no config, get_media_players() returns an empty list of players (not a string) + """ + def value_results(key): + if key == 'media/players': + return '' + else: + return False + + # GIVEN: A mocked out Settings() object + with patch('openlp.core.ui.media.Settings.value') as mocked_value: + mocked_value.side_effect = value_results + + # WHEN: get_media_players() is called + used_players, overridden_player = get_media_players() + + # THEN: the used_players should be an empty list, and the overridden player should be an empty string + self.assertEqual([], used_players, 'Used players should be an empty list') + self.assertEqual('', overridden_player, 'Overridden player should be an empty string') + + def test_get_media_players_no_players(self): + """ + Test that when there's no players but overridden player is set, get_media_players() returns 'auto' + """ + def value_results(key): + if key == 'media/override player': + return QtCore.Qt.Checked + else: + return '' + + # GIVEN: A mocked out Settings() object + with patch('openlp.core.ui.media.Settings.value') as mocked_value: + mocked_value.side_effect = value_results + + # WHEN: get_media_players() is called + used_players, overridden_player = get_media_players() + + # THEN: the used_players should be an empty list, and the overridden player should be an empty string + self.assertEqual([], used_players, 'Used players should be an empty list') + self.assertEqual('auto', overridden_player, 'Overridden player should be "auto"') + + def test_get_media_players_with_valid_list(self): + """ + Test that when get_media_players() is called the string list is interpreted correctly + """ + def value_results(key): + if key == 'media/players': + return '[vlc,webkit,phonon]' + else: + return False + + # GIVEN: A mocked out Settings() object + with patch('openlp.core.ui.media.Settings.value') as mocked_value: + mocked_value.side_effect = value_results + + # WHEN: get_media_players() is called + used_players, overridden_player = get_media_players() + + # THEN: the used_players should be an empty list, and the overridden player should be an empty string + self.assertEqual(['vlc', 'webkit', 'phonon'], used_players, 'Used players should be correct') + self.assertEqual('', overridden_player, 'Overridden player should be an empty string') + + def test_get_media_players_with_overridden_player(self): + """ + Test that when get_media_players() is called the overridden player is correctly set + """ + def value_results(key): + if key == 'media/players': + return '[vlc,webkit,phonon]' + else: + return QtCore.Qt.Checked + + # GIVEN: A mocked out Settings() object + with patch('openlp.core.ui.media.Settings.value') as mocked_value: + mocked_value.side_effect = value_results + + # WHEN: get_media_players() is called + used_players, overridden_player = get_media_players() + + # THEN: the used_players should be an empty list, and the overridden player should be an empty string + self.assertEqual(['vlc', 'webkit', 'phonon'], used_players, 'Used players should be correct') + self.assertEqual('vlc,webkit,phonon', overridden_player, 'Overridden player should be a string of players') \ No newline at end of file