From 13374f1002d9014f2471e0749ace17e6049983df Mon Sep 17 00:00:00 2001 From: "Jeffrey S. Smith" Date: Mon, 2 Sep 2013 23:07:29 -0500 Subject: [PATCH 001/212] Add preliminary ProPresenter song import support --- openlp/plugins/songs/lib/__init__.py | 2 + openlp/plugins/songs/lib/importer.py | 26 +++++++--- openlp/plugins/songs/lib/ppimport.py | 73 ++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+), 8 deletions(-) create mode 100644 openlp/plugins/songs/lib/ppimport.py diff --git a/openlp/plugins/songs/lib/__init__.py b/openlp/plugins/songs/lib/__init__.py index 271a94710..5918dd330 100644 --- a/openlp/plugins/songs/lib/__init__.py +++ b/openlp/plugins/songs/lib/__init__.py @@ -538,6 +538,8 @@ def strip_rtf(text, default_encoding=None): out.append('\xA0') elif char in '{}\\' and not ignorable: out.append(char) + elif char in '\r\n' and not ignorable: + out.append(SPECIAL_CHARS['par']) elif char == '-' and not ignorable: out.append('\u00AD') elif char == '_' and not ignorable: diff --git a/openlp/plugins/songs/lib/importer.py b/openlp/plugins/songs/lib/importer.py index 8e7a9f36e..f760bd8cd 100644 --- a/openlp/plugins/songs/lib/importer.py +++ b/openlp/plugins/songs/lib/importer.py @@ -49,6 +49,7 @@ from .songproimport import SongProImport from .sundayplusimport import SundayPlusImport from .foilpresenterimport import FoilPresenterImport from .zionworximport import ZionWorxImport +from .ppimport import ProPresenterImport # Imports that might fail @@ -157,14 +158,15 @@ class SongFormat(object): MediaShout = 8 OpenSong = 9 PowerSong = 10 - SongBeamer = 11 - SongPro = 12 - SongShowPlus = 13 - SongsOfFellowship = 14 - SundayPlus = 15 - WordsOfWorship = 16 - WorshipCenterPro = 17 - ZionWorx = 18 + ProPresenter = 11 + SongBeamer = 12 + SongPro = 13 + SongShowPlus = 14 + SongsOfFellowship = 15 + SundayPlus = 16 + WordsOfWorship = 17 + WorshipCenterPro = 18 + ZionWorx = 19 # Set optional attribute defaults __defaults__ = { @@ -262,6 +264,13 @@ class SongFormat(object): 'invalidSourceMsg': translate('SongsPlugin.ImportWizardForm', 'You need to specify a valid PowerSong 1.0 database folder.') }, + ProPresenter: { + 'class': ProPresenterImport, + 'name': 'ProPresenter', + 'prefix': 'proPresenter', + 'filter': '%s (*.pro4)' % translate('SongsPlugin.ImportWizardForm', + 'ProPresenter Song Files') + }, SongBeamer: { 'class': SongBeamerImport, 'name': 'SongBeamer', @@ -347,6 +356,7 @@ class SongFormat(object): SongFormat.MediaShout, SongFormat.OpenSong, SongFormat.PowerSong, + SongFormat.ProPresenter, SongFormat.SongBeamer, SongFormat.SongPro, SongFormat.SongShowPlus, diff --git a/openlp/plugins/songs/lib/ppimport.py b/openlp/plugins/songs/lib/ppimport.py new file mode 100644 index 000000000..11ba9d2e7 --- /dev/null +++ b/openlp/plugins/songs/lib/ppimport.py @@ -0,0 +1,73 @@ +# -*- 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 # +# --------------------------------------------------------------------------- # +# This program is free software; you can redistribute it and/or modify it # +# under the terms of the GNU General Public License as published by the Free # +# Software Foundation; version 2 of the License. # +# # +# This program is distributed in the hope that it will be useful, but WITHOUT # +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # +# more details. # +# # +# You should have received a copy of the GNU General Public License along # +# with this program; if not, write to the Free Software Foundation, Inc., 59 # +# Temple Place, Suite 330, Boston, MA 02111-1307 USA # +############################################################################### +""" +The :mod:`ppimport` module provides the functionality for importing +ProPresenter song files into the current installation database. +""" + +import os +import base64 +from lxml import objectify + +from openlp.core.ui.wizard import WizardStrings +from openlp.plugins.songs.lib import strip_rtf +from .songimport import SongImport + +class ProPresenterImport(SongImport): + """ + The :class:`ProPresenterImport` class provides OpenLP with the + ability to import ProPresenter song files. + """ + def doImport(self): + self.import_wizard.progress_bar.setMaximum(len(self.import_source)) + for file_path in self.import_source: + if self.stop_import_flag: + return + self.import_wizard.increment_progress_bar( + WizardStrings.ImportingType % os.path.basename(file_path)) + root = objectify.parse(open(file_path, 'rb')).getroot() + self.processSong(root) + + def processSong(self, root): + self.setDefaults() + self.title = root.get('CCLISongTitle') + self.copyright = root.get('CCLICopyrightInfo') + self.comments = root.get('notes') + self.ccliNumber = root.get('CCLILicenseNumber') + for author_key in ['author', 'artist', 'CCLIArtistCredits']: + author = root.get(author_key) + if len(author) > 0: + self.parse_author(author) + for slide in root.slides.RVDisplaySlide: + RTFData = slide.displayElements.RVTextElement.get('RTFData') + rtf = base64.standard_b64decode(RTFData) + words, encoding = strip_rtf(rtf.decode()) + self.addVerse(words) + if not self.finish(): + self.logError(self.import_source) From 0ee684d3a87cb63d92cbce15f02f7b9177cfaf81 Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Wed, 26 Mar 2014 20:49:12 +0100 Subject: [PATCH 002/212] Added support for ews import --- openlp/plugins/songs/lib/ewimport.py | 233 +++++++++++++++++++++------ openlp/plugins/songs/lib/importer.py | 5 +- 2 files changed, 185 insertions(+), 53 deletions(-) diff --git a/openlp/plugins/songs/lib/ewimport.py b/openlp/plugins/songs/lib/ewimport.py index 9e3ae566e..9117aaadc 100644 --- a/openlp/plugins/songs/lib/ewimport.py +++ b/openlp/plugins/songs/lib/ewimport.py @@ -34,13 +34,13 @@ EasyWorship song databases into the current installation database. import os import struct import re +import zlib from openlp.core.lib import translate from openlp.plugins.songs.lib import VerseType from openlp.plugins.songs.lib import retrieve_windows_encoding, strip_rtf from .songimport import SongImport -RTF_STRIPPING_REGEX = re.compile(r'\{\\tx[^}]*\}') # regex: at least two newlines, can have spaces between them SLIDE_BREAK_REGEX = re.compile(r'\n *?\n[\n ]*') NUMBER_REGEX = re.compile(r'[0-9]+') @@ -77,7 +77,91 @@ class EasyWorshipSongImport(SongImport): def do_import(self): """ - Import the songs + Determines the type of file to import and calls the appropiate method + + :return: + """ + if self.import_source.lower().endswith('ews'): + self.import_ews() + else: + self.import_db() + + def import_ews(self): + """ + Import the songs from service file + The full spec of the ews files can be found here: + https://github.com/meinders/lithium-ews/blob/master/docs/ews%20file%20format.md + + :return: + """ + # Open ews file if it exists + if not os.path.isfile(self.import_source): + return + # Make sure there is room for at least a header and one entry + if os.path.getsize(self.import_source) < 892: + return + # Take a stab at how text is encoded + self.encoding = 'cp1252' + self.ews_file = open(self.import_source, 'rb') + # Get file version + type, = struct.unpack('<38s', self.ews_file.read(38)) + version = type.decode()[-3:] + # Set fileposition based on filetype/version + file_pos = 0 + if version == ' 5': + file_pos = 56 + elif version == ' 3': + file_pos = 48 + elif version == '1.6': + file_pos = 40 + else: + return + entry_count = self.get_i32(file_pos) + entry_length = self.get_i16(file_pos+4) + file_pos += 6 + self.import_wizard.progress_bar.setMaximum(entry_count) + # Loop over songs + for x in range(1, entry_count): + # Load entry metadata + self.set_defaults() + self.title = self.get_string(file_pos, 50) + resource = self.get_string(file_pos + 51, 255) + authors = self.get_string(file_pos + 307, 50) + copyright = self.get_string(file_pos + 358, 100) + admin = self.get_string(file_pos + 459, 50) + cont_ptr = self.get_i32(file_pos + 800) + cont_type = self.get_i32(file_pos + 820) + notes = self.get_string(file_pos + 1155, 160) + self.ccli_number = self.get_string(file_pos + 1410, 10) + # Only handle content type 1 (songs) + if cont_type != 1: + file_pos += entry_length + continue + # Load song content + content_length = self.get_i32(cont_ptr) + deflated_content = self.get_bytes(cont_ptr + 4, content_length - 10) + deflated_length = self.get_i32(cont_ptr + 4 + content_length - 6) + inflated_content = zlib.decompress(deflated_content, 15, deflated_length) + if copyright: + self.copyright = copyright + if admin: + if copyright: + self.copyright += ', ' + self.copyright += translate('SongsPlugin.EasyWorshipSongImport', + 'Administered by %s') % admin + # Set the SongImport object members. + self.set_song_import_object(authors, inflated_content) + if self.stop_import_flag: + break + if not self.finish(): + self.log_error(self.import_source) + # Set file_pos for next entry + file_pos += entry_length + self.ews_file.close() + + def import_db(self): + """ + Import the songs from the database :return: """ @@ -176,7 +260,6 @@ class EasyWorshipSongImport(SongImport): ccli = self.get_field(fi_ccli) authors = self.get_field(fi_author) words = self.get_field(fi_words) - # Set the SongImport object members. if copy: self.copyright = copy.decode() if admin: @@ -187,55 +270,11 @@ class EasyWorshipSongImport(SongImport): if ccli: self.ccli_number = ccli.decode() if authors: - # Split up the authors - author_list = authors.split(b'/') - if len(author_list) < 2: - author_list = authors.split(b';') - if len(author_list) < 2: - author_list = authors.split(b',') - for author_name in author_list: - self.add_author(author_name.decode().strip()) - if words: - # Format the lyrics - result = strip_rtf(words.decode(), self.encoding) - if result is None: - return - words, self.encoding = result - verse_type = VerseType.tags[VerseType.Verse] - for verse in SLIDE_BREAK_REGEX.split(words): - verse = verse.strip() - if not verse: - continue - verse_split = verse.split('\n', 1) - first_line_is_tag = False - # EW tags: verse, chorus, pre-chorus, bridge, tag, - # intro, ending, slide - for tag in VerseType.tags + ['tag', 'slide']: - tag = tag.lower() - ew_tag = verse_split[0].strip().lower() - if ew_tag.startswith(tag): - verse_type = tag[0] - if tag == 'tag' or tag == 'slide': - verse_type = VerseType.tags[VerseType.Other] - first_line_is_tag = True - number_found = False - # check if tag is followed by number and/or note - if len(ew_tag) > len(tag): - match = NUMBER_REGEX.search(ew_tag) - if match: - number = match.group() - verse_type += number - number_found = True - match = NOTE_REGEX.search(ew_tag) - if match: - self.comments += ew_tag + '\n' - if not number_found: - verse_type += '1' - break - self.add_verse(verse_split[-1].strip() if first_line_is_tag else verse, verse_type) - if len(self.comments) > 5: - self.comments += str(translate('SongsPlugin.EasyWorshipSongImport', - '\n[above are Song Tags with notes imported from EasyWorship]')) + authors = authors.decode() + else: + authors = '' + # Set the SongImport object members. + self.set_song_import_object(authors, words) if self.stop_import_flag: break if not self.finish(): @@ -243,6 +282,61 @@ class EasyWorshipSongImport(SongImport): db_file.close() self.memo_file.close() + def set_song_import_object(self, authors, words): + """ + Set the SongImport object members. + """ + if authors: + # Split up the authors + author_list = authors.split('/') + if len(author_list) < 2: + author_list = authors.split(';') + if len(author_list) < 2: + author_list = authors.split(',') + for author_name in author_list: + self.add_author(author_name.strip()) + if words: + # Format the lyrics + result = strip_rtf(words.decode(), self.encoding) + if result is None: + return + words, self.encoding = result + verse_type = VerseType.tags[VerseType.Verse] + for verse in SLIDE_BREAK_REGEX.split(words): + verse = verse.strip() + if not verse: + continue + verse_split = verse.split('\n', 1) + first_line_is_tag = False + # EW tags: verse, chorus, pre-chorus, bridge, tag, + # intro, ending, slide + for tag in VerseType.tags + ['tag', 'slide']: + tag = tag.lower() + ew_tag = verse_split[0].strip().lower() + if ew_tag.startswith(tag): + verse_type = tag[0] + if tag == 'tag' or tag == 'slide': + verse_type = VerseType.tags[VerseType.Other] + first_line_is_tag = True + number_found = False + # check if tag is followed by number and/or note + if len(ew_tag) > len(tag): + match = NUMBER_REGEX.search(ew_tag) + if match: + number = match.group() + verse_type += number + number_found = True + match = NOTE_REGEX.search(ew_tag) + if match: + self.comments += ew_tag + '\n' + if not number_found: + verse_type += '1' + break + self.add_verse(verse_split[-1].strip() if first_line_is_tag else verse, verse_type) + if len(self.comments) > 5: + self.comments += str(translate('SongsPlugin.EasyWorshipSongImport', + '\n[above are Song Tags with notes imported from EasyWorship]')) + def find_field(self, field_name): """ Find a field in the descriptions @@ -323,3 +417,38 @@ class EasyWorshipSongImport(SongImport): return self.memo_file.read(blob_size) else: return 0 + + def get_bytes(self, pos, length): + """ + Get bytes from ews_file + """ + self.ews_file.seek(pos) + return self.ews_file.read(length) + + def get_string(self, pos, length): + """ + Get string from ews_file + """ + bytes = self.get_bytes(pos, length) + mask = '<' + str(length) + 's' + byte_str, = struct.unpack(mask, bytes) + return byte_str.decode('unicode-escape').replace('\0', '').strip() + + def get_i16(self, pos): + """ + Get short int from ews_file + """ + + bytes = self.get_bytes(pos, 2) + mask = ' Date: Fri, 28 Mar 2014 17:06:16 +0100 Subject: [PATCH 003/212] Add support for the Author type property --- openlp/core/lib/db.py | 3 +- openlp/plugins/songs/forms/editsongdialog.py | 9 ++++- openlp/plugins/songs/forms/editsongform.py | 40 +++++++++++--------- openlp/plugins/songs/lib/__init__.py | 7 ---- openlp/plugins/songs/lib/db.py | 33 +++++++++++++++- openlp/plugins/songs/lib/mediaitem.py | 32 +++++++++++++--- openlp/plugins/songs/lib/upgrade.py | 11 +++++- 7 files changed, 100 insertions(+), 35 deletions(-) diff --git a/openlp/core/lib/db.py b/openlp/core/lib/db.py index c81cb5a3a..b4e027035 100644 --- a/openlp/core/lib/db.py +++ b/openlp/core/lib/db.py @@ -194,6 +194,7 @@ class Manager(object): db_ver, up_ver = upgrade_db(self.db_url, upgrade_mod) except (SQLAlchemyError, DBAPIError): log.exception('Error loading database: %s', self.db_url) + return if db_ver > up_ver: critical_error_message_box( translate('OpenLP.Manager', 'Database Error'), @@ -215,7 +216,7 @@ class Manager(object): Save an object to the database :param object_instance: The object to save - :param commit: Commit the session with this object + :param commit: Commit the session with this object """ for try_count in range(3): try: diff --git a/openlp/plugins/songs/forms/editsongdialog.py b/openlp/plugins/songs/forms/editsongdialog.py index f2ef5af06..7a491032b 100644 --- a/openlp/plugins/songs/forms/editsongdialog.py +++ b/openlp/plugins/songs/forms/editsongdialog.py @@ -122,6 +122,11 @@ class Ui_EditSongDialog(object): self.author_add_layout.setObjectName('author_add_layout') self.authors_combo_box = create_combo_box(self.authors_group_box, 'authors_combo_box') self.author_add_layout.addWidget(self.authors_combo_box) + self.author_types_combo_box = create_combo_box(self.authors_group_box, 'author_types_combo_box', editable=False) + # Need to give these boxes some min width, else they are too small + self.authors_combo_box.setMinimumWidth(150) + self.author_types_combo_box.setMinimumWidth(80) + self.author_add_layout.addWidget(self.author_types_combo_box) self.author_add_button = QtGui.QPushButton(self.authors_group_box) self.author_add_button.setObjectName('author_add_button') self.author_add_layout.addWidget(self.author_add_button) @@ -330,7 +335,7 @@ class Ui_EditSongDialog(object): translate('SongsPlugin.EditSongForm', 'Warning: You have not entered a verse order.') -def create_combo_box(parent, name): +def create_combo_box(parent, name, editable=True): """ Utility method to generate a standard combo box for this dialog. @@ -340,7 +345,7 @@ def create_combo_box(parent, name): combo_box = QtGui.QComboBox(parent) combo_box.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToMinimumContentsLength) combo_box.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed) - combo_box.setEditable(True) + combo_box.setEditable(editable) combo_box.setInsertPolicy(QtGui.QComboBox.NoInsert) combo_box.setObjectName(name) return combo_box diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index 8b1e3a897..2c651e983 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -42,7 +42,7 @@ from openlp.core.common import Registry, RegistryProperties, AppLocation, UiStri from openlp.core.lib import FileDialog, PluginStatus, MediaType, create_separated_list from openlp.core.lib.ui import set_case_insensitive_completer, critical_error_message_box, find_and_set_in_combo_box from openlp.plugins.songs.lib import VerseType, clean_song -from openlp.plugins.songs.lib.db import Book, Song, Author, Topic, MediaFile +from openlp.plugins.songs.lib.db import Book, Song, Author, AuthorSong, Topic, MediaFile from openlp.plugins.songs.lib.ui import SongStrings from openlp.plugins.songs.lib.xml import SongXML from openlp.plugins.songs.forms.editsongdialog import Ui_EditSongDialog @@ -122,12 +122,12 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): combo.setItemData(row, obj.id) set_case_insensitive_completer(cache, combo) - def _add_author_to_list(self, author): + def _add_author_to_list(self, author, author_type): """ Add an author to the author list. """ - author_item = QtGui.QListWidgetItem(str(author.display_name)) - author_item.setData(QtCore.Qt.UserRole, author.id) + author_item = QtGui.QListWidgetItem(author.get_display_name(author_type)) + author_item.setData(QtCore.Qt.UserRole, (author.id, author_type)) self.authors_list_view.addItem(author_item) def _extract_verse_order(self, verse_order): @@ -302,6 +302,14 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): self.authors.append(author.display_name) set_case_insensitive_completer(self.authors, self.authors_combo_box) + #Types + self.author_types_combo_box.clear() + self.author_types_combo_box.addItem('') + # Don't iterate over the dictionary to give them this specific order + self.author_types_combo_box.addItem(Author.Types[Author.TYPE_WORDS], Author.TYPE_WORDS) + self.author_types_combo_box.addItem(Author.Types[Author.TYPE_MUSIC], Author.TYPE_MUSIC) + self.author_types_combo_box.addItem(Author.Types[Author.TYPE_TRANSLATION], Author.TYPE_TRANSLATION) + def load_topics(self): """ Load the topics into the combobox. @@ -454,10 +462,8 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): self.tag_rows() # clear the results self.authors_list_view.clear() - for author in self.song.authors: - author_name = QtGui.QListWidgetItem(str(author.display_name)) - author_name.setData(QtCore.Qt.UserRole, author.id) - self.authors_list_view.addItem(author_name) + for author_song in self.song.authors_songs: + self._add_author_to_list(author_song.author, author_song.author_type) # clear the results self.topics_list_view.clear() for topic in self.song.topics: @@ -496,6 +502,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): """ item = int(self.authors_combo_box.currentIndex()) text = self.authors_combo_box.currentText().strip(' \r\n\t') + author_type = self.author_types_combo_box.itemData(self.author_types_combo_box.currentIndex()) # This if statement is for OS X, which doesn't seem to work well with # the QCompleter auto-completion class. See bug #812628. if text in self.authors: @@ -513,7 +520,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): author = Author.populate(first_name=text.rsplit(' ', 1)[0], last_name=text.rsplit(' ', 1)[1], display_name=text) self.manager.save_object(author) - self._add_author_to_list(author) + self._add_author_to_list(author, author_type) self.load_authors() self.authors_combo_box.setCurrentIndex(0) else: @@ -525,7 +532,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): critical_error_message_box( message=translate('SongsPlugin.EditSongForm', 'This author is already in the list.')) else: - self._add_author_to_list(author) + self._add_author_to_list(author, author_type) self.authors_combo_box.setCurrentIndex(0) else: QtGui.QMessageBox.warning( @@ -538,8 +545,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): """ Run a set of actions when an author in the list is selected (mainly enable the delete button). """ - if self.authors_list_view.count() > 1: - self.author_remove_button.setEnabled(True) + self.author_remove_button.setEnabled(True) def on_author_remove_button_clicked(self): """ @@ -906,13 +912,13 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): else: self.song.theme_name = None self._process_lyrics() - self.song.authors = [] + self.song.authors_songs = [] for row in range(self.authors_list_view.count()): item = self.authors_list_view.item(row) - author_id = (item.data(QtCore.Qt.UserRole)) - author = self.manager.get_object(Author, author_id) - if author is not None: - self.song.authors.append(author) + author_song = AuthorSong() + author_song.author_id = item.data(QtCore.Qt.UserRole)[0] + author_song.author_type = item.data(QtCore.Qt.UserRole)[1] + self.song.authors_songs.append(author_song) self.song.topics = [] for row in range(self.topics_list_view.count()): item = self.topics_list_view.item(row) diff --git a/openlp/plugins/songs/lib/__init__.py b/openlp/plugins/songs/lib/__init__.py index dc198d4b7..60e8788c8 100644 --- a/openlp/plugins/songs/lib/__init__.py +++ b/openlp/plugins/songs/lib/__init__.py @@ -389,13 +389,6 @@ def clean_song(manager, song): song.lyrics = str(song.lyrics, encoding='utf8') verses = SongXML().get_verses(song.lyrics) song.search_lyrics = ' '.join([clean_string(verse[1]) for verse in verses]) - # The song does not have any author, add one. - if not song.authors: - name = SongStrings.AuthorUnknown - author = manager.get_object_filtered(Author, Author.display_name == name) - if author is None: - author = Author.populate(display_name=name, last_name='', first_name='') - song.authors.append(author) if song.copyright: song.copyright = CONTROL_CHARS.sub('', song.copyright).strip() diff --git a/openlp/plugins/songs/lib/db.py b/openlp/plugins/songs/lib/db.py index c3965e2ed..5d360f4f1 100644 --- a/openlp/plugins/songs/lib/db.py +++ b/openlp/plugins/songs/lib/db.py @@ -39,12 +39,34 @@ from sqlalchemy.sql.expression import func from openlp.core.lib.db import BaseModel, init_db from openlp.core.utils import get_natural_key +from openlp.core.lib import translate class Author(BaseModel): """ Author model """ + #These types are defined by OpenLyrics: http://openlyrics.info/dataformat.html#authors + TYPE_WORDS = 'words' + TYPE_MUSIC = 'music' + TYPE_TRANSLATION = 'translation' + Types = { + TYPE_WORDS: translate('OpenLP.Ui', 'Words'), + TYPE_MUSIC: translate('OpenLP.Ui', 'Music'), + TYPE_TRANSLATION: translate('OpenLP.Ui', 'Translation') + } + def get_display_name(self, author_type=None): + if author_type: + return "%s: %s"%(self.Types[author_type], self.display_name) + return self.display_name + +class AuthorSong(BaseModel): + """ + Relationship between Authors and Songs (many to many). + Need to define this relationship table explicit to get access to the + Association Object (author_type). + http://docs.sqlalchemy.org/en/latest/orm/relationships.html#association-object + """ pass @@ -67,6 +89,7 @@ class Song(BaseModel): """ Song model """ + def __init__(self): self.sort_key = [] @@ -120,6 +143,7 @@ def init_schema(url): * author_id * song_id + * author_type **media_files Table** * id @@ -230,7 +254,8 @@ def init_schema(url): 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) + Column('song_id', types.Integer(), ForeignKey('songs.id'), primary_key=True), + Column('author_type', types.String(), primary_key=True) ) # Definition of the "songs_topics" table @@ -241,10 +266,14 @@ def init_schema(url): ) mapper(Author, authors_table) + mapper(AuthorSong, authors_songs_table, properties={ + 'author': relation(Author) + }) 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), + 'authors_songs': relation(AuthorSong, cascade="all, delete-orphan"), + 'authors': relation(Author, secondary=authors_songs_table, viewonly=True), '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) diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index ad981135f..ca0c42483 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -464,23 +464,45 @@ class SongMediaItem(MediaManagerItem): def generate_footer(self, item, song): """ Generates the song footer based on a song and adds details to a service item. - author_list is only required for initial song generation. :param item: The service item to be amended :param song: The song to be used to generate the footer + :return List of all authors (only required for initial song generation) """ - author_list = [str(author.display_name) for author in song.authors] + authors_words = [] + authors_music = [] + authors_translation = [] + authors_none = [] + for author_song in song.authors_songs: + if author_song.author_type == Author.TYPE_WORDS: + authors_words.append(author_song.author.display_name) + elif author_song.author_type == Author.TYPE_MUSIC: + authors_music.append(author_song.author.display_name) + elif author_song.author_type == Author.TYPE_TRANSLATION: + authors_translation.append(author_song.author.display_name) + else: + authors_none.append(author_song.author.display_name) + authors_all = authors_words + authors_music + authors_translation + authors_none item.audit = [ - song.title, author_list, song.copyright, str(song.ccli_number) + song.title, authors_all, song.copyright, str(song.ccli_number) ] item.raw_footer = [] item.raw_footer.append(song.title) - item.raw_footer.append(create_separated_list(author_list)) + if authors_none: + item.raw_footer.append("%s: %s"%(translate('OpenLP.Ui', 'Written by'), create_separated_list(authors_words))) + if authors_words: + item.raw_footer.append("%s: %s"%(Author.Types[Author.TYPE_WORDS], create_separated_list(authors_words))) + if authors_music: + item.raw_footer.append("%s: %s"%(Author.Types[Author.TYPE_MUSIC], create_separated_list(authors_music))) + if authors_translation: + item.raw_footer.append("%s: %s"%(Author.Types[Author.TYPE_TRANSLATION], create_separated_list(authors_translation))) + if not authors_all: #No authors defined + item.raw_footer.append(SongStrings.AuthorUnknown) item.raw_footer.append(song.copyright) if Settings().value('core/ccli number'): item.raw_footer.append(translate('SongsPlugin.MediaItem', 'CCLI License: ') + Settings().value('core/ccli number')) - return author_list + return authors_all def service_load(self, item): """ diff --git a/openlp/plugins/songs/lib/upgrade.py b/openlp/plugins/songs/lib/upgrade.py index fdb0f17ef..15395fbb9 100644 --- a/openlp/plugins/songs/lib/upgrade.py +++ b/openlp/plugins/songs/lib/upgrade.py @@ -36,7 +36,7 @@ from sqlalchemy.sql.expression import func, false, null, text from openlp.core.lib.db import get_upgrade_op -__version__ = 3 +__version__ = 4 def upgrade_1(session, metadata): @@ -82,3 +82,12 @@ 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())) + +def upgrade_4(session, metadata): + """ + Version 4 upgrade. + + This upgrade adds a column for author type to the authors table + """ + op = get_upgrade_op(session) + op.add_column('authors_songs', Column('author_type', types.String(), primary_key=True, server_default=null())) From 615442063e919730d40274a1886cc219c9d20183 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Sat, 29 Mar 2014 11:08:52 +0100 Subject: [PATCH 004/212] Fix display of Author type none --- 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 ca0c42483..b71f27029 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -489,7 +489,7 @@ class SongMediaItem(MediaManagerItem): item.raw_footer = [] item.raw_footer.append(song.title) if authors_none: - item.raw_footer.append("%s: %s"%(translate('OpenLP.Ui', 'Written by'), create_separated_list(authors_words))) + item.raw_footer.append("%s: %s"%(translate('OpenLP.Ui', 'Written by'), create_separated_list(authors_none))) if authors_words: item.raw_footer.append("%s: %s"%(Author.Types[Author.TYPE_WORDS], create_separated_list(authors_words))) if authors_music: From 87051a094a2fc29a52ed0efad82b5708e3772816 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Sun, 30 Mar 2014 19:23:36 +0200 Subject: [PATCH 005/212] Put Author types in an own enum class --- openlp/plugins/songs/forms/editsongform.py | 8 ++++---- openlp/plugins/songs/lib/db.py | 24 +++++++++++++--------- openlp/plugins/songs/lib/mediaitem.py | 16 +++++++-------- 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index ca17b98dc..2915ea4e8 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -42,7 +42,7 @@ from openlp.core.common import Registry, RegistryProperties, AppLocation, UiStri from openlp.core.lib import FileDialog, PluginStatus, MediaType, create_separated_list from openlp.core.lib.ui import set_case_insensitive_completer, critical_error_message_box, find_and_set_in_combo_box from openlp.plugins.songs.lib import VerseType, clean_song -from openlp.plugins.songs.lib.db import Book, Song, Author, AuthorSong, Topic, MediaFile +from openlp.plugins.songs.lib.db import Book, Song, Author, AuthorSong, AuthorType, Topic, MediaFile from openlp.plugins.songs.lib.ui import SongStrings from openlp.plugins.songs.lib.xml import SongXML from openlp.plugins.songs.forms.editsongdialog import Ui_EditSongDialog @@ -306,9 +306,9 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): self.author_types_combo_box.clear() self.author_types_combo_box.addItem('') # Don't iterate over the dictionary to give them this specific order - self.author_types_combo_box.addItem(Author.Types[Author.TYPE_WORDS], Author.TYPE_WORDS) - self.author_types_combo_box.addItem(Author.Types[Author.TYPE_MUSIC], Author.TYPE_MUSIC) - self.author_types_combo_box.addItem(Author.Types[Author.TYPE_TRANSLATION], Author.TYPE_TRANSLATION) + self.author_types_combo_box.addItem(AuthorType.Types[AuthorType.Words], AuthorType.Words) + self.author_types_combo_box.addItem(AuthorType.Types[AuthorType.Music], AuthorType.Music) + self.author_types_combo_box.addItem(AuthorType.Types[AuthorType.Translation], AuthorType.Translation) def load_topics(self): """ diff --git a/openlp/plugins/songs/lib/db.py b/openlp/plugins/songs/lib/db.py index 5d360f4f1..0c1b3642c 100644 --- a/openlp/plugins/songs/lib/db.py +++ b/openlp/plugins/songs/lib/db.py @@ -46,18 +46,9 @@ class Author(BaseModel): """ Author model """ - #These types are defined by OpenLyrics: http://openlyrics.info/dataformat.html#authors - TYPE_WORDS = 'words' - TYPE_MUSIC = 'music' - TYPE_TRANSLATION = 'translation' - Types = { - TYPE_WORDS: translate('OpenLP.Ui', 'Words'), - TYPE_MUSIC: translate('OpenLP.Ui', 'Music'), - TYPE_TRANSLATION: translate('OpenLP.Ui', 'Translation') - } def get_display_name(self, author_type=None): if author_type: - return "%s: %s"%(self.Types[author_type], self.display_name) + return "%s: %s"%(AuthorType.Types[author_type], self.display_name) return self.display_name class AuthorSong(BaseModel): @@ -69,6 +60,19 @@ class AuthorSong(BaseModel): """ pass +class AuthorType(object): + """ + Enumeration for Author types. + They are defined by OpenLyrics: http://openlyrics.info/dataformat.html#authors + """ + Words = 'words' + Music = 'music' + Translation = 'translation' + Types = { + Words: translate('OpenLP.Ui', 'Words'), + Music: translate('OpenLP.Ui', 'Music'), + Translation: translate('OpenLP.Ui', 'Translation') + } class Book(BaseModel): """ diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index b71f27029..c40162474 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -44,7 +44,7 @@ from openlp.plugins.songs.forms.songmaintenanceform import SongMaintenanceForm from openlp.plugins.songs.forms.songimportform import SongImportForm from openlp.plugins.songs.forms.songexportform import SongExportForm from openlp.plugins.songs.lib import VerseType, clean_string, delete_song -from openlp.plugins.songs.lib.db import Author, Song, Book, MediaFile +from openlp.plugins.songs.lib.db import Author, AuthorType, Song, Book, MediaFile from openlp.plugins.songs.lib.ui import SongStrings from openlp.plugins.songs.lib.xml import OpenLyrics, SongXML @@ -467,18 +467,18 @@ class SongMediaItem(MediaManagerItem): :param item: The service item to be amended :param song: The song to be used to generate the footer - :return List of all authors (only required for initial song generation) + :return: List of all authors (only required for initial song generation) """ authors_words = [] authors_music = [] authors_translation = [] authors_none = [] for author_song in song.authors_songs: - if author_song.author_type == Author.TYPE_WORDS: + if author_song.author_type == AuthorType.Words: authors_words.append(author_song.author.display_name) - elif author_song.author_type == Author.TYPE_MUSIC: + elif author_song.author_type == AuthorType.Music: authors_music.append(author_song.author.display_name) - elif author_song.author_type == Author.TYPE_TRANSLATION: + elif author_song.author_type == AuthorType.Translation: authors_translation.append(author_song.author.display_name) else: authors_none.append(author_song.author.display_name) @@ -491,11 +491,11 @@ class SongMediaItem(MediaManagerItem): if authors_none: item.raw_footer.append("%s: %s"%(translate('OpenLP.Ui', 'Written by'), create_separated_list(authors_none))) if authors_words: - item.raw_footer.append("%s: %s"%(Author.Types[Author.TYPE_WORDS], create_separated_list(authors_words))) + item.raw_footer.append("%s: %s"%(AuthorType.Types[AuthorType.Words], create_separated_list(authors_words))) if authors_music: - item.raw_footer.append("%s: %s"%(Author.Types[Author.TYPE_MUSIC], create_separated_list(authors_music))) + item.raw_footer.append("%s: %s"%(AuthorType.Types[AuthorType.Music], create_separated_list(authors_music))) if authors_translation: - item.raw_footer.append("%s: %s"%(Author.Types[Author.TYPE_TRANSLATION], create_separated_list(authors_translation))) + item.raw_footer.append("%s: %s"%(AuthorType.Types[AuthorType.Translation], create_separated_list(authors_translation))) if not authors_all: #No authors defined item.raw_footer.append(SongStrings.AuthorUnknown) item.raw_footer.append(song.copyright) From c7358e4a9f46f91f27e276f3949204228016b344 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Sun, 30 Mar 2014 19:38:26 +0200 Subject: [PATCH 006/212] The author_type column is part of the primary key and thus can't be NULL --- openlp/plugins/songs/forms/editsongform.py | 6 ------ openlp/plugins/songs/lib/db.py | 4 ++-- openlp/plugins/songs/lib/mediaitem.py | 4 ++-- openlp/plugins/songs/lib/upgrade.py | 5 +++-- 4 files changed, 7 insertions(+), 12 deletions(-) diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index 2915ea4e8..f3a695f03 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -214,12 +214,6 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): critical_error_message_box( message=translate('SongsPlugin.EditSongForm', 'You need to type in at least one verse.')) return False - if self.authors_list_view.count() == 0: - self.song_tab_widget.setCurrentIndex(1) - self.authors_list_view.setFocus() - critical_error_message_box( - message=translate('SongsPlugin.EditSongForm', 'You need to have an author for this song.')) - return False if self.verse_order_edit.text(): result = self._validate_verse_list(self.verse_order_edit.text(), self.verse_list_widget.rowCount()) if not result: diff --git a/openlp/plugins/songs/lib/db.py b/openlp/plugins/songs/lib/db.py index 0c1b3642c..4e925e4b6 100644 --- a/openlp/plugins/songs/lib/db.py +++ b/openlp/plugins/songs/lib/db.py @@ -35,7 +35,7 @@ import re from sqlalchemy import Column, ForeignKey, Table, types from sqlalchemy.orm import mapper, relation, reconstructor -from sqlalchemy.sql.expression import func +from sqlalchemy.sql.expression import func, text from openlp.core.lib.db import BaseModel, init_db from openlp.core.utils import get_natural_key @@ -259,7 +259,7 @@ def init_schema(url): '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), - Column('author_type', types.String(), primary_key=True) + Column('author_type', types.String(), primary_key=True, nullable=False, server_default=text('""')) ) # Definition of the "songs_topics" table diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index c40162474..ceb7848d6 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -234,8 +234,8 @@ class SongMediaItem(MediaManagerItem): if song.temporary: continue author_list = [author.display_name for author in song.authors] - song_title = str(song.title) - song_detail = '%s (%s)' % (song_title, create_separated_list(author_list)) + song_detail = '%s (%s)' % (song.title, create_separated_list(author_list)) if author_list\ + else '%s'%song.title song_name = QtGui.QListWidgetItem(song_detail) song_name.setData(QtCore.Qt.UserRole, song.id) self.list_view.addItem(song_name) diff --git a/openlp/plugins/songs/lib/upgrade.py b/openlp/plugins/songs/lib/upgrade.py index 87f960f3c..4ec4ca1c4 100644 --- a/openlp/plugins/songs/lib/upgrade.py +++ b/openlp/plugins/songs/lib/upgrade.py @@ -102,7 +102,8 @@ def upgrade_4(session, metadata): """ Version 4 upgrade. - This upgrade adds a column for author type to the authors table + This upgrade adds a column for author type to the authors_songs table """ op = get_upgrade_op(session) - op.add_column('authors_songs', Column('author_type', types.String(), primary_key=True, server_default=null())) + op.add_column('authors_songs', Column('author_type', types.String(), primary_key=True, + nullable=False, server_default=text('""'))) From c98970d0e4d050e53bde9e53b3bf1d358c8750d2 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Sun, 30 Mar 2014 20:01:03 +0200 Subject: [PATCH 007/212] Fix mediaitem tests --- openlp/plugins/songs/lib/db.py | 1 - .../openlp_plugins/songs/test_mediaitem.py | 42 ++++++++++++------- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/openlp/plugins/songs/lib/db.py b/openlp/plugins/songs/lib/db.py index 4e925e4b6..204dac603 100644 --- a/openlp/plugins/songs/lib/db.py +++ b/openlp/plugins/songs/lib/db.py @@ -81,7 +81,6 @@ class Book(BaseModel): def __repr__(self): return '' % (str(self.id), self.name, self.publisher) - class MediaFile(BaseModel): """ MediaFile model diff --git a/tests/functional/openlp_plugins/songs/test_mediaitem.py b/tests/functional/openlp_plugins/songs/test_mediaitem.py index 2b5f02483..627663c62 100644 --- a/tests/functional/openlp_plugins/songs/test_mediaitem.py +++ b/tests/functional/openlp_plugins/songs/test_mediaitem.py @@ -10,6 +10,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.common import Registry, Settings from openlp.core.lib import ServiceItem from openlp.plugins.songs.lib.mediaitem import SongMediaItem +from openlp.plugins.songs.lib.db import AuthorType from tests.functional import patch, MagicMock from tests.helpers.testmixin import TestMixin @@ -45,10 +46,12 @@ class TestMediaItem(TestCase, TestMixin): # GIVEN: A Song and a Service Item mock_song = MagicMock() mock_song.title = 'My Song' + mock_song.authors_songs = [] mock_author = MagicMock() mock_author.display_name = 'my author' - mock_song.authors = [] - mock_song.authors.append(mock_author) + mock_author_song = MagicMock() + mock_author_song.author = mock_author + mock_song.authors_songs.append(mock_author_song) mock_song.copyright = 'My copyright' service_item = ServiceItem(None) @@ -56,7 +59,7 @@ class TestMediaItem(TestCase, TestMixin): author_list = self.media_item.generate_footer(service_item, mock_song) # THEN: I get the following Array returned - self.assertEqual(service_item.raw_footer, ['My Song', 'my author', 'My copyright'], + self.assertEqual(service_item.raw_footer, ['My Song', 'Written by: my author', 'My copyright'], 'The array should be returned correctly with a song, one author and copyright') self.assertEqual(author_list, ['my author'], 'The author list should be returned correctly with one author') @@ -68,13 +71,25 @@ class TestMediaItem(TestCase, TestMixin): # GIVEN: A Song and a Service Item mock_song = MagicMock() mock_song.title = 'My Song' + mock_song.authors_songs = [] mock_author = MagicMock() mock_author.display_name = 'my author' - mock_song.authors = [] - mock_song.authors.append(mock_author) + mock_author_song = MagicMock() + mock_author_song.author = mock_author + mock_author_song.author_type=AuthorType.Music + mock_song.authors_songs.append(mock_author_song) mock_author = MagicMock() mock_author.display_name = 'another author' - mock_song.authors.append(mock_author) + mock_author_song = MagicMock() + mock_author_song.author = mock_author + mock_author_song.author_type=AuthorType.Words + mock_song.authors_songs.append(mock_author_song) + mock_author = MagicMock() + mock_author.display_name = 'translator' + mock_author_song = MagicMock() + mock_author_song.author = mock_author + mock_author_song.author_type=AuthorType.Translation + mock_song.authors_songs.append(mock_author_song) mock_song.copyright = 'My copyright' service_item = ServiceItem(None) @@ -82,22 +97,19 @@ class TestMediaItem(TestCase, TestMixin): author_list = self.media_item.generate_footer(service_item, mock_song) # THEN: I get the following Array returned - self.assertEqual(service_item.raw_footer, ['My Song', 'my author and another author', 'My copyright'], + self.assertEqual(service_item.raw_footer, ['My Song', 'Words: another author', 'Music: my author', + 'Translation: translator', 'My copyright'], 'The array should be returned correctly with a song, two authors and copyright') - self.assertEqual(author_list, ['my author', 'another author'], + self.assertEqual(author_list, ['another author', 'my author', 'translator'], 'The author list should be returned correctly with two authors') def build_song_footer_base_ccli_test(self): """ - Test build songs footer with basic song and two authors + Test build songs footer with basic song and a CCLI number """ # GIVEN: A Song and a Service Item and a configured CCLI license mock_song = MagicMock() mock_song.title = 'My Song' - mock_author = MagicMock() - mock_author.display_name = 'my author' - mock_song.authors = [] - mock_song.authors.append(mock_author) mock_song.copyright = 'My copyright' service_item = ServiceItem(None) Settings().setValue('core/ccli number', '1234') @@ -106,7 +118,7 @@ class TestMediaItem(TestCase, TestMixin): self.media_item.generate_footer(service_item, mock_song) # THEN: I get the following Array returned - self.assertEqual(service_item.raw_footer, ['My Song', 'my author', 'My copyright', 'CCLI License: 1234'], + self.assertEqual(service_item.raw_footer, ['My Song', 'Author Unknown', 'My copyright', 'CCLI License: 1234'], 'The array should be returned correctly with a song, an author, copyright and ccli') # WHEN: I amend the CCLI value @@ -114,5 +126,5 @@ class TestMediaItem(TestCase, TestMixin): self.media_item.generate_footer(service_item, mock_song) # THEN: I would get an amended footer string - self.assertEqual(service_item.raw_footer, ['My Song', 'my author', 'My copyright', 'CCLI License: 4321'], + self.assertEqual(service_item.raw_footer, ['My Song', 'Author Unknown', 'My copyright', 'CCLI License: 4321'], 'The array should be returned correctly with a song, an author, copyright and amended ccli') From ad9717ea42e78bb6f66dc6c3bd9f43fe82f98d91 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Sun, 30 Mar 2014 23:42:46 +0200 Subject: [PATCH 008/212] Add support for author types to OpenLyrics import and export --- openlp/core/ui/wizard.py | 2 +- openlp/plugins/songs/lib/db.py | 3 ++- openlp/plugins/songs/lib/xml.py | 22 ++++++++++++++-------- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/openlp/core/ui/wizard.py b/openlp/core/ui/wizard.py index 05951d14a..5815457b5 100644 --- a/openlp/core/ui/wizard.py +++ b/openlp/core/ui/wizard.py @@ -197,7 +197,7 @@ class OpenLPWizard(QtGui.QWizard, RegistryProperties): """ Run the wizard. """ - self.setDefaults() + self.set_defaults() return QtGui.QWizard.exec_(self) def reject(self): diff --git a/openlp/plugins/songs/lib/db.py b/openlp/plugins/songs/lib/db.py index 204dac603..f597fc2d2 100644 --- a/openlp/plugins/songs/lib/db.py +++ b/openlp/plugins/songs/lib/db.py @@ -275,8 +275,9 @@ def init_schema(url): mapper(Book, song_books_table) mapper(MediaFile, media_files_table) mapper(Song, songs_table, properties={ + # Use the authors_songs relation when you need access to the 'author_type' attribute. 'authors_songs': relation(AuthorSong, cascade="all, delete-orphan"), - 'authors': relation(Author, secondary=authors_songs_table, viewonly=True), + 'authors': relation(Author, secondary=authors_songs_table), '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) diff --git a/openlp/plugins/songs/lib/xml.py b/openlp/plugins/songs/lib/xml.py index 667afebdd..7e41008bd 100644 --- a/openlp/plugins/songs/lib/xml.py +++ b/openlp/plugins/songs/lib/xml.py @@ -71,7 +71,7 @@ from lxml import etree, objectify from openlp.core.common import translate from openlp.core.lib import FormattingTags from openlp.plugins.songs.lib import VerseType, clean_song -from openlp.plugins.songs.lib.db import Author, Book, Song, Topic +from openlp.plugins.songs.lib.db import Author, AuthorSong, Book, Song, Topic from openlp.core.utils import get_application_version log = logging.getLogger(__name__) @@ -166,7 +166,7 @@ class OpenLyrics(object): supported by the :class:`OpenLyrics` class: ```` - OpenLP does not support the attribute *type* and *lang*. + OpenLP does not support the attribute *lang*. ```` This property is not supported. @@ -269,10 +269,12 @@ class OpenLyrics(object): 'verseOrder', properties, song.verse_order.lower()) if song.ccli_number: self._add_text_to_element('ccliNo', properties, song.ccli_number) - if song.authors: + if song.authors_songs: authors = etree.SubElement(properties, 'authors') - for author in song.authors: - self._add_text_to_element('author', authors, author.display_name) + for author_song in song.authors_songs: + element = self._add_text_to_element('author', authors, author_song.author.display_name) + if author_song.author_type: + element.set('type', author_song.author_type) book = self.manager.get_object_filtered(Book, Book.id == song.song_book_id) if book is not None: book = book.name @@ -501,16 +503,20 @@ class OpenLyrics(object): if hasattr(properties, 'authors'): for author in properties.authors.author: display_name = self._text(author) + author_type = author.get('type', '') if display_name: - authors.append(display_name) - for display_name in authors: + authors.append((display_name, author_type)) + for (display_name, author_type) in authors: author = self.manager.get_object_filtered(Author, Author.display_name == display_name) if author is None: # We need to create a new author, as the author does not exist. author = Author.populate(display_name=display_name, last_name=display_name.split(' ')[-1], first_name=' '.join(display_name.split(' ')[:-1])) - song.authors.append(author) + author_song = AuthorSong() + author_song.author = author + author_song.author_type = author_type + song.authors_songs.append(author_song) def _process_cclinumber(self, properties, song): """ From 88c641715caee5b3683ed71c4cf6eeeb4bee40f9 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Sun, 30 Mar 2014 23:45:52 +0200 Subject: [PATCH 009/212] Translate this string --- openlp/plugins/songs/lib/ui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/plugins/songs/lib/ui.py b/openlp/plugins/songs/lib/ui.py index 14f4777c9..4f60bec1f 100644 --- a/openlp/plugins/songs/lib/ui.py +++ b/openlp/plugins/songs/lib/ui.py @@ -40,7 +40,7 @@ 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 = translate('OpenLP.Ui', 'Author Unknown') #Shown when a song has no author CopyrightSymbol = translate('OpenLP.Ui', '\xa9', 'Copyright symbol.') SongBook = translate('OpenLP.Ui', 'Song Book', 'Singular') SongBooks = translate('OpenLP.Ui', 'Song Books', 'Plural') From ad982e9fa31b0da6f2f54b4b01c3c332bb463c1f Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Sun, 30 Mar 2014 23:49:55 +0200 Subject: [PATCH 010/212] Add try/catch block to upgrade function until better solution is found --- openlp/plugins/songs/lib/upgrade.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/openlp/plugins/songs/lib/upgrade.py b/openlp/plugins/songs/lib/upgrade.py index 4ec4ca1c4..d0123feaa 100644 --- a/openlp/plugins/songs/lib/upgrade.py +++ b/openlp/plugins/songs/lib/upgrade.py @@ -104,6 +104,9 @@ def upgrade_4(session, metadata): This upgrade adds a column for author type to the authors_songs table """ - op = get_upgrade_op(session) - op.add_column('authors_songs', Column('author_type', types.String(), primary_key=True, - nullable=False, server_default=text('""'))) + try: + op = get_upgrade_op(session) + op.add_column('authors_songs', Column('author_type', types.String(), primary_key=True, + nullable=False, server_default=text('""'))) + except OperationalError: + log.info('Upgrade 4 has already been run') From 830e3d6624c717cf140e013206c65c096c874ccf Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Tue, 1 Apr 2014 23:07:49 +0200 Subject: [PATCH 011/212] PEP8 --- openlp/plugins/songs/lib/db.py | 6 +++++- openlp/plugins/songs/lib/mediaitem.py | 17 ++++++++++------- openlp/plugins/songs/lib/ui.py | 2 +- openlp/plugins/songs/lib/upgrade.py | 1 + 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/openlp/plugins/songs/lib/db.py b/openlp/plugins/songs/lib/db.py index f597fc2d2..59592a7e0 100644 --- a/openlp/plugins/songs/lib/db.py +++ b/openlp/plugins/songs/lib/db.py @@ -48,9 +48,10 @@ class Author(BaseModel): """ def get_display_name(self, author_type=None): if author_type: - return "%s: %s"%(AuthorType.Types[author_type], self.display_name) + return "%s: %s" % (AuthorType.Types[author_type], self.display_name) return self.display_name + class AuthorSong(BaseModel): """ Relationship between Authors and Songs (many to many). @@ -60,6 +61,7 @@ class AuthorSong(BaseModel): """ pass + class AuthorType(object): """ Enumeration for Author types. @@ -74,6 +76,7 @@ class AuthorType(object): Translation: translate('OpenLP.Ui', 'Translation') } + class Book(BaseModel): """ Book model @@ -81,6 +84,7 @@ class Book(BaseModel): def __repr__(self): return '' % (str(self.id), self.name, self.publisher) + class MediaFile(BaseModel): """ MediaFile model diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index ceb7848d6..71c5fea9c 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -234,8 +234,7 @@ class SongMediaItem(MediaManagerItem): if song.temporary: continue author_list = [author.display_name for author in song.authors] - song_detail = '%s (%s)' % (song.title, create_separated_list(author_list)) if author_list\ - else '%s'%song.title + song_detail = '%s (%s)' % (song.title, create_separated_list(author_list)) if author_list else song.title song_name = QtGui.QListWidgetItem(song_detail) song_name.setData(QtCore.Qt.UserRole, song.id) self.list_view.addItem(song_name) @@ -489,14 +488,18 @@ class SongMediaItem(MediaManagerItem): item.raw_footer = [] item.raw_footer.append(song.title) if authors_none: - item.raw_footer.append("%s: %s"%(translate('OpenLP.Ui', 'Written by'), create_separated_list(authors_none))) + item.raw_footer.append("%s: %s" % (translate('OpenLP.Ui', 'Written by'), + create_separated_list(authors_none))) if authors_words: - item.raw_footer.append("%s: %s"%(AuthorType.Types[AuthorType.Words], create_separated_list(authors_words))) + item.raw_footer.append("%s: %s" % (AuthorType.Types[AuthorType.Words], + create_separated_list(authors_words))) if authors_music: - item.raw_footer.append("%s: %s"%(AuthorType.Types[AuthorType.Music], create_separated_list(authors_music))) + item.raw_footer.append("%s: %s" % (AuthorType.Types[AuthorType.Music], + create_separated_list(authors_music))) if authors_translation: - item.raw_footer.append("%s: %s"%(AuthorType.Types[AuthorType.Translation], create_separated_list(authors_translation))) - if not authors_all: #No authors defined + item.raw_footer.append("%s: %s" % (AuthorType.Types[AuthorType.Translation], + create_separated_list(authors_translation))) + if not authors_all: # No authors defined item.raw_footer.append(SongStrings.AuthorUnknown) item.raw_footer.append(song.copyright) if Settings().value('core/ccli number'): diff --git a/openlp/plugins/songs/lib/ui.py b/openlp/plugins/songs/lib/ui.py index 4f60bec1f..193b70608 100644 --- a/openlp/plugins/songs/lib/ui.py +++ b/openlp/plugins/songs/lib/ui.py @@ -40,7 +40,7 @@ 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 = translate('OpenLP.Ui', 'Author Unknown') #Shown when a song has no author + AuthorUnknown = translate('OpenLP.Ui', 'Author Unknown') # Shown when a song has no author CopyrightSymbol = translate('OpenLP.Ui', '\xa9', 'Copyright symbol.') SongBook = translate('OpenLP.Ui', 'Song Book', 'Singular') SongBooks = translate('OpenLP.Ui', 'Song Books', 'Plural') diff --git a/openlp/plugins/songs/lib/upgrade.py b/openlp/plugins/songs/lib/upgrade.py index d0123feaa..96cd65051 100644 --- a/openlp/plugins/songs/lib/upgrade.py +++ b/openlp/plugins/songs/lib/upgrade.py @@ -98,6 +98,7 @@ def upgrade_3(session, metadata): except OperationalError: log.info('Upgrade 3 has already been run') + def upgrade_4(session, metadata): """ Version 4 upgrade. From 691247ffda9ac8a91ad62af6742d275087d39c33 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 2 Apr 2014 08:47:27 +0200 Subject: [PATCH 012/212] Don't allow a song to be saved without an author --- openlp/plugins/songs/forms/editsongform.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index f3a695f03..ebf967755 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -214,6 +214,12 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): critical_error_message_box( message=translate('SongsPlugin.EditSongForm', 'You need to type in at least one verse.')) return False + if self.authors_list_view.count() == 0: + self.song_tab_widget.setCurrentIndex(1) + self.authors_list_view.setFocus() + critical_error_message_box( + message=translate('SongsPlugin.EditSongForm', 'You need to have an author for this song.')) + return False if self.verse_order_edit.text(): result = self._validate_verse_list(self.verse_order_edit.text(), self.verse_list_widget.rowCount()) if not result: From 31b220c9726da648ecb3202b8d0c16c25fc9de6e Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 2 Apr 2014 08:49:03 +0200 Subject: [PATCH 013/212] Indentation --- openlp/plugins/songs/forms/editsongform.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index ebf967755..6417b4a73 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -217,8 +217,8 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): if self.authors_list_view.count() == 0: self.song_tab_widget.setCurrentIndex(1) self.authors_list_view.setFocus() - critical_error_message_box( - message=translate('SongsPlugin.EditSongForm', 'You need to have an author for this song.')) + critical_error_message_box(message=translate('SongsPlugin.EditSongForm', + 'You need to have an author for this song.')) return False if self.verse_order_edit.text(): result = self._validate_verse_list(self.verse_order_edit.text(), self.verse_list_widget.rowCount()) From dffee7fbc159e8ce776aec02ec1370af8f9bb45a Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 2 Apr 2014 09:03:53 +0200 Subject: [PATCH 014/212] Restore setting a default author --- openlp/plugins/songs/lib/__init__.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/openlp/plugins/songs/lib/__init__.py b/openlp/plugins/songs/lib/__init__.py index 60e8788c8..aa9fbc4c9 100644 --- a/openlp/plugins/songs/lib/__init__.py +++ b/openlp/plugins/songs/lib/__init__.py @@ -389,6 +389,13 @@ def clean_song(manager, song): song.lyrics = str(song.lyrics, encoding='utf8') verses = SongXML().get_verses(song.lyrics) song.search_lyrics = ' '.join([clean_string(verse[1]) for verse in verses]) + # The song does not have any author, add one. + if not song.authors and not song.authors_songs: # Need to check both relations + name = SongStrings.AuthorUnknown + author = manager.get_object_filtered(Author, Author.display_name == name) + if author is None: + author = Author.populate(display_name=name, last_name='', first_name='') + song.authors.append(author) if song.copyright: song.copyright = CONTROL_CHARS.sub('', song.copyright).strip() From adedb6eaebb1b20f3365ff3628818151fd037557 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 2 Apr 2014 09:04:12 +0200 Subject: [PATCH 015/212] Fix setDefaults -> set_defaults --- openlp/core/ui/firsttimeform.py | 4 ++-- openlp/core/ui/themeform.py | 4 ++-- openlp/plugins/bibles/forms/bibleimportform.py | 2 +- openlp/plugins/bibles/forms/bibleupgradeform.py | 2 +- openlp/plugins/songs/forms/duplicatesongremovalform.py | 2 +- openlp/plugins/songs/forms/songimportform.py | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/openlp/core/ui/firsttimeform.py b/openlp/core/ui/firsttimeform.py index aa89da6c0..fc6a61db6 100644 --- a/openlp/core/ui/firsttimeform.py +++ b/openlp/core/ui/firsttimeform.py @@ -110,10 +110,10 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard, RegistryProperties): """ Run the wizard. """ - self.setDefaults() + self.set_defaults() return QtGui.QWizard.exec_(self) - def setDefaults(self): + def set_defaults(self): """ Set up display at start of theme edit. """ diff --git a/openlp/core/ui/themeform.py b/openlp/core/ui/themeform.py index fbfc1035c..c8182232b 100644 --- a/openlp/core/ui/themeform.py +++ b/openlp/core/ui/themeform.py @@ -90,7 +90,7 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard, RegistryProperties): self.footer_font_combo_box.activated.connect(self.update_theme) self.footer_size_spin_box.valueChanged.connect(self.update_theme) - def setDefaults(self): + def set_defaults(self): """ Set up display at start of theme edit. """ @@ -261,7 +261,7 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard, RegistryProperties): log.debug('Editing theme %s' % self.theme.theme_name) self.temp_background_filename = '' self.update_theme_allowed = False - self.setDefaults() + self.set_defaults() self.update_theme_allowed = True self.theme_name_label.setVisible(not edit) self.theme_name_edit.setVisible(not edit) diff --git a/openlp/plugins/bibles/forms/bibleimportform.py b/openlp/plugins/bibles/forms/bibleimportform.py index ee5bee2d0..79b0bc699 100644 --- a/openlp/plugins/bibles/forms/bibleimportform.py +++ b/openlp/plugins/bibles/forms/bibleimportform.py @@ -465,7 +465,7 @@ class BibleImportForm(OpenLPWizard): self.license_details_page.registerField('license_copyright', self.copyright_edit) self.license_details_page.registerField('license_permissions', self.permissions_edit) - def setDefaults(self): + def set_defaults(self): """ Set default values for the wizard pages. """ diff --git a/openlp/plugins/bibles/forms/bibleupgradeform.py b/openlp/plugins/bibles/forms/bibleupgradeform.py index 9925b1ebc..0c004da8a 100644 --- a/openlp/plugins/bibles/forms/bibleupgradeform.py +++ b/openlp/plugins/bibles/forms/bibleupgradeform.py @@ -307,7 +307,7 @@ class BibleUpgradeForm(OpenLPWizard): if self.currentPage() == self.progress_page: return True - def setDefaults(self): + def set_defaults(self): """ Set default values for the wizard pages. """ diff --git a/openlp/plugins/songs/forms/duplicatesongremovalform.py b/openlp/plugins/songs/forms/duplicatesongremovalform.py index 22299cde5..8ca673448 100644 --- a/openlp/plugins/songs/forms/duplicatesongremovalform.py +++ b/openlp/plugins/songs/forms/duplicatesongremovalform.py @@ -244,7 +244,7 @@ class DuplicateSongRemovalForm(OpenLPWizard, RegistryProperties): self.break_search = True self.plugin.media_item.on_search_text_button_clicked() - def setDefaults(self): + def set_defaults(self): """ Set default form values for the song import wizard. """ diff --git a/openlp/plugins/songs/forms/songimportform.py b/openlp/plugins/songs/forms/songimportform.py index 27f0d9343..21569a034 100644 --- a/openlp/plugins/songs/forms/songimportform.py +++ b/openlp/plugins/songs/forms/songimportform.py @@ -304,7 +304,7 @@ class SongImportForm(OpenLPWizard, RegistryProperties): """ self.source_page.emit(QtCore.SIGNAL('completeChanged()')) - def setDefaults(self): + def set_defaults(self): """ Set default form values for the song import wizard. """ From d62cd37db4fe7347516967e59b328ff3cb289f55 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 2 Apr 2014 09:07:46 +0200 Subject: [PATCH 016/212] Restore comment --- openlp/plugins/songs/lib/ui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/plugins/songs/lib/ui.py b/openlp/plugins/songs/lib/ui.py index 193b70608..151b11b4b 100644 --- a/openlp/plugins/songs/lib/ui.py +++ b/openlp/plugins/songs/lib/ui.py @@ -40,7 +40,7 @@ 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 = translate('OpenLP.Ui', 'Author Unknown') # Shown when a song has no author + AuthorUnknown = translate('OpenLP.Ui', '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') From 5c405050f1c9a0af8dd427862a8efabff66a243e Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Fri, 4 Apr 2014 19:57:03 +0200 Subject: [PATCH 017/212] Added test for ews import. --- openlp/plugins/songs/lib/ewimport.py | 10 ++- .../openlp_plugins/songs/test_ewimport.py | 59 +++++++++++++++++- tests/resources/easyworshipsongs/test1.ews | Bin 0 -> 2722 bytes 3 files changed, 66 insertions(+), 3 deletions(-) create mode 100644 tests/resources/easyworshipsongs/test1.ews diff --git a/openlp/plugins/songs/lib/ewimport.py b/openlp/plugins/songs/lib/ewimport.py index 9117aaadc..32f3a3b7e 100644 --- a/openlp/plugins/songs/lib/ewimport.py +++ b/openlp/plugins/songs/lib/ewimport.py @@ -91,17 +91,24 @@ class EasyWorshipSongImport(SongImport): Import the songs from service file The full spec of the ews files can be found here: https://github.com/meinders/lithium-ews/blob/master/docs/ews%20file%20format.md + or here: http://wiki.openlp.org/Development:EasyWorship_EWS_Format :return: """ # Open ews file if it exists if not os.path.isfile(self.import_source): + log.debug('Given ews file does not exists.') return # Make sure there is room for at least a header and one entry if os.path.getsize(self.import_source) < 892: + log.debug('Given ews file is to small to contain valid data.') return # Take a stab at how text is encoded self.encoding = 'cp1252' + self.encoding = retrieve_windows_encoding(self.encoding) + if not self.encoding: + log.debug('No encoding set.') + return self.ews_file = open(self.import_source, 'rb') # Get file version type, = struct.unpack('<38s', self.ews_file.read(38)) @@ -115,13 +122,14 @@ class EasyWorshipSongImport(SongImport): elif version == '1.6': file_pos = 40 else: + log.debug('Given ews file is of unknown version.') return entry_count = self.get_i32(file_pos) entry_length = self.get_i16(file_pos+4) file_pos += 6 self.import_wizard.progress_bar.setMaximum(entry_count) # Loop over songs - for x in range(1, entry_count): + for i in range(entry_count): # Load entry metadata self.set_defaults() self.title = self.get_string(file_pos, 50) diff --git a/tests/functional/openlp_plugins/songs/test_ewimport.py b/tests/functional/openlp_plugins/songs/test_ewimport.py index a22519bec..9a09e0b34 100644 --- a/tests/functional/openlp_plugins/songs/test_ewimport.py +++ b/tests/functional/openlp_plugins/songs/test_ewimport.py @@ -69,6 +69,21 @@ SONG_TEST_DATA = [ 'Just to bow and receive a new blessing,\nIn the beautiful garden of prayer.', 'v3')], 'verse_order_list': []}] +EWS_SONG_TEST_DATA =\ + { 'title' : 'Vi pløjed og vi så\'de', + 'authors' : ['Matthias Claudius'], + 'verses' : + [('Vi pløjed og vi så\'de\nvor sæd i sorten jord,\nså bad vi ham os hjælpe,\nsom højt i Himlen bor,\n' + 'og han lod snefald hegne\nmod frosten barsk og hård,\nhan lod det tø og regne\nog varme mildt i vår.', + 'v1'), + ('Alle gode gaver\nde kommer ovenned,\nså tak da Gud, ja, pris dog Gud\nfor al hans kærlighed!', 'c1'), + ('Han er jo den, hvis vilje\nopholder alle ting,\nhan klæder markens lilje\nog runder himlens ring,\n' + 'ham lyder vind og vove,\nham rører ravnes nød,\nhvi skulle ej hans småbørn\nda og få dagligt brød?', 'v2'), + ('Ja, tak, du kære Fader,\nså mild, så rig, så rund,\nfor korn i hæs og lader,\nfor godt i allen stund!\n' + 'Vi kan jo intet give,\nsom nogen ting er værd,\nmen tag vort stakkels hjerte,\nså ringe som det er!', + 'v3')], + } + class EasyWorshipSongImportLogger(EasyWorshipSongImport): """ @@ -349,9 +364,9 @@ class TestEasyWorshipSongImport(TestCase): self.assertIsNone(importer.do_import(), 'do_import should return None when db_size is less than 0x800') mocked_retrieve_windows_encoding.assert_call(encoding) - def file_import_test(self): + def db_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 database files and check that the imported data is correct. """ # GIVEN: Test files with a mocked out SongImport class, a mocked out "manager", a mocked out "import_wizard", @@ -402,3 +417,43 @@ class TestEasyWorshipSongImport(TestCase): 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() + + def ews_file_import_test(self): + """ + Test the actual import of song from ews file and check that the imported data is correct. + """ + + # 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: + mocked_retrieve_windows_encoding.return_value = 'cp1252' + mocked_manager = MagicMock() + mocked_import_wizard = MagicMock() + mocked_add_author = MagicMock() + mocked_add_verse = MagicMock() + mocked_finish = MagicMock() + mocked_title = MagicMock() + mocked_finish.return_value = True + importer = EasyWorshipSongImportLogger(mocked_manager) + importer.import_wizard = mocked_import_wizard + importer.stop_import_flag = False + importer.add_author = mocked_add_author + importer.add_verse = mocked_add_verse + importer.title = mocked_title + importer.finish = mocked_finish + importer.topics = [] + + # WHEN: Importing ews file + importer.import_source = os.path.join(TEST_PATH, 'test1.ews') + + # THEN: do_import should return none, the song data should be as expected, and finish should have been + # called. + title = EWS_SONG_TEST_DATA['title'] + self.assertIsNone(importer.do_import(), 'do_import should return None when it has completed') + self.assertIn(title, importer._title_assignment_list, 'title for should be "%s"' % title) + mocked_add_author.assert_any_call(EWS_SONG_TEST_DATA['authors'][0]) + for verse_text, verse_tag in EWS_SONG_TEST_DATA['verses']: + mocked_add_verse.assert_any_call(verse_text, verse_tag) + mocked_finish.assert_called_with() + diff --git a/tests/resources/easyworshipsongs/test1.ews b/tests/resources/easyworshipsongs/test1.ews new file mode 100644 index 0000000000000000000000000000000000000000..2cb9676f140623ced292334ae7d9bb050e7f23e3 GIT binary patch literal 2722 zcmZ=wEUpaCFDlN+EKmqe&PYuu%}G^o%LEc(sYS(^`FRRJV9FrH00trsFq#p_kYEqX zR4B;#k(HXFke{wlmZ?zuR6QjXqGXiB9s<6JB_$b|iNy-eIfO%$DnG6P`GPv10}XG|;e0qyR;6%46ynmLeek|Ns9V9uBbZ zU;+YWW*Bc+dYueUxuoVPxFqHkXQwKVp_^1KqxqY-5c6PWV5qqDcKX@A%LW3+-+Syi z{3eqzbOq<_rP_QPij0pKEq*?_GWkevGW%`b1zPXV_1unqD;{qs#QtqU*&{3a|Hktq z{l6bNzDwW|o36;UuaoQ-uQ_$~X55u?$08(+rY+ibtE1|&m%M@4hhy29alV)1a^@7y zOXKX%(v<4Yn7+u!I%eOag+*I;Sy#*npYnQlb9?2N8)uK-{Gs@@$c%YC@8!3(hs~$$ zKDpPl;^f|?-zV+flfTirYyFYfDegRyFFU(GE!%VZPj%pAi6;&JyaT7DpKiV@w%YXO z-Y>nH*IqY2UB4z`j%@wf8@IaClQuA#rf- z6b=e(YMNjFCqvBPaKe$yz4sO|T|clwu_HKUuGSoW*}48xidkD0OLdBEJGc61O^Oif z)Q~kBrrPdccsuR70rRthSpM9q{~l5HFYZmacIqdOO6A0Fay`KUI@c~~WNh3h`R@&9 zXtnr_eoM!@nv;Oob*vW*~w>~fA0J|zvi~pes0MI z^J5Aoeiz$iE}mx>Tv%M&FL$ToT2h^w%gx3~AL^EiZJ5d}v4?NLiQgth^J9Y_c^z&P zT=4kGCpUB5Rg>#C*0$dYJI7wL;D_4ueA%D(^%lsUbX{zAd(H>BsC1D{(=|*MZSzh0 zSE_c^Qz>ZrCB>h2Q?!2z}wsHpRhn-s02KOZ_E=5ign$u(<{9@;wili#bD4v9$ zcB`j-Jb3kOMR-9q$Ab&S%R(|$XHVQc>Hbc?$mJ={oPrCzeqDXtzUo`R)wIy9;-4Ai zPwGEe&bs36$6SGXo~O>|y#G9Luf?r6^KiAPlU?V1w|=hsmFdo=IqZJwlQm~76ghM! zp+I$;fYM5vldqbsPsVCxNUxO7`ZD8oSk(0i26oCnM5Hc Date: Fri, 4 Apr 2014 22:28:55 +0200 Subject: [PATCH 018/212] Some cleanup and comments --- openlp/plugins/songs/lib/ewimport.py | 52 +++++++++++++++++-- .../openlp_plugins/songs/test_ewimport.py | 1 - 2 files changed, 48 insertions(+), 5 deletions(-) diff --git a/openlp/plugins/songs/lib/ewimport.py b/openlp/plugins/songs/lib/ewimport.py index e273223e7..a4a8b6944 100644 --- a/openlp/plugins/songs/lib/ewimport.py +++ b/openlp/plugins/songs/lib/ewimport.py @@ -110,6 +110,15 @@ class EasyWorshipSongImport(SongImport): log.debug('No encoding set.') return self.ews_file = open(self.import_source, 'rb') + # EWS header, version '1.6'/' 3'/' 5': + # Offset Field Data type Length Details + # -------------------------------------------------------------------------------------------------- + # 0 Filetype string 38 Specifies the file type and version. + # "EasyWorship Schedule File Version 1.6" or + # "EasyWorship Schedule File Version 3" or + # "EasyWorship Schedule File Version 5" + # 40/48/56 Entry count int32le 4 Number of items in the schedule + # 44/52/60 Entry length int16le 2 Length of schedule entries: 0x0718 = 1816 # Get file version type, = struct.unpack('<38s', self.ews_file.read(38)) version = type.decode()[-3:] @@ -130,22 +139,39 @@ class EasyWorshipSongImport(SongImport): self.import_wizard.progress_bar.setMaximum(entry_count) # Loop over songs for i in range(entry_count): - # Load entry metadata + # Load EWS entry metadata: + # Offset Field Data type Length Details + # ------------------------------------------------------------------------------------------------ + # 0 Title cstring 50 + # 307 Author cstring 50 + # 358 Copyright cstring 100 + # 459 Administrator cstring 50 + # 800 Content pointer int32le 4 Position of the content for this entry. + # 820 Content type int32le 4 0x01 = Song, 0x02 = Scripture, 0x03 = Presentation, + # 0x04 = Video, 0x05 = Live video, 0x07 = Image, + # 0x08 = Audio, 0x09 = Web + # 1410 Song number cstring 10 self.set_defaults() - self.title = self.get_string(file_pos, 50) - resource = self.get_string(file_pos + 51, 255) + self.title = self.get_string(file_pos + 0, 50) authors = self.get_string(file_pos + 307, 50) copyright = self.get_string(file_pos + 358, 100) admin = self.get_string(file_pos + 459, 50) cont_ptr = self.get_i32(file_pos + 800) cont_type = self.get_i32(file_pos + 820) - notes = self.get_string(file_pos + 1155, 160) self.ccli_number = self.get_string(file_pos + 1410, 10) # Only handle content type 1 (songs) if cont_type != 1: file_pos += entry_length continue # Load song content + # Offset Field Data type Length Details + # ------------------------------------------------------------------------------------------------ + # 0 Length int32le 4 Length (L) of the content, including the compressed content + # and the following fields (14 bytes total). + # 4 Content string L-14 Content compressed with deflate. + # Checksum int32be 4 Alder-32 checksum. + # (unknown) 4 0x51 0x4b 0x03 0x04 + # Content length int32le 4 Length of content after decompression content_length = self.get_i32(cont_ptr) deflated_content = self.get_bytes(cont_ptr + 4, content_length - 10) deflated_length = self.get_i32(cont_ptr + 4 + content_length - 6) @@ -293,6 +319,10 @@ class EasyWorshipSongImport(SongImport): def set_song_import_object(self, authors, words): """ Set the SongImport object members. + + :param authors: String with authons + :param words: Bytes with rtf-encoding + :return: """ if authors: # Split up the authors @@ -429,6 +459,10 @@ class EasyWorshipSongImport(SongImport): def get_bytes(self, pos, length): """ Get bytes from ews_file + + :param pos: Position to read from + :param length: Bytes to read + :return: Bytes read """ self.ews_file.seek(pos) return self.ews_file.read(length) @@ -436,6 +470,10 @@ class EasyWorshipSongImport(SongImport): def get_string(self, pos, length): """ Get string from ews_file + + :param pos: Position to read from + :param length: Characters to read + :return: String read """ bytes = self.get_bytes(pos, length) mask = '<' + str(length) + 's' @@ -445,6 +483,9 @@ class EasyWorshipSongImport(SongImport): def get_i16(self, pos): """ Get short int from ews_file + + :param pos: Position to read from + :return: Short integer read """ bytes = self.get_bytes(pos, 2) @@ -455,6 +496,9 @@ class EasyWorshipSongImport(SongImport): def get_i32(self, pos): """ Get long int from ews_file + + :param pos: Position to read from + :return: Long integer read """ bytes = self.get_bytes(pos, 4) mask = ' Date: Sat, 5 Apr 2014 09:22:24 +0200 Subject: [PATCH 019/212] PEP8 fixes --- openlp/plugins/songs/lib/ewimport.py | 14 +++++++------- .../openlp_plugins/songs/test_ewimport.py | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/openlp/plugins/songs/lib/ewimport.py b/openlp/plugins/songs/lib/ewimport.py index a4a8b6944..e36021d04 100644 --- a/openlp/plugins/songs/lib/ewimport.py +++ b/openlp/plugins/songs/lib/ewimport.py @@ -143,7 +143,7 @@ class EasyWorshipSongImport(SongImport): # Offset Field Data type Length Details # ------------------------------------------------------------------------------------------------ # 0 Title cstring 50 - # 307 Author cstring 50 + # 307 Author cstring 50 # 358 Copyright cstring 100 # 459 Administrator cstring 50 # 800 Content pointer int32le 4 Position of the content for this entry. @@ -166,7 +166,7 @@ class EasyWorshipSongImport(SongImport): # Load song content # Offset Field Data type Length Details # ------------------------------------------------------------------------------------------------ - # 0 Length int32le 4 Length (L) of the content, including the compressed content + # 0 Length int32le 4 Length (L) of content, including the compressed content # and the following fields (14 bytes total). # 4 Content string L-14 Content compressed with deflate. # Checksum int32be 4 Alder-32 checksum. @@ -319,7 +319,7 @@ class EasyWorshipSongImport(SongImport): def set_song_import_object(self, authors, words): """ Set the SongImport object members. - + :param authors: String with authons :param words: Bytes with rtf-encoding :return: @@ -459,7 +459,7 @@ class EasyWorshipSongImport(SongImport): def get_bytes(self, pos, length): """ Get bytes from ews_file - + :param pos: Position to read from :param length: Bytes to read :return: Bytes read @@ -470,7 +470,7 @@ class EasyWorshipSongImport(SongImport): def get_string(self, pos, length): """ Get string from ews_file - + :param pos: Position to read from :param length: Characters to read :return: String read @@ -483,7 +483,7 @@ class EasyWorshipSongImport(SongImport): def get_i16(self, pos): """ Get short int from ews_file - + :param pos: Position to read from :return: Short integer read """ @@ -496,7 +496,7 @@ class EasyWorshipSongImport(SongImport): def get_i32(self, pos): """ Get long int from ews_file - + :param pos: Position to read from :return: Long integer read """ diff --git a/tests/functional/openlp_plugins/songs/test_ewimport.py b/tests/functional/openlp_plugins/songs/test_ewimport.py index 11e5d4f95..1e84134bd 100644 --- a/tests/functional/openlp_plugins/songs/test_ewimport.py +++ b/tests/functional/openlp_plugins/songs/test_ewimport.py @@ -70,19 +70,18 @@ SONG_TEST_DATA = [ 'verse_order_list': []}] EWS_SONG_TEST_DATA =\ - { 'title' : 'Vi pløjed og vi så\'de', - 'authors' : ['Matthias Claudius'], - 'verses' : + {'title': 'Vi pløjed og vi så\'de', + 'authors': ['Matthias Claudius'], + 'verses': [('Vi pløjed og vi så\'de\nvor sæd i sorten jord,\nså bad vi ham os hjælpe,\nsom højt i Himlen bor,\n' - 'og han lod snefald hegne\nmod frosten barsk og hård,\nhan lod det tø og regne\nog varme mildt i vår.', + 'og han lod snefald hegne\nmod frosten barsk og hård,\nhan lod det tø og regne\nog varme mildt i vår.', 'v1'), ('Alle gode gaver\nde kommer ovenned,\nså tak da Gud, ja, pris dog Gud\nfor al hans kærlighed!', 'c1'), ('Han er jo den, hvis vilje\nopholder alle ting,\nhan klæder markens lilje\nog runder himlens ring,\n' 'ham lyder vind og vove,\nham rører ravnes nød,\nhvi skulle ej hans småbørn\nda og få dagligt brød?', 'v2'), ('Ja, tak, du kære Fader,\nså mild, så rig, så rund,\nfor korn i hæs og lader,\nfor godt i allen stund!\n' 'Vi kan jo intet give,\nsom nogen ting er værd,\nmen tag vort stakkels hjerte,\nså ringe som det er!', - 'v3')], - } + 'v3')]} class EasyWorshipSongImportLogger(EasyWorshipSongImport): @@ -435,7 +434,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() From cc635e9b9678b95ecfc3c3cdf42dd9e0150da2d5 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Tue, 8 Apr 2014 11:16:14 +0200 Subject: [PATCH 020/212] SQLite doesn't support changing a primary key --- openlp/plugins/songs/forms/editsongform.py | 2 +- openlp/plugins/songs/lib/upgrade.py | 13 ++++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index 6417b4a73..ba4298922 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -528,7 +528,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): elif item > 0: item_id = (self.authors_combo_box.itemData(item)) author = self.manager.get_object(Author, item_id) - if self.authors_list_view.findItems(str(author.display_name), QtCore.Qt.MatchExactly): + if self.authors_list_view.findItems(author.get_display_name(author_type), QtCore.Qt.MatchExactly): critical_error_message_box( message=translate('SongsPlugin.EditSongForm', 'This author is already in the list.')) else: diff --git a/openlp/plugins/songs/lib/upgrade.py b/openlp/plugins/songs/lib/upgrade.py index 96cd65051..59355bbec 100644 --- a/openlp/plugins/songs/lib/upgrade.py +++ b/openlp/plugins/songs/lib/upgrade.py @@ -32,7 +32,7 @@ backend for the Songs plugin """ import logging -from sqlalchemy import Column, types +from sqlalchemy import Column, ForeignKey, types from sqlalchemy.exc import OperationalError from sqlalchemy.sql.expression import func, false, null, text @@ -106,8 +106,15 @@ def upgrade_4(session, metadata): This upgrade adds a column for author type to the authors_songs table """ try: + # Since SQLite doesn't support changing the primary key of a table, we need to recreate the table + # and copy the old values op = get_upgrade_op(session) - op.add_column('authors_songs', Column('author_type', types.String(), primary_key=True, - nullable=False, server_default=text('""'))) + op.create_table('authors_songs_tmp', + Column('author_id', types.Integer(), ForeignKey('authors.id'), primary_key=True), + Column('song_id', types.Integer(), ForeignKey('songs.id'), primary_key=True), + Column('author_type', types.String(), primary_key=True, nullable=False, server_default=text('""'))) + op.execute('INSERT INTO authors_songs_tmp SELECT author_id, song_id, "" FROM authors_songs') + op.drop_table('authors_songs') + op.rename_table('authors_songs_tmp', 'authors_songs') except OperationalError: log.info('Upgrade 4 has already been run') From 7f8e76b8ddcb0d814698e2119f39845d9fd9e250 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Tue, 8 Apr 2014 11:21:56 +0200 Subject: [PATCH 021/212] Restore previous delete button behavior --- openlp/plugins/songs/forms/editsongform.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index ba4298922..c5ddb2a62 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -545,7 +545,8 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): """ Run a set of actions when an author in the list is selected (mainly enable the delete button). """ - self.author_remove_button.setEnabled(True) + if self.authors_list_view.count() > 1: + self.author_remove_button.setEnabled(True) def on_author_remove_button_clicked(self): """ From fb85b9858a2094198dcb31cfb54d9df1c7232059 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Tue, 8 Apr 2014 11:29:47 +0200 Subject: [PATCH 022/212] PEP8 --- openlp/plugins/songs/forms/editsongform.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index c5ddb2a62..d27f43343 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -302,7 +302,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): self.authors.append(author.display_name) set_case_insensitive_completer(self.authors, self.authors_combo_box) - #Types + # Types self.author_types_combo_box.clear() self.author_types_combo_box.addItem('') # Don't iterate over the dictionary to give them this specific order @@ -689,7 +689,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): 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+\w.)') regex = re.compile(r'\D*(\d+)\D*') match = regex.match(verse_num) if match: From 577c7321ffec12c4895d30415bfbcda1f22f8952 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Tue, 8 Apr 2014 20:52:05 +0200 Subject: [PATCH 023/212] Add new type for 'Words and Music' --- openlp/plugins/songs/forms/editsongform.py | 1 + openlp/plugins/songs/lib/db.py | 4 ++++ openlp/plugins/songs/lib/xml.py | 10 ++++++++-- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index d27f43343..4d6e1addb 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -308,6 +308,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): # Don't iterate over the dictionary to give them this specific order self.author_types_combo_box.addItem(AuthorType.Types[AuthorType.Words], AuthorType.Words) self.author_types_combo_box.addItem(AuthorType.Types[AuthorType.Music], AuthorType.Music) + self.author_types_combo_box.addItem(AuthorType.Types[AuthorType.WordsAndMusic], AuthorType.WordsAndMusic) self.author_types_combo_box.addItem(AuthorType.Types[AuthorType.Translation], AuthorType.Translation) def load_topics(self): diff --git a/openlp/plugins/songs/lib/db.py b/openlp/plugins/songs/lib/db.py index 59592a7e0..fa53bccc8 100644 --- a/openlp/plugins/songs/lib/db.py +++ b/openlp/plugins/songs/lib/db.py @@ -66,13 +66,17 @@ class AuthorType(object): """ Enumeration for Author types. They are defined by OpenLyrics: http://openlyrics.info/dataformat.html#authors + + The 'words+music' type is not an official type, but is provided for convenience. """ Words = 'words' Music = 'music' + WordsAndMusic = 'words+music' Translation = 'translation' Types = { Words: translate('OpenLP.Ui', 'Words'), Music: translate('OpenLP.Ui', 'Music'), + WordsAndMusic: translate('OpenLP.Ui', 'Words and Music'), Translation: translate('OpenLP.Ui', 'Translation') } diff --git a/openlp/plugins/songs/lib/xml.py b/openlp/plugins/songs/lib/xml.py index 7e41008bd..64c9aaf05 100644 --- a/openlp/plugins/songs/lib/xml.py +++ b/openlp/plugins/songs/lib/xml.py @@ -71,7 +71,7 @@ from lxml import etree, objectify from openlp.core.common import translate from openlp.core.lib import FormattingTags from openlp.plugins.songs.lib import VerseType, clean_song -from openlp.plugins.songs.lib.db import Author, AuthorSong, Book, Song, Topic +from openlp.plugins.songs.lib.db import Author, AuthorSong, AuthorType, Book, Song, Topic from openlp.core.utils import get_application_version log = logging.getLogger(__name__) @@ -274,7 +274,13 @@ class OpenLyrics(object): for author_song in song.authors_songs: element = self._add_text_to_element('author', authors, author_song.author.display_name) if author_song.author_type: - element.set('type', author_song.author_type) + # Handle the special case 'words+music': Need to create two separate authors for that + if author_song.author_type == AuthorType.WordsAndMusic: + element.set('type', AuthorType.Words) + element = self._add_text_to_element('author', authors, author_song.author.display_name) + element.set('type', AuthorType.Music) + else: + element.set('type', author_song.author_type) book = self.manager.get_object_filtered(Book, Book.id == song.song_book_id) if book is not None: book = book.name From efad4a55fae57ef56c7e8e9ce6b3e624218582ea Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Tue, 8 Apr 2014 20:57:01 +0200 Subject: [PATCH 024/212] Display 'Words and Music' type in the footer --- openlp/plugins/songs/lib/mediaitem.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index 71c5fea9c..af7157a63 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -470,6 +470,7 @@ class SongMediaItem(MediaManagerItem): """ authors_words = [] authors_music = [] + authors_words_music = [] authors_translation = [] authors_none = [] for author_song in song.authors_songs: @@ -477,11 +478,13 @@ class SongMediaItem(MediaManagerItem): authors_words.append(author_song.author.display_name) elif author_song.author_type == AuthorType.Music: authors_music.append(author_song.author.display_name) + elif author_song.author_type == AuthorType.WordsAndMusic: + authors_words_music.append(author_song.author.display_name) elif author_song.author_type == AuthorType.Translation: authors_translation.append(author_song.author.display_name) else: authors_none.append(author_song.author.display_name) - authors_all = authors_words + authors_music + authors_translation + authors_none + authors_all = authors_words_music + authors_words + authors_music + authors_translation + authors_none item.audit = [ song.title, authors_all, song.copyright, str(song.ccli_number) ] @@ -490,6 +493,9 @@ class SongMediaItem(MediaManagerItem): if authors_none: item.raw_footer.append("%s: %s" % (translate('OpenLP.Ui', 'Written by'), create_separated_list(authors_none))) + if authors_words_music: + item.raw_footer.append("%s: %s" % (AuthorType.Types[AuthorType.WordsAndMusic], + create_separated_list(authors_words_music))) if authors_words: item.raw_footer.append("%s: %s" % (AuthorType.Types[AuthorType.Words], create_separated_list(authors_words))) @@ -499,8 +505,6 @@ class SongMediaItem(MediaManagerItem): if authors_translation: item.raw_footer.append("%s: %s" % (AuthorType.Types[AuthorType.Translation], create_separated_list(authors_translation))) - if not authors_all: # No authors defined - item.raw_footer.append(SongStrings.AuthorUnknown) item.raw_footer.append(song.copyright) if Settings().value('core/ccli number'): item.raw_footer.append(translate('SongsPlugin.MediaItem', From abf1671754bf008874bcaea1850a6b17eaf9bcee Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Tue, 8 Apr 2014 20:58:28 +0200 Subject: [PATCH 025/212] Ignore Komodo project and project directory --- .bzrignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.bzrignore b/.bzrignore index f149d97a7..716add8bb 100644 --- a/.bzrignore +++ b/.bzrignore @@ -5,6 +5,8 @@ *.ropeproject *.e4* .eric4project +.komodotools +*.komodoproject list openlp.org 2.0.e4* documentation/build/html From e0c638176278a8c2990325b6ea5eee0f11af4c04 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Tue, 8 Apr 2014 21:02:13 +0200 Subject: [PATCH 026/212] Fix test --- tests/functional/openlp_plugins/songs/test_mediaitem.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/functional/openlp_plugins/songs/test_mediaitem.py b/tests/functional/openlp_plugins/songs/test_mediaitem.py index 627663c62..1b372aa20 100644 --- a/tests/functional/openlp_plugins/songs/test_mediaitem.py +++ b/tests/functional/openlp_plugins/songs/test_mediaitem.py @@ -118,7 +118,7 @@ class TestMediaItem(TestCase, TestMixin): self.media_item.generate_footer(service_item, mock_song) # THEN: I get the following Array returned - self.assertEqual(service_item.raw_footer, ['My Song', 'Author Unknown', 'My copyright', 'CCLI License: 1234'], + self.assertEqual(service_item.raw_footer, ['My Song', 'My copyright', 'CCLI License: 1234'], 'The array should be returned correctly with a song, an author, copyright and ccli') # WHEN: I amend the CCLI value @@ -126,5 +126,5 @@ class TestMediaItem(TestCase, TestMixin): self.media_item.generate_footer(service_item, mock_song) # THEN: I would get an amended footer string - self.assertEqual(service_item.raw_footer, ['My Song', 'Author Unknown', 'My copyright', 'CCLI License: 4321'], + self.assertEqual(service_item.raw_footer, ['My Song', 'My copyright', 'CCLI License: 4321'], 'The array should be returned correctly with a song, an author, copyright and amended ccli') From d681f0a059b3c04423d4e06a919e6ad747c077bc Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Mon, 14 Apr 2014 19:09:47 +0100 Subject: [PATCH 027/212] Start to improve remote --- openlp/plugins/remotes/lib/httpserver.py | 56 ++++++++++++++++------- openlp/plugins/remotes/remoteplugin.py | 11 ++++- resources/images/network_auth.png | Bin 0 -> 608 bytes resources/images/network_server.png | Bin 0 -> 1133 bytes resources/images/network_ssl.png | Bin 0 -> 577 bytes resources/images/openlp-2.qrc | 5 ++ 6 files changed, 55 insertions(+), 17 deletions(-) create mode 100644 resources/images/network_auth.png create mode 100644 resources/images/network_server.png create mode 100644 resources/images/network_ssl.png diff --git a/openlp/plugins/remotes/lib/httpserver.py b/openlp/plugins/remotes/lib/httpserver.py index 22d0349f8..cc2d02ff3 100644 --- a/openlp/plugins/remotes/lib/httpserver.py +++ b/openlp/plugins/remotes/lib/httpserver.py @@ -38,9 +38,10 @@ import os import logging import time -from PyQt4 import QtCore +from PyQt4 import QtCore, QtGui -from openlp.core.common import AppLocation, Settings +from openlp.core.common import AppLocation, Settings, RegistryProperties +from openlp.core.lib import build_icon from openlp.plugins.remotes.lib import HttpRouter @@ -95,12 +96,13 @@ class HttpThread(QtCore.QThread): self.http_server.start_server() -class OpenLPServer(): +class OpenLPServer(RegistryProperties): def __init__(self): """ Initialise the http server, and start the server of the correct type http / https """ - log.debug('Initialise httpserver') + super(OpenLPServer, self).__init__() + log.debug('Initialise OpenLP') self.settings_section = 'remotes' self.http_thread = HttpThread(self) self.http_thread.start() @@ -112,25 +114,47 @@ class OpenLPServer(): address = Settings().value(self.settings_section + '/ip address') if Settings().value(self.settings_section + '/https enabled'): port = Settings().value(self.settings_section + '/https port') - self.httpd = HTTPSServer((address, port), CustomHandler) - log.debug('Started ssl httpd...') + self.start_server_instance(address, port, HTTPSServer) else: port = Settings().value(self.settings_section + '/port') - 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.start_server_instance(address, port, ThreadingHTTPServer) if hasattr(self, 'httpd') and self.httpd: self.httpd.serve_forever() + icon = QtGui.QImage(':/remote/network_server.png') + icon = icon.scaled(80, 80, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation) + overlay = QtGui.QImage(':/remote/network_ssl.png') + overlay = overlay.scaled(40, 40, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation) + painter = QtGui.QPainter(icon) + painter.drawImage(0, 0, overlay) + painter.end() + print("Hi") + self.default_theme_label.setText("hello") + self.default_theme_label.setIcon(build_icon(icon)) + self.default_theme_label.show() else: log.debug('Failed to start server') + def start_server_instance(self, address, port, server_class): + """ + Start the server + + :param address: The server address + :param port: The run port + :param server_class: the class to start + """ + loop = 1 + while loop < 4: + try: + self.httpd = server_class((address, port), CustomHandler) + log.debug("Server started for class %s %s %d" % (server_class, address, port)) + except OSError: + log.debug("failed to start http server thread state %d %s" % + (loop, self.http_thread.isRunning() is True)) + loop += 1 + time.sleep(0.1) + except: + log.error('Failed to start server ') + def stop_server(self): """ Stop the server diff --git a/openlp/plugins/remotes/remoteplugin.py b/openlp/plugins/remotes/remoteplugin.py index 393f08dd9..0e3825bdd 100644 --- a/openlp/plugins/remotes/remoteplugin.py +++ b/openlp/plugins/remotes/remoteplugin.py @@ -30,6 +30,8 @@ import logging import time +from PyQt4 import QtGui + from openlp.core.lib import Plugin, StringContent, translate, build_icon from openlp.plugins.remotes.lib import RemoteTab, OpenLPServer @@ -67,6 +69,13 @@ class RemotesPlugin(Plugin): log.debug('initialise') super(RemotesPlugin, self).initialise() self.server = OpenLPServer() + self.default_theme_label = QtGui.QToolButton(self.main_window.status_bar) + self.default_theme_label.setCheckable(False) + self.default_theme_label.setAutoRaise(True) + self.default_theme_label.setObjectName('default_theme_label') + self.main_window.status_bar.insertPermanentWidget(2, self.default_theme_label) + self.default_theme_label.hide() + self.server.default_theme_label = self.default_theme_label def finalise(self): """ @@ -108,5 +117,5 @@ class RemotesPlugin(Plugin): """ log.debug('remote config changed') self.finalise() - time.sleep(0.5) + time.sleep(1) self.initialise() diff --git a/resources/images/network_auth.png b/resources/images/network_auth.png new file mode 100644 index 0000000000000000000000000000000000000000..45e7a5c1788d214b1e63abdb2746cdfb938598ec GIT binary patch literal 608 zcmV-m0-ybfP)FV7f{fuWJai358$p+0R>_w(?=`@XQp(zNRTr6`JD zTAEkp{ksO;ZujtdCY#H=&xmo#D8?zHq~0av#q+hC!|52_5%i3mA6;Et$!7~`aon{F z*AN~dJVdyPjNBZNXe=V3x%;nOF2`t_Q?-rRa_Z7Xb7RP*xXG&5;V_^F)Ze>u1Ft$d+)n z_Y8$%;W1F%X&EP>0Z#l3bp8^Mc%UqV?hM_A55Oj{ z4t#=({S(CF@j2k-zi!DeJ$=2jlwK6ej77{h%yMrzC}SQ^2{7#TAP0O`e5peIjbzhV u86Urp0tWun*zuQ;F5q~}CxOPEyz4hAGR?5R1SdxT0000swx|LFf3SGE$k@yK*Te>U# z3R9R5P`Z&)XX9cjh!m!1y0BF;OpPYby!Z0nz2|oEUSeWpCMkJ01d{vCyZ3j_|D1C# zq0jN-ix+<3lf1}gGL%k#K*>Y`ni;>ndWFv(KQ_MKqGr=Em$ge%sr<=bKYNzwXI;@R zz*Vg_;xrvut6d4#bt#!lQYx9GqoX5ACK5sjNz7U(rKDy(((k%`17ydu$mMdFDHKqz z*E{o;lmGxC0+9d_VUW=`fYusXD=4iYqD_3QVUMOGfLmJ=2Y@jK0D%Y)B7DvD1Dxk) zBOnUBCj!L>02tE_Hz8?BOjQ4dX-9w%!k8EVfZGTpq(ruU8O>7oA;KUb7#tw-zCC9E zkO;%xmH@Fy5INTkAcVl{*MD^E2Frj!0N5asxu?{a84L6C;kE<_(Y6{kKp5;c!GH3? z05Ht(JP%3*!v_e001dl=R?CO)`=S1w$}mt`!*xA4jss?np%F2o)oLLK0tCUWhqrQs zK+L8mGDFP))(pPmdACI2Gad-Q>euk*#>VyaH7v`5jQW4iHilqK2*_~&q|<3kOiUn~ z%_5V@pwkw+$~2UKVK@c}{2;*f^%Yva53O}i1WW|L+JsUWjwc5o1ekSqcLInY1che+ znXp{rkRbGs!TL7I*ciC3i(2gpj^m)&Y{GF|IIcVV2(aPsP7r0e0bsPohf-pEd>s4x zzvAWoOQcdsj969-0L;*=JEf?Ug6}vOYc!yO00e?{}LeINh4eGAWZQ79DP zdLE|Vyn*Ao`25Ke%oi7MesK{`Cj>&hNf0s*AA+?8h%ol_DQ<3VVB0ntwvA@93D&Uy z071)#){K_#!w&+aMn{ofS%K%e;rXB$TIt?O8OE4<^hicO#{f(NSXf$G`k_=RnR2;o z0DJ+U7(Gh^!0gD|3yZfDHQ$*q;VX4OK5kitbBF%OxIfD@bD1UJGz`P2KjtGx4ypq zO^?n(pjxe-0C+z@tyZJ6v$IaWCn9*B2ivw$sZ`MA*uC9SN(s;NdM+hWO4RFhYmgie z_xx9-Qt1f5r|$wN7K_ifwzf9ztU&1r;P)(<+F=08q_00t&F1I%F#z!2z#$c(OoM@BmdoR*iyz z*z*Dgm?;aVIHxQOoC9YDX3HNE9{l7AL>!(vR2-fx9e4_&D+m7@K~?ZQ@I52tgDr%# zI?^7bJVX2?C;6!YJG+6uH;wH;lOVo2JV9(6~ytwJuv$Vf4-@_QLj?2 P00000NkvXXu0mjf+JX4) literal 0 HcmV?d00001 diff --git a/resources/images/openlp-2.qrc b/resources/images/openlp-2.qrc index 6af0e77a5..79036f08f 100644 --- a/resources/images/openlp-2.qrc +++ b/resources/images/openlp-2.qrc @@ -149,6 +149,11 @@ messagebox_info.png messagebox_warning.png + + network_server.png + network_ssl.png + network_auth.png + song_usage_active.png song_usage_inactive.png From 721bcc20e2ee5dc1dfaab0f1e722cd411c140e35 Mon Sep 17 00:00:00 2001 From: Jonathan Springer Date: Tue, 15 Apr 2014 16:51:55 -0400 Subject: [PATCH 028/212] Reorder imports --- tests/functional/openlp_core_ui/test_firsttimeform.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/functional/openlp_core_ui/test_firsttimeform.py b/tests/functional/openlp_core_ui/test_firsttimeform.py index 9fc6f5137..2e26c286a 100644 --- a/tests/functional/openlp_core_ui/test_firsttimeform.py +++ b/tests/functional/openlp_core_ui/test_firsttimeform.py @@ -31,12 +31,12 @@ 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 +from tests.functional import MagicMock +from tests.helpers.testmixin import TestMixin + class TestFirstTimeForm(TestCase, TestMixin): From aa6b75695610d3b959ef563863c18d8f08343f24 Mon Sep 17 00:00:00 2001 From: Jonathan Springer Date: Wed, 16 Apr 2014 16:26:49 -0400 Subject: [PATCH 029/212] Change how we define which menu items are included in the main menu on Mac OS X --- openlp/core/ui/mainwindow.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index 81e822c16..4e1d28ef8 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -320,14 +320,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()) - # Order things differently in OS X so that Preferences menu item in the - # app menu is correct (this gets picked up automatically by Qt). + # Qt on OS X look for keywords in the menu items title to determine which menu items get added to the main menu. + # If we are running on Mac OS X the menu items whose title contains those keywords but don't belong in the main + # menu need to be marked as such with QAction.NoRole. 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)) + self.settings_shortcuts_item.setMenuRole(QtGui.QAction.NoRole) + self.formatting_tag_item.setMenuRole(QtGui.QAction.NoRole) + 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)) From b6e0aa6c2eb4a49d36e4623f70ae667888b77a44 Mon Sep 17 00:00:00 2001 From: Jonathan Springer Date: Wed, 16 Apr 2014 16:44:22 -0400 Subject: [PATCH 030/212] PEP8 clean up in tests --- tests/functional/openlp_core_common/test_common.py | 5 +++-- tests/functional/openlp_core_lib/test_image_manager.py | 2 +- tests/functional/openlp_core_lib/test_ui.py | 2 +- tests/functional/openlp_core_ui/test_maindisplay.py | 2 +- tests/functional/openlp_plugins/songs/test_ewimport.py | 10 +++++----- tests/functional/openlp_plugins/songs/test_lib.py | 1 - .../openlp_plugins/songs/test_songbeamerimport.py | 6 +++--- .../openlp_plugins/songs/test_songshowplusimport.py | 10 +++++----- tests/helpers/songfileimport.py | 10 +++++----- .../openlp_plugins/bibles/test_lib_parse_reference.py | 2 +- 10 files changed, 25 insertions(+), 25 deletions(-) diff --git a/tests/functional/openlp_core_common/test_common.py b/tests/functional/openlp_core_common/test_common.py index 90b7d0520..993a4cede 100644 --- a/tests/functional/openlp_core_common/test_common.py +++ b/tests/functional/openlp_core_common/test_common.py @@ -40,6 +40,7 @@ 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 @@ -79,5 +80,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_image_manager.py b/tests/functional/openlp_core_lib/test_image_manager.py index 37b6d6fdd..072978993 100644 --- a/tests/functional/openlp_core_lib/test_image_manager.py +++ b/tests/functional/openlp_core_lib/test_image_manager.py @@ -171,4 +171,4 @@ class TestImageManager(TestCase, TestMixin): self.lock.release() # The sleep time is adjusted in the test case. time.sleep(self.sleep_time) - return '' \ No newline at end of file + return '' 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..984c87028 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): @@ -231,8 +231,8 @@ class TestEasyWorshipSongImport(TestCase): # 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): """ @@ -404,10 +404,10 @@ class TestEasyWorshipSongImport(TestCase): 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)) + % (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_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 f08cedec5..d95d49d80 100644 --- a/tests/functional/openlp_plugins/songs/test_songbeamerimport.py +++ b/tests/functional/openlp_plugins/songs/test_songbeamerimport.py @@ -91,7 +91,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): """ @@ -149,10 +149,10 @@ class TestSongBeamerImport(TestCase): 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)) + (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)) + (song_file, song_number)) mocked_finish.assert_called_with() def check_verse_marks_test(self): diff --git a/tests/functional/openlp_plugins/songs/test_songshowplusimport.py b/tests/functional/openlp_plugins/songs/test_songshowplusimport.py index f2839c332..63e5beb8a 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): """ @@ -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..b961b7d64 100644 --- a/tests/helpers/songfileimport.py +++ b/tests/helpers/songfileimport.py @@ -117,23 +117,23 @@ class SongImportTestHelper(TestCase): 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)) + (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)) + (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)) + (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)) + (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)) + (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..b005992ff 100644 --- a/tests/interfaces/openlp_plugins/bibles/test_lib_parse_reference.py +++ b/tests/interfaces/openlp_plugins/bibles/test_lib_parse_reference.py @@ -104,4 +104,4 @@ class TestBibleManager(TestCase, TestMixin): 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") + "results") From 85fc53954d905488c5bec91680ebf80ef2c7cb0e Mon Sep 17 00:00:00 2001 From: Jonathan Springer Date: Wed, 16 Apr 2014 16:49:24 -0400 Subject: [PATCH 031/212] More PEP8 fixes --- openlp/core/ui/mainwindow.py | 2 +- openlp/plugins/songs/lib/songbeamerimport.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index 4e1d28ef8..dca7bf815 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -320,7 +320,7 @@ 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()) - # Qt on OS X look for keywords in the menu items title to determine which menu items get added to the main menu. + # Qt on OS X look for keywords in the menu items title to determine which menu items get added to the main menu. # If we are running on Mac OS X the menu items whose title contains those keywords but don't belong in the main # menu need to be marked as such with QAction.NoRole. if sys.platform == 'darwin': 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) From ef5b1cf4ac08062f80758bb539bdee3491b35c38 Mon Sep 17 00:00:00 2001 From: Jonathan Springer Date: Wed, 16 Apr 2014 17:19:22 -0400 Subject: [PATCH 032/212] Revert PEP8 clean ups as they were already done in a different branch --- openlp/core/ui/mainwindow.py | 2 +- openlp/plugins/songs/lib/songbeamerimport.py | 2 +- tests/functional/openlp_core_common/test_common.py | 5 ++--- tests/functional/openlp_core_lib/test_image_manager.py | 2 +- tests/functional/openlp_core_lib/test_ui.py | 2 +- tests/functional/openlp_core_ui/test_maindisplay.py | 2 +- tests/functional/openlp_plugins/songs/test_ewimport.py | 10 +++++----- tests/functional/openlp_plugins/songs/test_lib.py | 1 + .../openlp_plugins/songs/test_songbeamerimport.py | 6 +++--- .../openlp_plugins/songs/test_songshowplusimport.py | 10 +++++----- tests/helpers/songfileimport.py | 10 +++++----- .../openlp_plugins/bibles/test_lib_parse_reference.py | 2 +- 12 files changed, 27 insertions(+), 27 deletions(-) diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index dca7bf815..4e1d28ef8 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -320,7 +320,7 @@ 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()) - # Qt on OS X look for keywords in the menu items title to determine which menu items get added to the main menu. + # Qt on OS X look for keywords in the menu items title to determine which menu items get added to the main menu. # If we are running on Mac OS X the menu items whose title contains those keywords but don't belong in the main # menu need to be marked as such with QAction.NoRole. if sys.platform == 'darwin': diff --git a/openlp/plugins/songs/lib/songbeamerimport.py b/openlp/plugins/songs/lib/songbeamerimport.py index a0b166ded..5b86591e8 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/tests/functional/openlp_core_common/test_common.py b/tests/functional/openlp_core_common/test_common.py index 993a4cede..90b7d0520 100644 --- a/tests/functional/openlp_core_common/test_common.py +++ b/tests/functional/openlp_core_common/test_common.py @@ -40,7 +40,6 @@ 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 @@ -80,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_image_manager.py b/tests/functional/openlp_core_lib/test_image_manager.py index 072978993..37b6d6fdd 100644 --- a/tests/functional/openlp_core_lib/test_image_manager.py +++ b/tests/functional/openlp_core_lib/test_image_manager.py @@ -171,4 +171,4 @@ class TestImageManager(TestCase, TestMixin): self.lock.release() # The sleep time is adjusted in the test case. time.sleep(self.sleep_time) - return '' + return '' \ No newline at end of file diff --git a/tests/functional/openlp_core_lib/test_ui.py b/tests/functional/openlp_core_lib/test_ui.py index 025b1a638..91d59ab5a 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 6d67a3b67..b1a4dc7f7 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 984c87028..c1b9db52d 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): @@ -231,8 +231,8 @@ class TestEasyWorshipSongImport(TestCase): # 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): """ @@ -404,10 +404,10 @@ class TestEasyWorshipSongImport(TestCase): 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)) + % (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_lib.py b/tests/functional/openlp_plugins/songs/test_lib.py index b67c1a4be..2ab808bc9 100644 --- a/tests/functional/openlp_plugins/songs/test_lib.py +++ b/tests/functional/openlp_plugins/songs/test_lib.py @@ -129,6 +129,7 @@ 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 d95d49d80..f08cedec5 100644 --- a/tests/functional/openlp_plugins/songs/test_songbeamerimport.py +++ b/tests/functional/openlp_plugins/songs/test_songbeamerimport.py @@ -91,7 +91,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): """ @@ -149,10 +149,10 @@ class TestSongBeamerImport(TestCase): 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)) + (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)) + (song_file, song_number)) mocked_finish.assert_called_with() def check_verse_marks_test(self): diff --git a/tests/functional/openlp_plugins/songs/test_songshowplusimport.py b/tests/functional/openlp_plugins/songs/test_songshowplusimport.py index 63e5beb8a..f2839c332 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): """ @@ -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 b961b7d64..49a09528c 100644 --- a/tests/helpers/songfileimport.py +++ b/tests/helpers/songfileimport.py @@ -117,23 +117,23 @@ class SongImportTestHelper(TestCase): 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)) + (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)) + (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)) + (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)) + (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)) + (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 b005992ff..84f80e7ed 100644 --- a/tests/interfaces/openlp_plugins/bibles/test_lib_parse_reference.py +++ b/tests/interfaces/openlp_plugins/bibles/test_lib_parse_reference.py @@ -104,4 +104,4 @@ class TestBibleManager(TestCase, TestMixin): 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") + "results") From e86539888d2dfff0984e01e959cc01318659e692 Mon Sep 17 00:00:00 2001 From: Jonathan Springer Date: Thu, 17 Apr 2014 20:09:31 -0400 Subject: [PATCH 033/212] Fix typo --- openlp/core/ui/mainwindow.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index 4e1d28ef8..2b03c05cb 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -320,9 +320,9 @@ 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()) - # Qt on OS X look for keywords in the menu items title to determine which menu items get added to the main menu. - # If we are running on Mac OS X the menu items whose title contains those keywords but don't belong in the main - # menu need to be marked as such with QAction.NoRole. + # Qt on OS X looks for keywords in the menu items title to determine which menu items get added to the main + # menu. If we are running on Mac OS X the menu items whose title contains those keywords but don't belong in the + # main menu need to be marked as such with QAction.NoRole. if sys.platform == 'darwin': self.settings_shortcuts_item.setMenuRole(QtGui.QAction.NoRole) self.formatting_tag_item.setMenuRole(QtGui.QAction.NoRole) From 25687dfd4803b7c117d2c210b52f047bbc7c601b Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Fri, 18 Apr 2014 21:29:52 +0100 Subject: [PATCH 034/212] Complete icon and ssl change code --- openlp/plugins/remotes/lib/httprouter.py | 25 ++++++------ openlp/plugins/remotes/lib/httpserver.py | 50 +++++++++++++++--------- openlp/plugins/remotes/lib/remotetab.py | 30 ++++++++++++-- openlp/plugins/remotes/remoteplugin.py | 25 ++++++------ 4 files changed, 82 insertions(+), 48 deletions(-) diff --git a/openlp/plugins/remotes/lib/httprouter.py b/openlp/plugins/remotes/lib/httprouter.py index 5a10a14ae..125094a64 100644 --- a/openlp/plugins/remotes/lib/httprouter.py +++ b/openlp/plugins/remotes/lib/httprouter.py @@ -149,11 +149,11 @@ class HttpRouter(RegistryProperties): """ Initialise the router stack and any other variables. """ - authcode = "%s:%s" % (Settings().value('remotes/user id'), Settings().value('remotes/password')) + auth_code = "%s:%s" % (Settings().value('remotes/user id'), Settings().value('remotes/password')) try: - self.auth = base64.b64encode(authcode) + self.auth = base64.b64encode(auth_code) except TypeError: - self.auth = base64.b64encode(authcode.encode()).decode() + self.auth = base64.b64encode(auth_code.encode()).decode() self.routes = [ ('^/$', {'function': self.serve_file, 'secure': False}), ('^/(stage)$', {'function': self.serve_file, 'secure': False}), @@ -376,7 +376,6 @@ class HttpRouter(RegistryProperties): Examines the extension of the file and determines what the content_type should be, defaults to text/plain Returns the extension and the content_type """ - content_type = 'text/plain' ext = os.path.splitext(file_name)[1] content_type = FILE_TYPES.get(ext, 'text/plain') return ext, content_type @@ -439,7 +438,7 @@ class HttpRouter(RegistryProperties): if plugin.status == PluginStatus.Active: try: text = json.loads(self.request_data)['request']['text'] - except KeyError as ValueError: + except KeyError: return self.do_http_error() text = urllib.parse.unquote(text) self.alerts_manager.emit(QtCore.SIGNAL('alerts_text'), [text]) @@ -488,7 +487,7 @@ class HttpRouter(RegistryProperties): if self.request_data: try: data = json.loads(self.request_data)['request']['id'] - except KeyError as ValueError: + except KeyError: return self.do_http_error() log.info(data) # This slot expects an int within a list. @@ -547,7 +546,7 @@ class HttpRouter(RegistryProperties): """ try: text = json.loads(self.request_data)['request']['text'] - except KeyError as ValueError: + except KeyError: return self.do_http_error() text = urllib.parse.unquote(text) plugin = self.plugin_manager.get_plugin_by_name(plugin_name) @@ -563,12 +562,12 @@ class HttpRouter(RegistryProperties): Go live on an item of type ``plugin``. """ try: - id = json.loads(self.request_data)['request']['id'] - except KeyError as ValueError: + request_id = json.loads(self.request_data)['request']['id'] + except KeyError: return self.do_http_error() plugin = self.plugin_manager.get_plugin_by_name(plugin_name) if plugin.status == PluginStatus.Active and plugin.media_item: - plugin.media_item.emit(QtCore.SIGNAL('%s_go_live' % plugin_name), [id, True]) + plugin.media_item.emit(QtCore.SIGNAL('%s_go_live' % plugin_name), [request_id, True]) return self.do_http_success() def add_to_service(self, plugin_name): @@ -576,11 +575,11 @@ class HttpRouter(RegistryProperties): Add item of type ``plugin_name`` to the end of the service. """ try: - id = json.loads(self.request_data)['request']['id'] - except KeyError as ValueError: + request_id = json.loads(self.request_data)['request']['id'] + except KeyError: return self.do_http_error() plugin = self.plugin_manager.get_plugin_by_name(plugin_name) if plugin.status == PluginStatus.Active and plugin.media_item: - item_id = plugin.media_item.create_item_from_id(id) + item_id = plugin.media_item.create_item_from_id(request_id) plugin.media_item.emit(QtCore.SIGNAL('%s_add_to_service' % plugin_name), [item_id, True]) self.do_http_success() diff --git a/openlp/plugins/remotes/lib/httpserver.py b/openlp/plugins/remotes/lib/httpserver.py index cc2d02ff3..7b3d667cd 100644 --- a/openlp/plugins/remotes/lib/httpserver.py +++ b/openlp/plugins/remotes/lib/httpserver.py @@ -38,10 +38,9 @@ import os import logging import time -from PyQt4 import QtCore, QtGui +from PyQt4 import QtCore from openlp.core.common import AppLocation, Settings, RegistryProperties -from openlp.core.lib import build_icon from openlp.plugins.remotes.lib import HttpRouter @@ -72,7 +71,21 @@ class CustomHandler(BaseHTTPRequestHandler, HttpRouter): self.do_post_processor() -class ThreadingHTTPServer(ThreadingMixIn, HTTPServer): +class StoppableHttpServer(HTTPServer): + """ + Http server that reacts to self.stop flag + """ + + def serve_forever(self): + """ + Handle one request at a time until stopped. + """ + self.stop = False + while not self.stop: + self.handle_request() + + +class ThreadingHTTPServer(ThreadingMixIn, StoppableHttpServer): pass @@ -95,6 +108,10 @@ class HttpThread(QtCore.QThread): """ self.http_server.start_server() + def stop(self): + log.debug("stop called") + self.http_server.stop = True + class OpenLPServer(RegistryProperties): def __init__(self): @@ -112,25 +129,19 @@ class OpenLPServer(RegistryProperties): Start the correct server and save the handler """ address = Settings().value(self.settings_section + '/ip address') - if Settings().value(self.settings_section + '/https enabled'): + self.address = address + self.is_secure = Settings().value(self.settings_section + '/https enabled') + self.needs_authentication = Settings().value(self.settings_section + '/authentication enabled') + if self.is_secure: port = Settings().value(self.settings_section + '/https port') + self.port = port self.start_server_instance(address, port, HTTPSServer) else: port = Settings().value(self.settings_section + '/port') + self.port = port self.start_server_instance(address, port, ThreadingHTTPServer) if hasattr(self, 'httpd') and self.httpd: self.httpd.serve_forever() - icon = QtGui.QImage(':/remote/network_server.png') - icon = icon.scaled(80, 80, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation) - overlay = QtGui.QImage(':/remote/network_ssl.png') - overlay = overlay.scaled(40, 40, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation) - painter = QtGui.QPainter(icon) - painter.drawImage(0, 0, overlay) - painter.end() - print("Hi") - self.default_theme_label.setText("hello") - self.default_theme_label.setIcon(build_icon(icon)) - self.default_theme_label.show() else: log.debug('Failed to start server') @@ -149,7 +160,7 @@ class OpenLPServer(RegistryProperties): log.debug("Server started for class %s %s %d" % (server_class, address, port)) except OSError: log.debug("failed to start http server thread state %d %s" % - (loop, self.http_thread.isRunning() is True)) + (loop, self.http_thread.isRunning())) loop += 1 time.sleep(0.1) except: @@ -159,12 +170,13 @@ class OpenLPServer(RegistryProperties): """ Stop the server """ - self.http_thread.exit(0) + if self.http_thread.isRunning(): + self.http_thread.stop() self.httpd = None log.debug('Stopped the server.') -class HTTPSServer(HTTPServer): +class HTTPSServer(StoppableHttpServer): def __init__(self, address, handler): """ Initialise the secure handlers for the SSL server if required.s @@ -178,4 +190,4 @@ class HTTPSServer(HTTPServer): keyfile=os.path.join(local_data, 'remotes', 'openlp.key'), server_side=True) self.server_bind() - self.server_activate() + self.server_activate() \ No newline at end of file diff --git a/openlp/plugins/remotes/lib/remotetab.py b/openlp/plugins/remotes/lib/remotetab.py index d6b96cc1c..ba8766188 100644 --- a/openlp/plugins/remotes/lib/remotetab.py +++ b/openlp/plugins/remotes/lib/remotetab.py @@ -32,7 +32,7 @@ import os.path from PyQt4 import QtCore, QtGui, QtNetwork from openlp.core.common import AppLocation, Settings, translate -from openlp.core.lib import SettingsTab +from openlp.core.lib import SettingsTab, build_icon ZERO_URL = '0.0.0.0' @@ -234,6 +234,7 @@ class RemoteTab(SettingsTab): """ Load the configuration and update the server configuration if necessary """ + self.is_secure = Settings().value(self.settings_section + '/https enabled') self.port_spin_box.setValue(Settings().value(self.settings_section + '/port')) self.https_port_spin_box.setValue(Settings().value(self.settings_section + '/https port')) self.address_edit.setText(Settings().value(self.settings_section + '/ip address')) @@ -263,9 +264,7 @@ 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 \ - Settings().value(self.settings_section + '/authentication enabled') != \ - self.user_login_group_box.isChecked(): + self.https_settings_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()) @@ -275,6 +274,7 @@ class RemoteTab(SettingsTab): Settings().setValue(self.settings_section + '/authentication enabled', self.user_login_group_box.isChecked()) Settings().setValue(self.settings_section + '/user id', self.user_id.text()) Settings().setValue(self.settings_section + '/password', self.password.text()) + self.generate_icon() def on_twelve_hour_check_box_changed(self, check_state): """ @@ -290,3 +290,25 @@ class RemoteTab(SettingsTab): Invert the HTTP group box based on Https group settings """ self.http_settings_group_box.setEnabled(not self.https_settings_group_box.isChecked()) + + def generate_icon(self): + """ + Generate icon for main window + """ + self.remote_server_icon.hide() + icon = QtGui.QImage(':/remote/network_server.png') + icon = icon.scaled(80, 80, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation) + if self.is_secure: + overlay = QtGui.QImage(':/remote/network_ssl.png') + overlay = overlay.scaled(60, 60, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation) + painter = QtGui.QPainter(icon) + painter.drawImage(0, 0, overlay) + painter.end() + if Settings().value(self.settings_section + '/authentication enabled'): + overlay = QtGui.QImage(':/remote/network_auth.png') + overlay = overlay.scaled(60, 60, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation) + painter = QtGui.QPainter(icon) + painter.drawImage(20, 0, overlay) + painter.end() + self.remote_server_icon.setIcon(build_icon(icon)) + self.remote_server_icon.show() \ No newline at end of file diff --git a/openlp/plugins/remotes/remoteplugin.py b/openlp/plugins/remotes/remoteplugin.py index 069e3e5ec..2538991da 100644 --- a/openlp/plugins/remotes/remoteplugin.py +++ b/openlp/plugins/remotes/remoteplugin.py @@ -28,7 +28,6 @@ ############################################################################### import logging -import time from PyQt4 import QtGui @@ -69,13 +68,13 @@ class RemotesPlugin(Plugin): log.debug('initialise') super(RemotesPlugin, self).initialise() self.server = OpenLPServer() - self.default_theme_label = QtGui.QToolButton(self.main_window.status_bar) - self.default_theme_label.setCheckable(False) - self.default_theme_label.setAutoRaise(True) - self.default_theme_label.setObjectName('default_theme_label') - self.main_window.status_bar.insertPermanentWidget(2, self.default_theme_label) - self.default_theme_label.hide() - self.server.default_theme_label = self.default_theme_label + self.remote_server_icon = QtGui.QToolButton(self.main_window.status_bar) + self.remote_server_icon.setCheckable(False) + self.remote_server_icon.setAutoRaise(True) + self.remote_server_icon.setObjectName('remote_server_icon') + self.main_window.status_bar.insertPermanentWidget(2, self.remote_server_icon) + self.settings_tab.remote_server_icon = self.remote_server_icon + self.settings_tab.generate_icon() def finalise(self): """ @@ -113,9 +112,11 @@ class RemotesPlugin(Plugin): def config_update(self): """ - Called when Config is changed to restart the server on new address or port + Called when Config is changed to requests a restart with the server on new address or port """ log.debug('remote config changed') - self.finalise() - time.sleep(1) - self.initialise() + QtGui.QMessageBox.information(self.main_window, + translate('RemotePlugin', 'Server Config Change'), + translate('RemotePlugin', 'Server configuration changes will require a restart ' + 'to take effect.'), + QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok)) From e94643b5d17808f8463609baa1fa1cb049904b2c Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Fri, 18 Apr 2014 21:35:39 +0100 Subject: [PATCH 035/212] Fix invalid code --- openlp/plugins/remotes/lib/httpserver.py | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/openlp/plugins/remotes/lib/httpserver.py b/openlp/plugins/remotes/lib/httpserver.py index 7b3d667cd..9ed5c205a 100644 --- a/openlp/plugins/remotes/lib/httpserver.py +++ b/openlp/plugins/remotes/lib/httpserver.py @@ -71,21 +71,7 @@ class CustomHandler(BaseHTTPRequestHandler, HttpRouter): self.do_post_processor() -class StoppableHttpServer(HTTPServer): - """ - Http server that reacts to self.stop flag - """ - - def serve_forever(self): - """ - Handle one request at a time until stopped. - """ - self.stop = False - while not self.stop: - self.handle_request() - - -class ThreadingHTTPServer(ThreadingMixIn, StoppableHttpServer): +class ThreadingHTTPServer(ThreadingMixIn, HTTPServer): pass @@ -176,7 +162,7 @@ class OpenLPServer(RegistryProperties): log.debug('Stopped the server.') -class HTTPSServer(StoppableHttpServer): +class HTTPSServer(HTTPServer): def __init__(self, address, handler): """ Initialise the secure handlers for the SSL server if required.s From 5dc35112c33e81be6bd076596dac734f42d5772a Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sat, 19 Apr 2014 06:09:54 +0100 Subject: [PATCH 036/212] pep8 again --- openlp/core/ui/servicemanager.py | 3 ++- openlp/plugins/remotes/lib/httprouter.py | 1 + scripts/jenkins_script.py | 8 ++++---- scripts/translation_utils.py | 2 +- tests/functional/openlp_core_common/test_common.py | 4 ++-- tests/functional/openlp_core_lib/test_lib.py | 2 +- .../functional/openlp_core_ui/test_formattingtagsform.py | 4 ++-- tests/functional/openlp_plugins/bibles/test_http.py | 4 ++-- .../presentations/test_pptviewcontroller.py | 4 ++-- .../openlp_plugins/songs/test_foilpresenterimport.py | 2 +- 10 files changed, 18 insertions(+), 16 deletions(-) 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) diff --git a/openlp/plugins/remotes/lib/httprouter.py b/openlp/plugins/remotes/lib/httprouter.py index 125094a64..4241b34dc 100644 --- a/openlp/plugins/remotes/lib/httprouter.py +++ b/openlp/plugins/remotes/lib/httprouter.py @@ -452,6 +452,7 @@ class HttpRouter(RegistryProperties): """ Perform an action on the slide controller. """ + log.debug("controller_text var = %s" % var) current_item = self.live_controller.service_item data = [] if current_item: diff --git a/scripts/jenkins_script.py b/scripts/jenkins_script.py index 386ab69ef..cf438964d 100644 --- a/scripts/jenkins_script.py +++ b/scripts/jenkins_script.py @@ -115,14 +115,14 @@ class JenkinsTrigger(object): url = build.info['url'] print('[%s] %s' % (result_string, url)) # On failure open the browser. - #if result_string == "FAILURE": + # 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*. + This returns the name of branch of the working directory. For example it returns *lp:~googol/openlp/render*. """ # Run the bzr command. bzr = Popen(('bzr', 'info'), stdout=PIPE, stderr=PIPE) @@ -166,7 +166,7 @@ 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", + # 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) @@ -177,7 +177,7 @@ def main(): jenkins_trigger = JenkinsTrigger(token) try: jenkins_trigger.trigger_build() - except HTTPError as e: + except HTTPError: print("Wrong token.") return # Open the browser before printing the output. 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_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 b9bb8f11c..92af51619 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 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 From af792941af9e6c57aec3964d95fb311672a8967f Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sat, 19 Apr 2014 06:13:46 +0100 Subject: [PATCH 037/212] pep8 again --- tests/functional/openlp_core_lib/test_file_dialog.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/functional/openlp_core_lib/test_file_dialog.py b/tests/functional/openlp_core_lib/test_file_dialog.py index b2c2178a9..ab7663a83 100644 --- a/tests/functional/openlp_core_lib/test_file_dialog.py +++ b/tests/functional/openlp_core_lib/test_file_dialog.py @@ -53,8 +53,8 @@ class TestFileDialog(TestCase): self.mocked_os.rest() 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. + # GIVEN: A List of known values as a return value from QFileDialog.getOpenFileNames and a list of valid 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 [ From 905a6267051e10fe8009158cce35a736d3d58613 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sat, 19 Apr 2014 06:26:49 +0100 Subject: [PATCH 038/212] fixes --- openlp/plugins/remotes/lib/httpserver.py | 2 +- openlp/plugins/remotes/lib/remotetab.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openlp/plugins/remotes/lib/httpserver.py b/openlp/plugins/remotes/lib/httpserver.py index 9ed5c205a..9a904090d 100644 --- a/openlp/plugins/remotes/lib/httpserver.py +++ b/openlp/plugins/remotes/lib/httpserver.py @@ -176,4 +176,4 @@ class HTTPSServer(HTTPServer): keyfile=os.path.join(local_data, 'remotes', 'openlp.key'), server_side=True) self.server_bind() - self.server_activate() \ No newline at end of file + self.server_activate() diff --git a/openlp/plugins/remotes/lib/remotetab.py b/openlp/plugins/remotes/lib/remotetab.py index ba8766188..42d46c581 100644 --- a/openlp/plugins/remotes/lib/remotetab.py +++ b/openlp/plugins/remotes/lib/remotetab.py @@ -311,4 +311,4 @@ class RemoteTab(SettingsTab): painter.drawImage(20, 0, overlay) painter.end() self.remote_server_icon.setIcon(build_icon(icon)) - self.remote_server_icon.show() \ No newline at end of file + self.remote_server_icon.show() From a2c99317c6a262e0474b527dfa351330baece378 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sat, 19 Apr 2014 06:42:45 +0100 Subject: [PATCH 039/212] fixes --- openlp/plugins/songs/lib/songbeamerimport.py | 2 +- tests/functional/openlp_core_lib/test_ui.py | 2 +- tests/functional/openlp_core_ui/test_maindisplay.py | 2 +- tests/functional/openlp_plugins/songs/test_ewimport.py | 10 +++++----- tests/functional/openlp_plugins/songs/test_lib.py | 1 - .../openlp_plugins/songs/test_songbeamerimport.py | 6 +++--- .../openlp_plugins/songs/test_songshowplusimport.py | 10 +++++----- tests/helpers/songfileimport.py | 10 +++++----- .../openlp_plugins/bibles/test_lib_parse_reference.py | 2 +- 9 files changed, 22 insertions(+), 23 deletions(-) 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/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..981bc677d 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): @@ -231,8 +231,8 @@ class TestEasyWorshipSongImport(TestCase): # 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): """ @@ -404,10 +404,10 @@ class TestEasyWorshipSongImport(TestCase): 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)) + % (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_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 f08cedec5..284f972ef 100644 --- a/tests/functional/openlp_plugins/songs/test_songbeamerimport.py +++ b/tests/functional/openlp_plugins/songs/test_songbeamerimport.py @@ -91,7 +91,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): """ @@ -149,10 +149,10 @@ class TestSongBeamerImport(TestCase): 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)) + (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)) + (song_file, song_number)) mocked_finish.assert_called_with() def check_verse_marks_test(self): diff --git a/tests/functional/openlp_plugins/songs/test_songshowplusimport.py b/tests/functional/openlp_plugins/songs/test_songshowplusimport.py index f2839c332..63e5beb8a 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): """ @@ -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..16935ffca 100644 --- a/tests/helpers/songfileimport.py +++ b/tests/helpers/songfileimport.py @@ -117,23 +117,23 @@ class SongImportTestHelper(TestCase): 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)) + (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)) + (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)) + (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)) + (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)) + (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..6883f9eb5 100644 --- a/tests/interfaces/openlp_plugins/bibles/test_lib_parse_reference.py +++ b/tests/interfaces/openlp_plugins/bibles/test_lib_parse_reference.py @@ -104,4 +104,4 @@ class TestBibleManager(TestCase, TestMixin): 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") + "results") From d62b92718d544c17f6e2fa0f5a85b6f573669cc1 Mon Sep 17 00:00:00 2001 From: Stewart Becker Date: Sat, 19 Apr 2014 22:57:49 +0100 Subject: [PATCH 040/212] Fixed typo --- 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 81e822c16..59bfb32af 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -598,7 +598,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow, RegistryProperties): if self.arguments: self.open_cmd_line_files() elif Settings().value(self.general_settings_section + '/auto open'): - self.service_manager_contents.load_Last_file() + self.service_manager_contents.load_last_file() self.timer_version_id = self.startTimer(1000) view_mode = Settings().value('%s/view mode' % self.general_settings_section) if view_mode == 'default': From 51c14bdf5131e907a8109ae48abc52c0f084c9f4 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sun, 20 Apr 2014 15:24:18 +0200 Subject: [PATCH 041/212] fixed bug 1296104 Fixes: https://launchpad.net/bugs/1296104 --- openlp/core/lib/renderer.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/openlp/core/lib/renderer.py b/openlp/core/lib/renderer.py index 233af3784..e24381558 100644 --- a/openlp/core/lib/renderer.py +++ b/openlp/core/lib/renderer.py @@ -248,6 +248,9 @@ class Renderer(OpenLPMixin, RegistryMixin, RegistryProperties): elif item.is_capable(ItemCapabilities.CanSoftBreak): pages = [] if '[---]' in text: + # Remove two or more option slide breaks next to each other (causing infinite loop). + while u'\n[---]\n[---]\n' in text: + text = text.replace(u'\n[---]\n[---]\n', u'\n[---]\n') while True: slides = text.split('\n[---]\n', 2) # If there are (at least) two occurrences of [---] we use the first two slides (and neglect the last From 85c217910297482658b0d92cc9635f4cbd8c350d Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sun, 20 Apr 2014 16:03:07 +0200 Subject: [PATCH 042/212] added test --- .../openlp_core_ui/test_shortcutlistform.py | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 tests/interfaces/openlp_core_ui/test_shortcutlistform.py diff --git a/tests/interfaces/openlp_core_ui/test_shortcutlistform.py b/tests/interfaces/openlp_core_ui/test_shortcutlistform.py new file mode 100644 index 000000000..27b48838d --- /dev/null +++ b/tests/interfaces/openlp_core_ui/test_shortcutlistform.py @@ -0,0 +1,79 @@ +# -*- 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.shortcutform package. +""" +from unittest import TestCase + +from PyQt4 import QtCore, QtGui, QtTest + +from openlp.core.common import Registry +from openlp.core.ui.shortcutlistform import ShortcutListForm +from tests.interfaces import patch +from tests.helpers.testmixin import TestMixin + + +class TestShortcutform(TestCase, TestMixin): + + def setUp(self): + """ + Create the UI + """ + Registry.create() + self.get_application() + self.main_window = QtGui.QMainWindow() + Registry().register('main_window', self.main_window) + self.form = ShortcutListForm() + + def tearDown(self): + """ + Delete all the C++ objects at the end so that we don't have a segfault + """ + del self.form + del self.main_window + + def adjust_button_test(self): + """ + Test the _adjust_button() method + """ + # GIVEN: A button. + button = QtGui.QPushButton() + checked= True + enabled = True + text = "new!" + + # WHEN: Call the method. + with patch('PyQt4.QtGui.QPushButton.setChecked') as mocked_check_method: + self.form._adjust_button(button, checked, enabled, text) + + + # THEN: The button should be changed. + self.assertEqual(button.text(), text, "The text should match.") + mocked_check_method.assert_called_once_with(True) + self.assertEqual(button.isEnabled(), enabled, "The button should be disabled.") \ No newline at end of file From 57cd04a9570628d7dfe840fd2d052a66f8be59ed Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sun, 20 Apr 2014 16:10:37 +0200 Subject: [PATCH 043/212] Fixes: https://launchpad.net/bugs/1296104 From 79d3e95c32dd265d8e41f9b14cb55515e03c6b78 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sun, 20 Apr 2014 16:13:09 +0200 Subject: [PATCH 044/212] pep8 fixes --- tests/interfaces/openlp_core_ui/test_shortcutlistform.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/interfaces/openlp_core_ui/test_shortcutlistform.py b/tests/interfaces/openlp_core_ui/test_shortcutlistform.py index 27b48838d..472bce03f 100644 --- a/tests/interfaces/openlp_core_ui/test_shortcutlistform.py +++ b/tests/interfaces/openlp_core_ui/test_shortcutlistform.py @@ -64,7 +64,7 @@ class TestShortcutform(TestCase, TestMixin): """ # GIVEN: A button. button = QtGui.QPushButton() - checked= True + checked = True enabled = True text = "new!" @@ -72,7 +72,6 @@ class TestShortcutform(TestCase, TestMixin): with patch('PyQt4.QtGui.QPushButton.setChecked') as mocked_check_method: self.form._adjust_button(button, checked, enabled, text) - # THEN: The button should be changed. self.assertEqual(button.text(), text, "The text should match.") mocked_check_method.assert_called_once_with(True) From df800850e1cee6a43955e35a7886d2d0950dc26e Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sun, 20 Apr 2014 18:00:24 +0200 Subject: [PATCH 045/212] fixed bug 1240037 (Error occured when moving directory) Fixes: https://launchpad.net/bugs/1240037 --- 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 81e822c16..e17b1a976 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -1334,7 +1334,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow, RegistryProperties): if self.copy_data: log.info('Copying data to new path') try: - self.showStatusMessage( + self.show_status_message( translate('OpenLP.MainWindow', 'Copying OpenLP data to new data directory location - %s ' '- Please wait for copy to finish').replace('%s', self.new_data_path)) dir_util.copy_tree(old_data_path, self.new_data_path) From cf798e73d592f43285eb9b1654dc808ee5f71ebf Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sun, 20 Apr 2014 18:02:50 +0200 Subject: [PATCH 046/212] removed not needed code --- openlp/core/ui/mainwindow.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index e17b1a976..9c193b079 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -1364,8 +1364,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow, RegistryProperties): args = [] for a in self.arguments: args.extend([a]) - for arg in args: - filename = arg + for filename in args: if not isinstance(filename, str): filename = str(filename, sys.getfilesystemencoding()) if filename.endswith(('.osz', '.oszl')): From 54ed6aeea02a6a5076049d8e6c1334b52b71e94e Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Sun, 20 Apr 2014 21:03:35 +0200 Subject: [PATCH 047/212] Made the EWS import a seperate point. Also some small updates to comments. --- openlp/plugins/songs/lib/ewimport.py | 10 +----- openlp/plugins/songs/lib/importer.py | 46 ++++++++++++++++------------ 2 files changed, 27 insertions(+), 29 deletions(-) diff --git a/openlp/plugins/songs/lib/ewimport.py b/openlp/plugins/songs/lib/ewimport.py index 1bbe5fed4..faa4122c8 100644 --- a/openlp/plugins/songs/lib/ewimport.py +++ b/openlp/plugins/songs/lib/ewimport.py @@ -78,8 +78,6 @@ class EasyWorshipSongImport(SongImport): def do_import(self): """ Determines the type of file to import and calls the appropiate method - - :return: """ if self.import_source.lower().endswith('ews'): self.import_ews() @@ -92,8 +90,6 @@ class EasyWorshipSongImport(SongImport): The full spec of the ews files can be found here: https://github.com/meinders/lithium-ews/blob/master/docs/ews%20file%20format.md or here: http://wiki.openlp.org/Development:EasyWorship_EWS_Format - - :return: """ # Open ews file if it exists if not os.path.isfile(self.import_source): @@ -196,8 +192,6 @@ class EasyWorshipSongImport(SongImport): def import_db(self): """ Import the songs from the database - - :return: """ # Open the DB and MB files if they exist import_source_mb = self.import_source.replace('.DB', '.MB') @@ -322,7 +316,6 @@ class EasyWorshipSongImport(SongImport): :param authors: String with authons :param words: Bytes with rtf-encoding - :return: """ if authors: # Split up the authors @@ -380,7 +373,6 @@ class EasyWorshipSongImport(SongImport): Find a field in the descriptions :param field_name: field to find - :return: """ return [i for i, x in enumerate(self.field_descriptions) if x.name == field_name][0] @@ -417,7 +409,7 @@ class EasyWorshipSongImport(SongImport): Extract the field :param field_desc_index: Field index value - :return: + :return: The field value """ field = self.fields[field_desc_index] field_desc = self.field_descriptions[field_desc_index] diff --git a/openlp/plugins/songs/lib/importer.py b/openlp/plugins/songs/lib/importer.py index 0f04075d5..ed32a43ee 100644 --- a/openlp/plugins/songs/lib/importer.py +++ b/openlp/plugins/songs/lib/importer.py @@ -153,19 +153,20 @@ class SongFormat(object): CCLI = 3 DreamBeam = 4 EasySlides = 5 - EasyWorship = 6 - FoilPresenter = 7 - MediaShout = 8 - OpenSong = 9 - PowerSong = 10 - SongBeamer = 11 - SongPro = 12 - SongShowPlus = 13 - SongsOfFellowship = 14 - SundayPlus = 15 - WordsOfWorship = 16 - WorshipCenterPro = 17 - ZionWorx = 18 + EasyWorshipDB = 6 + EasyWorshipService = 7 + FoilPresenter = 8 + MediaShout = 9 + OpenSong = 10 + PowerSong = 11 + SongBeamer = 12 + SongPro = 13 + SongShowPlus = 14 + SongsOfFellowship = 15 + SundayPlus = 16 + WordsOfWorship = 17 + WorshipCenterPro = 18 + ZionWorx = 19 # Set optional attribute defaults __defaults__ = { @@ -224,15 +225,19 @@ class SongFormat(object): 'selectMode': SongFormatSelect.SingleFile, 'filter': '%s (*.xml)' % translate('SongsPlugin.ImportWizardForm', 'EasySlides XML File') }, - EasyWorship: { + EasyWorshipDB: { 'class': EasyWorshipSongImport, - 'name': 'EasyWorship', + 'name': 'EasyWorship Song Database', 'prefix': 'ew', 'selectMode': SongFormatSelect.SingleFile, - 'filter': '%s (*.db);; %s (*.ews)' % (translate('SongsPlugin.ImportWizardForm', - 'EasyWorship Song Database'), - translate('SongsPlugin.ImportWizardForm', - 'EasyWorship Service File')) + 'filter': '%s (*.db)' % translate('SongsPlugin.ImportWizardForm', 'EasyWorship Song Database') + }, + EasyWorshipService: { + 'class': EasyWorshipSongImport, + 'name': 'EasyWorship Service File', + 'prefix': 'ew', + 'selectMode': SongFormatSelect.SingleFile, + 'filter': '%s (*.ews)' % translate('SongsPlugin.ImportWizardForm', 'EasyWorship Service File') }, FoilPresenter: { 'class': FoilPresenterImport, @@ -344,7 +349,8 @@ class SongFormat(object): SongFormat.CCLI, SongFormat.DreamBeam, SongFormat.EasySlides, - SongFormat.EasyWorship, + SongFormat.EasyWorshipDB, + SongFormat.EasyWorshipService, SongFormat.FoilPresenter, SongFormat.MediaShout, SongFormat.OpenSong, From ce55fd1d57b61be6e36aac7e8374a0b141ee5f12 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sun, 20 Apr 2014 21:16:08 +0100 Subject: [PATCH 048/212] fixes --- openlp/core/ui/pluginform.py | 1 - openlp/plugins/remotes/lib/remotetab.py | 2 +- openlp/plugins/remotes/remoteplugin.py | 20 ++++++++++++++------ 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/openlp/core/ui/pluginform.py b/openlp/core/ui/pluginform.py index 91b98b97a..78bdee4a5 100644 --- a/openlp/core/ui/pluginform.py +++ b/openlp/core/ui/pluginform.py @@ -30,7 +30,6 @@ The actual plugin view form """ import logging -import os from PyQt4 import QtGui diff --git a/openlp/plugins/remotes/lib/remotetab.py b/openlp/plugins/remotes/lib/remotetab.py index 42d46c581..4db25cfc2 100644 --- a/openlp/plugins/remotes/lib/remotetab.py +++ b/openlp/plugins/remotes/lib/remotetab.py @@ -310,5 +310,5 @@ class RemoteTab(SettingsTab): painter = QtGui.QPainter(icon) painter.drawImage(20, 0, overlay) painter.end() - self.remote_server_icon.setIcon(build_icon(icon)) + self.remote_server_icon.setPixmap(QtGui.QPixmap.fromImage(icon)) self.remote_server_icon.show() diff --git a/openlp/plugins/remotes/remoteplugin.py b/openlp/plugins/remotes/remoteplugin.py index 2538991da..582192df4 100644 --- a/openlp/plugins/remotes/remoteplugin.py +++ b/openlp/plugins/remotes/remoteplugin.py @@ -68,12 +68,20 @@ class RemotesPlugin(Plugin): log.debug('initialise') super(RemotesPlugin, self).initialise() self.server = OpenLPServer() - self.remote_server_icon = QtGui.QToolButton(self.main_window.status_bar) - self.remote_server_icon.setCheckable(False) - self.remote_server_icon.setAutoRaise(True) - self.remote_server_icon.setObjectName('remote_server_icon') - self.main_window.status_bar.insertPermanentWidget(2, self.remote_server_icon) - self.settings_tab.remote_server_icon = self.remote_server_icon + if not hasattr(self, 'remote_server_icon'): + self.remote_server_icon = QtGui.QLabel(self.main_window.status_bar) + size_policy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) + size_policy.setHorizontalStretch(0) + size_policy.setVerticalStretch(0) + size_policy.setHeightForWidth(self.remote_server_icon.sizePolicy().hasHeightForWidth()) + self.remote_server_icon.setSizePolicy(size_policy) + self.remote_server_icon.setFrameShadow(QtGui.QFrame.Plain) + self.remote_server_icon.setLineWidth(1) + self.remote_server_icon.setScaledContents(True) + self.remote_server_icon.setFixedSize(20, 20) + self.remote_server_icon.setObjectName('remote_server_icon') + self.main_window.status_bar.insertPermanentWidget(2, self.remote_server_icon) + self.settings_tab.remote_server_icon = self.remote_server_icon self.settings_tab.generate_icon() def finalise(self): From ac44396a405b57d7650b135ebe21d27b51995852 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Mon, 21 Apr 2014 11:58:09 +0200 Subject: [PATCH 049/212] 2 row layout for author add widgets --- openlp/plugins/songs/forms/editsongdialog.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/openlp/plugins/songs/forms/editsongdialog.py b/openlp/plugins/songs/forms/editsongdialog.py index 7a491032b..7935b892a 100644 --- a/openlp/plugins/songs/forms/editsongdialog.py +++ b/openlp/plugins/songs/forms/editsongdialog.py @@ -118,18 +118,21 @@ class Ui_EditSongDialog(object): self.authors_group_box.setObjectName('authors_group_box') self.authors_layout = QtGui.QVBoxLayout(self.authors_group_box) self.authors_layout.setObjectName('authors_layout') - self.author_add_layout = QtGui.QHBoxLayout() + self.author_add_layout = QtGui.QVBoxLayout() self.author_add_layout.setObjectName('author_add_layout') + self.author_type_layout = QtGui.QHBoxLayout() + self.author_type_layout.setObjectName('author_type_layout') self.authors_combo_box = create_combo_box(self.authors_group_box, 'authors_combo_box') self.author_add_layout.addWidget(self.authors_combo_box) self.author_types_combo_box = create_combo_box(self.authors_group_box, 'author_types_combo_box', editable=False) # Need to give these boxes some min width, else they are too small self.authors_combo_box.setMinimumWidth(150) self.author_types_combo_box.setMinimumWidth(80) - self.author_add_layout.addWidget(self.author_types_combo_box) + self.author_type_layout.addWidget(self.author_types_combo_box) self.author_add_button = QtGui.QPushButton(self.authors_group_box) self.author_add_button.setObjectName('author_add_button') - self.author_add_layout.addWidget(self.author_add_button) + self.author_type_layout.addWidget(self.author_add_button) + self.author_add_layout.addLayout(self.author_type_layout) self.authors_layout.addLayout(self.author_add_layout) self.authors_list_view = QtGui.QListWidget(self.authors_group_box) self.authors_list_view.setAlternatingRowColors(True) From 70a5795cd249f2621e1c3b67b91ec0a8f9ce2ede Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Mon, 21 Apr 2014 11:58:53 +0200 Subject: [PATCH 050/212] Remove min width --- openlp/plugins/songs/forms/editsongdialog.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/openlp/plugins/songs/forms/editsongdialog.py b/openlp/plugins/songs/forms/editsongdialog.py index 7935b892a..d0fb51a2d 100644 --- a/openlp/plugins/songs/forms/editsongdialog.py +++ b/openlp/plugins/songs/forms/editsongdialog.py @@ -125,9 +125,6 @@ class Ui_EditSongDialog(object): self.authors_combo_box = create_combo_box(self.authors_group_box, 'authors_combo_box') self.author_add_layout.addWidget(self.authors_combo_box) self.author_types_combo_box = create_combo_box(self.authors_group_box, 'author_types_combo_box', editable=False) - # Need to give these boxes some min width, else they are too small - self.authors_combo_box.setMinimumWidth(150) - self.author_types_combo_box.setMinimumWidth(80) self.author_type_layout.addWidget(self.author_types_combo_box) self.author_add_button = QtGui.QPushButton(self.authors_group_box) self.author_add_button.setObjectName('author_add_button') From cc0af8845ec8bc80e73d974706e6fb41477fabfb Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Mon, 21 Apr 2014 12:06:17 +0200 Subject: [PATCH 051/212] PEP8 --- openlp/plugins/songs/lib/upgrade.py | 3 ++- tests/functional/openlp_plugins/songs/test_mediaitem.py | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/openlp/plugins/songs/lib/upgrade.py b/openlp/plugins/songs/lib/upgrade.py index 59355bbec..580ae767d 100644 --- a/openlp/plugins/songs/lib/upgrade.py +++ b/openlp/plugins/songs/lib/upgrade.py @@ -112,7 +112,8 @@ def upgrade_4(session, metadata): op.create_table('authors_songs_tmp', Column('author_id', types.Integer(), ForeignKey('authors.id'), primary_key=True), Column('song_id', types.Integer(), ForeignKey('songs.id'), primary_key=True), - Column('author_type', types.String(), primary_key=True, nullable=False, server_default=text('""'))) + Column('author_type', types.String(), primary_key=True, + nullable=False, server_default=text('""'))) op.execute('INSERT INTO authors_songs_tmp SELECT author_id, song_id, "" FROM authors_songs') op.drop_table('authors_songs') op.rename_table('authors_songs_tmp', 'authors_songs') diff --git a/tests/functional/openlp_plugins/songs/test_mediaitem.py b/tests/functional/openlp_plugins/songs/test_mediaitem.py index 1b372aa20..308881c2e 100644 --- a/tests/functional/openlp_plugins/songs/test_mediaitem.py +++ b/tests/functional/openlp_plugins/songs/test_mediaitem.py @@ -76,19 +76,19 @@ class TestMediaItem(TestCase, TestMixin): mock_author.display_name = 'my author' mock_author_song = MagicMock() mock_author_song.author = mock_author - mock_author_song.author_type=AuthorType.Music + mock_author_song.author_type = AuthorType.Music mock_song.authors_songs.append(mock_author_song) mock_author = MagicMock() mock_author.display_name = 'another author' mock_author_song = MagicMock() mock_author_song.author = mock_author - mock_author_song.author_type=AuthorType.Words + mock_author_song.author_type = AuthorType.Words mock_song.authors_songs.append(mock_author_song) mock_author = MagicMock() mock_author.display_name = 'translator' mock_author_song = MagicMock() mock_author_song.author = mock_author - mock_author_song.author_type=AuthorType.Translation + mock_author_song.author_type = AuthorType.Translation mock_song.authors_songs.append(mock_author_song) mock_song.copyright = 'My copyright' service_item = ServiceItem(None) From 6eccf3eadf3d0048319dcf70f2fd407a27ee383c Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Mon, 21 Apr 2014 16:40:54 +0200 Subject: [PATCH 052/212] change display of list view --- openlp/plugins/songs/lib/db.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/plugins/songs/lib/db.py b/openlp/plugins/songs/lib/db.py index fa53bccc8..91649c951 100644 --- a/openlp/plugins/songs/lib/db.py +++ b/openlp/plugins/songs/lib/db.py @@ -48,7 +48,7 @@ class Author(BaseModel): """ def get_display_name(self, author_type=None): if author_type: - return "%s: %s" % (AuthorType.Types[author_type], self.display_name) + return "%s (%s)" % (self.display_name, AuthorType.Types[author_type]) return self.display_name From e6162e033f0d3b1039c468ea687e6c40bbea8c8b Mon Sep 17 00:00:00 2001 From: Stewart Becker Date: Mon, 21 Apr 2014 16:49:41 +0100 Subject: [PATCH 053/212] Add tests for OpenSongImport, and fixes so that they pass --- openlp/plugins/songs/lib/opensongimport.py | 42 ++++++++++++--- openlp/plugins/songs/lib/songimport.py | 49 ++++++++++++++++++ tests/helpers/songfileimport.py | 12 ++--- tests/resources/opensongsongs/Amazing Grace | 51 +++++++++++++++++++ .../opensongsongs/Amazing Grace.json | 42 +++++++++++++++ .../opensongsongs/Beautiful Garden Of Prayer | 45 ++++++++++++++++ .../Beautiful Garden Of Prayer.json | 35 +++++++++++++ 7 files changed, 262 insertions(+), 14 deletions(-) create mode 100644 tests/resources/opensongsongs/Amazing Grace create mode 100644 tests/resources/opensongsongs/Amazing Grace.json create mode 100644 tests/resources/opensongsongs/Beautiful Garden Of Prayer create mode 100644 tests/resources/opensongsongs/Beautiful Garden Of Prayer.json diff --git a/openlp/plugins/songs/lib/opensongimport.py b/openlp/plugins/songs/lib/opensongimport.py index bee0989a0..cafec1cea 100644 --- a/openlp/plugins/songs/lib/opensongimport.py +++ b/openlp/plugins/songs/lib/opensongimport.py @@ -107,9 +107,14 @@ class OpenSongImport(SongImport): """ Initialise the class. """ - SongImport.__init__(self, manager, **kwargs) + super(OpenSongImport, self).__init__(manager, **kwargs) def do_import(self): + """ + Receive a single file or a list of files to import. + """ + if not isinstance(self.import_source, list): + return self.import_wizard.progress_bar.setMaximum(len(self.import_source)) for filename in self.import_source: if self.stop_import_flag: @@ -141,19 +146,39 @@ class OpenSongImport(SongImport): 'author': self.parse_author, 'title': 'title', 'aka': 'alternate_title', - 'hymn_number': 'song_number' + 'hymn_number': self.parse_song_book_name_and_number, + 'user1': self.add_comment, + 'user2': self.add_comment, + 'user3': self.add_comment } for attr, fn_or_string in list(decode.items()): if attr in fields: ustring = str(root.__getattr__(attr)) if isinstance(fn_or_string, str): - setattr(self, fn_or_string, ustring) + match = re.match('(\D*)(\d+.*)', ustring) + if match: + setattr(self, fn_or_string, int(ustring)) + else: + setattr(self, fn_or_string, ustring) else: fn_or_string(ustring) - if 'theme' in fields and str(root.theme) not in self.topics: - self.topics.append(str(root.theme)) - if 'alttheme' in fields and str(root.alttheme) not in self.topics: - self.topics.append(str(root.alttheme)) + # Themes look like "God: Awe/Wonder", but we just want + # "Awe" and "Wonder". We use a set to ensure each topic + # is only added once, in case it is already there, which + # is actually quite likely if the alttheme is set + topics = set(self.topics) + if 'theme' in fields: + theme = str(root.theme) + subthemes = theme[theme.find(':')+1:].split('/') + for topic in subthemes: + topics.add(topic.strip()) + if 'alttheme' in fields: + theme = str(root.alttheme) + subthemes = theme[theme.find(':')+1:].split('/') + for topic in subthemes: + topics.add(topic.strip()) + self.topics = list(topics) + self.topics.sort() # data storage while importing verses = {} # keep track of verses appearance order @@ -209,8 +234,9 @@ class OpenSongImport(SongImport): # Tidy text and remove the ____s from extended words this_line = self.tidy_text(this_line) this_line = this_line.replace('_', '') - this_line = this_line.replace('|', '\n') + this_line = this_line.replace('||', '\n[---]\n') this_line = this_line.strip() + this_line = this_line.replace('|', '\n') verses[verse_tag][verse_num][inst].append(this_line) # done parsing # add verses in original order diff --git a/openlp/plugins/songs/lib/songimport.py b/openlp/plugins/songs/lib/songimport.py index a5fbb99e0..54260c354 100644 --- a/openlp/plugins/songs/lib/songimport.py +++ b/openlp/plugins/songs/lib/songimport.py @@ -188,6 +188,55 @@ class SongImport(QtCore.QObject): self.title = lines[0] self.add_verse(text) + def parse_song_book_name_and_number(self, book_and_number): + """ + Build the book name and song number from a single string + """ + # Turn 'Spring Harvest 1997 No. 34' or + # 'Spring Harvest 1997 (34)' or + # 'Spring Harvest 1997 34' into + # Book name:'Spring Harvest 1997' and + # Song number: 34 + # + # Also, turn 'NRH231.' into + # Book name:'NRH' and + # Song number: 231 + + book_and_number = book_and_number.strip() + if book_and_number == '': + return + book_and_number = book_and_number.replace('No.', ' ') + if ' ' in book_and_number: + parts = book_and_number.split(' ') + self.song_book_name = ' '.join(parts[:-1]) + self.song_number = parts[-1].strip('()') + else: + # Something like 'ABC123.' + match = re.match(r'(.*\D)(\d+)', book_and_number) + match_num = re.match(r'(\d+)', book_and_number) + if match: + # Name and number + self.song_book_name = match.group(1) + self.song_number = match.group(2) + # These last two cases aren't tested yet, but + # are here in an attempt to do something vaguely + # sensible if we get a string in a different format + elif match_num: + # Number only + self.song_number = match_num.group(1) + else: + # Name only + self.song_book_name = book_and_number + + def add_comment(self, comment): + """ + Build the comments field + """ + if self.comments.find(comment) >= 0: + return + if comment != '': + self.comments += comment.strip() + '\n' + def add_copyright(self, copyright): """ Build the copyright field diff --git a/tests/helpers/songfileimport.py b/tests/helpers/songfileimport.py index 49a09528c..cf0fbd487 100644 --- a/tests/helpers/songfileimport.py +++ b/tests/helpers/songfileimport.py @@ -56,13 +56,13 @@ class SongImportTestHelper(TestCase): 'openlp.plugins.songs.lib.%s.%s.add_verse' % (self.importer_module_name, self.importer_class_name)) self.finish_patcher = patch( 'openlp.plugins.songs.lib.%s.%s.finish' % (self.importer_module_name, self.importer_class_name)) - self.parse_author_patcher = patch( - 'openlp.plugins.songs.lib.%s.%s.parse_author' % (self.importer_module_name, self.importer_class_name)) + self.add_author_patcher = patch( + 'openlp.plugins.songs.lib.%s.%s.add_author' % (self.importer_module_name, self.importer_class_name)) self.song_import_patcher = patch('openlp.plugins.songs.lib.%s.SongImport' % self.importer_module_name) self.mocked_add_copyright = self.add_copyright_patcher.start() self.mocked_add_verse = self.add_verse_patcher.start() self.mocked_finish = self.finish_patcher.start() - self.mocked_parse_author = self.parse_author_patcher.start() + self.mocked_add_author = self.add_author_patcher.start() self.mocked_song_importer = self.song_import_patcher.start() self.mocked_manager = MagicMock() self.mocked_import_wizard = MagicMock() @@ -75,7 +75,7 @@ class SongImportTestHelper(TestCase): self.add_copyright_patcher.stop() self.add_verse_patcher.stop() self.finish_patcher.stop() - self.parse_author_patcher.stop() + self.add_author_patcher.stop() self.song_import_patcher.stop() def load_external_result_data(self, file_name): @@ -112,7 +112,7 @@ class SongImportTestHelper(TestCase): self.assertIsNone(importer.do_import(), 'do_import should return None when it has completed') 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) + self.mocked_add_author.assert_any_call(author) if song_copyright: self.mocked_add_copyright.assert_called_with(song_copyright) if ccli_number: @@ -132,7 +132,7 @@ class SongImportTestHelper(TestCase): 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' % + self.assertEqual(importer.verse_order_list, 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/resources/opensongsongs/Amazing Grace b/tests/resources/opensongsongs/Amazing Grace new file mode 100644 index 000000000..678cb7214 --- /dev/null +++ b/tests/resources/opensongsongs/Amazing Grace @@ -0,0 +1,51 @@ + + + Amazing Grace (Demonstration) + John Newton, Edwin Excell & John P. Rees + Public Domain + V1 V2 V3 V4 V5 + + + 22025 + God: Assurance/Grace/Salvation + Worship: Praise + + + + [V] +. D D7 G D +1A______ma________zing grace! How sweet the sound! +2’Twas grace that taught my heart to fear, +3The Lord has pro____mised good to me, +4Thro' ma________ny dan____gers, toils and snares +5When we’ve been there ten thou__sand years, + +. Bm E A A7 +1That saved a wretch like me! +2And grace my fears re___lieved. +3His Word my hope se___cures. +4I have al___rea____dy come. +5Bright shi___ning as the sun, + +. D D7 G D +1I once was lost, but now am found; +2How pre___cious did that grace ap____pear, +3He will my shield and por___tion be +4’Tis grace that brought me safe thus far, +5We’ve no less days to sing God’s praise, + +. Bm A G D +1Was blind, but now I see. +2The hour I first be_lieved. +3As long as life en_dures. +4And grace will lead me home. +5Than when we first be_gun. + + + Demonstration Songs 0 + + + + + + \ No newline at end of file diff --git a/tests/resources/opensongsongs/Amazing Grace.json b/tests/resources/opensongsongs/Amazing Grace.json new file mode 100644 index 000000000..97b8c77b7 --- /dev/null +++ b/tests/resources/opensongsongs/Amazing Grace.json @@ -0,0 +1,42 @@ +{ + "authors": [ + "John Newton", + "Edwin Excell", + "John P. Rees" + ], + "ccli_number": 22025, + "comments": "\n\n\n", + "copyright": "Public Domain ", + "song_book_name": "Demonstration Songs", + "song_number": 0, + "title": "Amazing Grace (Demonstration)", + "topics": [ + "Assurance", + "Grace", + "Praise", + "Salvation" + ], + "verse_order_list": [], + "verses": [ + [ + "Amazing grace! How sweet the sound!\nThat saved a wretch like me!\nI once was lost, but now am found;\nWas blind, but now I see.", + "v1" + ], + [ + "'Twas grace that taught my heart to fear,\nAnd grace my fears relieved.\nHow precious did that grace appear,\nThe hour I first believed.", + "v2" + ], + [ + "The Lord has promised good to me,\nHis Word my hope secures.\nHe will my shield and portion be\nAs long as life endures.", + "v3" + ], + [ + "Thro' many dangers, toils and snares\nI have already come.\n'Tis grace that brought me safe thus far,\nAnd grace will lead me home.", + "v4" + ], + [ + "When we've been there ten thousand years,\nBright shining as the sun,\nWe've no less days to sing God's praise,\nThan when we first begun.", + "v5" + ] + ] +} \ No newline at end of file diff --git a/tests/resources/opensongsongs/Beautiful Garden Of Prayer b/tests/resources/opensongsongs/Beautiful Garden Of Prayer new file mode 100644 index 000000000..95b21f032 --- /dev/null +++ b/tests/resources/opensongsongs/Beautiful Garden Of Prayer @@ -0,0 +1,45 @@ + + + Beautiful Garden Of Prayer (Demonstration) + Eleanor Allen Schroll & James H. Fillmore + Public Domain + V1 C V2 C V3 C + + + 60252 + God: Prayer/Devotion + Prayer: Prayer/Devotion + + + + [V1] + There's a garden where Jesus is waiting, + There's a place that is wondrously fair. + For it glows with the light of His presence,| + 'Tis the beautiful garden of prayer. + +[V2] + There's a garden where Jesus is waiting, + And I go with my burden and care. + Just to learn from His lips, words of comfort,| + In the beautiful garden of prayer. + +[V3] + There's a garden where Jesus is waiting, + And He bids you to come meet Him there, + Just to bow and receive a new blessing,| + In the beautiful garden of prayer. + +[C] + O the beautiful garden, the garden of prayer, + O the beautiful garden of prayer. + There my Savior awaits, and He opens the gates|| + To the beautiful garden of prayer. + + DS0 + + + + + + \ No newline at end of file diff --git a/tests/resources/opensongsongs/Beautiful Garden Of Prayer.json b/tests/resources/opensongsongs/Beautiful Garden Of Prayer.json new file mode 100644 index 000000000..d9f4926dd --- /dev/null +++ b/tests/resources/opensongsongs/Beautiful Garden Of Prayer.json @@ -0,0 +1,35 @@ +{ + "authors": [ + "Eleanor Allen Schroll", + "James H. Fillmore" + ], + "ccli_number": 60252, + "comments": "", + "copyright": "Public Domain ", + "song_book_name": "DS", + "song_number": 0, + "title": "Beautiful Garden Of Prayer (Demonstration)", + "topics": [ + "Devotion", + "Prayer" + ], + "verse_order_list": ["v1", "c1", "v2", "c1", "v3", "c1"], + "verses": [ + [ + "There's a garden where Jesus is waiting,\nThere's a place that is wondrously fair.\nFor it glows with the light of His presence,\n\n'Tis the beautiful garden of prayer.", + "v1" + ], + [ + "There's a garden where Jesus is waiting,\nAnd I go with my burden and care.\nJust to learn from His lips, words of comfort,\n\nIn the beautiful garden of prayer.", + "v2" + ], + [ + "There's a garden where Jesus is waiting,\nAnd He bids you to come meet Him there,\nJust to bow and receive a new blessing,\n\nIn the beautiful garden of prayer.", + "v3" + ], + [ + "O the beautiful garden, the garden of prayer,\nO the beautiful garden of prayer.\nThere my Savior awaits, and He opens the gates\n[---]\nTo the beautiful garden of prayer.", + "c1" + ] + ] +} \ No newline at end of file From 57abcf9b40d2630269fe0e70363f41edd577800e Mon Sep 17 00:00:00 2001 From: Stewart Becker Date: Mon, 21 Apr 2014 18:23:00 +0100 Subject: [PATCH 054/212] Remebered to add the tests this time --- .../songs/test_opensongimport.py | 119 ++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 tests/functional/openlp_plugins/songs/test_opensongimport.py diff --git a/tests/functional/openlp_plugins/songs/test_opensongimport.py b/tests/functional/openlp_plugins/songs/test_opensongimport.py new file mode 100644 index 000000000..eba0112f7 --- /dev/null +++ b/tests/functional/openlp_plugins/songs/test_opensongimport.py @@ -0,0 +1,119 @@ +# -*- 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 OpenSong song importer. +""" + +import os +from unittest import TestCase + +from tests.helpers.songfileimport import SongImportTestHelper +from openlp.plugins.songs.lib.opensongimport import OpenSongImport +from tests.functional import patch, MagicMock + +TEST_PATH = os.path.abspath( + os.path.join(os.path.dirname(__file__), '..', '..', '..', 'resources', 'opensongsongs')) + + +class TestOpenSongFileImport(SongImportTestHelper): + + def __init__(self, *args, **kwargs): + self.importer_class_name = 'OpenSongImport' + self.importer_module_name = 'opensongimport' + super(TestOpenSongFileImport, self).__init__(*args, **kwargs) + + def test_song_import(self): + """ + Test that loading an OpenSong file works correctly on various files + """ + self.file_import(os.path.join(TEST_PATH, 'Amazing Grace'), + self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace.json'))) + self.file_import(os.path.join(TEST_PATH, 'Beautiful Garden Of Prayer'), + self.load_external_result_data(os.path.join(TEST_PATH, 'Beautiful Garden Of Prayer.json'))) + + +class TestOpenSongImport(TestCase): + """ + Test the functions in the :mod:`opensongimport` module. + """ + def create_importer_test(self): + """ + Test creating an instance of the OpenSong file importer + """ + # GIVEN: A mocked out SongImport class, and a mocked out "manager" + with patch('openlp.plugins.songs.lib.opensongimport.SongImport'): + mocked_manager = MagicMock() + + # WHEN: An importer object is created + importer = OpenSongImport(mocked_manager, filenames=[]) + + # THEN: The importer object should not be None + self.assertIsNotNone(importer, 'Import should not be none') + + def invalid_import_source_test(self): + """ + Test OpenSongImport.do_import handles different invalid import_source values + """ + # GIVEN: A mocked out SongImport class, and a mocked out "manager" + with patch('openlp.plugins.songs.lib.opensongimport.SongImport'): + mocked_manager = MagicMock() + mocked_import_wizard = MagicMock() + importer = OpenSongImport(mocked_manager, filenames=[]) + importer.import_wizard = mocked_import_wizard + importer.stop_import_flag = True + + # WHEN: Import source is not a list + for source in ['not a list', 0]: + importer.import_source = source + + # 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') + + def valid_import_source_test(self): + """ + Test OpenSongImport.do_import handles different invalid import_source values + """ + # GIVEN: A mocked out SongImport class, and a mocked out "manager" + with patch('openlp.plugins.songs.lib.opensongimport.SongImport'): + mocked_manager = MagicMock() + mocked_import_wizard = MagicMock() + importer = OpenSongImport(mocked_manager, filenames=[]) + importer.import_wizard = mocked_import_wizard + importer.stop_import_flag = True + + # WHEN: Import source is a list + importer.import_source = ['List', 'of', 'files'] + + # 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') + mocked_import_wizard.progress_bar.setMaximum.assert_called_with(len(importer.import_source)) From 3e1ed273be43ca50ada1ff7f664b9f48b379f364 Mon Sep 17 00:00:00 2001 From: Stewart Becker Date: Mon, 21 Apr 2014 19:20:06 +0100 Subject: [PATCH 055/212] Show calls to add_verse for debugging --- tests/helpers/songfileimport.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/helpers/songfileimport.py b/tests/helpers/songfileimport.py index cf0fbd487..0f97666ba 100644 --- a/tests/helpers/songfileimport.py +++ b/tests/helpers/songfileimport.py @@ -72,6 +72,7 @@ class SongImportTestHelper(TestCase): """ Clean up """ + print(str(self.mocked_add_verse.call_args_list)) self.add_copyright_patcher.stop() self.add_verse_patcher.stop() self.finish_patcher.stop() From 9a05c7603a19049d987c2d306a8e2c057bffddd7 Mon Sep 17 00:00:00 2001 From: Stewart Becker Date: Mon, 21 Apr 2014 19:42:54 +0100 Subject: [PATCH 056/212] Fixed apostrophe format --- tests/resources/opensongsongs/Amazing Grace | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/resources/opensongsongs/Amazing Grace b/tests/resources/opensongsongs/Amazing Grace index 678cb7214..363450ba5 100644 --- a/tests/resources/opensongsongs/Amazing Grace +++ b/tests/resources/opensongsongs/Amazing Grace @@ -15,10 +15,10 @@ [V] . D D7 G D 1A______ma________zing grace! How sweet the sound! -2’Twas grace that taught my heart to fear, +2'Twas grace that taught my heart to fear, 3The Lord has pro____mised good to me, 4Thro' ma________ny dan____gers, toils and snares -5When we’ve been there ten thou__sand years, +5When we've been there ten thou__sand years, . Bm E A A7 1That saved a wretch like me! @@ -31,8 +31,8 @@ 1I once was lost, but now am found; 2How pre___cious did that grace ap____pear, 3He will my shield and por___tion be -4’Tis grace that brought me safe thus far, -5We’ve no less days to sing God’s praise, +4'Tis grace that brought me safe thus far, +5We've no less days to sing God's praise, . Bm A G D 1Was blind, but now I see. From 7deeefafb4bd764b4d3008d72336a5c9430bcfa6 Mon Sep 17 00:00:00 2001 From: Stewart Becker Date: Mon, 21 Apr 2014 19:52:42 +0100 Subject: [PATCH 057/212] Remove call used only for debugging --- tests/helpers/songfileimport.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/helpers/songfileimport.py b/tests/helpers/songfileimport.py index 0f97666ba..cf0fbd487 100644 --- a/tests/helpers/songfileimport.py +++ b/tests/helpers/songfileimport.py @@ -72,7 +72,6 @@ class SongImportTestHelper(TestCase): """ Clean up """ - print(str(self.mocked_add_verse.call_args_list)) self.add_copyright_patcher.stop() self.add_verse_patcher.stop() self.finish_patcher.stop() From 4416a578618a3b36ec550493051de80fa34d1874 Mon Sep 17 00:00:00 2001 From: Stewart Becker Date: Tue, 22 Apr 2014 08:01:17 +0100 Subject: [PATCH 058/212] Fix whitespace issue --- openlp/plugins/songs/lib/songimport.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openlp/plugins/songs/lib/songimport.py b/openlp/plugins/songs/lib/songimport.py index 54260c354..93e0b508d 100644 --- a/openlp/plugins/songs/lib/songimport.py +++ b/openlp/plugins/songs/lib/songimport.py @@ -201,7 +201,6 @@ class SongImport(QtCore.QObject): # Also, turn 'NRH231.' into # Book name:'NRH' and # Song number: 231 - book_and_number = book_and_number.strip() if book_and_number == '': return From 6c08a80bfda00504312ec54f82cf51a320fbb17e Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Tue, 22 Apr 2014 12:29:15 +0200 Subject: [PATCH 059/212] added test; refactor --- openlp/core/ui/shortcutlistform.py | 20 +++--- openlp/core/utils/actions.py | 13 ++-- .../openlp_core_utils/test_actions.py | 62 +++++++++++++++++++ 3 files changed, 79 insertions(+), 16 deletions(-) diff --git a/openlp/core/ui/shortcutlistform.py b/openlp/core/ui/shortcutlistform.py index 4b64c3b54..dbfbbd439 100644 --- a/openlp/core/ui/shortcutlistform.py +++ b/openlp/core/ui/shortcutlistform.py @@ -244,10 +244,10 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog, RegistryProperties) self.primary_push_button.setChecked(False) self.alternate_push_button.setChecked(False) else: - if action.defaultShortcuts: - primary_label_text = action.defaultShortcuts[0].toString() - if len(action.defaultShortcuts) == 2: - alternate_label_text = action.defaultShortcuts[1].toString() + if action.default_shortcuts: + primary_label_text = action.default_shortcuts[0].toString() + if len(action.default_shortcuts) == 2: + alternate_label_text = action.default_shortcuts[1].toString() shortcuts = self._action_shortcuts(action) # We do not want to loose pending changes, that is why we have to keep the text when, this function has not # been triggered by a signal. @@ -292,7 +292,7 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog, RegistryProperties) self._adjust_button(self.alternate_push_button, False, text='') for category in self.action_list.categories: for action in category.actions: - self.changed_actions[action] = action.defaultShortcuts + self.changed_actions[action] = action.default_shortcuts self.refresh_shortcut_list() def on_default_radio_button_clicked(self, toggled): @@ -306,7 +306,7 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog, RegistryProperties) if action is None: return temp_shortcuts = self._action_shortcuts(action) - self.changed_actions[action] = action.defaultShortcuts + self.changed_actions[action] = action.default_shortcuts self.refresh_shortcut_list() primary_button_text = '' alternate_button_text = '' @@ -357,8 +357,8 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog, RegistryProperties) return shortcuts = self._action_shortcuts(action) new_shortcuts = [] - if action.defaultShortcuts: - new_shortcuts.append(action.defaultShortcuts[0]) + if action.default_shortcuts: + new_shortcuts.append(action.default_shortcuts[0]) # We have to check if the primary default shortcut is available. But we only have to check, if the action # has a default primary shortcut (an "empty" shortcut is always valid and if the action does not have a # default primary shortcut, then the alternative shortcut (not the default one) will become primary @@ -383,8 +383,8 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog, RegistryProperties) new_shortcuts = [] if shortcuts: new_shortcuts.append(shortcuts[0]) - if len(action.defaultShortcuts) == 2: - new_shortcuts.append(action.defaultShortcuts[1]) + if len(action.default_shortcuts) == 2: + new_shortcuts.append(action.default_shortcuts[1]) if len(new_shortcuts) == 2: if not self._validiate_shortcut(action, new_shortcuts[1]): return diff --git a/openlp/core/utils/actions.py b/openlp/core/utils/actions.py index 29f2d279b..b4efc561f 100644 --- a/openlp/core/utils/actions.py +++ b/openlp/core/utils/actions.py @@ -69,6 +69,7 @@ class CategoryActionList(object): """ Implement the __getitem__() method to make this class a dictionary type """ + assert False for weight, action in self.actions: if action.text() == key: return action @@ -78,7 +79,10 @@ class CategoryActionList(object): """ Implement the __contains__() method to make this class a dictionary type """ - return item in self + for weight, action in self.actions: + if action.text() == item: + return True + return False def __len__(self): """ @@ -107,10 +111,7 @@ class CategoryActionList(object): """ Implement the has_key() method to make this class a dictionary type """ - for weight, action in self.actions: - if action.text() == key: - return True - return False + return key in self def append(self, name): """ @@ -270,7 +271,7 @@ class ActionList(object): settings = Settings() settings.beginGroup('shortcuts') # Get the default shortcut from the config. - action.defaultShortcuts = settings.get_default_value(action.objectName()) + action.default_shortcuts = settings.get_default_value(action.objectName()) if weight is None: self.categories[category].actions.append(action) else: diff --git a/tests/functional/openlp_core_utils/test_actions.py b/tests/functional/openlp_core_utils/test_actions.py index 2868f8555..566b020fe 100644 --- a/tests/functional/openlp_core_utils/test_actions.py +++ b/tests/functional/openlp_core_utils/test_actions.py @@ -32,9 +32,11 @@ Package to test the openlp.core.utils.actions package. from unittest import TestCase from PyQt4 import QtGui, QtCore +from mock import MagicMock from openlp.core.common import Settings from openlp.core.utils import ActionList +from openlp.core.utils.actions import CategoryActionList from tests.helpers.testmixin import TestMixin @@ -149,3 +151,63 @@ 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.' + + +class TestCategoryActionList(TestCase): + def setUp(self): + """ + """ + self.added_action = MagicMock() + self.added_action.text = MagicMock('first') + self.not_added_action = MagicMock('second') + self.not_added_action.text = MagicMock() + self.list = CategoryActionList() + self.list.add(self.added_action, 10) + + def tearDown(self): + """ + + """ + del self.list + + def len_test(self): + """ + Test the __len__ method + """ + # GIVEN: The list. + + # WHEN: Check the length + length = len(self.list) + + # THEN: + self.assertEqual(length, 1, "The length should be 1.") + + # GIVEN: A list with an item. + self.list.append(self.not_added_action) + + # WHEN: Check the length. + length = len(self.list) + + # THEN: + self.assertEqual(length, 2, "The length should be 2.") + + def remove_test(self): + """ + Test the remove() method + """ + # GIVEN: The list + + # WHEN: Delete an item from the list. + self.list.remove(self.added_action) + + # THEN: Now the element should not be in the list anymore. + self.assertFalse(self.added_action in self.list) + + def contains_test(self): + """ + Test the __contains__() method + """ + # GIVEN: The list. + # WHEN: Do nothing. + # THEN: A not added item should not be in the list. + self.assertFalse(self.not_added_action in self.list) \ No newline at end of file From 1ae7c32adb435428193f05a39980d403712f9d2a Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Tue, 22 Apr 2014 12:32:02 +0200 Subject: [PATCH 060/212] fixed import --- tests/functional/openlp_core_utils/test_actions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/openlp_core_utils/test_actions.py b/tests/functional/openlp_core_utils/test_actions.py index 566b020fe..9ee0f5c6a 100644 --- a/tests/functional/openlp_core_utils/test_actions.py +++ b/tests/functional/openlp_core_utils/test_actions.py @@ -32,11 +32,11 @@ Package to test the openlp.core.utils.actions package. from unittest import TestCase from PyQt4 import QtGui, QtCore -from mock import MagicMock from openlp.core.common import Settings from openlp.core.utils import ActionList from openlp.core.utils.actions import CategoryActionList +from tests.functional import MagicMock from tests.helpers.testmixin import TestMixin From 5a3aecf997040b0705fd7319f8d8fb39a3455eaa Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Tue, 22 Apr 2014 17:06:21 +0200 Subject: [PATCH 061/212] fixed bugs in class --- openlp/core/utils/actions.py | 15 +- .../openlp_core_utils/test_actions.py | 147 +++++++++++------- 2 files changed, 94 insertions(+), 68 deletions(-) diff --git a/openlp/core/utils/actions.py b/openlp/core/utils/actions.py index b4efc561f..98eaced5b 100644 --- a/openlp/core/utils/actions.py +++ b/openlp/core/utils/actions.py @@ -69,18 +69,17 @@ class CategoryActionList(object): """ Implement the __getitem__() method to make this class a dictionary type """ - assert False - for weight, action in self.actions: - if action.text() == key: - return action - raise KeyError('Action "%s" does not exist.' % key) + try: + return self.actions[key] + except: + raise KeyError('Action "%s" does not exist.' % key) def __contains__(self, item): """ Implement the __contains__() method to make this class a dictionary type """ for weight, action in self.actions: - if action.text() == item: + if action == item: return True return False @@ -113,14 +112,14 @@ class CategoryActionList(object): """ return key in self - def append(self, name): + def append(self, action): """ Append an action """ weight = 0 if self.actions: weight = self.actions[-1][0] + 1 - self.add(name, weight) + self.add(action, weight) def add(self, action, weight=0): """ diff --git a/tests/functional/openlp_core_utils/test_actions.py b/tests/functional/openlp_core_utils/test_actions.py index 9ee0f5c6a..d79617037 100644 --- a/tests/functional/openlp_core_utils/test_actions.py +++ b/tests/functional/openlp_core_utils/test_actions.py @@ -36,10 +36,96 @@ from PyQt4 import QtGui, QtCore from openlp.core.common import Settings from openlp.core.utils import ActionList from openlp.core.utils.actions import CategoryActionList -from tests.functional import MagicMock +from tests.functional import MagicMock, patch from tests.helpers.testmixin import TestMixin +class TestCategoryActionList(TestCase): + def setUp(self): + """ + Create an instance and a few example actions. + """ + self.action1 = MagicMock() + self.action1.text.return_value = 'first' + self.action2 = MagicMock() + self.action2.text.return_value = 'second' + self.list = CategoryActionList() + + def tearDown(self): + """ + Clean up + """ + del self.list + + def contains_test(self): + """ + Test the __contains__() method + """ + # GIVEN: The list. + # WHEN: Add an action + self.list.append(self.action1) + # THEN: + self.assertTrue(self.action1 in self.list) + self.assertFalse(self.action2 in self.list) + + def len_test(self): + """ + Test the __len__ method + """ + # GIVEN: The list. + # WHEN: + # THEN: Check the length. + self.assertEqual(len(self.list), 0, "The length should be 0.") + + # GIVEN: The list. + # WHEN: Append an action. + self.list.append(self.action1) + + # THEN: Check the length. + self.assertEqual(len(self.list), 1, "The length should be 1.") + + def append_test(self): + """ + Test the append() method + """ + # GIVEN: The list. + # WHEN: Append an action. + self.list.append(self.action1) + self.list.append(self.action2) + + # THEN: Check if the actions are in the list and check if they have the correct weights. + self.assertTrue(self.action1 in self.list) + self.assertTrue(self.action2 in self.list) + self.assertEqual(self.list[0], (0, self.action1)) + self.assertEqual(self.list[1], (1, self.action2)) + + def add_test(self): + """ + Test the add() method + """ + # GIVEN: The list. + # WHEN: Append actions. + self.list.add(self.action1, 42) + self.list.add(self.action2, 99) + + # THEN: Check if they were added and have the specified weights. + self.assertTrue(self.action1 in self.list) + self.assertTrue(self.action2 in self.list) + self.assertEqual(self.list[0], (42, self.action1)) + self.assertEqual(self.list[1], (99, self.action2)) + + def remove_test(self): + """ + Test the remove() method + """ + # GIVEN: The list + # WHEN: Delete an item from the list. + self.list.remove(self.action1) + + # THEN: Now the element should not be in the list anymore. + self.assertFalse(self.action1 in self.list) + + class TestActionList(TestCase, TestMixin): """ Test the ActionList class @@ -152,62 +238,3 @@ class TestActionList(TestCase, TestMixin): 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.' - -class TestCategoryActionList(TestCase): - def setUp(self): - """ - """ - self.added_action = MagicMock() - self.added_action.text = MagicMock('first') - self.not_added_action = MagicMock('second') - self.not_added_action.text = MagicMock() - self.list = CategoryActionList() - self.list.add(self.added_action, 10) - - def tearDown(self): - """ - - """ - del self.list - - def len_test(self): - """ - Test the __len__ method - """ - # GIVEN: The list. - - # WHEN: Check the length - length = len(self.list) - - # THEN: - self.assertEqual(length, 1, "The length should be 1.") - - # GIVEN: A list with an item. - self.list.append(self.not_added_action) - - # WHEN: Check the length. - length = len(self.list) - - # THEN: - self.assertEqual(length, 2, "The length should be 2.") - - def remove_test(self): - """ - Test the remove() method - """ - # GIVEN: The list - - # WHEN: Delete an item from the list. - self.list.remove(self.added_action) - - # THEN: Now the element should not be in the list anymore. - self.assertFalse(self.added_action in self.list) - - def contains_test(self): - """ - Test the __contains__() method - """ - # GIVEN: The list. - # WHEN: Do nothing. - # THEN: A not added item should not be in the list. - self.assertFalse(self.not_added_action in self.list) \ No newline at end of file From d39e1fd4a28faf55d2f9727653ea75bd25ca39ac Mon Sep 17 00:00:00 2001 From: Stewart Becker Date: Tue, 22 Apr 2014 22:43:12 +0100 Subject: [PATCH 062/212] Added comments to OepnSong files and stricter line-break tests --- openlp/plugins/songs/lib/opensongimport.py | 22 ++++++++++++++----- tests/resources/opensongsongs/Amazing Grace | 5 +++++ .../opensongsongs/Beautiful Garden Of Prayer | 18 +++++++++++---- .../Beautiful Garden Of Prayer.json | 2 +- 4 files changed, 37 insertions(+), 10 deletions(-) diff --git a/openlp/plugins/songs/lib/opensongimport.py b/openlp/plugins/songs/lib/opensongimport.py index cafec1cea..b418e1d37 100644 --- a/openlp/plugins/songs/lib/opensongimport.py +++ b/openlp/plugins/songs/lib/opensongimport.py @@ -45,11 +45,11 @@ class OpenSongImport(SongImport): """ Import songs exported from OpenSong - The format is described loosly on the `OpenSong File Format Specification + The format is described loosely on the `OpenSong File Format Specification `_ page on the OpenSong web site. However, it doesn't describe the section, so here's an attempt: - If the first charachter of a line is a space, then the rest of that line is lyrics. If it is not a space the + If the first character of a line is a space, then the rest of that line is lyrics. If it is not a space the following applies. Verses can be expressed in one of 2 ways, either in complete verses, or by line grouping, i.e. grouping all line 1's @@ -93,12 +93,19 @@ class OpenSongImport(SongImport): All verses are imported and tagged appropriately. - Guitar chords can be provided "above" the lyrics (the line is preceeded by a period "."), and one or more "_" can + Guitar chords can be provided "above" the lyrics (the line is preceded by a period "."), and one or more "_" can be used to signify long-drawn-out words. Chords and "_" are removed by this importer. For example:: . A7 Bm 1 Some____ Words + Lines that contain only whitespace are ignored. + | indicates a blank line, and || a new slide. + + Slide 1 Line 1|Slide 1 Line 2||Slide 2 Line 1|Slide 2 Line 2 + + Lines beginning with ; are comments + The tag is used to populate the OpenLP verse display order field. The Author and Copyright tags are also imported to the appropriate places. """ @@ -193,7 +200,7 @@ class OpenSongImport(SongImport): else: lyrics = '' for this_line in lyrics.split('\n'): - if not this_line: + if not this_line.strip(): continue # skip this line if it is a comment if this_line.startswith(';'): @@ -236,7 +243,12 @@ class OpenSongImport(SongImport): this_line = this_line.replace('_', '') this_line = this_line.replace('||', '\n[---]\n') this_line = this_line.strip() - this_line = this_line.replace('|', '\n') + # If the line consists solely of a '|', then just use the implicit newline + # Otherwise, add a newline for each '|' + if this_line == '|': + this_line = '' + else: + this_line = this_line.replace('|', '\n') verses[verse_tag][verse_num][inst].append(this_line) # done parsing # add verses in original order diff --git a/tests/resources/opensongsongs/Amazing Grace b/tests/resources/opensongsongs/Amazing Grace index 363450ba5..97062dc21 100644 --- a/tests/resources/opensongsongs/Amazing Grace +++ b/tests/resources/opensongsongs/Amazing Grace @@ -13,6 +13,11 @@ [V] +;Test the chords format +;Chords beging with . +;Verses begin with their verse number +;Link words with _ +;Comments begin with ; . D D7 G D 1A______ma________zing grace! How sweet the sound! 2'Twas grace that taught my heart to fear, diff --git a/tests/resources/opensongsongs/Beautiful Garden Of Prayer b/tests/resources/opensongsongs/Beautiful Garden Of Prayer index 95b21f032..33d4943bd 100644 --- a/tests/resources/opensongsongs/Beautiful Garden Of Prayer +++ b/tests/resources/opensongsongs/Beautiful Garden Of Prayer @@ -12,28 +12,38 @@ - [V1] + +;Test breaks and newlines +;A single | on the end of a line adds an extra \n +;Blank lines are ignored, even with a space prefix +[V1] There's a garden where Jesus is waiting, + There's a place that is wondrously fair. For it glows with the light of His presence,| 'Tis the beautiful garden of prayer. +;A double || on the end of a line adds a new slide [V2] There's a garden where Jesus is waiting, And I go with my burden and care. - Just to learn from His lips, words of comfort,| + Just to learn from His lips, words of comfort,|| In the beautiful garden of prayer. +;A single | on a line adds just one line break [V3] There's a garden where Jesus is waiting, And He bids you to come meet Him there, - Just to bow and receive a new blessing,| + Just to bow and receive a new blessing, + | In the beautiful garden of prayer. +;A double || on a line adds a new slide [C] O the beautiful garden, the garden of prayer, O the beautiful garden of prayer. - There my Savior awaits, and He opens the gates|| + There my Savior awaits, and He opens the gates + || To the beautiful garden of prayer. DS0 diff --git a/tests/resources/opensongsongs/Beautiful Garden Of Prayer.json b/tests/resources/opensongsongs/Beautiful Garden Of Prayer.json index d9f4926dd..392bbaa18 100644 --- a/tests/resources/opensongsongs/Beautiful Garden Of Prayer.json +++ b/tests/resources/opensongsongs/Beautiful Garden Of Prayer.json @@ -20,7 +20,7 @@ "v1" ], [ - "There's a garden where Jesus is waiting,\nAnd I go with my burden and care.\nJust to learn from His lips, words of comfort,\n\nIn the beautiful garden of prayer.", + "There's a garden where Jesus is waiting,\nAnd I go with my burden and care.\nJust to learn from His lips, words of comfort,\n[---]\nIn the beautiful garden of prayer.", "v2" ], [ From 04d5a40542f2ee5b1ec05bdb2a1f84e05c38b740 Mon Sep 17 00:00:00 2001 From: Stewart Becker Date: Tue, 22 Apr 2014 22:48:48 +0100 Subject: [PATCH 063/212] Fix PEP8 issues --- tests/functional/openlp_core_lib/test_image_manager.py | 2 +- tests/functional/openlp_core_ui/test_media.py | 2 +- tests/functional/openlp_plugins/songs/test_opensongimport.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/functional/openlp_core_lib/test_image_manager.py b/tests/functional/openlp_core_lib/test_image_manager.py index 37b6d6fdd..072978993 100644 --- a/tests/functional/openlp_core_lib/test_image_manager.py +++ b/tests/functional/openlp_core_lib/test_image_manager.py @@ -171,4 +171,4 @@ class TestImageManager(TestCase, TestMixin): self.lock.release() # The sleep time is adjusted in the test case. time.sleep(self.sleep_time) - return '' \ No newline at end of file + return '' diff --git a/tests/functional/openlp_core_ui/test_media.py b/tests/functional/openlp_core_ui/test_media.py index d59690949..4c6fa7f86 100644 --- a/tests/functional/openlp_core_ui/test_media.py +++ b/tests/functional/openlp_core_ui/test_media.py @@ -125,4 +125,4 @@ class TestMedia(TestCase, TestMixin): # 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 + self.assertEqual('vlc,webkit,phonon', overridden_player, 'Overridden player should be a string of players') diff --git a/tests/functional/openlp_plugins/songs/test_opensongimport.py b/tests/functional/openlp_plugins/songs/test_opensongimport.py index eba0112f7..a63d09068 100644 --- a/tests/functional/openlp_plugins/songs/test_opensongimport.py +++ b/tests/functional/openlp_plugins/songs/test_opensongimport.py @@ -95,7 +95,7 @@ class TestOpenSongImport(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): """ From 13fb4496f3114571c9f8ef35aca8e8c1f66a63ae Mon Sep 17 00:00:00 2001 From: Jonathan Springer Date: Wed, 23 Apr 2014 22:57:02 -0400 Subject: [PATCH 064/212] Change type to conform to expected type --- openlp/plugins/images/lib/mediaitem.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index c28f1e834..36df55dac 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -353,7 +353,7 @@ class ImageMediaItem(MediaManagerItem): icon = build_icon(thumb) else: icon = create_thumb(imageFile.filename, thumb) - item_name = QtGui.QTreeWidgetItem(filename) + item_name = QtGui.QTreeWidgetItem([filename]) item_name.setText(0, filename) item_name.setIcon(0, icon) item_name.setToolTip(0, imageFile.filename) From 1cd7d73d10b60ef5a0b058bcd5cbbc69a7e7d6bc Mon Sep 17 00:00:00 2001 From: Stefan Strasser Date: Fri, 25 Apr 2014 22:04:43 +0200 Subject: [PATCH 065/212] fixed bug #1312629 Error on entering invalid character in verse-order Fixes: https://launchpad.net/bugs/1312629 --- openlp/plugins/songs/lib/__init__.py | 6 ++-- .../openlp_plugins/songs/test_lib.py | 32 +++++++++++++++++-- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/openlp/plugins/songs/lib/__init__.py b/openlp/plugins/songs/lib/__init__.py index dc198d4b7..343bc710b 100644 --- a/openlp/plugins/songs/lib/__init__.py +++ b/openlp/plugins/songs/lib/__init__.py @@ -206,14 +206,14 @@ class VerseType(object): Return the VerseType for a given tag :param verse_tag: The string to return a VerseType for - :param default: Default return value if no matching tag is found + :param default: Default return value if no matching tag is found (a valid VerseType or None) :return: A VerseType of the tag """ verse_tag = verse_tag[0].lower() for num, tag in enumerate(VerseType.tags): if verse_tag == tag: return num - if len(VerseType.names) > default: + if default in range(0,len(VerseType.names)) or default is None: return default else: return VerseType.Other @@ -231,7 +231,7 @@ class VerseType(object): for num, tag in enumerate(VerseType.translated_tags): if verse_tag == tag: return num - if len(VerseType.names) > default: + if default in range(0,len(VerseType.names)) or default is None: return default else: return VerseType.Other diff --git a/tests/functional/openlp_plugins/songs/test_lib.py b/tests/functional/openlp_plugins/songs/test_lib.py index b67c1a4be..b06a18a7c 100644 --- a/tests/functional/openlp_plugins/songs/test_lib.py +++ b/tests/functional/openlp_plugins/songs/test_lib.py @@ -445,9 +445,9 @@ class TestVerseType(TestCase): # THEN: The result should be VerseType.Chorus self.assertEqual(result, VerseType.Chorus, 'The result should be VerseType.Chorus, but was "%s"' % result) - def from_tag_with_invalid_default_test(self): + def from_tag_with_invalid_intdefault_test(self): """ - Test that the from_tag() method returns a sane default when passed an invalid tag and an invalid default. + Test that the from_tag() method returns a sane default when passed an invalid tag and an invalid int default. """ # GIVEN: A mocked out translate() function that just returns what it was given with patch('openlp.plugins.songs.lib.translate') as mocked_translate: @@ -458,3 +458,31 @@ class TestVerseType(TestCase): # THEN: The result should be VerseType.Other self.assertEqual(result, VerseType.Other, 'The result should be VerseType.Other, but was "%s"' % result) + + def from_tag_with_invalid_default_test(self): + """ + Test that the from_tag() method returns a sane default when passed an invalid tag and an invalid default. + """ + # GIVEN: A mocked out translate() function that just returns what it was given + with patch('openlp.plugins.songs.lib.translate') as mocked_translate: + mocked_translate.side_effect = lambda x, y: y + + # WHEN: We run the from_tag() method with an invalid verse type, we get the specified default back + result = VerseType.from_tag('@', 'asdf') + + # THEN: The result should be VerseType.Other + self.assertEqual(result, VerseType.Other, 'The result should be VerseType.Other, but was "%s"' % result) + + def from_tag_with_none_default_test(self): + """ + Test that the from_tag() method returns a sane default when passed an invalid tag and None as default. + """ + # GIVEN: A mocked out translate() function that just returns what it was given + with patch('openlp.plugins.songs.lib.translate') as mocked_translate: + mocked_translate.side_effect = lambda x, y: y + + # WHEN: We run the from_tag() method with an invalid verse type, we get the specified default back + result = VerseType.from_tag('m', None) + + # THEN: The result should be VerseType.Other + self.assertIsNone(result, 'The result should be None, but was "%s"' % result) From c2d1589dcb2787891faed11d2b77f8be08a62762 Mon Sep 17 00:00:00 2001 From: Stefan Strasser Date: Sat, 26 Apr 2014 07:07:08 +0200 Subject: [PATCH 066/212] fixed a pep-issue in songs-lib and corrected a comment in a test --- openlp/plugins/songs/lib/__init__.py | 4 ++-- tests/functional/openlp_plugins/songs/test_lib.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/openlp/plugins/songs/lib/__init__.py b/openlp/plugins/songs/lib/__init__.py index 343bc710b..a0a8e94bd 100644 --- a/openlp/plugins/songs/lib/__init__.py +++ b/openlp/plugins/songs/lib/__init__.py @@ -213,7 +213,7 @@ class VerseType(object): for num, tag in enumerate(VerseType.tags): if verse_tag == tag: return num - if default in range(0,len(VerseType.names)) or default is None: + if default in range(0, len(VerseType.names)) or default is None: return default else: return VerseType.Other @@ -231,7 +231,7 @@ class VerseType(object): for num, tag in enumerate(VerseType.translated_tags): if verse_tag == tag: return num - if default in range(0,len(VerseType.names)) or default is None: + if default in range(0, len(VerseType.names)) or default is None: return default else: return VerseType.Other diff --git a/tests/functional/openlp_plugins/songs/test_lib.py b/tests/functional/openlp_plugins/songs/test_lib.py index b06a18a7c..140126f26 100644 --- a/tests/functional/openlp_plugins/songs/test_lib.py +++ b/tests/functional/openlp_plugins/songs/test_lib.py @@ -484,5 +484,5 @@ class TestVerseType(TestCase): # WHEN: We run the from_tag() method with an invalid verse type, we get the specified default back result = VerseType.from_tag('m', None) - # THEN: The result should be VerseType.Other + # THEN: The result should be None self.assertIsNone(result, 'The result should be None, but was "%s"' % result) From aa899bfde0868e8a241c358398b12110b68890d2 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sat, 26 Apr 2014 10:53:36 +0200 Subject: [PATCH 067/212] fixes --- openlp/core/utils/actions.py | 2 +- .../openlp_core_utils/test_actions.py | 44 ++++++++++++++----- 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/openlp/core/utils/actions.py b/openlp/core/utils/actions.py index 98eaced5b..66da3f256 100644 --- a/openlp/core/utils/actions.py +++ b/openlp/core/utils/actions.py @@ -70,7 +70,7 @@ class CategoryActionList(object): Implement the __getitem__() method to make this class a dictionary type """ try: - return self.actions[key] + return self.actions[key][1] except: raise KeyError('Action "%s" does not exist.' % key) diff --git a/tests/functional/openlp_core_utils/test_actions.py b/tests/functional/openlp_core_utils/test_actions.py index d79617037..8cc6df5da 100644 --- a/tests/functional/openlp_core_utils/test_actions.py +++ b/tests/functional/openlp_core_utils/test_actions.py @@ -36,7 +36,7 @@ from PyQt4 import QtGui, QtCore from openlp.core.common import Settings from openlp.core.utils import ActionList from openlp.core.utils.actions import CategoryActionList -from tests.functional import MagicMock, patch +from tests.functional import MagicMock from tests.helpers.testmixin import TestMixin @@ -57,6 +57,23 @@ class TestCategoryActionList(TestCase): """ del self.list + def get_test(self): + """ + Test the __getitem__() method + """ + # GIVEN: The list. + self.list.append(self.action1) + + # WHEN: Add an action. + returned_action = self.list[0] + print(returned_action) + + # THEN: Check if the correct action was returned. + self.assertEqual(self.action1, returned_action) + + # THEN: Test if an exception is raised when trying to access a non existing item. + self.assertRaises(KeyError, self.list.__getitem__, 1) + def contains_test(self): """ Test the __contains__() method @@ -64,7 +81,8 @@ class TestCategoryActionList(TestCase): # GIVEN: The list. # WHEN: Add an action self.list.append(self.action1) - # THEN: + + # THEN: The actions should (not) be in the list. self.assertTrue(self.action1 in self.list) self.assertFalse(self.action2 in self.list) @@ -73,7 +91,7 @@ class TestCategoryActionList(TestCase): Test the __len__ method """ # GIVEN: The list. - # WHEN: + # WHEN: Do nothing. # THEN: Check the length. self.assertEqual(len(self.list), 0, "The length should be 0.") @@ -96,23 +114,27 @@ class TestCategoryActionList(TestCase): # THEN: Check if the actions are in the list and check if they have the correct weights. self.assertTrue(self.action1 in self.list) self.assertTrue(self.action2 in self.list) - self.assertEqual(self.list[0], (0, self.action1)) - self.assertEqual(self.list[1], (1, self.action2)) + self.assertEqual(self.list[0], self.action1) + self.assertEqual(self.list[1], self.action2) def add_test(self): """ Test the add() method """ - # GIVEN: The list. - # WHEN: Append actions. - self.list.add(self.action1, 42) - self.list.add(self.action2, 99) + # GIVEN: The list and weights. + action1_weight = 42 + action2_weight = 41 + + # WHEN: Add actions and their weights. + self.list.add(self.action1, action1_weight) + self.list.add(self.action2, action2_weight) # THEN: Check if they were added and have the specified weights. self.assertTrue(self.action1 in self.list) self.assertTrue(self.action2 in self.list) - self.assertEqual(self.list[0], (42, self.action1)) - self.assertEqual(self.list[1], (99, self.action2)) + # Now check if action1 is second and action2 is first (due to their weights). + self.assertEqual(self.list[0], self.action2) + self.assertEqual(self.list[1], self.action1) def remove_test(self): """ From 6bdadff334e6ac67571f2c80d8a4c86261928414 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sat, 26 Apr 2014 11:01:12 +0200 Subject: [PATCH 068/212] raise exception when item not in list --- openlp/core/utils/actions.py | 16 ++++------------ .../functional/openlp_core_utils/test_actions.py | 7 ++++++- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/openlp/core/utils/actions.py b/openlp/core/utils/actions.py index 66da3f256..d37a17ffe 100644 --- a/openlp/core/utils/actions.py +++ b/openlp/core/utils/actions.py @@ -69,17 +69,14 @@ class CategoryActionList(object): """ Implement the __getitem__() method to make this class a dictionary type """ - try: - return self.actions[key][1] - except: - raise KeyError('Action "%s" does not exist.' % key) + return self.actions[key][1] - def __contains__(self, item): + def __contains__(self, key): """ Implement the __contains__() method to make this class a dictionary type """ for weight, action in self.actions: - if action == item: + if action == key: return True return False @@ -106,12 +103,6 @@ class CategoryActionList(object): self.index += 1 return self.actions[self.index - 1][1] - def has_key(self, key): - """ - Implement the has_key() method to make this class a dictionary type - """ - return key in self - def append(self, action): """ Append an action @@ -136,6 +127,7 @@ class CategoryActionList(object): if action[1] == remove_action: self.actions.remove(action) return + raise ValueError('Action "%s" does not exist.' % remove_action) class CategoryList(object): diff --git a/tests/functional/openlp_core_utils/test_actions.py b/tests/functional/openlp_core_utils/test_actions.py index 8cc6df5da..890bde2ba 100644 --- a/tests/functional/openlp_core_utils/test_actions.py +++ b/tests/functional/openlp_core_utils/test_actions.py @@ -72,7 +72,7 @@ class TestCategoryActionList(TestCase): self.assertEqual(self.action1, returned_action) # THEN: Test if an exception is raised when trying to access a non existing item. - self.assertRaises(KeyError, self.list.__getitem__, 1) + self.assertRaises(IndexError, self.list.__getitem__, 1) def contains_test(self): """ @@ -141,12 +141,17 @@ class TestCategoryActionList(TestCase): Test the remove() method """ # GIVEN: The list + self.list.append(self.action1) + # WHEN: Delete an item from the list. self.list.remove(self.action1) # THEN: Now the element should not be in the list anymore. self.assertFalse(self.action1 in self.list) + # THEN: Check if an exception is raised when trying to remove a not present action. + self.assertRaises(ValueError, self.list.remove, self.action2) + class TestActionList(TestCase, TestMixin): """ From 533710fc5b389aefe804559eca1e6e95b8fec83f Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sat, 26 Apr 2014 11:02:04 +0200 Subject: [PATCH 069/212] blank line --- tests/functional/openlp_core_utils/test_actions.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/functional/openlp_core_utils/test_actions.py b/tests/functional/openlp_core_utils/test_actions.py index 890bde2ba..1d815a94b 100644 --- a/tests/functional/openlp_core_utils/test_actions.py +++ b/tests/functional/openlp_core_utils/test_actions.py @@ -264,4 +264,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.' - From 53c584ede4a4ccddd5f226a5150c571caadc0d62 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sat, 26 Apr 2014 11:26:20 +0200 Subject: [PATCH 070/212] removed print --- tests/functional/openlp_core_utils/test_actions.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/functional/openlp_core_utils/test_actions.py b/tests/functional/openlp_core_utils/test_actions.py index 1d815a94b..c608ce0b1 100644 --- a/tests/functional/openlp_core_utils/test_actions.py +++ b/tests/functional/openlp_core_utils/test_actions.py @@ -66,7 +66,6 @@ class TestCategoryActionList(TestCase): # WHEN: Add an action. returned_action = self.list[0] - print(returned_action) # THEN: Check if the correct action was returned. self.assertEqual(self.action1, returned_action) From 66c1f7c5da14b69e3069ec7f436d0348ba354150 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sat, 26 Apr 2014 11:35:50 +0200 Subject: [PATCH 071/212] renamed variable and parameter --- openlp/core/utils/actions.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/openlp/core/utils/actions.py b/openlp/core/utils/actions.py index d37a17ffe..3d679ac02 100644 --- a/openlp/core/utils/actions.py +++ b/openlp/core/utils/actions.py @@ -119,15 +119,15 @@ class CategoryActionList(object): self.actions.append((weight, action)) self.actions.sort(key=lambda act: act[0]) - def remove(self, remove_action): + def remove(self, action): """ Remove an action """ - for action in self.actions: - if action[1] == remove_action: - self.actions.remove(action) + for item in self.actions: + if item[1] == action: + self.actions.remove(item) return - raise ValueError('Action "%s" does not exist.' % remove_action) + raise ValueError('Action "%s" does not exist.' % action) class CategoryList(object): From 911864a4424acb62fc3e7b2384b7a2f59c1e396a Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sat, 26 Apr 2014 19:31:59 +0200 Subject: [PATCH 072/212] removed useless code --- openlp/core/utils/actions.py | 6 ----- .../openlp_core_utils/test_actions.py | 24 ++++--------------- 2 files changed, 4 insertions(+), 26 deletions(-) diff --git a/openlp/core/utils/actions.py b/openlp/core/utils/actions.py index 3d679ac02..256d36a1e 100644 --- a/openlp/core/utils/actions.py +++ b/openlp/core/utils/actions.py @@ -65,12 +65,6 @@ class CategoryActionList(object): self.index = 0 self.actions = [] - def __getitem__(self, key): - """ - Implement the __getitem__() method to make this class a dictionary type - """ - return self.actions[key][1] - def __contains__(self, key): """ Implement the __contains__() method to make this class a dictionary type diff --git a/tests/functional/openlp_core_utils/test_actions.py b/tests/functional/openlp_core_utils/test_actions.py index c608ce0b1..4958c4677 100644 --- a/tests/functional/openlp_core_utils/test_actions.py +++ b/tests/functional/openlp_core_utils/test_actions.py @@ -57,22 +57,6 @@ class TestCategoryActionList(TestCase): """ del self.list - def get_test(self): - """ - Test the __getitem__() method - """ - # GIVEN: The list. - self.list.append(self.action1) - - # WHEN: Add an action. - returned_action = self.list[0] - - # THEN: Check if the correct action was returned. - self.assertEqual(self.action1, returned_action) - - # THEN: Test if an exception is raised when trying to access a non existing item. - self.assertRaises(IndexError, self.list.__getitem__, 1) - def contains_test(self): """ Test the __contains__() method @@ -113,8 +97,8 @@ class TestCategoryActionList(TestCase): # THEN: Check if the actions are in the list and check if they have the correct weights. self.assertTrue(self.action1 in self.list) self.assertTrue(self.action2 in self.list) - self.assertEqual(self.list[0], self.action1) - self.assertEqual(self.list[1], self.action2) + self.assertEqual(self.list.actions[0], (0, self.action1)) + self.assertEqual(self.list.actions[1], (1, self.action2)) def add_test(self): """ @@ -132,8 +116,8 @@ class TestCategoryActionList(TestCase): self.assertTrue(self.action1 in self.list) self.assertTrue(self.action2 in self.list) # Now check if action1 is second and action2 is first (due to their weights). - self.assertEqual(self.list[0], self.action2) - self.assertEqual(self.list[1], self.action1) + self.assertEqual(self.list.actions[0], (41, self.action2)) + self.assertEqual(self.list.actions[1], (42, self.action1)) def remove_test(self): """ From 3f1e7f1912ce54c475f4e18353177e1e046a9160 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sat, 26 Apr 2014 19:36:47 +0200 Subject: [PATCH 073/212] fixes --- openlp/core/utils/actions.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/openlp/core/utils/actions.py b/openlp/core/utils/actions.py index 256d36a1e..924a5a2f9 100644 --- a/openlp/core/utils/actions.py +++ b/openlp/core/utils/actions.py @@ -170,9 +170,9 @@ class CategoryList(object): self.index += 1 return self.categories[self.index - 1] - def has_key(self, key): + def __contains__(self, key): """ - Implement the has_key() method to make this class like a dictionary + Implement the __contains__() method to make this class like a dictionary """ for category in self.categories: if category.name == key: @@ -186,10 +186,7 @@ class CategoryList(object): weight = 0 if self.categories: weight = self.categories[-1].weight + 1 - if actions: - self.add(name, weight, actions) - else: - self.add(name, weight) + self.add(name, weight, actions) def add(self, name, weight=0, actions=None): """ From 88a3ac62160bac2a23bb87f48ef07768b671e6ca Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sat, 26 Apr 2014 19:40:26 +0200 Subject: [PATCH 074/212] match list behaviour --- openlp/core/utils/actions.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openlp/core/utils/actions.py b/openlp/core/utils/actions.py index 924a5a2f9..d81e16b2e 100644 --- a/openlp/core/utils/actions.py +++ b/openlp/core/utils/actions.py @@ -209,6 +209,8 @@ class CategoryList(object): for category in self.categories: if category.name == name: self.categories.remove(category) + return + raise ValueError('Category "%s" does not exist.' % name) class ActionList(object): From 48fe8c5e37b8846e502347f74ac834b8993af821 Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Sat, 26 Apr 2014 21:47:11 +0200 Subject: [PATCH 075/212] Restructured the tests a bit. --- tests/functional/openlp_plugins/songs/test_ewimport.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/functional/openlp_plugins/songs/test_ewimport.py b/tests/functional/openlp_plugins/songs/test_ewimport.py index b58fcb249..244775bd5 100644 --- a/tests/functional/openlp_plugins/songs/test_ewimport.py +++ b/tests/functional/openlp_plugins/songs/test_ewimport.py @@ -400,10 +400,11 @@ class TestEasyWorshipSongImport(TestCase): # WHEN: Importing each file importer.import_source = os.path.join(TEST_PATH, 'Songs.DB') + import_result = importer.do_import() # 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.assertIsNone(import_result, 'do_import should return None when it has completed') for song_data in SONG_TEST_DATA: title = song_data['title'] author_calls = song_data['authors'] @@ -455,11 +456,12 @@ class TestEasyWorshipSongImport(TestCase): # WHEN: Importing ews file importer.import_source = os.path.join(TEST_PATH, 'test1.ews') + import_result = importer.do_import() # THEN: do_import should return none, the song data should be as expected, and finish should have been # called. title = EWS_SONG_TEST_DATA['title'] - self.assertIsNone(importer.do_import(), 'do_import should return None when it has completed') + self.assertIsNone(import_result, 'do_import should return None when it has completed') self.assertIn(title, importer._title_assignment_list, 'title for should be "%s"' % title) mocked_add_author.assert_any_call(EWS_SONG_TEST_DATA['authors'][0]) for verse_text, verse_tag in EWS_SONG_TEST_DATA['verses']: From 518d391e458ab22ad7e7c37588702d9bbe50fd2d Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sun, 27 Apr 2014 16:38:48 +0100 Subject: [PATCH 076/212] Fix trunk --- openlp/plugins/songs/lib/upgrade.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/plugins/songs/lib/upgrade.py b/openlp/plugins/songs/lib/upgrade.py index adb7d8af5..546343900 100644 --- a/openlp/plugins/songs/lib/upgrade.py +++ b/openlp/plugins/songs/lib/upgrade.py @@ -39,7 +39,7 @@ from sqlalchemy.sql.expression import func, false, null, text from openlp.core.lib.db import get_upgrade_op log = logging.getLogger(__name__) -__version__ = 3 +__version__ = 4 def upgrade_1(session, metadata): From 282ed7e1a6440ee4dba4804859be51e52d87fa85 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Tue, 29 Apr 2014 13:04:19 +0200 Subject: [PATCH 077/212] fixed u' --- openlp/core/lib/renderer.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/openlp/core/lib/renderer.py b/openlp/core/lib/renderer.py index e24381558..71a1f6058 100644 --- a/openlp/core/lib/renderer.py +++ b/openlp/core/lib/renderer.py @@ -249,8 +249,8 @@ class Renderer(OpenLPMixin, RegistryMixin, RegistryProperties): pages = [] if '[---]' in text: # Remove two or more option slide breaks next to each other (causing infinite loop). - while u'\n[---]\n[---]\n' in text: - text = text.replace(u'\n[---]\n[---]\n', u'\n[---]\n') + while '\n[---]\n[---]\n' in text: + text = text.replace('\n[---]\n[---]\n', '\n[---]\n') while True: slides = text.split('\n[---]\n', 2) # If there are (at least) two occurrences of [---] we use the first two slides (and neglect the last @@ -395,7 +395,7 @@ class Renderer(OpenLPMixin, RegistryMixin, RegistryProperties): off when displayed. :param lines: The text to be fitted on the slide split into lines. - :param line_end: The text added after each line. Either ``u' '`` or ``u'
``. + :param line_end: The text added after each line. Either ``' '`` or ``'
``. """ formatted = [] previous_html = '' @@ -419,7 +419,7 @@ class Renderer(OpenLPMixin, RegistryMixin, RegistryProperties): processed word by word. This is sometimes need for **bible** verses. :param lines: The text to be fitted on the slide split into lines. - :param line_end: The text added after each line. Either ``u' '`` or ``u'
``. This is needed for **bibles**. + :param line_end: The text added after each line. Either ``' '`` or ``'
``. This is needed for **bibles**. """ formatted = [] previous_html = '' @@ -456,7 +456,7 @@ class Renderer(OpenLPMixin, RegistryMixin, RegistryProperties): """ Tests the given text for not closed formatting tags and returns a tuple consisting of three unicode strings:: - (u'{st}{r}Text text text{/r}{/st}', u'{st}{r}', u'') + ('{st}{r}Text text text{/r}{/st}', '{st}{r}', '') The first unicode string is the text, with correct closing tags. The second unicode string are OpenLP's opening formatting tags and the third unicode string the html opening formatting tags. @@ -503,8 +503,8 @@ class Renderer(OpenLPMixin, RegistryMixin, RegistryProperties): The text contains html. :param raw_list: The elements which do not fit on a slide and needs to be processed using the binary chop. The elements can contain formatting tags. - :param separator: The separator for the elements. For lines this is ``u'
'`` and for words this is ``u' '``. - :param line_end: The text added after each "element line". Either ``u' '`` or ``u'
``. This is needed for + :param separator: The separator for the elements. For lines this is ``'
'`` and for words this is ``' '``. + :param line_end: The text added after each "element line". Either ``' '`` or ``'
``. This is needed for bibles. """ smallest_index = 0 From 4022cff5e58a2e634b23d22e639b529a2982e62a Mon Sep 17 00:00:00 2001 From: Stewart Becker Date: Wed, 30 Apr 2014 21:39:40 +0100 Subject: [PATCH 078/212] Fix some issues I ran into when importing our OpenSong 'database' --- openlp/plugins/songs/lib/__init__.py | 2 +- openlp/plugins/songs/lib/opensongimport.py | 14 +++++-- openlp/plugins/songs/lib/songimport.py | 6 +-- .../songs/test_opensongimport.py | 2 + tests/helpers/songfileimport.py | 5 ++- .../opensongsongs/Beautiful Garden Of Prayer | 17 +++++---- .../opensongsongs/One, Two, Three, Four, Five | 37 +++++++++++++++++++ .../One, Two, Three, Four, Five.json | 17 +++++++++ 8 files changed, 84 insertions(+), 16 deletions(-) create mode 100644 tests/resources/opensongsongs/One, Two, Three, Four, Five create mode 100644 tests/resources/opensongsongs/One, Two, Three, Four, Five.json diff --git a/openlp/plugins/songs/lib/__init__.py b/openlp/plugins/songs/lib/__init__.py index dc198d4b7..c15ce0b73 100644 --- a/openlp/plugins/songs/lib/__init__.py +++ b/openlp/plugins/songs/lib/__init__.py @@ -278,7 +278,7 @@ class VerseType(object): if verse_index is None: verse_index = VerseType.from_string(verse_name, default) elif len(verse_name) == 1: - verse_index = VerseType.from_translated_tag(verse_name, None) + verse_index = VerseType.from_translated_tag(verse_name, default) if verse_index is None: verse_index = VerseType.from_tag(verse_name, default) else: diff --git a/openlp/plugins/songs/lib/opensongimport.py b/openlp/plugins/songs/lib/opensongimport.py index b418e1d37..1563f23d1 100644 --- a/openlp/plugins/songs/lib/opensongimport.py +++ b/openlp/plugins/songs/lib/opensongimport.py @@ -162,8 +162,7 @@ class OpenSongImport(SongImport): if attr in fields: ustring = str(root.__getattr__(attr)) if isinstance(fn_or_string, str): - match = re.match('(\D*)(\d+.*)', ustring) - if match: + if attr in ['ccli']: setattr(self, fn_or_string, int(ustring)) else: setattr(self, fn_or_string, ustring) @@ -261,7 +260,14 @@ class OpenSongImport(SongImport): verse_def = '%s%s' % (verse_tag, verse_num[:length]) verse_joints[verse_def] = '%s\n[---]\n%s' % (verse_joints[verse_def], lines) \ if verse_def in verse_joints else lines - for verse_def, lines in verse_joints.items(): + # Parsing the dictionary produces the elements in a non-intuitive order. While it "works", it's not a + # natural layout should the user come back to edit the song. Instead we sort by the verse type, so that we + # get all the verses in order (v1, v2, ...), then the chorus(es), bridge(s), pre-chorus(es) etc. We use a + # tuple for the key, since tuples naturally sort in this manner. + verse_defs = sorted(verse_joints.keys(), + key=lambda verse_def: (VerseType.from_tag(verse_def[0]), int(verse_def[1:]))) + for verse_def in verse_defs: + lines = verse_joints[verse_def] self.add_verse(lines, verse_def) if not self.verses: self.add_verse('') @@ -282,6 +288,8 @@ class OpenSongImport(SongImport): # Assume it's no.1 if there are no digits verse_tag = verse_def verse_num = '1' + verse_index = VerseType.from_loose_input(verse_tag) + verse_tag = VerseType.tags[verse_index] verse_def = '%s%s' % (verse_tag, verse_num) if verse_num in verses.get(verse_tag, {}): self.verse_order_list.append(verse_def) diff --git a/openlp/plugins/songs/lib/songimport.py b/openlp/plugins/songs/lib/songimport.py index 93e0b508d..53310d258 100644 --- a/openlp/plugins/songs/lib/songimport.py +++ b/openlp/plugins/songs/lib/songimport.py @@ -202,7 +202,7 @@ class SongImport(QtCore.QObject): # Book name:'NRH' and # Song number: 231 book_and_number = book_and_number.strip() - if book_and_number == '': + if not book_and_number: return book_and_number = book_and_number.replace('No.', ' ') if ' ' in book_and_number: @@ -233,7 +233,7 @@ class SongImport(QtCore.QObject): """ if self.comments.find(comment) >= 0: return - if comment != '': + if comment: self.comments += comment.strip() + '\n' def add_copyright(self, copyright): @@ -242,7 +242,7 @@ class SongImport(QtCore.QObject): """ if self.copyright.find(copyright) >= 0: return - if self.copyright != '': + if self.copyright: self.copyright += ' ' self.copyright += copyright diff --git a/tests/functional/openlp_plugins/songs/test_opensongimport.py b/tests/functional/openlp_plugins/songs/test_opensongimport.py index a63d09068..70d3b342a 100644 --- a/tests/functional/openlp_plugins/songs/test_opensongimport.py +++ b/tests/functional/openlp_plugins/songs/test_opensongimport.py @@ -56,6 +56,8 @@ class TestOpenSongFileImport(SongImportTestHelper): self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace.json'))) self.file_import(os.path.join(TEST_PATH, 'Beautiful Garden Of Prayer'), self.load_external_result_data(os.path.join(TEST_PATH, 'Beautiful Garden Of Prayer.json'))) + self.file_import(os.path.join(TEST_PATH, 'One, Two, Three, Four, Five'), + self.load_external_result_data(os.path.join(TEST_PATH, 'One, Two, Three, Four, Five.json'))) class TestOpenSongImport(TestCase): diff --git a/tests/helpers/songfileimport.py b/tests/helpers/songfileimport.py index 009a2c92a..36beef6e5 100644 --- a/tests/helpers/songfileimport.py +++ b/tests/helpers/songfileimport.py @@ -33,7 +33,7 @@ song files from third party applications. import json from unittest import TestCase -from tests.functional import patch, MagicMock +from tests.functional import patch, MagicMock, call class SongImportTestHelper(TestCase): @@ -118,8 +118,11 @@ class SongImportTestHelper(TestCase): if ccli_number: self.assertEqual(importer.ccli_number, ccli_number, 'ccli_number for %s should be %s' % (source_file_name, ccli_number)) + expected_calls = [] for verse_text, verse_tag in add_verse_calls: self.mocked_add_verse.assert_any_call(verse_text, verse_tag) + expected_calls.append(call(verse_text, verse_tag)) + self.mocked_add_verse.assert_has_calls(expected_calls, any_order=False) if topics: self.assertEqual(importer.topics, topics, 'topics for %s should be %s' % (source_file_name, topics)) if comments: diff --git a/tests/resources/opensongsongs/Beautiful Garden Of Prayer b/tests/resources/opensongsongs/Beautiful Garden Of Prayer index 33d4943bd..29cd26cc8 100644 --- a/tests/resources/opensongsongs/Beautiful Garden Of Prayer +++ b/tests/resources/opensongsongs/Beautiful Garden Of Prayer @@ -16,6 +16,7 @@ ;Test breaks and newlines ;A single | on the end of a line adds an extra \n ;Blank lines are ignored, even with a space prefix +;We also check that the chorus is added after the verses, despite the order in the file [V1] There's a garden where Jesus is waiting, @@ -23,6 +24,14 @@ For it glows with the light of His presence,| 'Tis the beautiful garden of prayer. +;A double || on a line adds a new slide +[C] + O the beautiful garden, the garden of prayer, + O the beautiful garden of prayer. + There my Savior awaits, and He opens the gates + || + To the beautiful garden of prayer. + ;A double || on the end of a line adds a new slide [V2] There's a garden where Jesus is waiting, @@ -37,14 +46,6 @@ Just to bow and receive a new blessing, | In the beautiful garden of prayer. - -;A double || on a line adds a new slide -[C] - O the beautiful garden, the garden of prayer, - O the beautiful garden of prayer. - There my Savior awaits, and He opens the gates - || - To the beautiful garden of prayer.
DS0 diff --git a/tests/resources/opensongsongs/One, Two, Three, Four, Five b/tests/resources/opensongsongs/One, Two, Three, Four, Five new file mode 100644 index 000000000..4cd0d894f --- /dev/null +++ b/tests/resources/opensongsongs/One, Two, Three, Four, Five @@ -0,0 +1,37 @@ + + + 12345 + Traditional + Public Domain + T + + + + + + + + +;Test [T]ag element - should be turned into [o]ther +;And lines beginning with numbers +;And a title that contains only numeric characters +;That isdiffernt to the filename +;And most elements are empty +[T] + 1, 2, 3, 4, 5, + Once I caught a fish alive. + 6, 7, 8, 9, 10, + Then I let it go again. + + Why did you let it go? + Because it bit my finger so. + Which finger did it bite? + This little finger on my right. + + + + + + + + \ No newline at end of file diff --git a/tests/resources/opensongsongs/One, Two, Three, Four, Five.json b/tests/resources/opensongsongs/One, Two, Three, Four, Five.json new file mode 100644 index 000000000..30fe71c64 --- /dev/null +++ b/tests/resources/opensongsongs/One, Two, Three, Four, Five.json @@ -0,0 +1,17 @@ +{ + "authors": [ + "Traditional" + ], + "comments": "", + "copyright": "Public Domain ", + "title": "12345", + "topics": [ + ], + "verse_order_list": ["o1"], + "verses": [ + [ + "1, 2, 3, 4, 5,\nOnce I caught a fish alive.\n6, 7, 8, 9, 10,\nThen I let it go again.\nWhy did you let it go?\nBecause it bit my finger so.\nWhich finger did it bite?\nThis little finger on my right.", + "o1" + ] + ] +} \ No newline at end of file From eb64c136d0fb04a5082017b7d28bb91b95599a0d Mon Sep 17 00:00:00 2001 From: Stewart Becker Date: Wed, 30 Apr 2014 21:50:57 +0100 Subject: [PATCH 079/212] Deal with empty ccli tag in OpenSong imports --- openlp/plugins/songs/lib/opensongimport.py | 5 ++++- tests/resources/opensongsongs/One, Two, Three, Four, Five | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/openlp/plugins/songs/lib/opensongimport.py b/openlp/plugins/songs/lib/opensongimport.py index 1563f23d1..3d9733dd8 100644 --- a/openlp/plugins/songs/lib/opensongimport.py +++ b/openlp/plugins/songs/lib/opensongimport.py @@ -163,7 +163,10 @@ class OpenSongImport(SongImport): ustring = str(root.__getattr__(attr)) if isinstance(fn_or_string, str): if attr in ['ccli']: - setattr(self, fn_or_string, int(ustring)) + if ustring: + setattr(self, fn_or_string, int(ustring)) + else: + setattr(self, fn_or_string, None) else: setattr(self, fn_or_string, ustring) else: diff --git a/tests/resources/opensongsongs/One, Two, Three, Four, Five b/tests/resources/opensongsongs/One, Two, Three, Four, Five index 4cd0d894f..cc6bc107f 100644 --- a/tests/resources/opensongsongs/One, Two, Three, Four, Five +++ b/tests/resources/opensongsongs/One, Two, Three, Four, Five @@ -5,6 +5,7 @@ Public Domain T + From bdf3109aaa9e02897568244297a70bb0e4f63af5 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Thu, 1 May 2014 10:46:45 +0200 Subject: [PATCH 080/212] Fix SongShowPlus Import for certain cases Fixes: https://launchpad.net/bugs/1310623 --- .../plugins/songs/lib/songshowplusimport.py | 7 +++-- .../songs/test_songshowplusimport.py | 2 ++ .../a mighty fortress is our god.json | 29 ++++++++++++++++++ .../a mighty fortress is our god.sbsong | Bin 0 -> 1342 bytes 4 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 tests/resources/songshowplussongs/a mighty fortress is our god.json create mode 100644 tests/resources/songshowplussongs/a mighty fortress is our god.sbsong diff --git a/openlp/plugins/songs/lib/songshowplusimport.py b/openlp/plugins/songs/lib/songshowplusimport.py index 0a8dc4650..c1c6d04cb 100644 --- a/openlp/plugins/songs/lib/songshowplusimport.py +++ b/openlp/plugins/songs/lib/songshowplusimport.py @@ -121,7 +121,7 @@ class SongShowPlusImport(SongImport): null, verse_no, = struct.unpack("BB", song_data.read(2)) elif block_key == CUSTOM_VERSE: null, verse_name_length, = struct.unpack("BB", song_data.read(2)) - verse_name = song_data.read(verse_name_length) + verse_name = self.decode(song_data.read(verse_name_length)) length_descriptor_size, = struct.unpack("B", song_data.read(1)) log.debug(length_descriptor_size) # Detect if/how long the length descriptor is @@ -147,7 +147,10 @@ class SongShowPlusImport(SongImport): elif block_key == COPYRIGHT: self.add_copyright(self.decode(data)) elif block_key == CCLI_NO: - self.ccli_number = int(data) + try: + self.ccli_number = int(data) + except ValueError: + continue elif block_key == VERSE: self.add_verse(self.decode(data), "%s%s" % (VerseType.tags[VerseType.Verse], verse_no)) elif block_key == CHORUS: diff --git a/tests/functional/openlp_plugins/songs/test_songshowplusimport.py b/tests/functional/openlp_plugins/songs/test_songshowplusimport.py index 7292bb2b0..08400fdc5 100644 --- a/tests/functional/openlp_plugins/songs/test_songshowplusimport.py +++ b/tests/functional/openlp_plugins/songs/test_songshowplusimport.py @@ -57,6 +57,8 @@ class TestSongShowPlusFileImport(SongImportTestHelper): self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace.json'))) self.file_import(os.path.join(TEST_PATH, 'Beautiful Garden Of Prayer.sbsong'), self.load_external_result_data(os.path.join(TEST_PATH, 'Beautiful Garden Of Prayer.json'))) + self.file_import(os.path.join(TEST_PATH, 'a mighty fortress is our god.sbsong'), + self.load_external_result_data(os.path.join(TEST_PATH, 'a mighty fortress is our god.json'))) class TestSongShowPlusImport(TestCase): diff --git a/tests/resources/songshowplussongs/a mighty fortress is our god.json b/tests/resources/songshowplussongs/a mighty fortress is our god.json new file mode 100644 index 000000000..7e3d400b4 --- /dev/null +++ b/tests/resources/songshowplussongs/a mighty fortress is our god.json @@ -0,0 +1,29 @@ +{ + "authors": [ + "Martin Luther" + ], + "comments": "", + "copyright": "Public Domain", + "song_number": 0, + "title": "A Mighty Fortress is our God", + "topics": [], + "verse_order_list": [], + "verses": [ + [ + "A mighty fortress is our God, a bulwark never failing;\r\nOur helper He, amid the flood of mortal ills prevailing:\r\nFor still our ancient foe doth seek to work us woe;\r\nHis craft and power are great, and, armed with cruel hate,\r\nOn earth is not his equal.\r\n", + "v1" + ], + [ + "Did we in our own strength confide, our striving would be losing;\r\nWere not the right Man on our side, the Man of God’s own choosing:\r\nDost ask who that may be? Christ Jesus, it is He;\r\nLord Sabaoth, His Name, from age to age the same,\r\nAnd He must win the battle.\r\n", + "v2" + ], + [ + "And though this world, with devils filled, should threaten to undo us,\r\nWe will not fear, for God hath willed His truth to triumph through us:\r\nThe Prince of Darkness grim, we tremble not for him;\r\nHis rage we can endure, for lo, his doom is sure,\r\nOne little word shall fell him.\r\n", + "v3" + ], + [ + "That word above all earthly powers, no thanks to them, abideth;\r\nThe Spirit and the gifts are ours through Him Who with us sideth:\r\nLet goods and kindred go, this mortal life also;\r\nThe body they may kill: God’s truth abideth still,\r\nHis kingdom is forever.\r\n", + "v4" + ] + ] +} \ No newline at end of file diff --git a/tests/resources/songshowplussongs/a mighty fortress is our god.sbsong b/tests/resources/songshowplussongs/a mighty fortress is our god.sbsong new file mode 100644 index 0000000000000000000000000000000000000000..b66d52b2b73ef827e1427a90d8b54dfae1808508 GIT binary patch literal 1342 zcmZXU%We}f6o$j4Ekq3<=n5fp7F@cB5H01RvOrZM2&gTUTG6#9J~OuBv4ibNlpU|Z z>+vG+pP8tN#9}-iU(V(G&zTp!UT;&L+ppj6eYkAg%Eeze!JJ+-4nDw!{x%Go%V%ppMdqi~?Tls%%LsC>UZlU6oJkL2&sjRcU z`Z(x+NBD-_42xsN!P;s<;8eO0G=-9C*i(+IbBSh==iSZ2K_3t(WvsUdl%H7knaaSV zLpGF{r<4ON0=zZ}Z-%=uh)tp@GbyiW78dZUB2yJvWw`emL#$cT!otWTHtfi#%tM6g ziNV$gbssVry!mIN+^a!fh&)~+vcI#9uMv`2FhLDbDJ(n!j_fP39DHdtGL{{z+BtY2 z$i`_Zm|RF5pDRXm;!U7LK}@F#_BxZPH6dH=VkwpI=htsk1$<3RXsv+NC?sT@rnE2t zmQ_{;| zWGieWXN~L>!Y>&EY_51cTH|%Fvw^Qm(XPFqiZExAOKnlQWmnA*(YF%&DRPw=BXU)s zP_jX&lHkS6Mkp;EAQDx!%&F}H)FLjec#LgLS16ZO3p9|_S~g&G!kH>`C+@())OsDk zwnCn*gsX;iwv_NvSUD|%UW&VATY6K7b$DH^ohGXwyQT&rQ47{9-GKv4`F8-#OEjDO Nw;tl@?XG{w!aqH)mM#DQ literal 0 HcmV?d00001 From 41c950b819f05ce06eae4ace47e317f813dab97b Mon Sep 17 00:00:00 2001 From: Jonathan Springer Date: Thu, 1 May 2014 08:53:49 -0400 Subject: [PATCH 081/212] merge trunk From 3f75a913e2f33505a60562a71f5dbdd49d5750a1 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Thu, 1 May 2014 18:57:45 +0200 Subject: [PATCH 082/212] removed not working tests --- openlp/core/common/historycombobox.py | 2 +- .../interfaces/openlp_core_common/__init__.py | 28 +++++++++++++++ .../test_historycombobox.py} | 35 +++++++++++-------- .../openlp_core_lib/test_searchedit.py | 7 ---- .../openlp_core_ui/test_filerenamedialog.py | 2 +- 5 files changed, 50 insertions(+), 24 deletions(-) create mode 100644 tests/interfaces/openlp_core_common/__init__.py rename tests/interfaces/{openlp_core_ui/test_splashscreen.py => openlp_core_common/test_historycombobox.py} (74%) diff --git a/openlp/core/common/historycombobox.py b/openlp/core/common/historycombobox.py index b6d06ff9f..aadc0ea39 100644 --- a/openlp/core/common/historycombobox.py +++ b/openlp/core/common/historycombobox.py @@ -53,7 +53,7 @@ class HistoryComboBox(QtGui.QComboBox): self.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Preferred) self.setInsertPolicy(QtGui.QComboBox.InsertAtTop) - def keyPressEvent(self, event): + def keyPressEvlent(self, event): """ Override the inherited keyPressEvent method to emit the ``returnPressed`` signal and to save the current text to the dropdown list. diff --git a/tests/interfaces/openlp_core_common/__init__.py b/tests/interfaces/openlp_core_common/__init__.py new file mode 100644 index 000000000..6b241e7fc --- /dev/null +++ b/tests/interfaces/openlp_core_common/__init__.py @@ -0,0 +1,28 @@ +# -*- 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 # +############################################################################### diff --git a/tests/interfaces/openlp_core_ui/test_splashscreen.py b/tests/interfaces/openlp_core_common/test_historycombobox.py similarity index 74% rename from tests/interfaces/openlp_core_ui/test_splashscreen.py rename to tests/interfaces/openlp_core_common/test_historycombobox.py index 35c15f9ec..00a63f15f 100644 --- a/tests/interfaces/openlp_core_ui/test_splashscreen.py +++ b/tests/interfaces/openlp_core_common/test_historycombobox.py @@ -27,34 +27,39 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### """ -Test the openlp.core.ui.splashscreen class. +Module to test the :mod:`~openlp.core.common.historycombobox` module. """ + from unittest import TestCase -from PyQt4 import QtGui +from PyQt4 import QtCore, QtGui, QtTest -from openlp.core.ui import SplashScreen +from openlp.core.common import Registry +from openlp.core.common import HistoryComboBox from tests.helpers.testmixin import TestMixin +from tests.interfaces import MagicMock, patch -class TestSplashScreen(TestCase, TestMixin): +class TestHistoryComboBox(TestCase, TestMixin): def setUp(self): + Registry.create() self.get_application() self.main_window = QtGui.QMainWindow() + Registry().register('main_window', self.main_window) + self.combo = HistoryComboBox(self.main_window) 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 + del self.combo - def setupUi_test(self): + def getItems_test(self): """ - Test if the setupUi method.... + Test the getItems() method """ - # GIVEN: A splash screen instance. - splash = SplashScreen() + # GIVEN: The combo. - # THEN: Check if the splash has a setupUi method. - assert hasattr(splash, 'setupUi'), 'The Splash Screen should have a setupUi() method.' + # WHEN: Add two items. + self.combo.addItem('test1') + self.combo.addItem('test2') + + # THEN: The list of items should contain both strings. + self.assertEqual(self.combo.getItems(), ['test1', 'test2']) \ No newline at end of file diff --git a/tests/interfaces/openlp_core_lib/test_searchedit.py b/tests/interfaces/openlp_core_lib/test_searchedit.py index 22bf6fae3..f2cf18988 100644 --- a/tests/interfaces/openlp_core_lib/test_searchedit.py +++ b/tests/interfaces/openlp_core_lib/test_searchedit.py @@ -30,7 +30,6 @@ Module to test the EditCustomForm. """ from unittest import TestCase -from unittest.mock import MagicMock from PyQt4 import QtCore, QtGui, QtTest @@ -127,9 +126,3 @@ class TestSearchEdit(TestCase, TestMixin): # 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." diff --git a/tests/interfaces/openlp_core_ui/test_filerenamedialog.py b/tests/interfaces/openlp_core_ui/test_filerenamedialog.py index 905f167e9..7d14c7d9c 100644 --- a/tests/interfaces/openlp_core_ui/test_filerenamedialog.py +++ b/tests/interfaces/openlp_core_ui/test_filerenamedialog.py @@ -89,7 +89,7 @@ class TestStartFileRenameForm(TestCase, TestMixin): Test that the file_name_edit setFocus has called with True when executed """ # GIVEN: A mocked QDialog.exec_() method and mocked file_name_edit.setFocus() method. - with patch('PyQt4.QtGui.QDialog.exec_') as mocked_exec: + with patch('PyQt4.QtGui.QDialog.exec_'): mocked_set_focus = MagicMock() self.form.file_name_edit.setFocus = mocked_set_focus From 53d830b737952a8d4b997bf7914faff8157f9eb0 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Thu, 1 May 2014 19:06:47 +0200 Subject: [PATCH 083/212] removed old code --- openlp/plugins/songs/forms/editverseform.py | 2 -- openlp/plugins/songs/lib/mediaitem.py | 12 ++---------- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/openlp/plugins/songs/forms/editverseform.py b/openlp/plugins/songs/forms/editverseform.py index 79a69a015..bc2532b0d 100644 --- a/openlp/plugins/songs/forms/editverseform.py +++ b/openlp/plugins/songs/forms/editverseform.py @@ -122,8 +122,6 @@ class EditVerseForm(QtGui.QDialog, Ui_EditVerseDialog): text = text[:position + 4] match = VERSE_REGEX.match(text) if match: - # TODO: Not used, remove? - # verse_tag = match.group(1) try: verse_num = int(match.group(2)) + 1 except ValueError: diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index 32730ce3c..288711b06 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -518,16 +518,8 @@ class SongMediaItem(MediaManagerItem): log.debug('service_load') if self.plugin.status != PluginStatus.Active or not item.data_string: return - if item.data_string['title'].find('@') == -1: - # 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. - 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()) + search_results = self.plugin.manager.get_all_objects( + Song, Song.search_title == item.data_string['title'], Song.search_title.asc()) edit_id = 0 add_song = True if search_results: From 8fb086134e1297a3db6ff27cd698669e6c026027 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Thu, 1 May 2014 19:49:43 +0200 Subject: [PATCH 084/212] dead code, not used variables, docs, unused imports --- openlp/core/lib/__init__.py | 3 +-- openlp/core/lib/screen.py | 9 ++++----- openlp/core/ui/aboutform.py | 2 +- openlp/core/ui/exceptiondialog.py | 2 +- openlp/core/ui/firsttimeform.py | 13 +++++-------- openlp/core/ui/media/mediaplayer.py | 2 -- openlp/core/ui/media/phononplayer.py | 2 -- openlp/plugins/bibles/lib/manager.py | 12 ++++++------ openlp/plugins/bibles/lib/mediaitem.py | 1 - openlp/plugins/custom/forms/editcustomform.py | 1 - openlp/plugins/images/lib/db.py | 2 +- openlp/plugins/media/lib/mediaitem.py | 1 - openlp/plugins/songs/forms/songselectform.py | 2 -- 13 files changed, 19 insertions(+), 33 deletions(-) diff --git a/openlp/core/lib/__init__.py b/openlp/core/lib/__init__.py index 9561baff4..a7cba33a0 100644 --- a/openlp/core/lib/__init__.py +++ b/openlp/core/lib/__init__.py @@ -296,8 +296,7 @@ def create_separated_list(string_list): :param string_list: List of unicode strings """ - if LooseVersion(Qt.PYQT_VERSION_STR) >= LooseVersion('4.9') and \ - LooseVersion(Qt.qVersion()) >= LooseVersion('4.8'): + if LooseVersion(Qt.PYQT_VERSION_STR) >= LooseVersion('4.9') and LooseVersion(Qt.qVersion()) >= LooseVersion('4.8'): return QtCore.QLocale().createSeparatedList(string_list) if not string_list: return '' diff --git a/openlp/core/lib/screen.py b/openlp/core/lib/screen.py index 17ead5346..8f03d9ab2 100644 --- a/openlp/core/lib/screen.py +++ b/openlp/core/lib/screen.py @@ -63,8 +63,7 @@ class ScreenList(object): """ Initialise the screen list. - ``desktop`` - A ``QDesktopWidget`` object. + :param desktop: A QDesktopWidget object. """ screen_list = cls() screen_list.desktop = desktop @@ -153,9 +152,9 @@ class ScreenList(object): :param screen: A dict with the screen properties:: { - u'primary': True, - u'number': 0, - u'size': PyQt4.QtCore.QRect(0, 0, 1024, 768) + 'primary': True, + 'number': 0, + 'size': PyQt4.QtCore.QRect(0, 0, 1024, 768) } """ log.info('Screen %d found with resolution %s' % (screen['number'], screen['size'])) diff --git a/openlp/core/ui/aboutform.py b/openlp/core/ui/aboutform.py index e971bbec4..3825312bd 100644 --- a/openlp/core/ui/aboutform.py +++ b/openlp/core/ui/aboutform.py @@ -30,7 +30,7 @@ The About dialog. """ -from PyQt4 import QtCore, QtGui +from PyQt4 import QtGui from .aboutdialog import Ui_AboutDialog from openlp.core.lib import translate diff --git a/openlp/core/ui/exceptiondialog.py b/openlp/core/ui/exceptiondialog.py index 212fee4cd..b8b3941cd 100644 --- a/openlp/core/ui/exceptiondialog.py +++ b/openlp/core/ui/exceptiondialog.py @@ -30,7 +30,7 @@ The GUI widgets of the exception dialog. """ -from PyQt4 import QtCore, QtGui +from PyQt4 import QtGui from openlp.core.lib import translate from openlp.core.lib.ui import create_button, create_button_box diff --git a/openlp/core/ui/firsttimeform.py b/openlp/core/ui/firsttimeform.py index b9b5f5997..a98d7861a 100644 --- a/openlp/core/ui/firsttimeform.py +++ b/openlp/core/ui/firsttimeform.py @@ -170,6 +170,7 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard, RegistryProperties): elif self.currentId() == FirstTimePage.Progress: return -1 elif self.currentId() == FirstTimePage.NoInternet: + print(1111) return FirstTimePage.Progress elif self.currentId() == FirstTimePage.Themes: self.application.set_busy_cursor() @@ -199,8 +200,8 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard, RegistryProperties): self.no_internet_label.setText(self.no_internet_text + self.cancelWizardText) elif page_id == FirstTimePage.Defaults: self.theme_combo_box.clear() - for iter in range(self.themes_list_widget.count()): - item = self.themes_list_widget.item(iter) + for index in range(self.themes_list_widget.count()): + item = self.themes_list_widget.item(index) if item.checkState() == QtCore.Qt.Checked: self.theme_combo_box.addItem(item.text()) if self.has_run_wizard: @@ -292,13 +293,9 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard, RegistryProperties): """ themes = self.config.get('themes', 'files') themes = themes.split(',') - for theme in themes: - filename = self.config.get('theme_%s' % theme, 'filename') + for index, theme in enumerate(themes): screenshot = self.config.get('theme_%s' % theme, 'screenshot') - for index in range(self.themes_list_widget.count()): - item = self.themes_list_widget.item(index) - if item.data(QtCore.Qt.UserRole) == filename: - break + item = self.themes_list_widget.item(index) item.setIcon(build_icon(os.path.join(gettempdir(), 'openlp', screenshot))) def _get_file_size(self, url): diff --git a/openlp/core/ui/media/mediaplayer.py b/openlp/core/ui/media/mediaplayer.py index 3246d58c4..22ea7ecfc 100644 --- a/openlp/core/ui/media/mediaplayer.py +++ b/openlp/core/ui/media/mediaplayer.py @@ -29,8 +29,6 @@ """ The :mod:`~openlp.core.ui.media.mediaplayer` module contains the MediaPlayer class. """ -import os - from openlp.core.common import RegistryProperties from openlp.core.ui.media import MediaState diff --git a/openlp/core/ui/media/phononplayer.py b/openlp/core/ui/media/phononplayer.py index 5e94dbd0e..b343755a0 100644 --- a/openlp/core/ui/media/phononplayer.py +++ b/openlp/core/ui/media/phononplayer.py @@ -33,10 +33,8 @@ import logging import mimetypes from datetime import datetime -from PyQt4 import QtGui from PyQt4.phonon import Phonon -from openlp.core.common import Settings from openlp.core.lib import translate from openlp.core.ui.media import MediaState diff --git a/openlp/plugins/bibles/lib/manager.py b/openlp/plugins/bibles/lib/manager.py index 8ad446ab4..c57fc117e 100644 --- a/openlp/plugins/bibles/lib/manager.py +++ b/openlp/plugins/bibles/lib/manager.py @@ -54,19 +54,19 @@ class BibleFormat(object): WebDownload = 3 @staticmethod - def get_class(format): + def get_class(bible_format): """ Return the appropriate implementation class. - :param format: The Bible format. + :param bible_format: The Bible format. """ - if format == BibleFormat.OSIS: + if bible_format == BibleFormat.OSIS: return OSISBible - elif format == BibleFormat.CSV: + elif bible_format == BibleFormat.CSV: return CSVBible - elif format == BibleFormat.OpenSong: + elif bible_format == BibleFormat.OpenSong: return OpenSongBible - elif format == BibleFormat.WebDownload: + elif bible_format == BibleFormat.WebDownload: return HTTPBible else: return None diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index a2cb1b594..c13448a4a 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -837,7 +837,6 @@ class BibleMediaItem(MediaManagerItem): # If there are no more items we check whether we have to add bible_text. if bible_text: raw_slides.append(bible_text.lstrip()) - bible_text = '' # Service Item: Capabilities if self.settings.layout_style == LayoutStyle.Continuous and not second_bible: # Split the line but do not replace line breaks in renderer. diff --git a/openlp/plugins/custom/forms/editcustomform.py b/openlp/plugins/custom/forms/editcustomform.py index 010f7d1bc..9c7a080aa 100644 --- a/openlp/plugins/custom/forms/editcustomform.py +++ b/openlp/plugins/custom/forms/editcustomform.py @@ -197,7 +197,6 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog): self.slide_list_view.clear() self.slide_list_view.addItems(slides) else: - old_slides = [] old_row = self.slide_list_view.currentRow() # Create a list with all (old/unedited) slides. old_slides = [self.slide_list_view.item(row).text() for row in range(self.slide_list_view.count())] diff --git a/openlp/plugins/images/lib/db.py b/openlp/plugins/images/lib/db.py index 68ca3d11d..896f93b17 100644 --- a/openlp/plugins/images/lib/db.py +++ b/openlp/plugins/images/lib/db.py @@ -31,7 +31,7 @@ The :mod:`db` module provides the database and schema that is the backend for th """ from sqlalchemy import Column, ForeignKey, Table, types -from sqlalchemy.orm import mapper, relation, reconstructor +from sqlalchemy.orm import mapper from openlp.core.lib.db import BaseModel, init_db diff --git a/openlp/plugins/media/lib/mediaitem.py b/openlp/plugins/media/lib/mediaitem.py index 87d8e3311..92426334a 100644 --- a/openlp/plugins/media/lib/mediaitem.py +++ b/openlp/plugins/media/lib/mediaitem.py @@ -314,7 +314,6 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties): def get_list(self, type=MediaType.Audio): media = Settings().value(self.settings_section + '/media files') media.sort(key=lambda filename: get_locale_key(os.path.split(str(filename))[1])) - extension = [] if type == MediaType.Audio: extension = self.media_controller.audio_extensions_list else: diff --git a/openlp/plugins/songs/forms/songselectform.py b/openlp/plugins/songs/forms/songselectform.py index d3ff5ab52..f9f658c5b 100755 --- a/openlp/plugins/songs/forms/songselectform.py +++ b/openlp/plugins/songs/forms/songselectform.py @@ -319,8 +319,6 @@ class SongSelectForm(QtGui.QDialog, Ui_SongSelectDialog): def on_search_finished(self): """ Slot which is called when the search is completed. - - :param songs: """ self.application.process_events() self.search_progress_bar.setVisible(False) From 40a0d612bd838a2d318c776f8cabe2dcfd93dd48 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Thu, 1 May 2014 20:02:20 +0200 Subject: [PATCH 085/212] moved code --- openlp/core/ui/slidecontroller.py | 76 ++++++++++++++++--------------- 1 file changed, 39 insertions(+), 37 deletions(-) diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index bf92e9d76..ab5b231a4 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -45,6 +45,13 @@ from openlp.core.lib.ui import create_action from openlp.core.utils.actions import ActionList, CategoryOrder from openlp.core.ui.listpreviewwidget import ListPreviewWidget +try: + from openlp.plugins.songs.lib import VerseType + SONGS_PLUGIN_AVAILABLE = True +except ImportError: + SONGS_PLUGIN_AVAILABLE = False + + # Threshold which has to be trespassed to toggle. HIDE_MENU_THRESHOLD = 27 AUDIO_TIME_LABEL_STYLESHEET = 'background-color: palette(background); ' \ @@ -400,11 +407,6 @@ class SlideController(DisplayController, RegistryProperties): 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: - from openlp.plugins.songs.lib import VerseType - SONGS_PLUGIN_AVAILABLE = True - except ImportError: - SONGS_PLUGIN_AVAILABLE = False sender_name = self.sender().objectName() verse_type = sender_name[15:] if sender_name[:15] == 'shortcutAction_' else '' if SONGS_PLUGIN_AVAILABLE: @@ -495,38 +497,6 @@ class SlideController(DisplayController, RegistryProperties): self.on_theme_display(False) self.on_hide_display(False) - 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, field=None): - """ - Live event to select the next service item from the service manager. - """ - self.keypress_queue.append(ServiceItemAction.Next) - self._process_queue() - - def _process_queue(self): - """ - Process the service item request queue. The key presses can arrive - faster than the processing so implement a FIFO queue. - """ - if self.keypress_queue: - while len(self.keypress_queue) and not self.keypress_loop: - self.keypress_loop = True - keypress_command = self.keypress_queue.popleft() - if keypress_command == ServiceItemAction.Previous: - self.service_manager.previous_item() - elif keypress_command == ServiceItemAction.PreviousLastSlide: - # Go to the last slide of the previous item - self.service_manager.previous_item(last_slide=True) - else: - self.service_manager.next_item() - self.keypress_loop = False - def screen_size_changed(self): """ Settings dialog has changed the screen size of adjust output and screen previews. @@ -1382,3 +1352,35 @@ class LiveController(RegistryMixin, OpenLPMixin, SlideController): process the bootstrap post setup request """ self.post_set_up() + + 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, field=None): + """ + Live event to select the next service item from the service manager. + """ + self.keypress_queue.append(ServiceItemAction.Next) + self._process_queue() + + def _process_queue(self): + """ + Process the service item request queue. The key presses can arrive faster than the processing so implement a + FIFO queue. + """ + if self.keypress_queue: + while len(self.keypress_queue) and not self.keypress_loop: + self.keypress_loop = True + keypress_command = self.keypress_queue.popleft() + if keypress_command == ServiceItemAction.Previous: + self.service_manager.previous_item() + elif keypress_command == ServiceItemAction.PreviousLastSlide: + # Go to the last slide of the previous item + self.service_manager.previous_item(last_slide=True) + else: + self.service_manager.next_item() + self.keypress_loop = False From e0c623fd80b66b6deb4d1eb8cf2c3434a32d8346 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Fri, 2 May 2014 08:32:16 +0200 Subject: [PATCH 086/212] From 4231692ae11de4c60b40a116047d50888be56507 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Fri, 2 May 2014 08:34:57 +0200 Subject: [PATCH 087/212] removed print --- openlp/core/common/historycombobox.py | 2 +- openlp/core/ui/firsttimeform.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/openlp/core/common/historycombobox.py b/openlp/core/common/historycombobox.py index aadc0ea39..b6d06ff9f 100644 --- a/openlp/core/common/historycombobox.py +++ b/openlp/core/common/historycombobox.py @@ -53,7 +53,7 @@ class HistoryComboBox(QtGui.QComboBox): self.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Preferred) self.setInsertPolicy(QtGui.QComboBox.InsertAtTop) - def keyPressEvlent(self, event): + def keyPressEvent(self, event): """ Override the inherited keyPressEvent method to emit the ``returnPressed`` signal and to save the current text to the dropdown list. diff --git a/openlp/core/ui/firsttimeform.py b/openlp/core/ui/firsttimeform.py index a98d7861a..531c4b49f 100644 --- a/openlp/core/ui/firsttimeform.py +++ b/openlp/core/ui/firsttimeform.py @@ -170,7 +170,6 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard, RegistryProperties): elif self.currentId() == FirstTimePage.Progress: return -1 elif self.currentId() == FirstTimePage.NoInternet: - print(1111) return FirstTimePage.Progress elif self.currentId() == FirstTimePage.Themes: self.application.set_busy_cursor() From deff310f530ea02868125ce3890c7cba1b7d8f0a Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Fri, 2 May 2014 08:42:17 +0200 Subject: [PATCH 088/212] removed u' --- openlp/core/common/applocation.py | 2 +- openlp/core/common/settings.py | 3 +-- openlp/core/lib/plugin.py | 2 +- openlp/core/lib/screen.py | 2 +- openlp/core/ui/printserviceform.py | 2 +- openlp/core/ui/themestab.py | 2 +- openlp/core/ui/wizard.py | 2 +- openlp/core/utils/__init__.py | 2 +- openlp/plugins/alerts/forms/__init__.py | 2 +- openlp/plugins/bibles/forms/__init__.py | 2 +- openlp/plugins/bibles/lib/__init__.py | 4 ++-- openlp/plugins/bibles/lib/biblestab.py | 2 +- openlp/plugins/bibles/lib/db.py | 13 ++++++------- openlp/plugins/bibles/lib/http.py | 2 +- openlp/plugins/bibles/lib/mediaitem.py | 2 +- openlp/plugins/images/forms/__init__.py | 2 +- .../presentations/lib/presentationcontroller.py | 2 +- openlp/plugins/songs/forms/__init__.py | 2 +- openlp/plugins/songs/forms/songimportform.py | 8 ++++---- openlp/plugins/songs/lib/__init__.py | 2 +- openlp/plugins/songs/lib/easyslidesimport.py | 2 +- openlp/plugins/songs/lib/songbeamerimport.py | 2 +- openlp/plugins/songs/lib/songselect.py | 4 ++-- openlp/plugins/songs/lib/xml.py | 2 +- tests/functional/openlp_core_lib/test_lib.py | 2 +- tests/functional/openlp_core_utils/test_utils.py | 8 ++++---- 26 files changed, 39 insertions(+), 41 deletions(-) diff --git a/openlp/core/common/applocation.py b/openlp/core/common/applocation.py index 1fce25000..2bc4027b6 100644 --- a/openlp/core/common/applocation.py +++ b/openlp/core/common/applocation.py @@ -110,7 +110,7 @@ class AppLocation(object): :param extension: Defaults to *None*. The extension to search for. For example:: - u'.png' + '.png' """ path = AppLocation.get_data_path() if section: diff --git a/openlp/core/common/settings.py b/openlp/core/common/settings.py index 590452f73..3b7b31ca1 100644 --- a/openlp/core/common/settings.py +++ b/openlp/core/common/settings.py @@ -68,8 +68,7 @@ class Settings(QtCore.QSettings): ``__obsolete_settings__`` Each entry is structured in the following way:: - (u'general/enable slide loop', u'advanced/slide limits', - [(SlideLimits.Wrap, True), (SlideLimits.End, False)]) + ('general/enable slide loop', 'advanced/slide limits', [(SlideLimits.Wrap, True), (SlideLimits.End, False)]) The first entry is the *old key*; it will be removed. diff --git a/openlp/core/lib/plugin.py b/openlp/core/lib/plugin.py index 1f459524c..8cd13088d 100644 --- a/openlp/core/lib/plugin.py +++ b/openlp/core/lib/plugin.py @@ -129,7 +129,7 @@ class Plugin(QtCore.QObject, RegistryProperties): class MyPlugin(Plugin): def __init__(self): - super(MyPlugin, self).__init__('MyPlugin', version=u'0.1') + super(MyPlugin, self).__init__('MyPlugin', version='0.1') :param name: Defaults to *None*. The name of the plugin. :param default_settings: A dict containing the plugin's settings. The value to each key is the default value diff --git a/openlp/core/lib/screen.py b/openlp/core/lib/screen.py index 8f03d9ab2..d05200f2f 100644 --- a/openlp/core/lib/screen.py +++ b/openlp/core/lib/screen.py @@ -135,7 +135,7 @@ class ScreenList(object): Returns a list with the screens. This should only be used to display available screens to the user:: - [u'Screen 1 (primary)', u'Screen 2'] + ['Screen 1 (primary)', 'Screen 2'] """ screen_list = [] for screen in self.screen_list: diff --git a/openlp/core/ui/printserviceform.py b/openlp/core/ui/printserviceform.py index 489eefa78..41caaab52 100644 --- a/openlp/core/ui/printserviceform.py +++ b/openlp/core/ui/printserviceform.py @@ -242,7 +242,7 @@ class PrintServiceForm(QtGui.QDialog, Ui_PrintServiceDialog, RegistryProperties) Creates a html element. If ``text`` is given, the element's text will set and if a ``parent`` is given, the element is appended. - :param tag: The html tag, e. g. ``u'span'``. Defaults to ``None``. + :param tag: The html tag, e. g. ``'span'``. Defaults to ``None``. :param text: The text for the tag. Defaults to ``None``. :param parent: The parent element. Defaults to ``None``. :param classId: Value for the class attribute diff --git a/openlp/core/ui/themestab.py b/openlp/core/ui/themestab.py index 1f54f984b..0478f0ed0 100644 --- a/openlp/core/ui/themestab.py +++ b/openlp/core/ui/themestab.py @@ -190,7 +190,7 @@ class ThemesTab(SettingsTab): :param theme_list: The list of available themes:: - [u'Bible Theme', u'Song Theme'] + ['Bible Theme', 'Song Theme'] """ # Reload as may have been triggered by the ThemeManager. self.global_theme = Settings().value(self.settings_section + '/global theme') diff --git a/openlp/core/ui/wizard.py b/openlp/core/ui/wizard.py index 5815457b5..5996296b5 100644 --- a/openlp/core/ui/wizard.py +++ b/openlp/core/ui/wizard.py @@ -279,7 +279,7 @@ class OpenLPWizard(QtGui.QWizard, RegistryProperties): :param filters: The file extension filters. It should contain the file description as well as the file extension. For example:: - u'OpenLP 2.0 Databases (*.sqlite)' + 'OpenLP 2.0 Databases (*.sqlite)' """ if filters: filters += ';;' diff --git a/openlp/core/utils/__init__.py b/openlp/core/utils/__init__.py index a5b5f356a..add663132 100644 --- a/openlp/core/utils/__init__.py +++ b/openlp/core/utils/__init__.py @@ -113,7 +113,7 @@ def get_application_version(): """ Returns the application version of the running instance of OpenLP:: - {u'full': u'1.9.4-bzr1249', u'version': u'1.9.4', u'build': u'bzr1249'} + {'full': '1.9.4-bzr1249', 'version': '1.9.4', 'build': 'bzr1249'} """ global APPLICATION_VERSION if APPLICATION_VERSION: diff --git a/openlp/plugins/alerts/forms/__init__.py b/openlp/plugins/alerts/forms/__init__.py index 55d44372e..fbe3c7170 100644 --- a/openlp/plugins/alerts/forms/__init__.py +++ b/openlp/plugins/alerts/forms/__init__.py @@ -32,7 +32,7 @@ other class holds all the functional code, like slots and loading and saving. The first class, commonly known as the **Dialog** class, is typically named ``Ui_Dialog``. It is a slightly modified version of the class that the ``pyuic4`` command produces from Qt4's .ui file. Typical modifications will be -converting most strings from "" to u'' and using OpenLP's ``translate()`` function for translating strings. +converting most strings from "" to '' and using OpenLP's ``translate()`` function for translating strings. The second class, commonly known as the **Form** class, is typically named ``Form``. This class is the one which is instantiated and used. It uses dual inheritance to inherit from (usually) QtGui.QDialog and the Ui class mentioned diff --git a/openlp/plugins/bibles/forms/__init__.py b/openlp/plugins/bibles/forms/__init__.py index 974d6d7a5..d6c77a9a3 100644 --- a/openlp/plugins/bibles/forms/__init__.py +++ b/openlp/plugins/bibles/forms/__init__.py @@ -33,7 +33,7 @@ other class holds all the functional code, like slots and loading and saving. The first class, commonly known as the **Dialog** class, is typically named ``Ui_Dialog``. It is a slightly modified version of the class that the ``pyuic4`` command produces from Qt4's .ui file. Typical modifications will be -converting most strings from "" to u'' and using OpenLP's ``translate()`` function for translating strings. +converting most strings from "" to '' and using OpenLP's ``translate()`` function for translating strings. The second class, commonly known as the **Form** class, is typically named ``Form``. This class is the one which is instantiated and used. It uses dual inheritance to inherit from (usually) QtGui.QDialog and the Ui class mentioned diff --git a/openlp/plugins/bibles/lib/__init__.py b/openlp/plugins/bibles/lib/__init__.py index 50a0e2a63..d67319797 100644 --- a/openlp/plugins/bibles/lib/__init__.py +++ b/openlp/plugins/bibles/lib/__init__.py @@ -262,7 +262,7 @@ def parse_reference(reference, bible, language_selection, book_ref_id=False): For example:: - [(u'John', 3, 16, 18), (u'John', 4, 1, 1)] + [('John', 3, 16, 18), ('John', 4, 1, 1)] **Reference string details:** @@ -311,7 +311,7 @@ def parse_reference(reference, bible, language_selection, book_ref_id=False): ``(?P[0-9]+)`` The ``to_verse`` reference is equivalent to group 2. - The full reference is matched against get_reference_match(u'full'). This regular expression looks like this: + The full reference is matched against get_reference_match('full'). This regular expression looks like this: ``^\s*(?!\s)(?P[\d]*[^\d]+)(?Dialog``. It is a slightly modified version of the class that the ``pyuic4`` command produces from Qt4's .ui file. Typical modifications will be -converting most strings from "" to u'' and using OpenLP's ``translate()`` function for translating strings. +converting most strings from "" to '' and using OpenLP's ``translate()`` function for translating strings. The second class, commonly known as the **Form** class, is typically named ``Form``. This class is the one which is instantiated and used. It uses dual inheritance to inherit from (usually) QtGui.QDialog and the Ui class mentioned diff --git a/openlp/plugins/presentations/lib/presentationcontroller.py b/openlp/plugins/presentations/lib/presentationcontroller.py index 6c8d7fb8c..3060bcdb0 100644 --- a/openlp/plugins/presentations/lib/presentationcontroller.py +++ b/openlp/plugins/presentations/lib/presentationcontroller.py @@ -354,7 +354,7 @@ class PresentationController(object): class MyPresentationController(PresentationController): def __init__(self, plugin): PresentationController.__init( - self, plugin, u'My Presenter App') + self, plugin, 'My Presenter App') :param plugin: Defaults to *None*. The presentationplugin object :param name: Name of the application, to appear in the application diff --git a/openlp/plugins/songs/forms/__init__.py b/openlp/plugins/songs/forms/__init__.py index 3094a0eda..eb29c12bf 100644 --- a/openlp/plugins/songs/forms/__init__.py +++ b/openlp/plugins/songs/forms/__init__.py @@ -34,7 +34,7 @@ code, like slots and loading and saving. The first class, commonly known as the **Dialog** class, is typically named ``Ui_Dialog``. It is a slightly modified version of the class that the ``pyuic4`` command produces from Qt4's .ui file. Typical modifications will be -converting most strings from "" to u'' and using OpenLP's ``translate()`` +converting most strings from "" to '' and using OpenLP's ``translate()`` function for translating strings. The second class, commonly known as the **Form** class, is typically named diff --git a/openlp/plugins/songs/forms/songimportform.py b/openlp/plugins/songs/forms/songimportform.py index 21569a034..2a05f06cd 100644 --- a/openlp/plugins/songs/forms/songimportform.py +++ b/openlp/plugins/songs/forms/songimportform.py @@ -231,11 +231,11 @@ class SongImportForm(OpenLPWizard, RegistryProperties): """ Opens a QFileDialog and writes the filenames to the given listbox. - :param title: The title of the dialog (unicode). + :param title: The title of the dialog (str). :param listbox: A listbox (QListWidget). - :param filters: The file extension filters. It should contain the file descriptions - as well as the file extensions. For example:: - u'SongBeamer Files (*.sng)' + :param filters: The file extension filters. It should contain the file descriptions as well as the file + extensions. For example:: + 'SongBeamer Files (*.sng)' """ if filters: filters += ';;' diff --git a/openlp/plugins/songs/lib/__init__.py b/openlp/plugins/songs/lib/__init__.py index aa9fbc4c9..95868ffa7 100644 --- a/openlp/plugins/songs/lib/__init__.py +++ b/openlp/plugins/songs/lib/__init__.py @@ -434,7 +434,7 @@ def strip_rtf(text, default_encoding=None): # Current font is the font tag we last met. font = '' # Character encoding is defined inside fonttable. - # font_table could contain eg u'0': u'cp1252' + # font_table could contain eg '0': u'cp1252' font_table = {'': ''} # Stack of things to keep track of when entering/leaving groups. stack = [] diff --git a/openlp/plugins/songs/lib/easyslidesimport.py b/openlp/plugins/songs/lib/easyslidesimport.py index e28e7bf97..ca9a9b755 100644 --- a/openlp/plugins/songs/lib/easyslidesimport.py +++ b/openlp/plugins/songs/lib/easyslidesimport.py @@ -292,7 +292,7 @@ class EasySlidesImport(SongImport): return True def _extract_region(self, line): - # this was true already: line[0:7] == u'[region': + # this was true already: line[0:7] == '[region': """ Extract the region from text diff --git a/openlp/plugins/songs/lib/songbeamerimport.py b/openlp/plugins/songs/lib/songbeamerimport.py index a0b166ded..9c5e74c06 100644 --- a/openlp/plugins/songs/lib/songbeamerimport.py +++ b/openlp/plugins/songs/lib/songbeamerimport.py @@ -171,7 +171,7 @@ class SongBeamerImport(SongImport): :param line: The line in the file. It should consist of a tag and a value for this tag (unicode):: - u'#Title=Nearer my God to Thee' + '#Title=Nearer my God to Thee' """ tag_val = line.split('=', 1) if len(tag_val) == 1: diff --git a/openlp/plugins/songs/lib/songselect.py b/openlp/plugins/songs/lib/songselect.py index a18b08c8a..232cdb9cf 100644 --- a/openlp/plugins/songs/lib/songselect.py +++ b/openlp/plugins/songs/lib/songselect.py @@ -146,14 +146,14 @@ class SongSelectImport(object): try: song_page = BeautifulSoup(self.opener.open(song['link']).read(), 'lxml') except (TypeError, HTTPError) as e: - log.exception(u'Could not get song from SongSelect, %s', e) + log.exception('Could not get song from SongSelect, %s', e) return None if callback: callback() try: lyrics_page = BeautifulSoup(self.opener.open(song['link'] + '/lyrics').read(), 'lxml') except (TypeError, HTTPError): - log.exception(u'Could not get lyrics from SongSelect') + log.exception('Could not get lyrics from SongSelect') return None if callback: callback() diff --git a/openlp/plugins/songs/lib/xml.py b/openlp/plugins/songs/lib/xml.py index 68641a1d5..b856cb53f 100644 --- a/openlp/plugins/songs/lib/xml.py +++ b/openlp/plugins/songs/lib/xml.py @@ -344,7 +344,7 @@ class OpenLyrics(object): """ Tests the given text for not closed formatting tags and returns a tuple consisting of two unicode strings:: - (u'{st}{r}', u'{/r}{/st}') + ('{st}{r}', '{/r}{/st}') The first unicode string are the start tags (for the next slide). The second unicode string are the end tags. diff --git a/tests/functional/openlp_core_lib/test_lib.py b/tests/functional/openlp_core_lib/test_lib.py index b4334a728..2e06e47fe 100644 --- a/tests/functional/openlp_core_lib/test_lib.py +++ b/tests/functional/openlp_core_lib/test_lib.py @@ -305,7 +305,7 @@ class TestLib(TestCase): # WHEN: We convert an image to a byte array result = image_to_byte(mocked_image) - # THEN: We should receive a value of u'base64mock' + # THEN: We should receive a value of 'base64mock' MockedQtCore.QByteArray.assert_called_with() MockedQtCore.QBuffer.assert_called_with(mocked_byte_array) mocked_buffer.open.assert_called_with('writeonly') diff --git a/tests/functional/openlp_core_utils/test_utils.py b/tests/functional/openlp_core_utils/test_utils.py index a97d757ea..075ecb14f 100644 --- a/tests/functional/openlp_core_utils/test_utils.py +++ b/tests/functional/openlp_core_utils/test_utils.py @@ -250,7 +250,7 @@ class TestUtils(TestCase): # THEN: The user agent is a Linux (or ChromeOS) user agent result = 'Linux' in user_agent or 'CrOS' in user_agent - self.assertTrue(result, u'The user agent should be a valid Linux user agent') + self.assertTrue(result, 'The user agent should be a valid Linux user agent') def get_user_agent_windows_test(self): """ @@ -265,7 +265,7 @@ class TestUtils(TestCase): user_agent = _get_user_agent() # THEN: The user agent is a Linux (or ChromeOS) user agent - self.assertIn('Windows', user_agent, u'The user agent should be a valid Windows user agent') + self.assertIn('Windows', user_agent, 'The user agent should be a valid Windows user agent') def get_user_agent_macos_test(self): """ @@ -280,7 +280,7 @@ class TestUtils(TestCase): user_agent = _get_user_agent() # THEN: The user agent is a Linux (or ChromeOS) user agent - self.assertIn('Mac OS X', user_agent, u'The user agent should be a valid OS X user agent') + self.assertIn('Mac OS X', user_agent, 'The user agent should be a valid OS X user agent') def get_user_agent_default_test(self): """ @@ -295,7 +295,7 @@ class TestUtils(TestCase): user_agent = _get_user_agent() # THEN: The user agent is a Linux (or ChromeOS) user agent - self.assertIn('NetBSD', user_agent, u'The user agent should be the default user agent') + self.assertIn('NetBSD', user_agent, 'The user agent should be the default user agent') def get_web_page_no_url_test(self): """ From 87587ab8e8c86d7649b288d02e6c84271fe75b04 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Fri, 2 May 2014 08:50:54 +0200 Subject: [PATCH 089/212] short lines --- openlp/plugins/bibles/lib/mediaitem.py | 43 ++++++++++---------------- 1 file changed, 16 insertions(+), 27 deletions(-) diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index 5d9c598ed..dd14c6409 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -360,8 +360,7 @@ class BibleMediaItem(MediaManagerItem): combo boxes on the 'Advanced Search' Tab. This is not of any importance of the 'Quick Search' Tab. :param bible: The bible to initialise (unicode). - :param last_book_id: The "book reference id" of the book which is chosen at the moment. - (int) + :param last_book_id: The "book reference id" of the book which is chosen at the moment. (int) """ log.debug('initialise_advanced_bible %s, %s', bible, last_book_id) book_data = self.plugin.manager.get_books(bible) @@ -421,9 +420,8 @@ class BibleMediaItem(MediaManagerItem): def update_auto_completer(self): """ - This updates the bible book completion list for the search field. The - completion depends on the bible. It is only updated when we are doing a - reference search, otherwise the auto completion list is removed. + This updates the bible book completion list for the search field. The completion depends on the bible. It is + only updated when we are doing a reference search, otherwise the auto completion list is removed. """ log.debug('update_auto_completer') # Save the current search type to the configuration. @@ -593,8 +591,7 @@ class BibleMediaItem(MediaManagerItem): :param range_from: The first number of the range (int). :param range_to: The last number of the range (int). :param combo: The combo box itself (QComboBox). - :param restore: If True, then the combo's currentText will be restored after - adjusting (if possible). + :param restore: If True, then the combo's currentText will be restored after adjusting (if possible). """ log.debug('adjust_combo_box %s, %s, %s', combo, range_from, range_to) if restore: @@ -640,8 +637,8 @@ class BibleMediaItem(MediaManagerItem): def on_quick_search_button(self): """ - Does a quick search and saves the search results. Quick search can - either be "Reference Search" or "Text Search". + Does a quick search and saves the search results. Quick search can either be "Reference Search" or + "Text Search". """ log.debug('Quick Search Button clicked') self.quickSearchButton.setEnabled(False) @@ -696,8 +693,7 @@ class BibleMediaItem(MediaManagerItem): def display_results(self, bible, second_bible=''): """ - Displays the search results in the media manager. All data needed for - further action is saved for/in each row. + Displays the search results in the media manager. All data needed for further action is saved for/in each row. """ items = self.build_display_results(bible, second_bible, self.search_results) for bible_verse in items: @@ -708,8 +704,7 @@ class BibleMediaItem(MediaManagerItem): def build_display_results(self, bible, second_bible, search_results): """ - Displays the search results in the media manager. All data needed for - further action is saved for/in each row. + Displays the search results in the media manager. All data needed for further action is saved for/in each row. """ verse_separator = get_reference_separator('sep_v_display') version = self.plugin.manager.get_meta_data(bible, 'name').value @@ -858,9 +853,8 @@ class BibleMediaItem(MediaManagerItem): def format_title(self, start_bitem, old_bitem): """ - This method is called, when we have to change the title, because - we are at the end of a verse range. E. g. if we want to add - Genesis 1:1-6 as well as Daniel 2:14. + This method is called, when we have to change the title, because we are at the end of a verse range. E. g. if we + want to add Genesis 1:1-6 as well as Daniel 2:14. :param start_bitem: The first item of a range. :param old_bitem: The last item of a range. @@ -890,10 +884,8 @@ class BibleMediaItem(MediaManagerItem): def check_title(self, bitem, old_bitem): """ - This method checks if we are at the end of an verse range. If that is - the case, we return True, otherwise False. E. g. if we added - - Genesis 1:1-6, but the next verse is Daniel 2:14, we return True. + This method checks if we are at the end of an verse range. If that is the case, we return True, otherwise False. + E. g. if we added Genesis 1:1-6, but the next verse is Daniel 2:14, we return True. :param bitem: The item we are dealing with at the moment. :param old_bitem: The item we were previously dealing with. @@ -917,18 +909,15 @@ class BibleMediaItem(MediaManagerItem): 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)): - # 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. + # 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. return True return False def format_verse(self, old_chapter, chapter, verse): """ - Formats and returns the text, each verse starts with, for the given - chapter and verse. The text is either surrounded by round, square, - - curly brackets or no brackets at all. For example:: + Formats and returns the text, each verse starts with, for the given chapter and verse. The text is either + surrounded by round, square, curly brackets or no brackets at all. For example:: '{su}1:1{/su}' From b993af2e90d84c98e05442f8b0f945fe4de797c8 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Fri, 2 May 2014 10:04:11 +0200 Subject: [PATCH 090/212] No newline at end of file... --- .../songshowplussongs/a mighty fortress is our god.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/resources/songshowplussongs/a mighty fortress is our god.json b/tests/resources/songshowplussongs/a mighty fortress is our god.json index 7e3d400b4..2ab95781d 100644 --- a/tests/resources/songshowplussongs/a mighty fortress is our god.json +++ b/tests/resources/songshowplussongs/a mighty fortress is our god.json @@ -26,4 +26,5 @@ "v4" ] ] -} \ No newline at end of file +} + From f4646f955b39462b03ed5b84797a346010e6fa52 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Fri, 2 May 2014 20:02:25 +0200 Subject: [PATCH 091/212] From 50fb727bae0032aeaea618c8ad67397176020d2b Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Fri, 2 May 2014 20:11:45 +0200 Subject: [PATCH 092/212] From 5d63820b174157c8c7714c39dc0e250557810d9f Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Fri, 2 May 2014 23:07:56 +0200 Subject: [PATCH 093/212] Added support for importing EW songs even if RTF is not escaped as espected, also fixed importing og songs with unicode in metadata. Fixes: https://launchpad.net/bugs/1299837 --- openlp/plugins/songs/lib/ewimport.py | 25 +++++++++++++----- .../openlp_plugins/songs/test_ewimport.py | 24 ++++++++++++++++- tests/resources/easyworshipsongs/Songs.DB | Bin 6144 -> 6144 bytes tests/resources/easyworshipsongs/Songs.MB | Bin 12288 -> 20480 bytes 4 files changed, 42 insertions(+), 7 deletions(-) diff --git a/openlp/plugins/songs/lib/ewimport.py b/openlp/plugins/songs/lib/ewimport.py index faa4122c8..604a70d81 100644 --- a/openlp/plugins/songs/lib/ewimport.py +++ b/openlp/plugins/songs/lib/ewimport.py @@ -281,7 +281,7 @@ class EasyWorshipSongImport(SongImport): raw_record = db_file.read(record_size) self.fields = self.record_structure.unpack(raw_record) self.set_defaults() - self.title = self.get_field(fi_title).decode() + self.title = self.get_field(fi_title).decode('unicode-escape') # Get remaining fields. copy = self.get_field(fi_copy) admin = self.get_field(fi_admin) @@ -289,16 +289,16 @@ class EasyWorshipSongImport(SongImport): authors = self.get_field(fi_author) words = self.get_field(fi_words) if copy: - self.copyright = copy.decode() + self.copyright = copy.decode('unicode-escape') if admin: if copy: self.copyright += ', ' self.copyright += translate('SongsPlugin.EasyWorshipSongImport', - 'Administered by %s') % admin.decode() + 'Administered by %s') % admin.decode('unicode-escape') if ccli: - self.ccli_number = ccli.decode() + self.ccli_number = ccli.decode('unicode-escape') if authors: - authors = authors.decode() + authors = authors.decode('unicode-escape') else: authors = '' # Set the SongImport object members. @@ -328,7 +328,20 @@ class EasyWorshipSongImport(SongImport): self.add_author(author_name.strip()) if words: # Format the lyrics - result = strip_rtf(words.decode(), self.encoding) + result = None + decoded_words = None + try: + decoded_words = words.decode() + except UnicodeDecodeError: + log.debug('The unicode chars in the rtf was not escaped in the expected manor, doing it manually.') + newbytes = bytearray() + for b in words: + if b > 127: + newbytes += bytearray(b'\\\'') + bytearray(str(hex(b))[-2:].encode()) + else: + newbytes.append(b) + decoded_words = newbytes.decode() + result = strip_rtf(decoded_words, self.encoding) if result is None: return words, self.encoding = result diff --git a/tests/functional/openlp_plugins/songs/test_ewimport.py b/tests/functional/openlp_plugins/songs/test_ewimport.py index 49bc367cd..e93ce9517 100644 --- a/tests/functional/openlp_plugins/songs/test_ewimport.py +++ b/tests/functional/openlp_plugins/songs/test_ewimport.py @@ -67,7 +67,21 @@ SONG_TEST_DATA = [ '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': []}] + 'verse_order_list': []}, + {'title': 'Vi pløjed og vi så\'de', + 'authors': ['Matthias Claudius'], + 'copyright': 'Public Domain', + 'ccli_number': 0, + 'verses': + [('Vi pløjed og vi så\'de\nvor sæd i sorten jord,\nså bad vi ham os hjælpe,\nsom højt i Himlen bor,\n' + 'og han lod snefald hegne\nmod frosten barsk og hård,\nhan lod det tø og regne\nog varme mildt i vår.', + 'v1'), + ('Alle gode gaver\nde kommer ovenned,\nså tak da Gud, ja, pris dog Gud\nfor al hans kærlighed!', 'c1'), + ('Han er jo den, hvis vilje\nopholder alle ting,\nhan klæder markens lilje\nog runder himlens ring,\n' + 'ham lyder vind og vove,\nham rører ravnes nød,\nhvi skulle ej hans småbørn\nda og få dagligt brød?', 'v2'), + ('Ja, tak, du kære Fader,\nså mild, så rig, så rund,\nfor korn i hæs og lader,\nfor godt i allen stund!\n' + 'Vi kan jo intet give,\nsom nogen ting er værd,\nmen tag vort stakkels hjerte,\nså ringe som det er!', 'v3')], + 'verse_order_list': []}] EWS_SONG_TEST_DATA =\ {'title': 'Vi pløjed og vi så\'de', @@ -139,6 +153,14 @@ class TestEasyWorshipSongImport(TestCase): """ Test the functions in the :mod:`ewimport` module. """ + def setUp(self): + self.songimport_patcher = patch('openlp.plugins.songs.lib.ewimport.EasyWorshipSongImport.__init__') + self.mocked_songimport = self.songimport_patcher.start() + self.mocked_songimport.return_value = None + + def tearDown(self): + self.songimport_patcher.stop() + def create_field_desc_entry_test(self): """ Test creating an instance of the :class`FieldDescEntry` class. diff --git a/tests/resources/easyworshipsongs/Songs.DB b/tests/resources/easyworshipsongs/Songs.DB index 8c9679b86a8bacecdd559011139959ccbd0c155a..b6a13abc5b28bd7fbf670ba370ad6517fc2e7cb9 100644 GIT binary patch delta 492 zcmXZYKS/zXg?^7pjlLZH2RANuvX#*#CoOYx>+ z=~6ie-PEo7(T)xdF6y95?b2|dE)Ip#p@Nb=P~V3i@54L3zUiB#r37>&;sCVUq}biP zrkb;UD%JvGK#SqzbI2by)yY9~v-BXjecD?tZ;Dsze}*G

>7rSg&Lpd1xwil%9v zDkSJFeWWc4X_t=ZJ6+H(x}kI-Qdu&{qQ|sIit4mMK_M!uFDzf_fPT<9{ibWui;=oV zgY<}=lBD%w6n@Ye@sSwfn7}v^NFvQ=w4oh$(1A{Lp&LEOpoCsz(T9h@LNb>rViYDU zjA0VCwnYN7SnzY^Ed0Qh_6QXKXUbuB(jg_Y=HQ{hqmj9nyCeWV^ npLGpt*}cQFflF%`{;a4Pvv$=}BK+Jvu7!#Ael7fz{HFZ_Ud61z delta 356 zcmXZYJ4gdj5Czb?n|*O(;@{Qy0Sg;Nl!(Y$tjVSmI}uw!R*UdRWg)2yf&r@-6+uv} z)D(8Funh?5M69(?rxL703qjCZIKzQEOf^%^lrw(hz=dN2u>L#69K65kpemYi5?ubW zS=lg5i5cTF%Y`Z{#UVC6KO}7Yw*iEuo*`ZO! z0wS>@+er#5gx*O~Rg0ZNIyBhZYag6{uRl1r@y%No&UYIhdWG0X+&nZ<|MUHYetG-wSn_jQLpD zX@4---I}eN(ql*72b-@bDhk})6%G^{4&wMGE zGqujPx?`V~+%v}~B4eCQt?t_$b$79?kfE3K%($tonuv}W_Svt4( zBlr@Tyt6M$Yv4RqaedBEXcUO=EjUmr#^D2}xfL$JkteJ~JTk7V;MqAFnxIhJJe7-y zS5oLSwqxQUb?{uko$zb^o+jQ@D#aA6CioI|e*{P3UE0-=?@FQBRBVFm6m=oxQwv6# z5vDsi^PJ+S`yGC^lA_#|5(P`az<-moYaK8>-Zchm1EL*5lAiWB-rVbh)t6?%HGj ze^Uaww`;#vlRj1d|4V5sitGRZ;Mb;XOa;^gnb(RHP$Gmz&w&PNHfAQLy6;?sbddMo z<{ElCq^6|O8jb01d?Jlye9EOq*F-h;v0!E_^sF{6xEBEh_o=y;svFPn;bnpVN1sv$ zf)buD+=?KLlmJ8?rIW*PFP`PPC)FXHKmOv z=7!KH0(|_YhFXKJ6TQCGEVrg={Ho*_F+DXlbL0jn7daMR0=#_EH07Vx$3aTbhYn}* zG4f|B!T7nU$tR>2L4Schd@L)9JsT2d3s0vc-r{DE0)-@XifJ*05DN_=EFmp-anu|h za32>SvLfdy!%};p1Nf`}gloyY(U<~z&?(2e_(XYSpKK!>)42b%aVK|vTwRWcE57AeQ9;2qs96$q7amy z8Pq&f?TL^Cdp$^gn~YOE)Twd|w#8zK23PXZC!6h|aRsz3`7!r12oz_LkZ z*o?DQpL~y%6;tN~IKk$p<{g1a9FWQ>ds|937MPY#v~Wz*-yu-}=Q1~{$t&V)Hi2D5 zsV8mjq`OZ;L>a;=CoDuXUkq Date: Fri, 2 May 2014 23:48:59 +0200 Subject: [PATCH 094/212] fixes for tests --- openlp/plugins/songs/lib/ewimport.py | 2 +- .../openlp_plugins/songs/test_ewimport.py | 37 +++++++++++++++++- tests/resources/easyworshipsongs/Songs.DB | Bin 6144 -> 10240 bytes tests/resources/easyworshipsongs/Songs.MB | Bin 20480 -> 32768 bytes 4 files changed, 37 insertions(+), 2 deletions(-) diff --git a/openlp/plugins/songs/lib/ewimport.py b/openlp/plugins/songs/lib/ewimport.py index 604a70d81..e12b7e181 100644 --- a/openlp/plugins/songs/lib/ewimport.py +++ b/openlp/plugins/songs/lib/ewimport.py @@ -333,7 +333,7 @@ class EasyWorshipSongImport(SongImport): try: decoded_words = words.decode() except UnicodeDecodeError: - log.debug('The unicode chars in the rtf was not escaped in the expected manor, doing it manually.') + # The unicode chars in the rtf was not escaped in the expected manor, doing it manually. newbytes = bytearray() for b in words: if b > 127: diff --git a/tests/functional/openlp_plugins/songs/test_ewimport.py b/tests/functional/openlp_plugins/songs/test_ewimport.py index e93ce9517..e6344e39b 100644 --- a/tests/functional/openlp_plugins/songs/test_ewimport.py +++ b/tests/functional/openlp_plugins/songs/test_ewimport.py @@ -153,13 +153,14 @@ class TestEasyWorshipSongImport(TestCase): """ Test the functions in the :mod:`ewimport` module. """ - def setUp(self): + """def setUp(self): self.songimport_patcher = patch('openlp.plugins.songs.lib.ewimport.EasyWorshipSongImport.__init__') self.mocked_songimport = self.songimport_patcher.start() self.mocked_songimport.return_value = None def tearDown(self): self.songimport_patcher.stop() + """ def create_field_desc_entry_test(self): """ @@ -489,3 +490,37 @@ class TestEasyWorshipSongImport(TestCase): for verse_text, verse_tag in EWS_SONG_TEST_DATA['verses']: mocked_add_verse.assert_any_call(verse_text, verse_tag) mocked_finish.assert_called_with() + + def import_rtf_unescaped_unicode_test(self): + """ + Test import of rtf without the expected escaping of unicode + """ + + # 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.strip_rtf') as mocked_strip_rtf, \ + 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() + mocked_add_author = MagicMock() + mocked_add_verse = MagicMock() + mocked_finish = MagicMock() + mocked_title = MagicMock() + mocked_finish.return_value = True + importer = EasyWorshipSongImportLogger(mocked_manager) + importer.import_wizard = mocked_import_wizard + importer.stop_import_flag = False + importer.add_author = mocked_add_author + importer.add_verse = mocked_add_verse + importer.title = mocked_title + importer.finish = mocked_finish + importer.topics = [] + + # WHEN: running set_song_import_object on a verse string without the needed escaping + importer.set_song_import_object('Test Author', b'Det som var fr\x86n begynnelsen') + + # THEN: The escaping shoud be added + mocked_strip_rtf.assert_called_with("Det som var fr\'86n begynnelsen", 'cp1252') diff --git a/tests/resources/easyworshipsongs/Songs.DB b/tests/resources/easyworshipsongs/Songs.DB index b6a13abc5b28bd7fbf670ba370ad6517fc2e7cb9..3476950578a9374493f8fc0c2d318652526ec414 100644 GIT binary patch delta 246 zcmZoLXb9j%8!klf8XU#5V8;V(DHl*R^zh6V z%#&<;1EYq?j9j9d?YR{hIT?V;fslb=b0*Ij7Kp)ou jiKl~+Re*tE6ZhtLzFelwnLKA$Hs?$HV&1H;dXWhL78Mjk diff --git a/tests/resources/easyworshipsongs/Songs.MB b/tests/resources/easyworshipsongs/Songs.MB index f5c7ad54cd12d287485e8e254e2ee1c1db26590e..949d7755c20d8efd6d91c96f791d26cb61069c44 100644 GIT binary patch delta 1089 zcmZXT&u$Yj5XN`Yw2D+DQd*U?gk%8GKT?HKK`TT!KsnHIq!Jf;ak}1(6R$V2ofWx- z69+Cx%LDKVG-sr~2QSfMFG$STi6C&vX6^BBzV93F_**z0m%cn*UT6hDDOjimLHK?D z$IVJP3@(;~AedCD*Ud_~6x3JUTkE=c(R#RAwjXS#xLmDIb{~HYi>sZL;&NxH__@`7 zaB78=40iCn2syV%#Q&YYISt$~SY&k;Gh?CKqg^@R8jF)h)T;ROe3vBNcqbyHE9f#6 z(Z(8yw0e{zU1aA%DXv}XdaM~DNwi^7&^E_ou3!#{u3yd^r%pY2&PhVzBFM3o_Ks^g z(~NW`$P(U4pGoODu3%2Sq19*G@u9IKa3eeKg0Zv^QGD^ChCjdt?@cwj3d;?tq$4q6 z=X7oyp-BeA!C(*YlomMy#iOa+`=nO`9`0*F5Ca1#`qr_!R5k~5qaN4V_35a+amv!o z6lX3XM)I3Zi<52Wx6dGPZ}`M{JL8)vkC=jpVJZggq$$&2P{$OZM%MNi#9o*iHFcu9 zbYShAE;v<8TkvKK8o{)YhqjC)ydN!TA_4}kVW_br!U{_Tb2R7jWV79zx^zQT$RvP! zk^L-H&ZxiNM4kFDa_G~OD&tjV?yzj_x>StHk zCF^r1^#XH|I9yB@&!ZDcf4wuZ%s`sq*Ml``#7bfCYq?l#y(*5Yx5E9&`|6#g;>FhO a85J!bO|I78hW{)sO~$p)|1X|w%EjL)3vt%~ delta 38 ucmZo@U}{*vIKg6L+#>#o32Gcnj0~H&85kHha|ry9-xQ!Kyjj)xHV**mn+vJ{ From e8c7547ab25a956f23a2ce071efc8d6c61f91f26 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Sat, 3 May 2014 10:47:31 +0200 Subject: [PATCH 095/212] Try to get ccli number even if there is text in the field --- openlp/plugins/songs/lib/songshowplusimport.py | 8 ++++---- .../songshowplussongs/a mighty fortress is our god.json | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/openlp/plugins/songs/lib/songshowplusimport.py b/openlp/plugins/songs/lib/songshowplusimport.py index c1c6d04cb..fe81b61a2 100644 --- a/openlp/plugins/songs/lib/songshowplusimport.py +++ b/openlp/plugins/songs/lib/songshowplusimport.py @@ -147,10 +147,10 @@ class SongShowPlusImport(SongImport): elif block_key == COPYRIGHT: self.add_copyright(self.decode(data)) elif block_key == CCLI_NO: - try: - self.ccli_number = int(data) - except ValueError: - continue + # Try to get the CCLI number even if the field contains additional text + match = re.search(r'\d+', self.decode(data)) + if match: + self.ccli_number = int(match.group()) elif block_key == VERSE: self.add_verse(self.decode(data), "%s%s" % (VerseType.tags[VerseType.Verse], verse_no)) elif block_key == CHORUS: diff --git a/tests/resources/songshowplussongs/a mighty fortress is our god.json b/tests/resources/songshowplussongs/a mighty fortress is our god.json index 2ab95781d..2788ad05c 100644 --- a/tests/resources/songshowplussongs/a mighty fortress is our god.json +++ b/tests/resources/songshowplussongs/a mighty fortress is our god.json @@ -2,6 +2,7 @@ "authors": [ "Martin Luther" ], + "ccli_number": 12456, "comments": "", "copyright": "Public Domain", "song_number": 0, From e0d813db3f6ed8ad8dc88b4c7c886561f1145e98 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Sat, 3 May 2014 12:03:50 +0200 Subject: [PATCH 096/212] Add an option to display the songbook in the footer --- openlp/plugins/songs/lib/mediaitem.py | 8 +++++--- openlp/plugins/songs/lib/songstab.py | 11 +++++++++++ openlp/plugins/songs/songsplugin.py | 1 + 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index 32730ce3c..e2cb0f141 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -36,8 +36,8 @@ from PyQt4 import QtCore, QtGui from sqlalchemy.sql import or_ from openlp.core.common import Registry, AppLocation, Settings, check_directory_exists, UiStrings, translate -from openlp.core.lib import MediaManagerItem, ItemCapabilities, PluginStatus, ServiceItemContext, check_item_selected, \ - create_separated_list +from openlp.core.lib import MediaManagerItem, ItemCapabilities, PluginStatus, ServiceItem, ServiceItemContext, \ + check_item_selected, create_separated_list from openlp.core.lib.ui import create_widget_action from openlp.plugins.songs.forms.editsongform import EditSongForm from openlp.plugins.songs.forms.songmaintenanceform import SongMaintenanceForm @@ -460,7 +460,7 @@ class SongMediaItem(MediaManagerItem): service_item.background_audio = [m.file_name for m in song.media_files] return True - def generate_footer(self, item, song): + def generate_footer(self, item: ServiceItem, song: Song): """ Generates the song footer based on a song and adds details to a service item. @@ -506,6 +506,8 @@ class SongMediaItem(MediaManagerItem): item.raw_footer.append("%s: %s" % (AuthorType.Types[AuthorType.Translation], create_separated_list(authors_translation))) item.raw_footer.append(song.copyright) + if Settings().value('songs/display songbook') and song.book: + item.raw_footer.append("%s #%s" % (song.book.name, song.song_number)) if Settings().value('core/ccli number'): item.raw_footer.append(translate('SongsPlugin.MediaItem', 'CCLI License: ') + Settings().value('core/ccli number')) diff --git a/openlp/plugins/songs/lib/songstab.py b/openlp/plugins/songs/lib/songstab.py index bf74a4e7c..1cf06d047 100644 --- a/openlp/plugins/songs/lib/songstab.py +++ b/openlp/plugins/songs/lib/songstab.py @@ -59,6 +59,9 @@ class SongsTab(SettingsTab): self.add_from_service_check_box = QtGui.QCheckBox(self.mode_group_box) self.add_from_service_check_box.setObjectName('add_from_service_check_box') self.mode_layout.addWidget(self.add_from_service_check_box) + self.display_songbook_check_box = QtGui.QCheckBox(self.mode_group_box) + self.display_songbook_check_box.setObjectName('songbook_check_box') + self.mode_layout.addWidget(self.display_songbook_check_box) self.left_layout.addWidget(self.mode_group_box) self.left_layout.addStretch() self.right_layout.addStretch() @@ -66,6 +69,7 @@ class SongsTab(SettingsTab): self.tool_bar_active_check_box.stateChanged.connect(self.on_tool_bar_active_check_box_changed) self.update_on_edit_check_box.stateChanged.connect(self.on_update_on_edit_check_box_changed) self.add_from_service_check_box.stateChanged.connect(self.on_add_from_service_check_box_changed) + self.display_songbook_check_box.stateChanged.connect(self.on_songbook_check_box_changed) def retranslateUi(self): self.mode_group_box.setTitle(translate('SongsPlugin.SongsTab', 'Songs Mode')) @@ -75,6 +79,7 @@ class SongsTab(SettingsTab): self.update_on_edit_check_box.setText(translate('SongsPlugin.SongsTab', 'Update service from song edit')) self.add_from_service_check_box.setText(translate('SongsPlugin.SongsTab', 'Import missing songs from service files')) + self.display_songbook_check_box.setText(translate('SongsPlugin.SongsTab', 'Display songbook in footer')) def on_search_as_type_check_box_changed(self, check_state): self.song_search = (check_state == QtCore.Qt.Checked) @@ -88,6 +93,9 @@ class SongsTab(SettingsTab): def on_add_from_service_check_box_changed(self, check_state): self.update_load = (check_state == QtCore.Qt.Checked) + def on_songbook_check_box_changed(self, check_state): + self.display_songbook = (check_state == QtCore.Qt.Checked) + def load(self): settings = Settings() settings.beginGroup(self.settings_section) @@ -95,10 +103,12 @@ class SongsTab(SettingsTab): self.tool_bar = settings.value('display songbar') self.update_edit = settings.value('update service on edit') self.update_load = settings.value('add song from service') + self.display_songbook = settings.value('display songbook') self.search_as_type_check_box.setChecked(self.song_search) self.tool_bar_active_check_box.setChecked(self.tool_bar) self.update_on_edit_check_box.setChecked(self.update_edit) self.add_from_service_check_box.setChecked(self.update_load) + self.display_songbook_check_box.setChecked(self.display_songbook) settings.endGroup() def save(self): @@ -108,6 +118,7 @@ class SongsTab(SettingsTab): settings.setValue('display songbar', self.tool_bar) settings.setValue('update service on edit', self.update_edit) settings.setValue('add song from service', self.update_load) + settings.setValue('display songbook', self.display_songbook) settings.endGroup() if self.tab_visited: self.settings_form.register_post_process('songs_config_updated') diff --git a/openlp/plugins/songs/songsplugin.py b/openlp/plugins/songs/songsplugin.py index b1ddaf412..79fc282a6 100644 --- a/openlp/plugins/songs/songsplugin.py +++ b/openlp/plugins/songs/songsplugin.py @@ -63,6 +63,7 @@ __default_settings__ = { 'songs/search as type': False, 'songs/add song from service': True, 'songs/display songbar': True, + 'songs/display songbook': False, 'songs/last directory import': '', 'songs/last directory export': '', 'songs/songselect username': '', From a98ea19306d59217e802f8a1362167481b073735 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Sat, 3 May 2014 12:42:18 +0200 Subject: [PATCH 097/212] Add test for songbook display --- openlp/plugins/songs/lib/mediaitem.py | 5 ++-- .../openlp_plugins/songs/test_mediaitem.py | 29 +++++++++++++++++-- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index e2cb0f141..413498af6 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -124,7 +124,8 @@ class SongMediaItem(MediaManagerItem): log.debug('config_updated') self.search_as_you_type = Settings().value(self.settings_section + '/search as type') self.update_service_on_edit = Settings().value(self.settings_section + '/update service on edit') - self.add_song_from_service = Settings().value(self.settings_section + '/add song from service',) + self.add_song_from_service = Settings().value(self.settings_section + '/add song from service') + self.display_songbook = Settings().value(self.settings_section + '/display songbook') def retranslateUi(self): self.search_text_label.setText('%s:' % UiStrings().Search) @@ -506,7 +507,7 @@ class SongMediaItem(MediaManagerItem): item.raw_footer.append("%s: %s" % (AuthorType.Types[AuthorType.Translation], create_separated_list(authors_translation))) item.raw_footer.append(song.copyright) - if Settings().value('songs/display songbook') and song.book: + if self.display_songbook and song.book: item.raw_footer.append("%s #%s" % (song.book.name, song.song_number)) if Settings().value('core/ccli number'): item.raw_footer.append(translate('SongsPlugin.MediaItem', diff --git a/tests/functional/openlp_plugins/songs/test_mediaitem.py b/tests/functional/openlp_plugins/songs/test_mediaitem.py index 308881c2e..824b57a20 100644 --- a/tests/functional/openlp_plugins/songs/test_mediaitem.py +++ b/tests/functional/openlp_plugins/songs/test_mediaitem.py @@ -1,8 +1,6 @@ """ This module contains tests for the lib submodule of the Songs plugin. """ -import os -from tempfile import mkstemp from unittest import TestCase from PyQt4 import QtCore, QtGui @@ -29,6 +27,7 @@ class TestMediaItem(TestCase, TestMixin): with patch('openlp.core.lib.mediamanageritem.MediaManagerItem._setup'), \ patch('openlp.plugins.songs.forms.editsongform.EditSongForm.__init__'): self.media_item = SongMediaItem(None, MagicMock()) + self.media_item.display_songbook = False self.get_application() self.build_settings() QtCore.QLocale.setDefault(QtCore.QLocale('en_GB')) @@ -128,3 +127,29 @@ class TestMediaItem(TestCase, TestMixin): # THEN: I would get an amended footer string self.assertEqual(service_item.raw_footer, ['My Song', 'My copyright', 'CCLI License: 4321'], 'The array should be returned correctly with a song, an author, copyright and amended ccli') + + def build_song_footer_base_songbook_test(self): + """ + Test build songs footer with basic song and a songbook + """ + # GIVEN: A Song and a Service Item and a configured CCLI license + mock_song = MagicMock() + mock_song.title = 'My Song' + mock_song.copyright = 'My copyright' + mock_song.book = MagicMock() + mock_song.book.name = "My songbook" + mock_song.song_number = 12 + service_item = ServiceItem(None) + + # WHEN: I generate the Footer with default settings + self.media_item.generate_footer(service_item, mock_song) + + # THEN: The songbook should not be in the footer + self.assertEqual(service_item.raw_footer, ['My Song', 'My copyright']) + + # WHEN: I activate the "display songbook" option + self.media_item.display_songbook = True + self.media_item.generate_footer(service_item, mock_song) + + # THEN: The songbook should be in the footer + self.assertEqual(service_item.raw_footer, ['My Song', 'My copyright', 'My songbook #12']) From 922bbc3b57219b1789a1e4b7da81d185c549e35a Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Sat, 3 May 2014 12:53:51 +0200 Subject: [PATCH 098/212] Typo, remove annotations --- openlp/plugins/songs/lib/mediaitem.py | 2 +- tests/functional/openlp_plugins/songs/test_mediaitem.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index 413498af6..699f812e3 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -461,7 +461,7 @@ class SongMediaItem(MediaManagerItem): service_item.background_audio = [m.file_name for m in song.media_files] return True - def generate_footer(self, item: ServiceItem, song: Song): + def generate_footer(self, item, song): """ Generates the song footer based on a song and adds details to a service item. diff --git a/tests/functional/openlp_plugins/songs/test_mediaitem.py b/tests/functional/openlp_plugins/songs/test_mediaitem.py index 824b57a20..22291c6a6 100644 --- a/tests/functional/openlp_plugins/songs/test_mediaitem.py +++ b/tests/functional/openlp_plugins/songs/test_mediaitem.py @@ -132,7 +132,7 @@ class TestMediaItem(TestCase, TestMixin): """ Test build songs footer with basic song and a songbook """ - # GIVEN: A Song and a Service Item and a configured CCLI license + # GIVEN: A Song and a Service Item mock_song = MagicMock() mock_song.title = 'My Song' mock_song.copyright = 'My copyright' From 8b76dcc216526619c82616193887c7a0dbc6d425 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Sat, 3 May 2014 14:56:58 +0200 Subject: [PATCH 099/212] Fix bug 1310523 --- openlp/plugins/songs/lib/xml.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openlp/plugins/songs/lib/xml.py b/openlp/plugins/songs/lib/xml.py index b856cb53f..87e5da21e 100644 --- a/openlp/plugins/songs/lib/xml.py +++ b/openlp/plugins/songs/lib/xml.py @@ -310,9 +310,9 @@ class OpenLyrics(object): verse_tag = verse[0]['type'][0].lower() verse_number = verse[0]['label'] verse_def = verse_tag + verse_number - verse_tags.append(verse_def) # Create the letter from the number of duplicates - verse[0]['suffix'] = chr(96 + verse_tags.count(verse_def)) + verse[0][u'suffix'] = chr(97 + (verse_tags.count(verse_def) % 26)) + verse_tags.append(verse_def) # If the verse tag is a duplicate use the suffix letter for verse in verse_list: verse_tag = verse[0]['type'][0].lower() From 938f59416d2b89d20e41730d3ea8f1b6f14f913b Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Sat, 3 May 2014 15:46:48 +0200 Subject: [PATCH 100/212] Fix bug 1199639 Fixes: https://launchpad.net/bugs/1199639 --- openlp/plugins/songs/forms/editsongform.py | 36 ++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index 1814655ea..81a66e3f7 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -107,6 +107,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): self.audio_list_widget.setAlternatingRowColors(True) self.find_verse_split = re.compile('---\[\]---\n', re.UNICODE) self.whitespace = re.compile(r'\W+', re.UNICODE) + self.find_tags = re.compile(u'\{/?\w+\}', re.UNICODE) def _load_objects(self, cls, combo, cache): """ @@ -234,8 +235,43 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): self.manager.save_object(book) else: return False + # Validate tags (lp#1199639) + misplaced_tags = [] + for i in range(self.verse_list_widget.rowCount()): + item = self.verse_list_widget.item(i, 0) + tags = self.find_tags.findall(item.text()) + if not self._validate_tags(tags): + field = item.data(QtCore.Qt.UserRole) + misplaced_tags.append('%s %s' % (VerseType.translated_name(field[0]), field[1:])) + if misplaced_tags: + critical_error_message_box( + message=translate('SongsPlugin.EditSongForm', + 'There are misplaced formatting tags in the following verses:\n\n%s\n\n' + 'Please correct these tags before continuing.' % ', '.join(misplaced_tags))) + return False return True + def _validate_tags(self, _tags): + """ + Validates a list of tags + Deletes the first affiliated tag pair which is located side by side in the list + and call itself recursively with the shortened tag list. + If there is any misplaced tag in the list, either the length of the tag list is not even, + or the function won't find any tag pairs side by side. + If there is no misplaced tag, the length of the list will be zero on any recursive run. + + :return: True if the function can't find any mismatched tags. Else False. + """ + if len(_tags) == 0: + return True + if len(_tags) % 2 != 0: + return False + for i in range(len(_tags)-1): + if _tags[i+1] == "{/" + _tags[i][1:]: + del _tags[i:i+2] + return self._validate_tags(_tags) + return False + def _process_lyrics(self): """ Process the lyric data entered by the user into the OpenLP XML format. From a7bdb9dfa0962b96c411859037707cedb4d13e1f Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Sat, 3 May 2014 15:56:32 +0200 Subject: [PATCH 101/212] Fix bug 1310523 Fixes: https://launchpad.net/bugs/1310523 --- openlp/plugins/songs/forms/editsongform.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index 81a66e3f7..992cb533c 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -237,11 +237,13 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): return False # Validate tags (lp#1199639) misplaced_tags = [] + verse_tags = [] for i in range(self.verse_list_widget.rowCount()): item = self.verse_list_widget.item(i, 0) tags = self.find_tags.findall(item.text()) + field = item.data(QtCore.Qt.UserRole) + verse_tags.append(field) if not self._validate_tags(tags): - field = item.data(QtCore.Qt.UserRole) misplaced_tags.append('%s %s' % (VerseType.translated_name(field[0]), field[1:])) if misplaced_tags: critical_error_message_box( @@ -249,6 +251,17 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): 'There are misplaced formatting tags in the following verses:\n\n%s\n\n' 'Please correct these tags before continuing.' % ', '.join(misplaced_tags))) return False + for tag in verse_tags: + if verse_tags.count(tag) > 26: + # lp#1310523: OpenLyrics allows only a-z variants of one verse: + # http://openlyrics.info/dataformat.html#verse-name + critical_error_message_box(message=translate( + 'SongsPlugin.EditSongForm', 'You have %(count)s verses named %(name)s %(number)s. ' + 'You can have at most 26 verses with the same name' % + {'count': verse_tags.count(tag), + 'name': VerseType.translated_name(tag[0]), + 'number': tag[1:]})) + return False return True def _validate_tags(self, _tags): From afaf9480c317c8336a1a722a0813e515b047f810 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Sat, 3 May 2014 15:58:31 +0200 Subject: [PATCH 102/212] Rename variable --- openlp/plugins/songs/forms/editsongform.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index 992cb533c..ae47a110f 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -264,7 +264,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): return False return True - def _validate_tags(self, _tags): + def _validate_tags(self, tags): """ Validates a list of tags Deletes the first affiliated tag pair which is located side by side in the list @@ -273,16 +273,17 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): or the function won't find any tag pairs side by side. If there is no misplaced tag, the length of the list will be zero on any recursive run. + :param tags: A list of tags :return: True if the function can't find any mismatched tags. Else False. """ - if len(_tags) == 0: + if len(tags) == 0: return True - if len(_tags) % 2 != 0: + if len(tags) % 2 != 0: return False - for i in range(len(_tags)-1): - if _tags[i+1] == "{/" + _tags[i][1:]: - del _tags[i:i+2] - return self._validate_tags(_tags) + for i in range(len(tags)-1): + if tags[i+1] == "{/" + tags[i][1:]: + del tags[i:i+2] + return self._validate_tags(tags) return False def _process_lyrics(self): From 635ab98f7301dedd8c5c820490e8b610a55cad34 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Sat, 3 May 2014 16:19:29 +0200 Subject: [PATCH 103/212] Fix bug 1216234 Fixes: https://launchpad.net/bugs/1216234 --- openlp/plugins/bibles/lib/mediaitem.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index dd14c6409..76f91071a 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -60,7 +60,6 @@ class BibleMediaItem(MediaManagerItem): log.info('Bible Media Item loaded') def __init__(self, parent, plugin): - self.icon_path = 'songs/song' self.lock_icon = build_icon(':/bibles/bibles_search_lock.png') self.unlock_icon = build_icon(':/bibles/bibles_search_unlock.png') MediaManagerItem.__init__(self, parent, plugin) @@ -172,6 +171,7 @@ class BibleMediaItem(MediaManagerItem): self.page_layout.addWidget(tab) tab.setVisible(False) lock_button.toggled.connect(self.on_lock_button_toggled) + second_combo_box.currentIndexChanged.connect(self.on_second_bible_combobox_index_changed) setattr(self, prefix + 'VersionLabel', version_label) setattr(self, prefix + 'VersionComboBox', version_combo_box) setattr(self, prefix + 'SecondLabel', second_label) @@ -263,11 +263,15 @@ class BibleMediaItem(MediaManagerItem): def config_update(self): log.debug('config_update') if Settings().value(self.settings_section + '/second bibles'): + self.quickSecondLabel.setVisible(True) + self.quickSecondComboBox.setVisible(True) self.advancedSecondLabel.setVisible(True) self.advancedSecondComboBox.setVisible(True) self.quickSecondLabel.setVisible(True) self.quickSecondComboBox.setVisible(True) else: + self.quickSecondLabel.setVisible(False) + self.quickSecondComboBox.setVisible(False) self.advancedSecondLabel.setVisible(False) self.advancedSecondComboBox.setVisible(False) self.quickSecondLabel.setVisible(False) @@ -459,6 +463,17 @@ class BibleMediaItem(MediaManagerItem): books.sort(key=get_locale_key) set_case_insensitive_completer(books, self.quick_search_edit) + def on_second_bible_combobox_index_changed(self, selection): + """ + Activate the style combobox only when no second bible is selected + """ + if selection == 0: + self.quickStyleComboBox.setEnabled(True) + self.advancedStyleComboBox.setEnabled(True) + else: + self.quickStyleComboBox.setEnabled(False) + self.advancedStyleComboBox.setEnabled(False) + def on_import_click(self): if not hasattr(self, 'import_wizard'): self.import_wizard = BibleImportForm(self, self.plugin.manager, self.plugin) From a6a08bcfdd1b10cbfc0febb29766aaa5e05afe6a Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Sat, 3 May 2014 17:01:43 +0200 Subject: [PATCH 104/212] Fix bug 1157938 Fixes: https://launchpad.net/bugs/1157938 --- .bzrignore | 1 + openlp/core/ui/aboutdialog.py | 2 +- openlp/core/ui/exceptiondialog.py | 3 ++- openlp/core/ui/filerenamedialog.py | 3 ++- openlp/core/ui/firsttimelanguagedialog.py | 2 ++ openlp/core/ui/firsttimewizard.py | 2 ++ openlp/core/ui/formattingtagdialog.py | 1 + openlp/core/ui/mainwindow.py | 2 +- openlp/core/ui/plugindialog.py | 2 ++ openlp/core/ui/printservicedialog.py | 1 + openlp/core/ui/serviceitemeditdialog.py | 2 ++ openlp/core/ui/settingsdialog.py | 2 +- openlp/core/ui/shortcutlistdialog.py | 1 + openlp/core/ui/starttimedialog.py | 2 ++ openlp/core/ui/themelayoutdialog.py | 2 ++ openlp/core/ui/themewizard.py | 1 + openlp/core/ui/wizard.py | 1 + openlp/plugins/alerts/forms/alertdialog.py | 2 +- openlp/plugins/bibles/forms/booknamedialog.py | 2 ++ openlp/plugins/bibles/forms/editbibledialog.py | 2 +- openlp/plugins/bibles/forms/languagedialog.py | 2 ++ openlp/plugins/custom/forms/editcustomdialog.py | 2 +- openlp/plugins/custom/forms/editcustomslidedialog.py | 3 ++- openlp/plugins/songs/forms/authorsdialog.py | 2 +- openlp/plugins/songs/forms/editsongdialog.py | 2 +- openlp/plugins/songs/forms/editversedialog.py | 1 + openlp/plugins/songs/forms/mediafilesdialog.py | 2 +- openlp/plugins/songs/forms/songbookdialog.py | 3 ++- openlp/plugins/songs/forms/songmaintenancedialog.py | 1 + openlp/plugins/songs/forms/topicsdialog.py | 3 ++- openlp/plugins/songusage/forms/songusagedeletedialog.py | 2 ++ openlp/plugins/songusage/forms/songusagedetaildialog.py | 1 + 32 files changed, 46 insertions(+), 14 deletions(-) diff --git a/.bzrignore b/.bzrignore index 6377150e0..a0c3f0b4f 100644 --- a/.bzrignore +++ b/.bzrignore @@ -32,3 +32,4 @@ tests.kdev4 *.orig __pycache__ *.dll +.directory diff --git a/openlp/core/ui/aboutdialog.py b/openlp/core/ui/aboutdialog.py index 276a073bb..251e0657c 100644 --- a/openlp/core/ui/aboutdialog.py +++ b/openlp/core/ui/aboutdialog.py @@ -44,7 +44,7 @@ class Ui_AboutDialog(object): Set up the UI for the dialog. """ about_dialog.setObjectName('about_dialog') - about_dialog.setWindowIcon(build_icon(':/icon/openlp-logo-16x16.png')) + about_dialog.setWindowIcon(build_icon(':/icon/openlp-logo.svg')) self.about_dialog_layout = QtGui.QVBoxLayout(about_dialog) self.about_dialog_layout.setObjectName('about_dialog_layout') self.logo_label = QtGui.QLabel(about_dialog) diff --git a/openlp/core/ui/exceptiondialog.py b/openlp/core/ui/exceptiondialog.py index b8b3941cd..329ed7797 100644 --- a/openlp/core/ui/exceptiondialog.py +++ b/openlp/core/ui/exceptiondialog.py @@ -32,7 +32,7 @@ The GUI widgets of the exception dialog. from PyQt4 import QtGui -from openlp.core.lib import translate +from openlp.core.lib import translate, build_icon from openlp.core.lib.ui import create_button, create_button_box @@ -45,6 +45,7 @@ class Ui_ExceptionDialog(object): Set up the UI. """ exception_dialog.setObjectName('exception_dialog') + exception_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) self.exception_layout = QtGui.QVBoxLayout(exception_dialog) self.exception_layout.setObjectName('exception_layout') self.message_layout = QtGui.QHBoxLayout() diff --git a/openlp/core/ui/filerenamedialog.py b/openlp/core/ui/filerenamedialog.py index 0900b82c3..4a316aece 100644 --- a/openlp/core/ui/filerenamedialog.py +++ b/openlp/core/ui/filerenamedialog.py @@ -31,7 +31,7 @@ The UI widgets for the rename dialog """ from PyQt4 import QtCore, QtGui -from openlp.core.lib import translate +from openlp.core.lib import translate, build_icon from openlp.core.lib.ui import create_button_box @@ -44,6 +44,7 @@ class Ui_FileRenameDialog(object): Set up the UI """ file_rename_dialog.setObjectName('file_rename_dialog') + file_rename_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) file_rename_dialog.resize(300, 10) self.dialog_layout = QtGui.QGridLayout(file_rename_dialog) self.dialog_layout.setObjectName('dialog_layout') diff --git a/openlp/core/ui/firsttimelanguagedialog.py b/openlp/core/ui/firsttimelanguagedialog.py index 10b341c2c..b4263e498 100644 --- a/openlp/core/ui/firsttimelanguagedialog.py +++ b/openlp/core/ui/firsttimelanguagedialog.py @@ -32,6 +32,7 @@ The UI widgets of the language selection dialog. from PyQt4 import QtGui from openlp.core.common import translate +from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box @@ -44,6 +45,7 @@ class Ui_FirstTimeLanguageDialog(object): Set up the UI. """ language_dialog.setObjectName('language_dialog') + language_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) language_dialog.resize(300, 50) self.dialog_layout = QtGui.QVBoxLayout(language_dialog) self.dialog_layout.setContentsMargins(8, 8, 8, 8) diff --git a/openlp/core/ui/firsttimewizard.py b/openlp/core/ui/firsttimewizard.py index 1a270f931..35623ded2 100644 --- a/openlp/core/ui/firsttimewizard.py +++ b/openlp/core/ui/firsttimewizard.py @@ -34,6 +34,7 @@ from PyQt4 import QtCore, QtGui import sys from openlp.core.common import translate +from openlp.core.lib import build_icon from openlp.core.lib.ui import add_welcome_page @@ -60,6 +61,7 @@ class Ui_FirstTimeWizard(object): Set up the UI. """ first_time_wizard.setObjectName('first_time_wizard') + first_time_wizard.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) first_time_wizard.resize(550, 386) first_time_wizard.setModal(True) first_time_wizard.setWizardStyle(QtGui.QWizard.ModernStyle) diff --git a/openlp/core/ui/formattingtagdialog.py b/openlp/core/ui/formattingtagdialog.py index 387bca0a7..569405a05 100644 --- a/openlp/core/ui/formattingtagdialog.py +++ b/openlp/core/ui/formattingtagdialog.py @@ -45,6 +45,7 @@ class Ui_FormattingTagDialog(object): Set up the UI """ formatting_tag_dialog.setObjectName('formatting_tag_dialog') + formatting_tag_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) formatting_tag_dialog.resize(725, 548) self.list_data_grid_layout = QtGui.QVBoxLayout(formatting_tag_dialog) self.list_data_grid_layout.setMargin(8) diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index 9c193b079..5b5aa0781 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -89,7 +89,7 @@ class Ui_MainWindow(object): Set up the user interface """ main_window.setObjectName('MainWindow') - main_window.setWindowIcon(build_icon(':/icon/openlp-logo-64x64.png')) + main_window.setWindowIcon(build_icon(':/icon/openlp-logo.svg')) main_window.setDockNestingEnabled(True) # Set up the main container, which contains all the other form widgets. self.main_content = QtGui.QWidget(main_window) diff --git a/openlp/core/ui/plugindialog.py b/openlp/core/ui/plugindialog.py index f4d2d991b..40311677e 100644 --- a/openlp/core/ui/plugindialog.py +++ b/openlp/core/ui/plugindialog.py @@ -32,6 +32,7 @@ The UI widgets of the plugin view dialog from PyQt4 import QtCore, QtGui from openlp.core.common import UiStrings, translate +from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box @@ -44,6 +45,7 @@ class Ui_PluginViewDialog(object): Set up the UI """ pluginViewDialog.setObjectName('pluginViewDialog') + pluginViewDialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) pluginViewDialog.setWindowModality(QtCore.Qt.ApplicationModal) self.plugin_layout = QtGui.QVBoxLayout(pluginViewDialog) self.plugin_layout.setObjectName('plugin_layout') diff --git a/openlp/core/ui/printservicedialog.py b/openlp/core/ui/printservicedialog.py index f59dcb044..0873a0b4a 100644 --- a/openlp/core/ui/printservicedialog.py +++ b/openlp/core/ui/printservicedialog.py @@ -56,6 +56,7 @@ class Ui_PrintServiceDialog(object): Set up the UI """ print_service_dialog.setObjectName('print_service_dialog') + print_service_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) print_service_dialog.resize(664, 594) self.main_layout = QtGui.QVBoxLayout(print_service_dialog) self.main_layout.setSpacing(0) diff --git a/openlp/core/ui/serviceitemeditdialog.py b/openlp/core/ui/serviceitemeditdialog.py index 76139c875..1fa19eb31 100644 --- a/openlp/core/ui/serviceitemeditdialog.py +++ b/openlp/core/ui/serviceitemeditdialog.py @@ -32,6 +32,7 @@ The UI widgets for the service item edit dialog from PyQt4 import QtGui from openlp.core.common import translate +from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box, create_button @@ -44,6 +45,7 @@ class Ui_ServiceItemEditDialog(object): Set up the UI """ serviceItemEditDialog.setObjectName('serviceItemEditDialog') + serviceItemEditDialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) self.dialog_layout = QtGui.QGridLayout(serviceItemEditDialog) self.dialog_layout.setContentsMargins(8, 8, 8, 8) self.dialog_layout.setSpacing(8) diff --git a/openlp/core/ui/settingsdialog.py b/openlp/core/ui/settingsdialog.py index f625680d6..fbac6a155 100644 --- a/openlp/core/ui/settingsdialog.py +++ b/openlp/core/ui/settingsdialog.py @@ -45,8 +45,8 @@ class Ui_SettingsDialog(object): Set up the UI """ settings_dialog.setObjectName('settings_dialog') + settings_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) settings_dialog.resize(800, 500) - settings_dialog.setWindowIcon(build_icon(':/system/system_settings.png')) self.dialog_layout = QtGui.QGridLayout(settings_dialog) self.dialog_layout.setObjectName('dialog_layout') self.dialog_layout.setMargin(8) diff --git a/openlp/core/ui/shortcutlistdialog.py b/openlp/core/ui/shortcutlistdialog.py index 54433da24..ef2dc4056 100644 --- a/openlp/core/ui/shortcutlistdialog.py +++ b/openlp/core/ui/shortcutlistdialog.py @@ -66,6 +66,7 @@ class Ui_ShortcutListDialog(object): Set up the UI """ shortcutListDialog.setObjectName('shortcutListDialog') + shortcutListDialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) shortcutListDialog.resize(500, 438) self.shortcut_list_layout = QtGui.QVBoxLayout(shortcutListDialog) self.shortcut_list_layout.setObjectName('shortcut_list_layout') diff --git a/openlp/core/ui/starttimedialog.py b/openlp/core/ui/starttimedialog.py index 54e0629a4..cfd507bb2 100644 --- a/openlp/core/ui/starttimedialog.py +++ b/openlp/core/ui/starttimedialog.py @@ -32,6 +32,7 @@ The UI widgets for the time dialog from PyQt4 import QtCore, QtGui from openlp.core.common import UiStrings, translate +from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box @@ -44,6 +45,7 @@ class Ui_StartTimeDialog(object): Set up the UI """ StartTimeDialog.setObjectName('StartTimeDialog') + StartTimeDialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) StartTimeDialog.resize(350, 10) self.dialog_layout = QtGui.QGridLayout(StartTimeDialog) self.dialog_layout.setObjectName('dialog_layout') diff --git a/openlp/core/ui/themelayoutdialog.py b/openlp/core/ui/themelayoutdialog.py index 023d6259c..d3fbeed2d 100644 --- a/openlp/core/ui/themelayoutdialog.py +++ b/openlp/core/ui/themelayoutdialog.py @@ -32,6 +32,7 @@ The layout of the theme from PyQt4 import QtGui from openlp.core.common import translate +from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box @@ -44,6 +45,7 @@ class Ui_ThemeLayoutDialog(object): Set up the UI """ themeLayoutDialog.setObjectName('themeLayoutDialogDialog') + themeLayoutDialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) self.preview_layout = QtGui.QVBoxLayout(themeLayoutDialog) self.preview_layout.setObjectName('preview_layout') self.preview_area = QtGui.QWidget(themeLayoutDialog) diff --git a/openlp/core/ui/themewizard.py b/openlp/core/ui/themewizard.py index 77ccb0663..bda52c807 100644 --- a/openlp/core/ui/themewizard.py +++ b/openlp/core/ui/themewizard.py @@ -46,6 +46,7 @@ class Ui_ThemeWizard(object): Set up the UI """ themeWizard.setObjectName('OpenLP.ThemeWizard') + themeWizard.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) themeWizard.setModal(True) themeWizard.setWizardStyle(QtGui.QWizard.ModernStyle) themeWizard.setOptions(QtGui.QWizard.IndependentPages | diff --git a/openlp/core/ui/wizard.py b/openlp/core/ui/wizard.py index 5996296b5..908981dc6 100644 --- a/openlp/core/ui/wizard.py +++ b/openlp/core/ui/wizard.py @@ -118,6 +118,7 @@ class OpenLPWizard(QtGui.QWizard, RegistryProperties): """ Set up the wizard UI. """ + self.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) self.setModal(True) self.setWizardStyle(QtGui.QWizard.ModernStyle) self.setOptions(QtGui.QWizard.IndependentPages | diff --git a/openlp/plugins/alerts/forms/alertdialog.py b/openlp/plugins/alerts/forms/alertdialog.py index e4fd29a39..47e61365a 100644 --- a/openlp/plugins/alerts/forms/alertdialog.py +++ b/openlp/plugins/alerts/forms/alertdialog.py @@ -46,7 +46,7 @@ class Ui_AlertDialog(object): """ alert_dialog.setObjectName('alert_dialog') alert_dialog.resize(400, 300) - alert_dialog.setWindowIcon(build_icon(':/icon/openlp-logo-16x16.png')) + alert_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) self.alert_dialog_layout = QtGui.QGridLayout(alert_dialog) self.alert_dialog_layout.setObjectName('alert_dialog_layout') self.alert_text_layout = QtGui.QFormLayout() diff --git a/openlp/plugins/bibles/forms/booknamedialog.py b/openlp/plugins/bibles/forms/booknamedialog.py index 5903391c3..120caeff0 100644 --- a/openlp/plugins/bibles/forms/booknamedialog.py +++ b/openlp/plugins/bibles/forms/booknamedialog.py @@ -30,12 +30,14 @@ from PyQt4 import QtCore, QtGui from openlp.core.common import translate +from openlp.core.lib import build_icon 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') + book_name_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) book_name_dialog.resize(400, 271) self.book_name_layout = QtGui.QVBoxLayout(book_name_dialog) self.book_name_layout.setSpacing(8) diff --git a/openlp/plugins/bibles/forms/editbibledialog.py b/openlp/plugins/bibles/forms/editbibledialog.py index 1fbaa2f1e..e9e39a053 100644 --- a/openlp/plugins/bibles/forms/editbibledialog.py +++ b/openlp/plugins/bibles/forms/editbibledialog.py @@ -39,8 +39,8 @@ from openlp.plugins.bibles.lib.db import BiblesResourcesDB class Ui_EditBibleDialog(object): def setupUi(self, edit_bible_dialog): edit_bible_dialog.setObjectName('edit_bible_dialog') + edit_bible_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) edit_bible_dialog.resize(520, 400) - edit_bible_dialog.setWindowIcon(build_icon(':/icon/openlp-logo-16x16.png')) edit_bible_dialog.setModal(True) self.dialog_layout = QtGui.QVBoxLayout(edit_bible_dialog) self.dialog_layout.setSpacing(8) diff --git a/openlp/plugins/bibles/forms/languagedialog.py b/openlp/plugins/bibles/forms/languagedialog.py index 10382ea13..ab40503d2 100644 --- a/openlp/plugins/bibles/forms/languagedialog.py +++ b/openlp/plugins/bibles/forms/languagedialog.py @@ -30,12 +30,14 @@ from PyQt4 import QtGui from openlp.core.common import translate +from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box class Ui_LanguageDialog(object): def setupUi(self, language_dialog): language_dialog.setObjectName('language_dialog') + language_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) language_dialog.resize(400, 165) self.language_layout = QtGui.QVBoxLayout(language_dialog) self.language_layout.setSpacing(8) diff --git a/openlp/plugins/custom/forms/editcustomdialog.py b/openlp/plugins/custom/forms/editcustomdialog.py index 22a33d921..5ed92f95e 100644 --- a/openlp/plugins/custom/forms/editcustomdialog.py +++ b/openlp/plugins/custom/forms/editcustomdialog.py @@ -41,8 +41,8 @@ class Ui_CustomEditDialog(object): :param custom_edit_dialog: The Dialog """ custom_edit_dialog.setObjectName('custom_edit_dialog') + custom_edit_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) custom_edit_dialog.resize(450, 350) - custom_edit_dialog.setWindowIcon(build_icon(':/icon/openlp-logo-16x16.png')) self.dialog_layout = QtGui.QVBoxLayout(custom_edit_dialog) self.dialog_layout.setObjectName('dialog_layout') self.title_layout = QtGui.QHBoxLayout() diff --git a/openlp/plugins/custom/forms/editcustomslidedialog.py b/openlp/plugins/custom/forms/editcustomslidedialog.py index 3b74566fc..c59cc4cd1 100644 --- a/openlp/plugins/custom/forms/editcustomslidedialog.py +++ b/openlp/plugins/custom/forms/editcustomslidedialog.py @@ -30,13 +30,14 @@ from PyQt4 import QtGui from openlp.core.common import UiStrings, translate -from openlp.core.lib import SpellTextEdit +from openlp.core.lib import SpellTextEdit, build_icon 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') + custom_slide_edit_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) custom_slide_edit_dialog.resize(350, 300) self.dialog_layout = QtGui.QVBoxLayout(custom_slide_edit_dialog) self.slide_text_edit = SpellTextEdit(self) diff --git a/openlp/plugins/songs/forms/authorsdialog.py b/openlp/plugins/songs/forms/authorsdialog.py index 65fb169b0..c5a93894b 100644 --- a/openlp/plugins/songs/forms/authorsdialog.py +++ b/openlp/plugins/songs/forms/authorsdialog.py @@ -43,8 +43,8 @@ class Ui_AuthorsDialog(object): Set up the UI for the dialog. """ authors_dialog.setObjectName('authors_dialog') + authorsDialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) authors_dialog.resize(300, 10) - authors_dialog.setWindowIcon(build_icon(':/icon/openlp-logo-16x16.png')) authors_dialog.setModal(True) self.dialog_layout = QtGui.QVBoxLayout(authors_dialog) self.dialog_layout.setObjectName('dialog_layout') diff --git a/openlp/plugins/songs/forms/editsongdialog.py b/openlp/plugins/songs/forms/editsongdialog.py index d0fb51a2d..a9ca71946 100644 --- a/openlp/plugins/songs/forms/editsongdialog.py +++ b/openlp/plugins/songs/forms/editsongdialog.py @@ -43,8 +43,8 @@ class Ui_EditSongDialog(object): """ def setupUi(self, edit_song_dialog): edit_song_dialog.setObjectName('edit_song_dialog') + edit_song_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) edit_song_dialog.resize(650, 400) - edit_song_dialog.setWindowIcon(build_icon(':/icon/openlp-logo-16x16.png')) edit_song_dialog.setModal(True) self.dialog_layout = QtGui.QVBoxLayout(edit_song_dialog) self.dialog_layout.setSpacing(8) diff --git a/openlp/plugins/songs/forms/editversedialog.py b/openlp/plugins/songs/forms/editversedialog.py index f1901b203..1dc3d9182 100644 --- a/openlp/plugins/songs/forms/editversedialog.py +++ b/openlp/plugins/songs/forms/editversedialog.py @@ -37,6 +37,7 @@ from openlp.plugins.songs.lib import VerseType class Ui_EditVerseDialog(object): def setupUi(self, edit_verse_dialog): edit_verse_dialog.setObjectName('edit_verse_dialog') + edit_verse_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) edit_verse_dialog.resize(400, 400) edit_verse_dialog.setModal(True) self.dialog_layout = QtGui.QVBoxLayout(edit_verse_dialog) diff --git a/openlp/plugins/songs/forms/mediafilesdialog.py b/openlp/plugins/songs/forms/mediafilesdialog.py index 3a1db39ed..495e08883 100644 --- a/openlp/plugins/songs/forms/mediafilesdialog.py +++ b/openlp/plugins/songs/forms/mediafilesdialog.py @@ -42,10 +42,10 @@ class Ui_MediaFilesDialog(object): Set up the user interface. """ media_files_dialog.setObjectName('media_files_dialog') + media_files_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) media_files_dialog.setWindowModality(QtCore.Qt.ApplicationModal) media_files_dialog.resize(400, 300) media_files_dialog.setModal(True) - media_files_dialog.setWindowIcon(build_icon(':/icon/openlp-logo-16x16.png')) self.files_vertical_layout = QtGui.QVBoxLayout(media_files_dialog) self.files_vertical_layout.setSpacing(8) self.files_vertical_layout.setMargin(8) diff --git a/openlp/plugins/songs/forms/songbookdialog.py b/openlp/plugins/songs/forms/songbookdialog.py index 8cacef1a2..1ed79a4eb 100644 --- a/openlp/plugins/songs/forms/songbookdialog.py +++ b/openlp/plugins/songs/forms/songbookdialog.py @@ -29,7 +29,7 @@ from PyQt4 import QtGui -from openlp.core.lib import translate +from openlp.core.lib import translate, build_icon from openlp.core.lib.ui import create_button_box @@ -42,6 +42,7 @@ class Ui_SongBookDialog(object): Set up the user interface. """ song_book_dialog.setObjectName('song_book_dialog') + song_book_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) song_book_dialog.resize(300, 10) self.dialog_layout = QtGui.QVBoxLayout(song_book_dialog) self.dialog_layout.setObjectName('dialog_layout') diff --git a/openlp/plugins/songs/forms/songmaintenancedialog.py b/openlp/plugins/songs/forms/songmaintenancedialog.py index 84e3535d3..893ae9c1c 100644 --- a/openlp/plugins/songs/forms/songmaintenancedialog.py +++ b/openlp/plugins/songs/forms/songmaintenancedialog.py @@ -44,6 +44,7 @@ class Ui_SongMaintenanceDialog(object): Set up the user interface for the song maintenance dialog """ song_maintenance_dialog.setObjectName('song_maintenance_dialog') + song_maintenance_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) song_maintenance_dialog.setWindowModality(QtCore.Qt.ApplicationModal) song_maintenance_dialog.resize(10, 350) self.dialog_layout = QtGui.QGridLayout(song_maintenance_dialog) diff --git a/openlp/plugins/songs/forms/topicsdialog.py b/openlp/plugins/songs/forms/topicsdialog.py index eb6229bf6..ffa7da333 100644 --- a/openlp/plugins/songs/forms/topicsdialog.py +++ b/openlp/plugins/songs/forms/topicsdialog.py @@ -29,7 +29,7 @@ from PyQt4 import QtGui -from openlp.core.lib import translate +from openlp.core.lib import translate, build_icon from openlp.core.lib.ui import create_button_box @@ -42,6 +42,7 @@ class Ui_TopicsDialog(object): Set up the user interface for the topics dialog. """ topics_dialog.setObjectName('topics_dialog') + topics_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) topics_dialog.resize(300, 10) self.dialog_layout = QtGui.QVBoxLayout(topics_dialog) self.dialog_layout.setObjectName('dialog_layout') diff --git a/openlp/plugins/songusage/forms/songusagedeletedialog.py b/openlp/plugins/songusage/forms/songusagedeletedialog.py index 01597a790..190292678 100644 --- a/openlp/plugins/songusage/forms/songusagedeletedialog.py +++ b/openlp/plugins/songusage/forms/songusagedeletedialog.py @@ -30,6 +30,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.common import translate +from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box @@ -44,6 +45,7 @@ class Ui_SongUsageDeleteDialog(object): :param song_usage_delete_dialog: """ song_usage_delete_dialog.setObjectName('song_usage_delete_dialog') + song_usage_delete_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) song_usage_delete_dialog.resize(291, 243) self.vertical_layout = QtGui.QVBoxLayout(song_usage_delete_dialog) self.vertical_layout.setSpacing(8) diff --git a/openlp/plugins/songusage/forms/songusagedetaildialog.py b/openlp/plugins/songusage/forms/songusagedetaildialog.py index ede5075a0..9431c7894 100644 --- a/openlp/plugins/songusage/forms/songusagedetaildialog.py +++ b/openlp/plugins/songusage/forms/songusagedetaildialog.py @@ -45,6 +45,7 @@ class Ui_SongUsageDetailDialog(object): :param song_usage_detail_dialog: """ song_usage_detail_dialog.setObjectName('song_usage_detail_dialog') + song_usage_detail_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) song_usage_detail_dialog.resize(609, 413) self.vertical_layout = QtGui.QVBoxLayout(song_usage_detail_dialog) self.vertical_layout.setSpacing(8) From b02fc541a348610513d477a32d559e6392d016fb Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Sat, 3 May 2014 17:22:10 +0200 Subject: [PATCH 105/212] Reorganize tests --- tests/functional/openlp_core_lib/test_ui.py | 64 ++++++++++----------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/tests/functional/openlp_core_lib/test_ui.py b/tests/functional/openlp_core_lib/test_ui.py index 025b1a638..1002550d2 100644 --- a/tests/functional/openlp_core_lib/test_ui.py +++ b/tests/functional/openlp_core_lib/test_ui.py @@ -82,6 +82,21 @@ class TestUi(TestCase): 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()) + def test_create_button(self): """ Test creating a button @@ -114,38 +129,6 @@ class TestUi(TestCase): self.assertEqual('my_btn', btn.objectName()) self.assertTrue(btn.isEnabled()) - def test_create_valign_selection_widgets(self): - """ - Test creating a combo box for valign selection - """ - # GIVEN: A dialog - dialog = QtGui.QDialog() - - # WHEN: We create the widgets - label, combo = create_valign_selection_widgets(dialog) - - # 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) - - 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()) - def test_create_action(self): """ Test creating an action @@ -170,3 +153,20 @@ class TestUi(TestCase): self.assertIsInstance(action.icon(), QtGui.QIcon) self.assertEqual('my tooltip', action.toolTip()) self.assertEqual('my statustip', action.statusTip()) + + def test_create_valign_selection_widgets(self): + """ + Test creating a combo box for valign selection + """ + # GIVEN: A dialog + dialog = QtGui.QDialog() + + # WHEN: We create the widgets + label, combo = create_valign_selection_widgets(dialog) + + # 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 f60379b542a285eb0e4789bfe18e9c2e078c1d54 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Sat, 3 May 2014 17:22:20 +0200 Subject: [PATCH 106/212] Add new test --- tests/functional/openlp_core_lib/test_ui.py | 27 +++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tests/functional/openlp_core_lib/test_ui.py b/tests/functional/openlp_core_lib/test_ui.py index 1002550d2..f1b8ee17a 100644 --- a/tests/functional/openlp_core_lib/test_ui.py +++ b/tests/functional/openlp_core_lib/test_ui.py @@ -170,3 +170,30 @@ class TestUi(TestCase): self.assertEqual(combo, label.buddy()) for text in [UiStrings().Top, UiStrings().Middle, UiStrings().Bottom]: self.assertTrue(combo.findText(text) >= 0) + + def test_find_and_set_in_combo_box(self): + """ + Test finding a string in a combo box and setting it as the selected item if present + """ + # GIVEN: A ComboBox + combo = QtGui.QComboBox() + combo.addItems(['One', 'Two', 'Three']) + combo.setCurrentIndex(1) + + # WHEN: We call the method with a non-existing value and set_missing=False + find_and_set_in_combo_box(combo, 'Four', set_missing=False) + + # THEN: The index should not have changed + self.assertEqual(1, combo.currentIndex()) + + # WHEN: We call the method with a non-existing value + find_and_set_in_combo_box(combo, 'Four') + + # THEN: The index should have been reset + self.assertEqual(0, combo.currentIndex()) + + # WHEN: We call the method with the default behavior + find_and_set_in_combo_box(combo, 'Three') + + # THEN: The index should have changed + self.assertEqual(2, combo.currentIndex()) From 7a1741258abc6a0ee44782c5bfd800b43ade8053 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Sat, 3 May 2014 17:25:45 +0200 Subject: [PATCH 107/212] Typo --- openlp/plugins/songs/forms/authorsdialog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/plugins/songs/forms/authorsdialog.py b/openlp/plugins/songs/forms/authorsdialog.py index c5a93894b..7bca76ca6 100644 --- a/openlp/plugins/songs/forms/authorsdialog.py +++ b/openlp/plugins/songs/forms/authorsdialog.py @@ -43,7 +43,7 @@ class Ui_AuthorsDialog(object): Set up the UI for the dialog. """ authors_dialog.setObjectName('authors_dialog') - authorsDialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) + authors_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) authors_dialog.resize(300, 10) authors_dialog.setModal(True) self.dialog_layout = QtGui.QVBoxLayout(authors_dialog) From 4a75849708df5561610b5a7fe40c70fec8ba8de4 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Sat, 3 May 2014 17:27:44 +0200 Subject: [PATCH 108/212] Fix readme links --- README.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.txt b/README.txt index b937e1d5f..e3e7cc579 100644 --- a/README.txt +++ b/README.txt @@ -5,7 +5,7 @@ You're probably reading this because you've just downloaded the source code for OpenLP 2.0. If you are looking for the installer file, please go to the download page on the web site:: - http://openlp.org/en/download.html + http://openlp.org/download If you're looking for how to contribute to OpenLP, then please look at the OpenLP wiki:: @@ -13,4 +13,3 @@ OpenLP wiki:: http://wiki.openlp.org/ Thanks for downloading OpenLP 2.0! - From d4edd91d5676a397f2c70f1d0dffb651554f309a Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Sat, 3 May 2014 17:28:31 +0200 Subject: [PATCH 109/212] This is no longer 2.0 --- README.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.txt b/README.txt index e3e7cc579..04294b1b8 100644 --- a/README.txt +++ b/README.txt @@ -1,8 +1,8 @@ -OpenLP 2.0 -========== +OpenLP +====== You're probably reading this because you've just downloaded the source code for -OpenLP 2.0. If you are looking for the installer file, please go to the download +OpenLP. If you are looking for the installer file, please go to the download page on the web site:: http://openlp.org/download @@ -12,4 +12,4 @@ OpenLP wiki:: http://wiki.openlp.org/ -Thanks for downloading OpenLP 2.0! +Thanks for downloading OpenLP! From 37653bfd134398a66ad3e24c57df1c29ae99fddf Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Sat, 3 May 2014 17:34:13 +0200 Subject: [PATCH 110/212] Add missing tag to test --- tests/utils/test_bzr_tags.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/utils/test_bzr_tags.py b/tests/utils/test_bzr_tags.py index 95017e94f..393f4ce25 100644 --- a/tests/utils/test_bzr_tags.py +++ b/tests/utils/test_bzr_tags.py @@ -53,6 +53,7 @@ TAGS = [ ['2.0.1', '?'], ['2.0.2', '?'], ['2.0.3', '?'], + ['2.0.4', '?'], ['2.1.0', '2119'] ] From f21d3cfa838335deccc20355bb9d512038c9cb8e Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sat, 3 May 2014 20:32:19 +0100 Subject: [PATCH 111/212] fix tests --- tests/functional/openlp_core_lib/test_image_manager.py | 2 +- tests/functional/openlp_core_ui/test_media.py | 2 +- tests/interfaces/openlp_core_common/test_historycombobox.py | 2 +- tests/interfaces/openlp_core_ui/test_shortcutlistform.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/functional/openlp_core_lib/test_image_manager.py b/tests/functional/openlp_core_lib/test_image_manager.py index 37b6d6fdd..072978993 100644 --- a/tests/functional/openlp_core_lib/test_image_manager.py +++ b/tests/functional/openlp_core_lib/test_image_manager.py @@ -171,4 +171,4 @@ class TestImageManager(TestCase, TestMixin): self.lock.release() # The sleep time is adjusted in the test case. time.sleep(self.sleep_time) - return '' \ No newline at end of file + return '' diff --git a/tests/functional/openlp_core_ui/test_media.py b/tests/functional/openlp_core_ui/test_media.py index d59690949..4c6fa7f86 100644 --- a/tests/functional/openlp_core_ui/test_media.py +++ b/tests/functional/openlp_core_ui/test_media.py @@ -125,4 +125,4 @@ class TestMedia(TestCase, TestMixin): # 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 + self.assertEqual('vlc,webkit,phonon', overridden_player, 'Overridden player should be a string of players') diff --git a/tests/interfaces/openlp_core_common/test_historycombobox.py b/tests/interfaces/openlp_core_common/test_historycombobox.py index 00a63f15f..c0131e46c 100644 --- a/tests/interfaces/openlp_core_common/test_historycombobox.py +++ b/tests/interfaces/openlp_core_common/test_historycombobox.py @@ -62,4 +62,4 @@ class TestHistoryComboBox(TestCase, TestMixin): self.combo.addItem('test2') # THEN: The list of items should contain both strings. - self.assertEqual(self.combo.getItems(), ['test1', 'test2']) \ No newline at end of file + self.assertEqual(self.combo.getItems(), ['test1', 'test2']) diff --git a/tests/interfaces/openlp_core_ui/test_shortcutlistform.py b/tests/interfaces/openlp_core_ui/test_shortcutlistform.py index 472bce03f..29c365194 100644 --- a/tests/interfaces/openlp_core_ui/test_shortcutlistform.py +++ b/tests/interfaces/openlp_core_ui/test_shortcutlistform.py @@ -75,4 +75,4 @@ class TestShortcutform(TestCase, TestMixin): # THEN: The button should be changed. self.assertEqual(button.text(), text, "The text should match.") mocked_check_method.assert_called_once_with(True) - self.assertEqual(button.isEnabled(), enabled, "The button should be disabled.") \ No newline at end of file + self.assertEqual(button.isEnabled(), enabled, "The button should be disabled.") From d6d3cd1394ca7cce172c0b6ca762fe38fff9de4d Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sat, 3 May 2014 21:00:17 +0100 Subject: [PATCH 112/212] Fix call path --- openlp/plugins/media/lib/mediaitem.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/plugins/media/lib/mediaitem.py b/openlp/plugins/media/lib/mediaitem.py index 92426334a..0780b175d 100644 --- a/openlp/plugins/media/lib/mediaitem.py +++ b/openlp/plugins/media/lib/mediaitem.py @@ -229,7 +229,7 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties): self.service_path = os.path.join(AppLocation.get_section_data_path(self.settings_section), 'thumbnails') check_directory_exists(self.service_path) self.load_list(Settings().value(self.settings_section + '/media files')) - self.populate_display_types() + self.rebuild_players() def rebuild_players(self): """ From 9b396e7699fb52480ecc99ed5597716eac338466 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Mon, 5 May 2014 18:20:54 +0200 Subject: [PATCH 113/212] Fix bug 1211913 Fixes: https://launchpad.net/bugs/1211913 --- openlp/plugins/songs/lib/sundayplusimport.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/openlp/plugins/songs/lib/sundayplusimport.py b/openlp/plugins/songs/lib/sundayplusimport.py index f22f8b058..5c5f73047 100644 --- a/openlp/plugins/songs/lib/sundayplusimport.py +++ b/openlp/plugins/songs/lib/sundayplusimport.py @@ -68,7 +68,7 @@ class SundayPlusImport(SongImport): for filename in self.import_source: if self.stop_import_flag: return - song_file = open(filename) + song_file = open(filename, 'rb') self.do_import_file(song_file) song_file.close() @@ -103,7 +103,7 @@ class SundayPlusImport(SongImport): # Now we are looking for the name. if data[i:i + 1] == '#': name_end = data.find(':', i + 1) - name = data[i + 1:name_end] + name = data[i + 1:name_end].upper() i = name_end + 1 while data[i:i + 1] == ' ': i += 1 @@ -129,13 +129,13 @@ class SundayPlusImport(SongImport): value = data[i:end] # If we are in the main group. if not cell: - if name == 'title': + if name == 'TITLE': self.title = self.decode(self.unescape(value)) - elif name == 'Author': + elif name == 'AUTHOR': author = self.decode(self.unescape(value)) if len(author): self.add_author(author) - elif name == 'Copyright': + elif name == 'COPYRIGHT': self.copyright = self.decode(self.unescape(value)) elif name[0:4] == 'CELL': self.parse(value, cell=name[4:]) @@ -147,12 +147,12 @@ class SundayPlusImport(SongImport): verse_type = VerseType.tags[VerseType.from_loose_input(value[0])] if len(value) >= 2 and value[-1] in ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']: verse_type = "%s%s" % (verse_type, value[-1]) - elif name == 'Hotkey': - # Hotkey always appears after MARKER_NAME, so it + elif name == 'HOTKEY': + # HOTKEY always appears after MARKER_NAME, so it # effectively overrides MARKER_NAME, if present. if len(value) and value in list(HOTKEY_TO_VERSE_TYPE.keys()): verse_type = HOTKEY_TO_VERSE_TYPE[value] - if name == 'rtf': + if name == 'RTF': value = self.unescape(value) result = strip_rtf(value, self.encoding) if result is None: From c7ce62bd43a79616e3b2cbd9e26ab0ca439f14ac Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Mon, 5 May 2014 19:29:57 +0200 Subject: [PATCH 114/212] Changed to ignore wrongly formatted songs. --- openlp/plugins/songs/lib/ewimport.py | 29 ++++++++++----- .../openlp_plugins/songs/test_ewimport.py | 37 ++++--------------- 2 files changed, 26 insertions(+), 40 deletions(-) diff --git a/openlp/plugins/songs/lib/ewimport.py b/openlp/plugins/songs/lib/ewimport.py index e12b7e181..b08193672 100644 --- a/openlp/plugins/songs/lib/ewimport.py +++ b/openlp/plugins/songs/lib/ewimport.py @@ -74,6 +74,7 @@ class EasyWorshipSongImport(SongImport): """ def __init__(self, manager, **kwargs): super(EasyWorshipSongImport, self).__init__(manager, **kwargs) + self.entry_error_log = '' def do_import(self): """ @@ -183,7 +184,12 @@ class EasyWorshipSongImport(SongImport): self.set_song_import_object(authors, inflated_content) if self.stop_import_flag: break - if not self.finish(): + if self.entry_error_log: + self.log_error(self.import_source, + translate('SongsPlugin.EasyWorshipSongImport', '"%s" could not be imported. %s') + % (self.title, self.entry_error_log)) + self.entry_error_log = '' + elif not self.finish(): self.log_error(self.import_source) # Set file_pos for next entry file_pos += entry_length @@ -305,7 +311,12 @@ class EasyWorshipSongImport(SongImport): self.set_song_import_object(authors, words) if self.stop_import_flag: break - if not self.finish(): + if self.entry_error_log: + self.log_error(self.import_source, + translate('SongsPlugin.EasyWorshipSongImport', '"%s" could not be imported. %s') + % (self.title, self.entry_error_log)) + self.entry_error_log = '' + elif not self.finish(): self.log_error(self.import_source) db_file.close() self.memo_file.close() @@ -333,16 +344,14 @@ class EasyWorshipSongImport(SongImport): try: decoded_words = words.decode() except UnicodeDecodeError: - # The unicode chars in the rtf was not escaped in the expected manor, doing it manually. - newbytes = bytearray() - for b in words: - if b > 127: - newbytes += bytearray(b'\\\'') + bytearray(str(hex(b))[-2:].encode()) - else: - newbytes.append(b) - decoded_words = newbytes.decode() + # The unicode chars in the rtf was not escaped in the expected manor + self.entry_error_log = translate('SongsPlugin.EasyWorshipSongImport', + 'Unexpected data formatting.') + return result = strip_rtf(decoded_words, self.encoding) if result is None: + self.entry_error_log = translate('SongsPlugin.EasyWorshipSongImport', + 'No song text found.') return words, self.encoding = result verse_type = VerseType.tags[VerseType.Verse] diff --git a/tests/functional/openlp_plugins/songs/test_ewimport.py b/tests/functional/openlp_plugins/songs/test_ewimport.py index e6344e39b..8d9302015 100644 --- a/tests/functional/openlp_plugins/songs/test_ewimport.py +++ b/tests/functional/openlp_plugins/songs/test_ewimport.py @@ -153,15 +153,7 @@ class TestEasyWorshipSongImport(TestCase): """ Test the functions in the :mod:`ewimport` module. """ - """def setUp(self): - self.songimport_patcher = patch('openlp.plugins.songs.lib.ewimport.EasyWorshipSongImport.__init__') - self.mocked_songimport = self.songimport_patcher.start() - self.mocked_songimport.return_value = None - - def tearDown(self): - self.songimport_patcher.stop() - """ - + def create_field_desc_entry_test(self): """ Test creating an instance of the :class`FieldDescEntry` class. @@ -495,32 +487,17 @@ class TestEasyWorshipSongImport(TestCase): """ Test import of rtf without the expected escaping of unicode """ - - # 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.strip_rtf') as mocked_strip_rtf, \ - patch('openlp.plugins.songs.lib.ewimport.retrieve_windows_encoding') \ - as mocked_retrieve_windows_encoding: - mocked_retrieve_windows_encoding.return_value = 'cp1252' + + # GIVEN: A mocked out SongImport class, a mocked out "manager" and mocked out "author" method. + with patch('openlp.plugins.songs.lib.ewimport.SongImport'): mocked_manager = MagicMock() - mocked_import_wizard = MagicMock() mocked_add_author = MagicMock() - mocked_add_verse = MagicMock() - mocked_finish = MagicMock() - mocked_title = MagicMock() - mocked_finish.return_value = True importer = EasyWorshipSongImportLogger(mocked_manager) - importer.import_wizard = mocked_import_wizard - importer.stop_import_flag = False importer.add_author = mocked_add_author - importer.add_verse = mocked_add_verse - importer.title = mocked_title - importer.finish = mocked_finish - importer.topics = [] + importer.encoding = 'cp1252' # WHEN: running set_song_import_object on a verse string without the needed escaping importer.set_song_import_object('Test Author', b'Det som var fr\x86n begynnelsen') - # THEN: The escaping shoud be added - mocked_strip_rtf.assert_called_with("Det som var fr\'86n begynnelsen", 'cp1252') + # THEN: The import should fail + self.assertEquals(importer.entry_error_log, 'Unexpected data formatting.', 'Import should fail') From 0fd815051f677f92ece5eb3c021ed0a1162af9af Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Mon, 5 May 2014 19:31:45 +0200 Subject: [PATCH 115/212] Fix for packaging --- MANIFEST.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MANIFEST.in b/MANIFEST.in index 35e83e30f..be81efb23 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -5,6 +5,8 @@ recursive-include openlp *.html recursive-include openlp *.js recursive-include openlp *.css recursive-include openlp *.png +recursive-include openlp *.ps +recursive-include openlp *.json recursive-include documentation * recursive-include resources * recursive-include scripts * From 0d48c09180b0648ca476c9977ef3392a48b4211d Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Tue, 6 May 2014 21:53:08 +0200 Subject: [PATCH 116/212] Added support for import of Zefania XML bibles. --- openlp/core/ui/wizard.py | 1 + .../plugins/bibles/forms/bibleimportform.py | 45 ++++++- openlp/plugins/bibles/lib/manager.py | 5 + openlp/plugins/bibles/lib/opensong.py | 7 ++ openlp/plugins/bibles/lib/zefania.py | 110 ++++++++++++++++++ 5 files changed, 166 insertions(+), 2 deletions(-) create mode 100644 openlp/plugins/bibles/lib/zefania.py diff --git a/openlp/core/ui/wizard.py b/openlp/core/ui/wizard.py index 5996296b5..37070092b 100644 --- a/openlp/core/ui/wizard.py +++ b/openlp/core/ui/wizard.py @@ -51,6 +51,7 @@ class WizardStrings(object): CSV = 'CSV' OS = 'OpenSong' OSIS = 'OSIS' + ZEF = 'Zefania' # These strings should need a good reason to be retranslated elsewhere. FinishedImport = translate('OpenLP.Ui', 'Finished import.') FormatLabel = translate('OpenLP.Ui', 'Format:') diff --git a/openlp/plugins/bibles/forms/bibleimportform.py b/openlp/plugins/bibles/forms/bibleimportform.py index 79b0bc699..171994d18 100644 --- a/openlp/plugins/bibles/forms/bibleimportform.py +++ b/openlp/plugins/bibles/forms/bibleimportform.py @@ -110,6 +110,7 @@ class BibleImportForm(OpenLPWizard): 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) + self.zefania_browse_button.clicked.connect(self.on_zefania_browse_button_clicked) def add_custom_pages(self): """ @@ -125,7 +126,7 @@ class BibleImportForm(OpenLPWizard): 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.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) @@ -247,6 +248,25 @@ class BibleImportForm(OpenLPWizard): 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.zefania_widget = QtGui.QWidget(self.select_page) + self.zefania_widget.setObjectName('ZefaniaWidget') + self.zefania_layout = QtGui.QFormLayout(self.zefania_widget) + self.zefania_layout.setMargin(0) + self.zefania_layout.setObjectName('ZefaniaLayout') + self.zefania_file_label = QtGui.QLabel(self.zefania_widget) + self.zefania_file_label.setObjectName('ZefaniaFileLabel') + self.zefania_file_layout = QtGui.QHBoxLayout() + self.zefania_file_layout.setObjectName('ZefaniaFileLayout') + self.zefania_file_edit = QtGui.QLineEdit(self.zefania_widget) + self.zefania_file_edit.setObjectName('ZefaniaFileEdit') + self.zefania_file_layout.addWidget(self.zefania_file_edit) + self.zefania_browse_button = QtGui.QToolButton(self.zefania_widget) + self.zefania_browse_button.setIcon(self.open_icon) + self.zefania_browse_button.setObjectName('ZefaniaBrowseButton') + self.zefania_file_layout.addWidget(self.zefania_browse_button) + self.zefania_layout.addRow(self.zefania_file_label, self.zefania_file_layout) + self.zefania_layout.setItem(5, QtGui.QFormLayout.LabelRole, self.spacer) + self.select_stack.addWidget(self.zefania_widget) self.select_page_layout.addLayout(self.select_stack) self.addPage(self.select_page) # License Page @@ -294,11 +314,13 @@ class BibleImportForm(OpenLPWizard): self.format_combo_box.setItemText(BibleFormat.OpenSong, WizardStrings.OS) self.format_combo_box.setItemText(BibleFormat.WebDownload, translate('BiblesPlugin.ImportWizardForm', 'Web Download')) + self.format_combo_box.setItemText(BibleFormat.Zefania, WizardStrings.ZEF) 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.zefania_file_label.setText(translate('BiblesPlugin.ImportWizardForm', 'Bible file:')) self.web_source_combo_box.setItemText(WebDownload.Crosswalk, translate('BiblesPlugin.ImportWizardForm', 'Crosswalk')) self.web_source_combo_box.setItemText(WebDownload.BibleGateway, translate('BiblesPlugin.ImportWizardForm', @@ -331,7 +353,8 @@ class BibleImportForm(OpenLPWizard): 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.open_song_file_label.minimumSizeHint().width(), + self.zefania_file_label.minimumSizeHint().width()) self.spacer.changeSize(label_width, 0, QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) def validateCurrentPage(self): @@ -366,6 +389,11 @@ class BibleImportForm(OpenLPWizard): critical_error_message_box(UiStrings().NFSs, WizardStrings.YouSpecifyFile % WizardStrings.OS) self.open_song_file_edit.setFocus() return False + elif self.field('source_format') == BibleFormat.Zefania: + if not self.field('zefania_file'): + critical_error_message_box(UiStrings().NFSs, WizardStrings.YouSpecifyFile % WizardStrings.ZEF) + self.zefania_file_edit.setFocus() + return False elif self.field('source_format') == BibleFormat.WebDownload: self.version_name_edit.setText(self.web_translation_combo_box.currentText()) return True @@ -447,6 +475,13 @@ class BibleImportForm(OpenLPWizard): self.get_file_name(WizardStrings.OpenTypeFile % WizardStrings.OS, self.open_song_file_edit, 'last directory import') + def on_zefania_browse_button_clicked(self): + """ + Show the file open dialog for the Zefania file. + """ + self.get_file_name(WizardStrings.OpenTypeFile % WizardStrings.ZEF, self.zefania_file_edit, + 'last directory import') + def register_fields(self): """ Register the bible import wizard fields. @@ -456,6 +491,7 @@ class BibleImportForm(OpenLPWizard): 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('zefania_file', self.zefania_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) @@ -479,6 +515,7 @@ class BibleImportForm(OpenLPWizard): self.setField('csv_booksfile', '') self.setField('csv_versefile', '') self.setField('opensong_file', '') + self.setField('zefania_file', '') self.setField('web_location', WebDownload.Crosswalk) self.setField('web_biblename', self.web_translation_combo_box.currentIndex()) self.setField('proxy_server', settings.value('proxy address')) @@ -562,6 +599,10 @@ class BibleImportForm(OpenLPWizard): proxy_username=self.field('proxy_username'), proxy_password=self.field('proxy_password') ) + elif bible_type == BibleFormat.Zefania: + # Import an Zefania bible. + importer = self.manager.import_bible(BibleFormat.Zefania, name=license_version, + filename=self.field('zefania_file')) if importer.do_import(license_version): self.manager.save_meta_data(license_version, license_version, license_copyright, license_permissions) self.manager.reload_bibles() diff --git a/openlp/plugins/bibles/lib/manager.py b/openlp/plugins/bibles/lib/manager.py index c57fc117e..7dab8b3d7 100644 --- a/openlp/plugins/bibles/lib/manager.py +++ b/openlp/plugins/bibles/lib/manager.py @@ -38,6 +38,7 @@ from .csvbible import CSVBible from .http import HTTPBible from .opensong import OpenSongBible from .osis import OSISBible +from .zefania import ZefaniaBible log = logging.getLogger(__name__) @@ -52,6 +53,7 @@ class BibleFormat(object): CSV = 1 OpenSong = 2 WebDownload = 3 + Zefania = 4 @staticmethod def get_class(bible_format): @@ -68,6 +70,8 @@ class BibleFormat(object): return OpenSongBible elif bible_format == BibleFormat.WebDownload: return HTTPBible + elif bible_format == BibleFormat.Zefania: + return ZefaniaBible else: return None @@ -81,6 +85,7 @@ class BibleFormat(object): BibleFormat.CSV, BibleFormat.OpenSong, BibleFormat.WebDownload, + BibleFormar.Zefania, ] diff --git a/openlp/plugins/bibles/lib/opensong.py b/openlp/plugins/bibles/lib/opensong.py index c7bfa01a2..fa8323d7f 100644 --- a/openlp/plugins/bibles/lib/opensong.py +++ b/openlp/plugins/bibles/lib/opensong.py @@ -81,6 +81,13 @@ class OpenSongBible(BibleDB): import_file = open(self.filename, 'rb') opensong = objectify.parse(import_file) bible = opensong.getroot() + # Check that we're not trying to import a Zefania XML bible, it is sometimes refered to as 'OpenSong' + if bible.tag.upper() == 'XMLBIBLE': + critical_error_message_box( + message=translate('BiblesPlugin.OpenSongImport', + 'Incorrect Bible file type supplied. This looks like a Zefania XML bible, ' + 'please use the Zefania import option.')) + return False language_id = self.get_language(bible_name) if not language_id: log.error('Importing books from "%s" failed' % self.filename) diff --git a/openlp/plugins/bibles/lib/zefania.py b/openlp/plugins/bibles/lib/zefania.py new file mode 100644 index 000000000..16680efa3 --- /dev/null +++ b/openlp/plugins/bibles/lib/zefania.py @@ -0,0 +1,110 @@ +# -*- 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 logging +from lxml import etree, objectify + +from openlp.core.common import translate +from openlp.core.lib.ui import critical_error_message_box +from openlp.plugins.bibles.lib.db import BibleDB, BiblesResourcesDB + + +log = logging.getLogger(__name__) + + +class ZefaniaBible(BibleDB): + """ + Zefania Bible format importer class. + """ + def __init__(self, parent, **kwargs): + """ + Constructor to create and set up an instance of the ZefaniaBible class. This class is used to import Bibles + from ZefaniaBible's XML format. + """ + log.debug(self.__class__.__name__) + BibleDB.__init__(self, parent, **kwargs) + self.filename = kwargs['filename'] + + def do_import(self, bible_name=None): + """ + Loads a Bible from file. + """ + log.debug('Starting Zefania import from "%s"' % self.filename) + if not isinstance(self.filename, str): + self.filename = str(self.filename, 'utf8') + 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. + import_file = open(self.filename, 'rb') + language_id = self.get_language(bible_name) + if not language_id: + log.error('Importing books from "%s" failed' % self.filename) + return False + zefania_bible_tree = etree.parse(import_file) + num_books = int(zefania_bible_tree.xpath("count(//BIBLEBOOK)")) + # Strip tags we don't use + etree.strip_tags(zefania_bible_tree, ('STYLE', 'GRAM', 'NOTE', 'SUP', 'XREF')) + xmlbible = zefania_bible_tree.getroot() + for BIBLEBOOK in xmlbible: + book_ref_id = self.get_book_ref_id_by_name(str(BIBLEBOOK.get('bname')), num_books) + if not book_ref_id: + book_ref_id = self.get_book_ref_id_by_localised_name(str(BIBLEBOOK.get('bname'))) + if not book_ref_id: + 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(book_details['name'], book_ref_id, book_details['testament_id']) + for CHAPTER in BIBLEBOOK: + if self.stop_import_flag: + break + chapter_number = CHAPTER.get("cnumber") + for VERS in CHAPTER: + verse_number = VERS.get("vnumber") + self.create_verse(db_book.id, chapter_number, verse_number, VERS.text.replace('
', '\n')) + self.wizard.increment_progress_bar( + translate('BiblesPlugin.Zefnia', 'Importing %(bookname)s %(chapter)s...' % + {'bookname': db_book.name, 'chapter': chapter_number})) + self.session.commit() + self.application.process_events() + except Exception as e: + critical_error_message_box( + message=translate('BiblesPlugin.ZefaniaImport', + 'Incorrect Bible file type supplied. Zefania Bibles may be ' + 'compressed. You must decompress them before import.')) + log.exception(str(e)) + success = False + finally: + if import_file: + import_file.close() + if self.stop_import_flag: + return False + else: + return success From ef2a03d5103698d7de40cee8a0d9a9b0be6fddec Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 7 May 2014 11:51:59 +0200 Subject: [PATCH 117/212] Fix bug 1316979 by making the authors relation viewonly Fixes: https://launchpad.net/bugs/1316979 --- .../songs/forms/songmaintenanceform.py | 10 +++--- openlp/plugins/songs/lib/__init__.py | 4 +-- openlp/plugins/songs/lib/db.py | 32 +++++++++++++++++-- .../plugins/songs/lib/foilpresenterimport.py | 2 +- openlp/plugins/songs/lib/olpimport.py | 2 +- openlp/plugins/songs/lib/songimport.py | 2 +- openlp/plugins/songs/lib/songselect.py | 2 +- 7 files changed, 40 insertions(+), 14 deletions(-) diff --git a/openlp/plugins/songs/forms/songmaintenanceform.py b/openlp/plugins/songs/forms/songmaintenanceform.py index 4e9bdad93..bdbae41fb 100644 --- a/openlp/plugins/songs/forms/songmaintenanceform.py +++ b/openlp/plugins/songs/forms/songmaintenanceform.py @@ -400,7 +400,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog, RegistryPrope """ Merges two authors into one author. - :param old_author: The object, which was edited, that will be deleted + :param old_author: The object, which was edited, that will be deleted """ # Find the duplicate. existing_author = self.manager.get_object_filtered( @@ -415,11 +415,9 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog, RegistryPrope # Find the songs, which have the old_author as author. songs = self.manager.get_all_objects(Song, Song.authors.contains(old_author)) for song in songs: - # We check if the song has already existing_author as author. If - # that is not the case we add it. - if existing_author not in song.authors: - song.authors.append(existing_author) - song.authors.remove(old_author) + for author_song in song.authors_songs: + song.add_author(existing_author, author_song.author_type) + song.remove_author(old_author, author_song.author_type) self.manager.save_object(song) self.manager.delete_object(Author, old_author.id) diff --git a/openlp/plugins/songs/lib/__init__.py b/openlp/plugins/songs/lib/__init__.py index 95868ffa7..f6d43fb6e 100644 --- a/openlp/plugins/songs/lib/__init__.py +++ b/openlp/plugins/songs/lib/__init__.py @@ -390,12 +390,12 @@ def clean_song(manager, song): verses = SongXML().get_verses(song.lyrics) song.search_lyrics = ' '.join([clean_string(verse[1]) for verse in verses]) # The song does not have any author, add one. - if not song.authors and not song.authors_songs: # Need to check both relations + if not song.authors_songs: name = SongStrings.AuthorUnknown author = manager.get_object_filtered(Author, Author.display_name == name) if author is None: author = Author.populate(display_name=name, last_name='', first_name='') - song.authors.append(author) + song.add_author(author) if song.copyright: song.copyright = CONTROL_CHARS.sub('', song.copyright).strip() diff --git a/openlp/plugins/songs/lib/db.py b/openlp/plugins/songs/lib/db.py index 91649c951..e77d98bda 100644 --- a/openlp/plugins/songs/lib/db.py +++ b/openlp/plugins/songs/lib/db.py @@ -114,6 +114,33 @@ class Song(BaseModel): """ self.sort_key = get_natural_key(self.title) + def add_author(self, author, author_type=None, ignore_type=False): + """ + Add an author to the song if it not yet exists + + :return: True if the author has been added successfully. False if it was already there. + """ + for author_song in self.authors_songs: + if author_song.author == author and (ignore_type or author_song.author_type == author_type): + return False + new_author_song = AuthorSong() + new_author_song.author = author + new_author_song.author_type = author_type + self.authors_songs.append(new_author_song) + return True + + def remove_author(self, author, author_type=None, ignore_type=False): + """ + Remove an existing author from the song + + :return: True if the author has been removed successfully. False if it could not be found. + """ + for author_song in self.authors_songs: + if author_song.author == author and (ignore_type or author_song.author_type == author_type): + self.authors_songs.remove(author_song) + return True + return False + class Topic(BaseModel): """ @@ -283,9 +310,10 @@ def init_schema(url): mapper(Book, song_books_table) mapper(MediaFile, media_files_table) mapper(Song, songs_table, properties={ - # Use the authors_songs relation when you need access to the 'author_type' attribute. + # Use the authors_songs relation when you need access to the 'author_type' attribute + # or when creating new relations 'authors_songs': relation(AuthorSong, cascade="all, delete-orphan"), - 'authors': relation(Author, secondary=authors_songs_table), + 'authors': relation(Author, secondary=authors_songs_table, viewonly=True), '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) diff --git a/openlp/plugins/songs/lib/foilpresenterimport.py b/openlp/plugins/songs/lib/foilpresenterimport.py index 6f8bd6978..2b31718c2 100644 --- a/openlp/plugins/songs/lib/foilpresenterimport.py +++ b/openlp/plugins/songs/lib/foilpresenterimport.py @@ -343,7 +343,7 @@ class FoilPresenter(object): author = Author.populate(display_name=display_name, last_name=display_name.split(' ')[-1], first_name=' '.join(display_name.split(' ')[:-1])) self.manager.save_object(author) - song.authors.append(author) + song.add_author(author) def _process_cclinumber(self, foilpresenterfolie, song): """ diff --git a/openlp/plugins/songs/lib/olpimport.py b/openlp/plugins/songs/lib/olpimport.py index 335ba606a..d38c8c9d8 100644 --- a/openlp/plugins/songs/lib/olpimport.py +++ b/openlp/plugins/songs/lib/olpimport.py @@ -187,7 +187,7 @@ class OpenLPSongImport(SongImport): first_name=author.first_name, last_name=author.last_name, display_name=author.display_name) - new_song.authors.append(existing_author) + new_song.authors.add_author(existing_author) if song.book: existing_song_book = self.manager.get_object_filtered(Book, Book.name == song.book.name) if existing_song_book is None: diff --git a/openlp/plugins/songs/lib/songimport.py b/openlp/plugins/songs/lib/songimport.py index a5fbb99e0..c4fda6120 100644 --- a/openlp/plugins/songs/lib/songimport.py +++ b/openlp/plugins/songs/lib/songimport.py @@ -325,7 +325,7 @@ class SongImport(QtCore.QObject): author = Author.populate(display_name=author_text, last_name=author_text.split(' ')[-1], first_name=' '.join(author_text.split(' ')[:-1])) - song.authors.append(author) + song.authors.add_author(author) if self.song_book_name: song_book = self.manager.get_object_filtered(Book, Book.name == self.song_book_name) if song_book is None: diff --git a/openlp/plugins/songs/lib/songselect.py b/openlp/plugins/songs/lib/songselect.py index 232cdb9cf..b75caa654 100644 --- a/openlp/plugins/songs/lib/songselect.py +++ b/openlp/plugins/songs/lib/songselect.py @@ -203,6 +203,6 @@ class SongSelectImport(object): author = Author.populate(first_name=author_name.rsplit(' ', 1)[0], last_name=author_name.rsplit(' ', 1)[1], display_name=author_name) - db_song.authors.append(author) + db_song.authors.add_author(author) self.db_manager.save_object(db_song) return db_song From aaacbeac6934cf1ff40d93f79d562ac8a86fe656 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 7 May 2014 12:21:27 +0200 Subject: [PATCH 118/212] Fixes --- openlp/plugins/songs/lib/olpimport.py | 2 +- openlp/plugins/songs/lib/songimport.py | 2 +- openlp/plugins/songs/lib/songselect.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/openlp/plugins/songs/lib/olpimport.py b/openlp/plugins/songs/lib/olpimport.py index d38c8c9d8..f4b066ef0 100644 --- a/openlp/plugins/songs/lib/olpimport.py +++ b/openlp/plugins/songs/lib/olpimport.py @@ -187,7 +187,7 @@ class OpenLPSongImport(SongImport): first_name=author.first_name, last_name=author.last_name, display_name=author.display_name) - new_song.authors.add_author(existing_author) + new_song.add_author(existing_author) if song.book: existing_song_book = self.manager.get_object_filtered(Book, Book.name == song.book.name) if existing_song_book is None: diff --git a/openlp/plugins/songs/lib/songimport.py b/openlp/plugins/songs/lib/songimport.py index c4fda6120..754288546 100644 --- a/openlp/plugins/songs/lib/songimport.py +++ b/openlp/plugins/songs/lib/songimport.py @@ -325,7 +325,7 @@ class SongImport(QtCore.QObject): author = Author.populate(display_name=author_text, last_name=author_text.split(' ')[-1], first_name=' '.join(author_text.split(' ')[:-1])) - song.authors.add_author(author) + song.add_author(author) if self.song_book_name: song_book = self.manager.get_object_filtered(Book, Book.name == self.song_book_name) if song_book is None: diff --git a/openlp/plugins/songs/lib/songselect.py b/openlp/plugins/songs/lib/songselect.py index b75caa654..195f542aa 100644 --- a/openlp/plugins/songs/lib/songselect.py +++ b/openlp/plugins/songs/lib/songselect.py @@ -203,6 +203,6 @@ class SongSelectImport(object): author = Author.populate(first_name=author_name.rsplit(' ', 1)[0], last_name=author_name.rsplit(' ', 1)[1], display_name=author_name) - db_song.authors.add_author(author) + db_song.add_author(author) self.db_manager.save_object(db_song) return db_song From ee7a8b980418fab3431cb98b34bb7d8cf9002cd6 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 7 May 2014 12:25:36 +0200 Subject: [PATCH 119/212] Fixes --- openlp/plugins/songs/lib/songselect.py | 2 +- tests/functional/openlp_plugins/songs/test_songselect.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/openlp/plugins/songs/lib/songselect.py b/openlp/plugins/songs/lib/songselect.py index 195f542aa..6fd084a47 100644 --- a/openlp/plugins/songs/lib/songselect.py +++ b/openlp/plugins/songs/lib/songselect.py @@ -196,7 +196,7 @@ class SongSelectImport(object): db_song.lyrics = song_xml.extract_xml() clean_song(self.db_manager, db_song) self.db_manager.save_object(db_song) - db_song.authors = [] + db_song.authors_songs = [] for author_name in song['authors']: author = self.db_manager.get_object_filtered(Author, Author.display_name == author_name) if not author: diff --git a/tests/functional/openlp_plugins/songs/test_songselect.py b/tests/functional/openlp_plugins/songs/test_songselect.py index 8d1237190..0f9001342 100644 --- a/tests/functional/openlp_plugins/songs/test_songselect.py +++ b/tests/functional/openlp_plugins/songs/test_songselect.py @@ -344,7 +344,7 @@ class TestSongSelect(TestCase): mocked_db_manager.get_object_filtered.assert_called_with(MockedAuthor, False) MockedAuthor.populate.assert_called_with(first_name='Public', last_name='Domain', display_name='Public Domain') - self.assertEqual(1, len(result.authors), 'There should only be one author') + self.assertEqual(1, len(result.authors_songs), 'There should only be one author') def save_song_existing_author_test(self): """ @@ -379,4 +379,4 @@ class TestSongSelect(TestCase): 'The save_object() method should have been called twice') mocked_db_manager.get_object_filtered.assert_called_with(MockedAuthor, False) self.assertEqual(0, MockedAuthor.populate.call_count, 'A new author should not have been instantiated') - self.assertEqual(1, len(result.authors), 'There should only be one author') + self.assertEqual(1, len(result.authors_songs), 'There should only be one author') From a9e620c175ab4b422683e6948f1e63becaed620b Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 7 May 2014 12:30:55 +0200 Subject: [PATCH 120/212] Remove direct usage of AuthorSong --- openlp/plugins/songs/forms/editsongform.py | 8 +++----- openlp/plugins/songs/lib/xml.py | 7 ++----- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index 1814655ea..0ea014c73 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -42,7 +42,7 @@ from openlp.core.common import Registry, RegistryProperties, AppLocation, UiStri from openlp.core.lib import FileDialog, PluginStatus, MediaType, create_separated_list from openlp.core.lib.ui import set_case_insensitive_completer, critical_error_message_box, find_and_set_in_combo_box from openlp.plugins.songs.lib import VerseType, clean_song -from openlp.plugins.songs.lib.db import Book, Song, Author, AuthorSong, AuthorType, Topic, MediaFile +from openlp.plugins.songs.lib.db import Book, Song, Author, AuthorType, Topic, MediaFile from openlp.plugins.songs.lib.ui import SongStrings from openlp.plugins.songs.lib.xml import SongXML from openlp.plugins.songs.forms.editsongdialog import Ui_EditSongDialog @@ -916,10 +916,8 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): self.song.authors_songs = [] for row in range(self.authors_list_view.count()): item = self.authors_list_view.item(row) - author_song = AuthorSong() - author_song.author_id = item.data(QtCore.Qt.UserRole)[0] - author_song.author_type = item.data(QtCore.Qt.UserRole)[1] - self.song.authors_songs.append(author_song) + self.song.add_author(self.manager.get_object(Author, item.data(QtCore.Qt.UserRole)[0]), + item.data(QtCore.Qt.UserRole)[1]) self.song.topics = [] for row in range(self.topics_list_view.count()): item = self.topics_list_view.item(row) diff --git a/openlp/plugins/songs/lib/xml.py b/openlp/plugins/songs/lib/xml.py index b856cb53f..f67a35201 100644 --- a/openlp/plugins/songs/lib/xml.py +++ b/openlp/plugins/songs/lib/xml.py @@ -71,7 +71,7 @@ from lxml import etree, objectify from openlp.core.common import translate from openlp.core.lib import FormattingTags from openlp.plugins.songs.lib import VerseType, clean_song -from openlp.plugins.songs.lib.db import Author, AuthorSong, AuthorType, Book, Song, Topic +from openlp.plugins.songs.lib.db import Author, AuthorType, Book, Song, Topic from openlp.core.utils import get_application_version log = logging.getLogger(__name__) @@ -519,10 +519,7 @@ class OpenLyrics(object): author = Author.populate(display_name=display_name, last_name=display_name.split(' ')[-1], first_name=' '.join(display_name.split(' ')[:-1])) - author_song = AuthorSong() - author_song.author = author - author_song.author_type = author_type - song.authors_songs.append(author_song) + song.add_author(author, author_type) def _process_cclinumber(self, properties, song): """ From 3d1cb3f3838330233169894f5f6cc1cef9a0661a Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 7 May 2014 13:00:34 +0200 Subject: [PATCH 121/212] Add test --- openlp/core/lib/ui.py | 2 +- tests/functional/openlp_core_lib/test_ui.py | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/openlp/core/lib/ui.py b/openlp/core/lib/ui.py index 965adb053..a1e37abcf 100644 --- a/openlp/core/lib/ui.py +++ b/openlp/core/lib/ui.py @@ -295,7 +295,7 @@ def set_case_insensitive_completer(cache, widget): Sets a case insensitive text completer for a widget. :param cache: The list of items to use as suggestions. - :param widget: A widget to set the completer (QComboBox or QTextEdit instance) + :param widget: A widget to set the completer (QComboBox or QLineEdit instance) """ completer = QtGui.QCompleter(cache) completer.setCaseSensitivity(QtCore.Qt.CaseInsensitive) diff --git a/tests/functional/openlp_core_lib/test_ui.py b/tests/functional/openlp_core_lib/test_ui.py index 025b1a638..d48e5d694 100644 --- a/tests/functional/openlp_core_lib/test_ui.py +++ b/tests/functional/openlp_core_lib/test_ui.py @@ -170,3 +170,19 @@ class TestUi(TestCase): self.assertIsInstance(action.icon(), QtGui.QIcon) self.assertEqual('my tooltip', action.toolTip()) self.assertEqual('my statustip', action.statusTip()) + + def test_set_case_insensitive_completer(self): + """ + Test setting a case insensitive completer on a widget + """ + # GIVEN: A QComboBox and a list of completion items + line_edit = QtGui.QLineEdit() + suggestions = ['one', 'Two', 'THRee', 'FOUR'] + + # WHEN: We call the function + set_case_insensitive_completer(suggestions, line_edit) + + # THEN: The Combobox should have a completer which is case insensitive + completer = line_edit.completer() + self.assertIsInstance(completer, QtGui.QCompleter) + self.assertEqual(completer.caseSensitivity(), QtCore.Qt.CaseInsensitive) From 13d9e77c08bafd4a939a537921ab093130a78ee4 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 7 May 2014 13:56:14 +0200 Subject: [PATCH 122/212] Fix bug 1313538 Fixes: https://launchpad.net/bugs/1313538 --- openlp/plugins/songs/lib/mediaitem.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index 288711b06..5cb8af47a 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -524,15 +524,15 @@ class SongMediaItem(MediaManagerItem): add_song = True if search_results: for song in search_results: - author_list = item.data_string['authors'] + author_list = item.data_string['authors'].split(', ') same_authors = True for author in song.authors: if author.display_name in author_list: - author_list = author_list.replace(author.display_name, '', 1) + author_list = author_list.remove(author.display_name) else: same_authors = False break - if same_authors and author_list.strip(', ') == '': + if same_authors and not author_list: add_song = False edit_id = song.id break From cca1ce57f370e6b7ad7d71c90f185f08371707bb Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 7 May 2014 14:07:13 +0200 Subject: [PATCH 123/212] Add test --- tests/functional/openlp_core_lib/test_ui.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/functional/openlp_core_lib/test_ui.py b/tests/functional/openlp_core_lib/test_ui.py index 025b1a638..8fdfabd14 100644 --- a/tests/functional/openlp_core_lib/test_ui.py +++ b/tests/functional/openlp_core_lib/test_ui.py @@ -146,6 +146,20 @@ class TestUi(TestCase): self.assertEqual('combo1', combo.objectName()) self.assertEqual(QtGui.QComboBox.AdjustToMinimumContentsLength, combo.sizeAdjustPolicy()) + def test_create_widget_action(self): + """ + Test creating an action for a widget + """ + # GIVEN: A button + button = QtGui.QPushButton() + + # WHEN: We call the function + action = create_widget_action(button, 'some action') + + # THEN: The action should be returned + self.assertIsInstance(action, QtGui.QAction) + self.assertEqual(action.objectName(), 'some action') + def test_create_action(self): """ Test creating an action From 6440ac008f94dc3385f42a6ed9dcd0ef5a085440 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 7 May 2014 15:59:31 +0200 Subject: [PATCH 124/212] Remove unused code --- openlp/plugins/songs/lib/db.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/openlp/plugins/songs/lib/db.py b/openlp/plugins/songs/lib/db.py index e77d98bda..93c27d32e 100644 --- a/openlp/plugins/songs/lib/db.py +++ b/openlp/plugins/songs/lib/db.py @@ -114,32 +114,32 @@ class Song(BaseModel): """ self.sort_key = get_natural_key(self.title) - def add_author(self, author, author_type=None, ignore_type=False): + def add_author(self, author, author_type=None): """ Add an author to the song if it not yet exists - :return: True if the author has been added successfully. False if it was already there. + :param author: Author object + :param author_type: AuthorType constant or None """ for author_song in self.authors_songs: - if author_song.author == author and (ignore_type or author_song.author_type == author_type): - return False + if author_song.author == author and author_song.author_type == author_type: + return new_author_song = AuthorSong() new_author_song.author = author new_author_song.author_type = author_type self.authors_songs.append(new_author_song) - return True - def remove_author(self, author, author_type=None, ignore_type=False): + def remove_author(self, author, author_type=None): """ Remove an existing author from the song - :return: True if the author has been removed successfully. False if it could not be found. + :param author: Author object + :param author_type: AuthorType constant or None """ for author_song in self.authors_songs: - if author_song.author == author and (ignore_type or author_song.author_type == author_type): + if author_song.author == author and author_song.author_type == author_type: self.authors_songs.remove(author_song) - return True - return False + return class Topic(BaseModel): From 1e6f00c2ebd19f92705a7d0dd6435fe20c02bd36 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Wed, 7 May 2014 17:05:27 +0100 Subject: [PATCH 125/212] Add test --- .../openlp_core_ui/test_slidecontroller.py | 2 +- .../openlp_plugins/remotes/test_router.py | 27 ++++++++++++++++--- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/tests/functional/openlp_core_ui/test_slidecontroller.py b/tests/functional/openlp_core_ui/test_slidecontroller.py index 56d87c511..104c83750 100644 --- a/tests/functional/openlp_core_ui/test_slidecontroller.py +++ b/tests/functional/openlp_core_ui/test_slidecontroller.py @@ -33,7 +33,7 @@ from unittest import TestCase from openlp.core.ui import SlideController -from tests.interfaces import MagicMock, patch +from tests.interfaces import MagicMock class TestSlideController(TestCase): diff --git a/tests/functional/openlp_plugins/remotes/test_router.py b/tests/functional/openlp_plugins/remotes/test_router.py index 9e13a448b..1388e6104 100644 --- a/tests/functional/openlp_plugins/remotes/test_router.py +++ b/tests/functional/openlp_plugins/remotes/test_router.py @@ -32,7 +32,7 @@ This module contains tests for the lib submodule of the Remotes plugin. import os from unittest import TestCase -from openlp.core.common import Settings +from openlp.core.common import Settings, Registry from openlp.plugins.remotes.lib.httpserver import HttpRouter from tests.functional import MagicMock, patch, mock_open from tests.helpers.testmixin import TestMixin @@ -92,15 +92,15 @@ class TestRouter(TestCase, TestMixin): Test the router control functionality """ # GIVEN: A testing set of Routes - router = HttpRouter() + #router = HttpRouter() mocked_function = MagicMock() test_route = [ (r'^/stage/api/poll$', {'function': mocked_function, 'secure': False}), ] - router.routes = test_route + self.router.routes = test_route # WHEN: called with a poll route - function, args = router.process_http_request('/stage/api/poll', None) + function, args = self.router.process_http_request('/stage/api/poll', None) # THEN: the function should have been called only once self.assertEqual(mocked_function, function['function'], 'The mocked function should match defined value.') @@ -126,6 +126,25 @@ class TestRouter(TestCase, TestMixin): # THEN: all types should match self.assertEqual(content_type, header[1], 'Mismatch of content type') + def main_poll_test(self): + """ + Test the main poll logic + """ + # GIVEN: a defined router with two slides + Registry().register('live_controller', MagicMock) + router = HttpRouter() + router.send_response = MagicMock() + router.send_header = MagicMock() + router.end_headers = MagicMock() + router.live_controller.slide_count = 2 + + # WHEN: main poll called + results = router.main_poll() + + # THEN: the correct response should be returned + self.assertEqual(results.decode('utf-8'), '{"results": {"slide_count": 2}}', + 'The resulting json strings should match') + def serve_file_without_params_test(self): """ Test the serve_file method without params From 55c9e3e19678357a28f6c49b1a04f9205271f137 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 7 May 2014 21:17:47 +0200 Subject: [PATCH 126/212] Log warning when CCLI no can't be parsed --- openlp/plugins/songs/lib/songshowplusimport.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openlp/plugins/songs/lib/songshowplusimport.py b/openlp/plugins/songs/lib/songshowplusimport.py index fe81b61a2..aebded029 100644 --- a/openlp/plugins/songs/lib/songshowplusimport.py +++ b/openlp/plugins/songs/lib/songshowplusimport.py @@ -151,6 +151,8 @@ class SongShowPlusImport(SongImport): match = re.search(r'\d+', self.decode(data)) if match: self.ccli_number = int(match.group()) + else: + log.warn("Can't parse CCLI Number from string: %s" % self.decode(data)) elif block_key == VERSE: self.add_verse(self.decode(data), "%s%s" % (VerseType.tags[VerseType.Verse], verse_no)) elif block_key == CHORUS: From c6d6be30c6b90388304eb8f1ca0af83ea0442f3c Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Wed, 7 May 2014 21:38:34 +0100 Subject: [PATCH 127/212] fix --- tests/functional/openlp_plugins/remotes/test_router.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/functional/openlp_plugins/remotes/test_router.py b/tests/functional/openlp_plugins/remotes/test_router.py index 1388e6104..037169b88 100644 --- a/tests/functional/openlp_plugins/remotes/test_router.py +++ b/tests/functional/openlp_plugins/remotes/test_router.py @@ -92,7 +92,6 @@ class TestRouter(TestCase, TestMixin): Test the router control functionality """ # GIVEN: A testing set of Routes - #router = HttpRouter() mocked_function = MagicMock() test_route = [ (r'^/stage/api/poll$', {'function': mocked_function, 'secure': False}), From 60fa4c2dcac032db960967c81c3bd32b756c12aa Mon Sep 17 00:00:00 2001 From: Jonathan Springer Date: Wed, 7 May 2014 18:52:06 -0400 Subject: [PATCH 128/212] Make vlcplayer stop call asynchronous to aviod a deadlock with the UI thread --- openlp/core/ui/media/vlcplayer.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openlp/core/ui/media/vlcplayer.py b/openlp/core/ui/media/vlcplayer.py index c80bb6218..d02526b0e 100644 --- a/openlp/core/ui/media/vlcplayer.py +++ b/openlp/core/ui/media/vlcplayer.py @@ -34,6 +34,7 @@ from distutils.version import LooseVersion import logging import os import sys +import threading from PyQt4 import QtGui @@ -207,7 +208,7 @@ class VlcPlayer(MediaPlayer): start_time = 0 if self.state != MediaState.Paused and controller.media_info.start_time > 0: start_time = controller.media_info.start_time - display.vlc_media_player.play() + threading.Thread(target=display.vlc_media_player.play).start() if not self.media_state_wait(display, vlc.State.Playing): return False self.volume(display, controller.media_info.volume) @@ -233,7 +234,7 @@ class VlcPlayer(MediaPlayer): """ Stop the current item """ - display.vlc_media_player.stop() + threading.Thread(target=display.vlc_media_player.stop).start() self.state = MediaState.Stopped def volume(self, display, vol): From 6de740333b4521d46fc9c518e5260eb1b6424598 Mon Sep 17 00:00:00 2001 From: Jonathan Springer Date: Wed, 7 May 2014 18:54:14 -0400 Subject: [PATCH 129/212] merge trunk From 13b9a7de72b01147199e928a831931668be681eb Mon Sep 17 00:00:00 2001 From: Jonathan Springer Date: Thu, 8 May 2014 10:57:26 -0400 Subject: [PATCH 130/212] Add test for the main window function set_service_modified --- .../openlp_core_ui/test_mainwindow.py | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/tests/functional/openlp_core_ui/test_mainwindow.py b/tests/functional/openlp_core_ui/test_mainwindow.py index 0b17828b9..b348f8f80 100644 --- a/tests/functional/openlp_core_ui/test_mainwindow.py +++ b/tests/functional/openlp_core_ui/test_mainwindow.py @@ -34,6 +34,7 @@ import os from unittest import TestCase from openlp.core.ui.mainwindow import MainWindow +from openlp.core.lib.ui import UiStrings from openlp.core.common.registry import Registry from tests.utils.constants import TEST_RESOURCES_PATH from tests.helpers.testmixin import TestMixin @@ -95,3 +96,41 @@ class TestMainWindow(TestCase, TestMixin): # THEN the file should not be opened assert not mocked_load_path.called, 'load_path should not have been called' + + def main_window_title_test(self): + """ + Test that running a new instance of OpenLP set the window title correctly + """ + # GIVEN a newly opened OpenLP instance + + # WHEN no changes are made to the service + + # THEN the main window's title shoud be the same as the OLPV2x string in the UiStrings class + self.assertEqual(self.main_window.windowTitle(), UiStrings().OLPV2x, + 'The main window\'s title should be the same as the OLPV2x string in UiStrings class') + + def set_service_modifed_test(self): + """ + Test that when setting the service's title the main window's title is set correctly + """ + # GIVEN a newly opened OpenLP instance + + # WHEN set_service_modified is called with with the modified flag set true and a file name + self.main_window.set_service_modified(True, 'test.osz') + + # THEN the main window's title should be set to the + self.assertEqual(self.main_window.windowTitle(), '%s - %s*' % (UiStrings().OLPV2x, 'test.osz'), + 'The main window\'s title should be set to " - test.osz*"') + + def set_service_unmodified_test(self): + """ + Test that when setting the service's title the main window's title is set correctly + """ + # GIVEN a newly opened OpenLP instance + + # WHEN set_service_modified is called with with the modified flag set False and a file name + self.main_window.set_service_modified(False, 'test.osz') + + # THEN the main window's title should be set to the + self.assertEqual(self.main_window.windowTitle(), '%s - %s' % (UiStrings().OLPV2x, 'test.osz'), + 'The main window\'s title should be set to " - test.osz"') From ec7a7b74fee96699a01ec1a13e4638bb75adb36a Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sat, 10 May 2014 08:38:38 +0100 Subject: [PATCH 131/212] test --- tests/helpers/testmixin.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/helpers/testmixin.py b/tests/helpers/testmixin.py index b4e8a5c59..0b168d45e 100644 --- a/tests/helpers/testmixin.py +++ b/tests/helpers/testmixin.py @@ -62,3 +62,9 @@ class TestMixin(object): """ os.close(self.fd) os.unlink(Settings().fileName()) + + + + + + From 6eb2d4f49dfba79b1361dc14160c88d9e917cf2a Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Tue, 13 May 2014 11:08:46 +0200 Subject: [PATCH 132/212] Write test for adding and removing authors --- .../openlp_plugins/songs/test_db.py | 121 ++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 tests/functional/openlp_plugins/songs/test_db.py diff --git a/tests/functional/openlp_plugins/songs/test_db.py b/tests/functional/openlp_plugins/songs/test_db.py new file mode 100644 index 000000000..f9afe8cd2 --- /dev/null +++ b/tests/functional/openlp_plugins/songs/test_db.py @@ -0,0 +1,121 @@ +# -*- 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 db submodule of the Songs plugin. +""" +from unittest import TestCase +from tests.functional import patch, MagicMock + +from openlp.plugins.songs.lib.db import Song, Author, AuthorType + + +class TestDB(TestCase): + """ + Test the functions in the :mod:`lib` module. + """ + def setUp(self): + pass + + def test_add_author(self): + """ + Test adding an author to a song + """ + # GIVEN: A song and an author + song = Song() + song.authors_songs = [] + author = Author() + author.first_name = "Max" + author.last_name = "Mustermann" + + # WHEN: We add an author to the song + song.add_author(author) + + # THEN: The author should have been added with author_type=None + self.assertEqual(1, len(song.authors_songs)) + self.assertEqual("Max", song.authors_songs[0].author.first_name) + self.assertEqual("Mustermann", song.authors_songs[0].author.last_name) + self.assertIsNone(song.authors_songs[0].author_type) + + def test_add_author_with_type(self): + """ + Test adding an author with a type specified to a song + """ + # GIVEN: A song and an author + song = Song() + song.authors_songs = [] + author = Author() + author.first_name = "Max" + author.last_name = "Mustermann" + + # WHEN: We add an author to the song + song.add_author(author, AuthorType.Words) + + # THEN: The author should have been added with author_type=None + self.assertEqual(1, len(song.authors_songs)) + self.assertEqual("Max", song.authors_songs[0].author.first_name) + self.assertEqual("Mustermann", song.authors_songs[0].author.last_name) + self.assertEqual(AuthorType.Words ,song.authors_songs[0].author_type) + + def test_remove_author(self): + """ + Test removing an author from a song + """ + # GIVEN: A song with an author + song = Song() + song.authors_songs = [] + author = Author() + author.first_name = "Max" + author.last_name = "Mustermann" + song.add_author(author) + + # WHEN: We remove the author + song.remove_author(author) + + # THEN: It should have been removed + self.assertEqual(0, len(song.authors_songs)) + + def test_remove_author_with_type(self): + """ + Test removing an author with a type specified from a song + """ + # GIVEN: A song with two authors + song = Song() + song.authors_songs = [] + author = Author() + author.first_name = "Max" + author.last_name = "Mustermann" + song.add_author(author) + song.add_author(author, AuthorType.Translation) + + # WHEN: We remove the author with a certain type + song.remove_author(author, AuthorType.Translation) + + # THEN: It should have been removed and the other author should still be there + self.assertEqual(1, len(song.authors_songs)) + self.assertEqual(None ,song.authors_songs[0].author_type) \ No newline at end of file From a1e2e921329986042b0f94d67a354a220acb725e Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Tue, 13 May 2014 11:09:42 +0200 Subject: [PATCH 133/212] PEP8 --- tests/functional/openlp_plugins/songs/test_db.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tests/functional/openlp_plugins/songs/test_db.py b/tests/functional/openlp_plugins/songs/test_db.py index f9afe8cd2..bb70cb588 100644 --- a/tests/functional/openlp_plugins/songs/test_db.py +++ b/tests/functional/openlp_plugins/songs/test_db.py @@ -80,7 +80,7 @@ class TestDB(TestCase): self.assertEqual(1, len(song.authors_songs)) self.assertEqual("Max", song.authors_songs[0].author.first_name) self.assertEqual("Mustermann", song.authors_songs[0].author.last_name) - self.assertEqual(AuthorType.Words ,song.authors_songs[0].author_type) + self.assertEqual(AuthorType.Words, song.authors_songs[0].author_type) def test_remove_author(self): """ @@ -90,8 +90,6 @@ class TestDB(TestCase): song = Song() song.authors_songs = [] author = Author() - author.first_name = "Max" - author.last_name = "Mustermann" song.add_author(author) # WHEN: We remove the author @@ -108,8 +106,6 @@ class TestDB(TestCase): song = Song() song.authors_songs = [] author = Author() - author.first_name = "Max" - author.last_name = "Mustermann" song.add_author(author) song.add_author(author, AuthorType.Translation) @@ -118,4 +114,4 @@ class TestDB(TestCase): # THEN: It should have been removed and the other author should still be there self.assertEqual(1, len(song.authors_songs)) - self.assertEqual(None ,song.authors_songs[0].author_type) \ No newline at end of file + self.assertEqual(None ,song.authors_songs[0].author_type) From b7b0176dece0d41c923fda32c8f39843335ab8c9 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Tue, 13 May 2014 11:12:07 +0200 Subject: [PATCH 134/212] Cleanups --- tests/functional/openlp_plugins/songs/test_db.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tests/functional/openlp_plugins/songs/test_db.py b/tests/functional/openlp_plugins/songs/test_db.py index bb70cb588..3080db77e 100644 --- a/tests/functional/openlp_plugins/songs/test_db.py +++ b/tests/functional/openlp_plugins/songs/test_db.py @@ -30,17 +30,14 @@ This module contains tests for the db submodule of the Songs plugin. """ from unittest import TestCase -from tests.functional import patch, MagicMock from openlp.plugins.songs.lib.db import Song, Author, AuthorType class TestDB(TestCase): """ - Test the functions in the :mod:`lib` module. + Test the functions in the :mod:`db` module. """ - def setUp(self): - pass def test_add_author(self): """ @@ -114,4 +111,4 @@ class TestDB(TestCase): # THEN: It should have been removed and the other author should still be there self.assertEqual(1, len(song.authors_songs)) - self.assertEqual(None ,song.authors_songs[0].author_type) + self.assertEqual(None, song.authors_songs[0].author_type) From 9b173dc0fef46f37aee095a4a6343eed0121acd5 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Tue, 13 May 2014 11:27:22 +0200 Subject: [PATCH 135/212] Refactor author match check --- openlp/plugins/songs/lib/mediaitem.py | 28 ++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index 5cb8af47a..04fa5a77a 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -524,15 +524,7 @@ class SongMediaItem(MediaManagerItem): add_song = True if search_results: for song in search_results: - author_list = item.data_string['authors'].split(', ') - same_authors = True - for author in song.authors: - if author.display_name in author_list: - author_list = author_list.remove(author.display_name) - else: - same_authors = False - break - if same_authors and not author_list: + if self._authors_match(song, item.data_string['authors']): add_song = False edit_id = song.id break @@ -558,6 +550,24 @@ class SongMediaItem(MediaManagerItem): self.generate_footer(item, song) return item + def _authors_match(self, song, authors): + """ + Checks whether authors from a song in the database match the authors of the song to be imported. + + :param song: A list of authors from the song in the database + :param authors: A string with authors from the song to be imported + :return: True when Authors do match, else false. + """ + author_list = authors.split(', ') + for author in song.authors: + if author.display_name in author_list: + author_list = author_list.remove(author.display_name) + else: + return False + # List must be empty at the end + return not author_list + + def search(self, string, show_error): """ Search for some songs From 97b603a2d6747dadbf1a9e492bd26e5c73857e8f Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Tue, 13 May 2014 11:44:19 +0200 Subject: [PATCH 136/212] Add test for author matching --- openlp/plugins/songs/lib/mediaitem.py | 5 ++-- .../openlp_plugins/songs/test_mediaitem.py | 29 +++++++++++++++++++ 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index 04fa5a77a..e51743413 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -556,18 +556,17 @@ class SongMediaItem(MediaManagerItem): :param song: A list of authors from the song in the database :param authors: A string with authors from the song to be imported - :return: True when Authors do match, else false. + :return: True when Authors do match, else False. """ author_list = authors.split(', ') for author in song.authors: if author.display_name in author_list: - author_list = author_list.remove(author.display_name) + author_list.remove(author.display_name) else: return False # List must be empty at the end return not author_list - def search(self, string, show_error): """ Search for some songs diff --git a/tests/functional/openlp_plugins/songs/test_mediaitem.py b/tests/functional/openlp_plugins/songs/test_mediaitem.py index 308881c2e..359e27a0f 100644 --- a/tests/functional/openlp_plugins/songs/test_mediaitem.py +++ b/tests/functional/openlp_plugins/songs/test_mediaitem.py @@ -128,3 +128,32 @@ class TestMediaItem(TestCase, TestMixin): # THEN: I would get an amended footer string self.assertEqual(service_item.raw_footer, ['My Song', 'My copyright', 'CCLI License: 4321'], 'The array should be returned correctly with a song, an author, copyright and amended ccli') + + def match_authors_test(self): + """ + Test the author matching when importing a song from a service + """ + # GIVEN: A song and a string with authors + song = MagicMock() + song.authors = [] + author = MagicMock() + author.display_name = "Hans Wurst" + song.authors.append(author) + author2 = MagicMock() + author2.display_name = "Max Mustermann" + song.authors.append(author2) + # There are occasions where an author appears twice in a song (with different types). + # We need to make sure that this case works (lp#1313538) + author3 = MagicMock() + author3.display_name = "Max Mustermann" + song.authors.append(author3) + authors_str = "Hans Wurst, Max Mustermann, Max Mustermann" + authors_str_wrong = "Hans Wurst, Max Mustermann" + + # WHEN: Checking for matching + correct_result = self.media_item._authors_match(song, authors_str) + wrong_result = self.media_item._authors_match(song, authors_str_wrong) + + # THEN: They should match + self.assertTrue(correct_result) + self.assertFalse(wrong_result) From a75790d6997da54334aa15a2a9cecedc9e274f3d Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Tue, 13 May 2014 11:54:12 +0200 Subject: [PATCH 137/212] Indentation --- tests/functional/openlp_plugins/songs/test_mediaitem.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/openlp_plugins/songs/test_mediaitem.py b/tests/functional/openlp_plugins/songs/test_mediaitem.py index d21342926..1b4374bcd 100644 --- a/tests/functional/openlp_plugins/songs/test_mediaitem.py +++ b/tests/functional/openlp_plugins/songs/test_mediaitem.py @@ -128,7 +128,7 @@ class TestMediaItem(TestCase, TestMixin): self.assertEqual(service_item.raw_footer, ['My Song', 'My copyright', 'CCLI License: 4321'], 'The array should be returned correctly with a song, an author, copyright and amended ccli') - def build_song_footer_base_songbook_test(self): + def build_song_footer_base_songbook_test(self): """ Test build songs footer with basic song and a songbook """ From d6afa4905556febbc49d1ce51e02697e3c7e4eea Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Tue, 13 May 2014 19:52:08 +0200 Subject: [PATCH 138/212] Test fixes --- .../openlp_plugins/songs/test_mediaitem.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/tests/functional/openlp_plugins/songs/test_mediaitem.py b/tests/functional/openlp_plugins/songs/test_mediaitem.py index 1b4374bcd..b3c097268 100644 --- a/tests/functional/openlp_plugins/songs/test_mediaitem.py +++ b/tests/functional/openlp_plugins/songs/test_mediaitem.py @@ -173,12 +173,16 @@ class TestMediaItem(TestCase, TestMixin): author3.display_name = "Max Mustermann" song.authors.append(author3) authors_str = "Hans Wurst, Max Mustermann, Max Mustermann" - authors_str_wrong = "Hans Wurst, Max Mustermann" # WHEN: Checking for matching - correct_result = self.media_item._authors_match(song, authors_str) - wrong_result = self.media_item._authors_match(song, authors_str_wrong) + result = self.media_item._authors_match(song, authors_str) # THEN: They should match - self.assertTrue(correct_result) - self.assertFalse(wrong_result) + self.assertTrue(result, "Authors should match") + + # WHEN: An author is missing in the string + authors_str = "Hans Wurst, Max Mustermann" + result = self.media_item._authors_match(song, authors_str) + + # THEN: They should not match + self.assertFalse(result, "Authors should not match") From 37f04c51996b7e8d58b3bb0f8816581bf33bf5b5 Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Wed, 14 May 2014 22:53:43 +0200 Subject: [PATCH 139/212] Added simple tests. --- openlp/plugins/bibles/lib/zefania.py | 4 +- .../bibles/test_zefaniaimport.py | 116 ++++++++++++++++++ tests/resources/bibles/zefania-dk1933.xml | 45 +++++++ 3 files changed, 164 insertions(+), 1 deletion(-) create mode 100644 tests/functional/openlp_plugins/bibles/test_zefaniaimport.py create mode 100644 tests/resources/bibles/zefania-dk1933.xml diff --git a/openlp/plugins/bibles/lib/zefania.py b/openlp/plugins/bibles/lib/zefania.py index 16680efa3..c52b58eae 100644 --- a/openlp/plugins/bibles/lib/zefania.py +++ b/openlp/plugins/bibles/lib/zefania.py @@ -70,8 +70,10 @@ class ZefaniaBible(BibleDB): return False zefania_bible_tree = etree.parse(import_file) num_books = int(zefania_bible_tree.xpath("count(//BIBLEBOOK)")) - # Strip tags we don't use + # Strip tags we don't use - keep content etree.strip_tags(zefania_bible_tree, ('STYLE', 'GRAM', 'NOTE', 'SUP', 'XREF')) + # Strip tags we don't use - remove content + etree.strip_elements(zefania_bible_tree, ('PROLOG', 'REMARK', 'CAPTION', 'MEDIA'), with_tail=False) xmlbible = zefania_bible_tree.getroot() for BIBLEBOOK in xmlbible: book_ref_id = self.get_book_ref_id_by_name(str(BIBLEBOOK.get('bname')), num_books) diff --git a/tests/functional/openlp_plugins/bibles/test_zefaniaimport.py b/tests/functional/openlp_plugins/bibles/test_zefaniaimport.py new file mode 100644 index 000000000..64fa3251e --- /dev/null +++ b/tests/functional/openlp_plugins/bibles/test_zefaniaimport.py @@ -0,0 +1,116 @@ +# -*- 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 Zefania Bible importer. +""" + +import os +from unittest import TestCase + +from tests.functional import MagicMock, patch +from openlp.plugins.bibles.lib.zefania import ZefaniaBible +from openlp.plugins.bibles.lib.db import BibleDB + +TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), + '..', '..', '..', 'resources', 'bibles')) +ZEFANIA_TEST_DATA = { + 'zefania-dk1933.xml': { + 'book': 'Genesis', + 'chapter' : 1, + 'verses': [ + (1, 'I Begyndelsen skabte Gud Himmelen og Jorden.\n'), + (2, 'Og Jorden var øde og tom, og der var Mørke over Verdensdybet. ' + 'Men Guds Ånd svævede over Vandene.'), + (3, 'Og Gud sagde: "Der blive Lys!" Og der blev Lys.'), + (4, 'Og Gud så, at Lyset var godt, og Gud satte Skel mellem Lyset og Mørket,'), + (5, 'og Gud kaldte Lyset Dag, og Mørket kaldte han Nat. Og det blev Aften, ' + 'og det blev Morgen, første Dag.'), + (6, 'Derpå sagde Gud: "Der blive en Hvælving midt i Vandene til at skille Vandene ad!"'), + (7, 'Og således skete det: Gud gjorde Hvælvingen og skilte Vandet under Hvælvingen ' + 'fra Vandet over Hvælvingen;'), + (8, 'og Gud kaldte Hvælvingen Himmel. Og det blev Aften, og det blev Morgen, anden Dag.'), + (9, 'Derpå sagde Gud: "Vandet under Himmelen samle sig på eet Sted, så det faste Land kommer til Syne!" ' + 'Og således skete det;'), + (10, 'og Gud kaldte det faste Land Jord, og Stedet, hvor Vandet samlede sig, kaldte han Hav. Og Gud så, ' + 'at det var godt.') + ] + } +} + + +class TestZefaniaImport(TestCase): + """ + Test the functions in the :mod:`zefaniaimport` module. + """ + + def setUp(self): + self.construtor_patcher = patch('openlp.plugins.bibles.lib.zefania.BibleDB') + self.construtor_patcher.start() + + def tearDown(self): + self.construtor_patcher.stop() + + def create_importer_test(self): + """ + Test creating an instance of the Zefania file importer + """ + # GIVEN: A mocked out BibleDB class, and a mocked out "manager" + #with patch('openlp.plugins.bibles.lib.zefania.BibleDB'): + if True: + mocked_manager = MagicMock() + + # WHEN: An importer object is created + importer = ZefaniaBible(mocked_manager, filename='') + + # THEN: The importer should be an instance of SongImport + self.assertIsInstance(importer, BibleDB) + + def file_import_test(self): + """ + Test the actual import of real song files + """ + # GIVEN: Test files with a mocked out "manager" and a mocked out "import_wizard" + #with patch('openlp.plugins.bibles.lib.zefania.BibleDB'): + if True: + for bible_file in ZEFANIA_TEST_DATA: + mocked_manager = MagicMock() + mocked_import_wizard = MagicMock() + importer = ZefaniaBible(mocked_manager, filename='') + importer.import_wizard = mocked_import_wizard + importer.get_book_ref_id_by_name = MagicMock() + importer.get_book_ref_id_by_name.return_value = { 'id': 1, 'testament_id': 1, 'name': 'Genesis', + 'abbreviation': 'Gen', 'chapters': 1 } + importer.create_verse = MagicMock() + + # WHEN: Importing each file + importer.filename = os.path.join(TEST_PATH, bible_file) + importer.do_import() + + # THEN: The create_verse() method should have been called + self.assertTrue(importer.create_verse.called) diff --git a/tests/resources/bibles/zefania-dk1933.xml b/tests/resources/bibles/zefania-dk1933.xml new file mode 100644 index 000000000..f538edb99 --- /dev/null +++ b/tests/resources/bibles/zefania-dk1933.xml @@ -0,0 +1,45 @@ + + + + + + + + Danish Version + + + The Holy Bible + The electronic edition of this Bible comes from the Danish 1933 edition. The Old Testament is an update from the 1931 edition, and the New Testament is an update from the 1907 edition. + Free Bible Software Group + + The Unbound Bible + Biola University: Administrative Computing + 13800 Biola Ave. + La Mirada, CA 90639 + United States of America + 562-903-4722 + + 2009-01-20 + Zefania XML Bible Markup Language + DAN + ftp://unboundftp.biola.edu/pub/danish1933_utf8.zip + DAN + provide the bible to the world + + + + + + I skabte Gud Himmelen og Jorden. + Og Jorden var øde og tom, og der var Mørke over Verdensdybet. Men Guds Ånd svævede over Vandene. + Og Gud sagde: "Der blive Lys!" Og der blev Lys. + Og Gud så, at Lyset var godt, og Gud satte Skel mellem Lyset og Mørket, + og Gud kaldte Lyset Dag, og Mørket kaldte han Nat. Og det blev Aften, og det blev Morgen, første Dag. + Derpå sagde Gud: "Der blive en Hvælving midt i Vandene til at skille Vandene ad!" + Og således skete det: Gud gjorde Hvælvingen og skilte Vandet under Hvælvingen fra Vandet over Hvælvingen; + og Gud kaldte Hvælvingen Himmel. Og det blev Aften, og det blev Morgen, anden Dag. + Derpå sagde Gud: "Vandet under Himmelen samle sig på eet Sted, så det faste Land kommer til Syne!" Og således skete det; + og Gud kaldte det faste Land Jord, og Stedet, hvor Vandet samlede sig, kaldte han Hav. Og Gud så, at det var godt. + + + \ No newline at end of file From de8074b00a9e64244ee0ad082d4df899fe8a5384 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Mon, 19 May 2014 21:19:05 +0200 Subject: [PATCH 140/212] Improve single click behavior (remove flickering) --- openlp/core/lib/mediamanageritem.py | 5 +++-- openlp/core/ui/listpreviewwidget.py | 1 - 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py index 4d7676ad6..5893e8c38 100644 --- a/openlp/core/lib/mediamanageritem.py +++ b/openlp/core/lib/mediamanageritem.py @@ -495,8 +495,8 @@ s if service_item: service_item.from_plugin = True self.preview_controller.add_service_item(service_item) - if keep_focus: - self.list_view.setFocus() + if not keep_focus: + self.preview_controller.preview_widget.setFocus() def on_live_click(self): """ @@ -535,6 +535,7 @@ s if remote: service_item.will_auto_start = True self.live_controller.add_service_item(service_item) + self.live_controller.preview_widget.setFocus() def create_item_from_id(self, item_id): """ diff --git a/openlp/core/ui/listpreviewwidget.py b/openlp/core/ui/listpreviewwidget.py index 7dbcfc2bd..831eb182b 100644 --- a/openlp/core/ui/listpreviewwidget.py +++ b/openlp/core/ui/listpreviewwidget.py @@ -136,7 +136,6 @@ class ListPreviewWidget(QtGui.QTableWidget, RegistryProperties): if self.service_item.is_text(): self.resizeRowsToContents() self.setColumnWidth(0, self.viewport().width()) - self.setFocus() self.change_slide(slide_number) def change_slide(self, slide): From 58179f8d832ec661fbf55243db4db78654715220 Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Tue, 20 May 2014 20:41:07 +0200 Subject: [PATCH 141/212] Tests --- openlp/plugins/bibles/lib/zefania.py | 1 + .../bibles/test_zefaniaimport.py | 27 ++++++++++++++----- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/openlp/plugins/bibles/lib/zefania.py b/openlp/plugins/bibles/lib/zefania.py index c52b58eae..4e8373a93 100644 --- a/openlp/plugins/bibles/lib/zefania.py +++ b/openlp/plugins/bibles/lib/zefania.py @@ -97,6 +97,7 @@ class ZefaniaBible(BibleDB): self.session.commit() self.application.process_events() except Exception as e: + print(str(e)) critical_error_message_box( message=translate('BiblesPlugin.ZefaniaImport', 'Incorrect Bible file type supplied. Zefania Bibles may be ' diff --git a/tests/functional/openlp_plugins/bibles/test_zefaniaimport.py b/tests/functional/openlp_plugins/bibles/test_zefaniaimport.py index 64fa3251e..942834d7a 100644 --- a/tests/functional/openlp_plugins/bibles/test_zefaniaimport.py +++ b/tests/functional/openlp_plugins/bibles/test_zefaniaimport.py @@ -70,11 +70,20 @@ class TestZefaniaImport(TestCase): """ def setUp(self): - self.construtor_patcher = patch('openlp.plugins.bibles.lib.zefania.BibleDB') + self.construtor_patcher = patch('openlp.plugins.bibles.lib.db.Registry') self.construtor_patcher.start() + self.get_lang_patcher = patch('openlp.plugins.bibles.lib.db.Manager') + self.get_lang_patcher.start() + self.brdb = patch('openlp.plugins.bibles.lib.zefania.BiblesResourcesDB') + self.brdb.start() + self.qt_core = patch('openlp.plugins.bibles.lib.db.QtCore') + self.qt_core.start() def tearDown(self): self.construtor_patcher.stop() + self.get_lang_patcher.stop() + self.brdb.stop() + self.qt_core.stop() def create_importer_test(self): """ @@ -86,7 +95,7 @@ class TestZefaniaImport(TestCase): mocked_manager = MagicMock() # WHEN: An importer object is created - importer = ZefaniaBible(mocked_manager, filename='') + importer = ZefaniaBible(mocked_manager, path='.', name='.', filename='') # THEN: The importer should be an instance of SongImport self.assertIsInstance(importer, BibleDB) @@ -96,21 +105,27 @@ class TestZefaniaImport(TestCase): Test the actual import of real song files """ # GIVEN: Test files with a mocked out "manager" and a mocked out "import_wizard" - #with patch('openlp.plugins.bibles.lib.zefania.BibleDB'): + #with patch('openlp.plugins.bibles.lib.zefania.BiblesResourcesDB') as brdb: if True: for bible_file in ZEFANIA_TEST_DATA: mocked_manager = MagicMock() mocked_import_wizard = MagicMock() - importer = ZefaniaBible(mocked_manager, filename='') - importer.import_wizard = mocked_import_wizard + importer = ZefaniaBible(mocked_manager, path='.', name='.', filename='') + importer.wizard = mocked_import_wizard + #importer.wizard.increment_progress_bar = MagicMock() importer.get_book_ref_id_by_name = MagicMock() importer.get_book_ref_id_by_name.return_value = { 'id': 1, 'testament_id': 1, 'name': 'Genesis', 'abbreviation': 'Gen', 'chapters': 1 } importer.create_verse = MagicMock() + importer.create_book = MagicMock() + importer.session = MagicMock() + importer.get_language = MagicMock() + importer.get_language.return_value = 'Danish' + importer.application.process_events = MagicMock() # WHEN: Importing each file importer.filename = os.path.join(TEST_PATH, bible_file) - importer.do_import() + importer.do_import('Dansk Version') # THEN: The create_verse() method should have been called self.assertTrue(importer.create_verse.called) From 4039cf1103230fec05d8035153f6f2fbbccefb32 Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Tue, 20 May 2014 23:54:18 +0200 Subject: [PATCH 142/212] More tests --- .../bibles/test_zefaniaimport.py | 64 ++++++++----------- 1 file changed, 27 insertions(+), 37 deletions(-) diff --git a/tests/functional/openlp_plugins/bibles/test_zefaniaimport.py b/tests/functional/openlp_plugins/bibles/test_zefaniaimport.py index 942834d7a..63e6ae40d 100644 --- a/tests/functional/openlp_plugins/bibles/test_zefaniaimport.py +++ b/tests/functional/openlp_plugins/bibles/test_zefaniaimport.py @@ -44,20 +44,20 @@ ZEFANIA_TEST_DATA = { 'book': 'Genesis', 'chapter' : 1, 'verses': [ - (1, 'I Begyndelsen skabte Gud Himmelen og Jorden.\n'), - (2, 'Og Jorden var øde og tom, og der var Mørke over Verdensdybet. ' + ('1', 'I Begyndelsen skabte Gud Himmelen og Jorden.'), + ('2', 'Og Jorden var øde og tom, og der var Mørke over Verdensdybet. ' 'Men Guds Ånd svævede over Vandene.'), - (3, 'Og Gud sagde: "Der blive Lys!" Og der blev Lys.'), - (4, 'Og Gud så, at Lyset var godt, og Gud satte Skel mellem Lyset og Mørket,'), - (5, 'og Gud kaldte Lyset Dag, og Mørket kaldte han Nat. Og det blev Aften, ' + ('3', 'Og Gud sagde: "Der blive Lys!" Og der blev Lys.'), + ('4', 'Og Gud så, at Lyset var godt, og Gud satte Skel mellem Lyset og Mørket,'), + ('5', 'og Gud kaldte Lyset Dag, og Mørket kaldte han Nat. Og det blev Aften, ' 'og det blev Morgen, første Dag.'), - (6, 'Derpå sagde Gud: "Der blive en Hvælving midt i Vandene til at skille Vandene ad!"'), - (7, 'Og således skete det: Gud gjorde Hvælvingen og skilte Vandet under Hvælvingen ' + ('6', 'Derpå sagde Gud: "Der blive en Hvælving midt i Vandene til at skille Vandene ad!"'), + ('7', 'Og således skete det: Gud gjorde Hvælvingen og skilte Vandet under Hvælvingen ' 'fra Vandet over Hvælvingen;'), - (8, 'og Gud kaldte Hvælvingen Himmel. Og det blev Aften, og det blev Morgen, anden Dag.'), - (9, 'Derpå sagde Gud: "Vandet under Himmelen samle sig på eet Sted, så det faste Land kommer til Syne!" ' + ('8', 'og Gud kaldte Hvælvingen Himmel. Og det blev Aften, og det blev Morgen, anden Dag.'), + ('9', 'Derpå sagde Gud: "Vandet under Himmelen samle sig på eet Sted, så det faste Land kommer til Syne!" ' 'Og således skete det;'), - (10, 'og Gud kaldte det faste Land Jord, og Stedet, hvor Vandet samlede sig, kaldte han Hav. Og Gud så, ' + ('10', 'og Gud kaldte det faste Land Jord, og Stedet, hvor Vandet samlede sig, kaldte han Hav. Og Gud så, ' 'at det var godt.') ] } @@ -70,62 +70,52 @@ class TestZefaniaImport(TestCase): """ def setUp(self): - self.construtor_patcher = patch('openlp.plugins.bibles.lib.db.Registry') - self.construtor_patcher.start() - self.get_lang_patcher = patch('openlp.plugins.bibles.lib.db.Manager') - self.get_lang_patcher.start() - self.brdb = patch('openlp.plugins.bibles.lib.zefania.BiblesResourcesDB') - self.brdb.start() - self.qt_core = patch('openlp.plugins.bibles.lib.db.QtCore') - self.qt_core.start() + self.registry_patcher = patch('openlp.plugins.bibles.lib.db.Registry') + self.registry_patcher.start() + self.manager_patcher = patch('openlp.plugins.bibles.lib.db.Manager') + self.manager_patcher.start() def tearDown(self): - self.construtor_patcher.stop() - self.get_lang_patcher.stop() - self.brdb.stop() - self.qt_core.stop() + self.registry_patcher.stop() + self.manager_patcher.stop() def create_importer_test(self): """ Test creating an instance of the Zefania file importer """ # GIVEN: A mocked out BibleDB class, and a mocked out "manager" - #with patch('openlp.plugins.bibles.lib.zefania.BibleDB'): - if True: - mocked_manager = MagicMock() + mocked_manager = MagicMock() - # WHEN: An importer object is created - importer = ZefaniaBible(mocked_manager, path='.', name='.', filename='') + # WHEN: An importer object is created + importer = ZefaniaBible(mocked_manager, path='.', name='.', filename='') - # THEN: The importer should be an instance of SongImport - self.assertIsInstance(importer, BibleDB) + # THEN: The importer should be an instance of SongImport + self.assertIsInstance(importer, BibleDB) def file_import_test(self): """ Test the actual import of real song files """ # GIVEN: Test files with a mocked out "manager" and a mocked out "import_wizard" - #with patch('openlp.plugins.bibles.lib.zefania.BiblesResourcesDB') as brdb: - if True: + with patch('openlp.plugins.bibles.lib.zefania.ZefaniaBible.application') as brdb: for bible_file in ZEFANIA_TEST_DATA: mocked_manager = MagicMock() mocked_import_wizard = MagicMock() importer = ZefaniaBible(mocked_manager, path='.', name='.', filename='') importer.wizard = mocked_import_wizard - #importer.wizard.increment_progress_bar = MagicMock() importer.get_book_ref_id_by_name = MagicMock() - importer.get_book_ref_id_by_name.return_value = { 'id': 1, 'testament_id': 1, 'name': 'Genesis', - 'abbreviation': 'Gen', 'chapters': 1 } importer.create_verse = MagicMock() importer.create_book = MagicMock() importer.session = MagicMock() importer.get_language = MagicMock() importer.get_language.return_value = 'Danish' - importer.application.process_events = MagicMock() - # WHEN: Importing each file + # WHEN: Importing bible file importer.filename = os.path.join(TEST_PATH, bible_file) importer.do_import('Dansk Version') - # THEN: The create_verse() method should have been called + # THEN: The create_verse() method should have been called with each verse in the file. self.assertTrue(importer.create_verse.called) + for verse_tag, verse_text in ZEFANIA_TEST_DATA['zefania-dk1933.xml']['verses']: + importer.create_verse.assert_any_call(importer.create_book().id, '1', verse_tag, verse_text) + From c0b167ca781cd2a024ff3953573c5acc26ed7580 Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Wed, 21 May 2014 11:30:36 +0200 Subject: [PATCH 143/212] Added tests for import of OpenSong bible format --- .../bibles/test_opensongimport.py | 140 ++++++++++++++++++ .../bibles/test_zefaniaimport.py | 16 +- tests/resources/bibles/opensong-dk1933.xml | 46 ++++++ 3 files changed, 194 insertions(+), 8 deletions(-) create mode 100644 tests/functional/openlp_plugins/bibles/test_opensongimport.py create mode 100644 tests/resources/bibles/opensong-dk1933.xml diff --git a/tests/functional/openlp_plugins/bibles/test_opensongimport.py b/tests/functional/openlp_plugins/bibles/test_opensongimport.py new file mode 100644 index 000000000..542cf289d --- /dev/null +++ b/tests/functional/openlp_plugins/bibles/test_opensongimport.py @@ -0,0 +1,140 @@ +# -*- 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 OpenSong Bible importer. +""" + +import os +from unittest import TestCase + +from tests.functional import MagicMock, patch +from openlp.plugins.bibles.lib.opensong import OpenSongBible +from openlp.plugins.bibles.lib.db import BibleDB + +TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), + '..', '..', '..', 'resources', 'bibles')) +OPENSONG_TEST_DATA = { + 'opensong-dk1933.xml': { + 'book': 'Genesis', + 'chapter': 1, + 'verses': [ + (1, 'I Begyndelsen skabte Gud Himmelen og Jorden.'), + (2, 'Og Jorden var øde og tom, og der var Mørke over Verdensdybet. ' + 'Men Guds Ånd svævede over Vandene.'), + (3, 'Og Gud sagde: "Der blive Lys!" Og der blev Lys.'), + (4, 'Og Gud så, at Lyset var godt, og Gud satte Skel mellem Lyset og Mørket,'), + (5, 'og Gud kaldte Lyset Dag, og Mørket kaldte han Nat. Og det blev Aften, ' + 'og det blev Morgen, første Dag.'), + (6, 'Derpå sagde Gud: "Der blive en Hvælving midt i Vandene til at skille Vandene ad!"'), + (7, 'Og således skete det: Gud gjorde Hvælvingen og skilte Vandet under Hvælvingen ' + 'fra Vandet over Hvælvingen;'), + (8, 'og Gud kaldte Hvælvingen Himmel. Og det blev Aften, og det blev Morgen, anden Dag.'), + (9, 'Derpå sagde Gud: "Vandet under Himmelen samle sig på eet Sted, så det faste Land kommer til Syne!" ' + 'Og således skete det;'), + (10, 'og Gud kaldte det faste Land Jord, og Stedet, hvor Vandet samlede sig, kaldte han Hav. Og Gud så, ' + 'at det var godt.') + ] + } +} + + +class TestOpenSongImport(TestCase): + """ + Test the functions in the :mod:`opensongimport` module. + """ + + def setUp(self): + self.registry_patcher = patch('openlp.plugins.bibles.lib.db.Registry') + self.registry_patcher.start() + self.manager_patcher = patch('openlp.plugins.bibles.lib.db.Manager') + self.manager_patcher.start() + + def tearDown(self): + self.registry_patcher.stop() + self.manager_patcher.stop() + + def create_importer_test(self): + """ + Test creating an instance of the OpenSong file importer + """ + # GIVEN: A mocked out "manager" + mocked_manager = MagicMock() + + # WHEN: An importer object is created + importer = OpenSongBible(mocked_manager, path='.', name='.', filename='') + + # THEN: The importer should be an instance of BibleDB + self.assertIsInstance(importer, BibleDB) + + def file_import_test(self): + """ + Test the actual import of real song files + """ + # GIVEN: Test files with a mocked out "manager", "import_wizard", and mocked functions + # get_book_ref_id_by_name, create_verse, create_book, session and get_language. + with patch('openlp.plugins.bibles.lib.opensong.OpenSongBible.application'): + for bible_file in OPENSONG_TEST_DATA: + mocked_manager = MagicMock() + mocked_import_wizard = MagicMock() + importer = OpenSongBible(mocked_manager, path='.', name='.', filename='') + importer.wizard = mocked_import_wizard + importer.get_book_ref_id_by_name = MagicMock() + importer.create_verse = MagicMock() + importer.create_book = MagicMock() + importer.session = MagicMock() + importer.get_language = MagicMock() + importer.get_language.return_value = 'Danish' + + # WHEN: Importing bible file + importer.filename = os.path.join(TEST_PATH, bible_file) + importer.do_import() + + # THEN: The create_verse() method should have been called with each verse in the file. + self.assertTrue(importer.create_verse.called) + for verse_tag, verse_text in OPENSONG_TEST_DATA[bible_file]['verses']: + importer.create_verse.assert_any_call(importer.create_book().id, 1, verse_tag, verse_text) + + def zefania_import_error_test(self): + """ + Test that we give an error message if trying to import a zefania bible + """ + # GIVEN: A mocked out "manager" and mocked out critical_error_message_box and an import + with patch('openlp.plugins.bibles.lib.opensong.critical_error_message_box') as \ + mocked_critical_error_message_box: + mocked_manager = MagicMock() + importer = OpenSongBible(mocked_manager, path='.', name='.', filename='') + + # WHEN: An trying to import a zefania bible + importer.filename = os.path.join(TEST_PATH, 'zefania-dk1933.xml') + importer.do_import() + + # THEN: The importer should have "shown" an error message + mocked_critical_error_message_box.assert_called_with(message='Incorrect Bible file type supplied. ' + 'This looks like a Zefania XML bible, ' + 'please use the Zefania import option.') diff --git a/tests/functional/openlp_plugins/bibles/test_zefaniaimport.py b/tests/functional/openlp_plugins/bibles/test_zefaniaimport.py index 63e6ae40d..50307b401 100644 --- a/tests/functional/openlp_plugins/bibles/test_zefaniaimport.py +++ b/tests/functional/openlp_plugins/bibles/test_zefaniaimport.py @@ -42,7 +42,7 @@ TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), ZEFANIA_TEST_DATA = { 'zefania-dk1933.xml': { 'book': 'Genesis', - 'chapter' : 1, + 'chapter': 1, 'verses': [ ('1', 'I Begyndelsen skabte Gud Himmelen og Jorden.'), ('2', 'Og Jorden var øde og tom, og der var Mørke over Verdensdybet. ' @@ -83,21 +83,22 @@ class TestZefaniaImport(TestCase): """ Test creating an instance of the Zefania file importer """ - # GIVEN: A mocked out BibleDB class, and a mocked out "manager" + # GIVEN: A mocked out "manager" mocked_manager = MagicMock() # WHEN: An importer object is created importer = ZefaniaBible(mocked_manager, path='.', name='.', filename='') - # THEN: The importer should be an instance of SongImport + # THEN: The importer should be an instance of BibleDB self.assertIsInstance(importer, BibleDB) def file_import_test(self): """ Test the actual import of real song files """ - # GIVEN: Test files with a mocked out "manager" and a mocked out "import_wizard" - with patch('openlp.plugins.bibles.lib.zefania.ZefaniaBible.application') as brdb: + # GIVEN: Test files with a mocked out "manager", "import_wizard", and mocked functions + # get_book_ref_id_by_name, create_verse, create_book, session and get_language. + with patch('openlp.plugins.bibles.lib.zefania.ZefaniaBible.application'): for bible_file in ZEFANIA_TEST_DATA: mocked_manager = MagicMock() mocked_import_wizard = MagicMock() @@ -112,10 +113,9 @@ class TestZefaniaImport(TestCase): # WHEN: Importing bible file importer.filename = os.path.join(TEST_PATH, bible_file) - importer.do_import('Dansk Version') + importer.do_import() # THEN: The create_verse() method should have been called with each verse in the file. self.assertTrue(importer.create_verse.called) - for verse_tag, verse_text in ZEFANIA_TEST_DATA['zefania-dk1933.xml']['verses']: + for verse_tag, verse_text in ZEFANIA_TEST_DATA[bible_file]['verses']: importer.create_verse.assert_any_call(importer.create_book().id, '1', verse_tag, verse_text) - diff --git a/tests/resources/bibles/opensong-dk1933.xml b/tests/resources/bibles/opensong-dk1933.xml new file mode 100644 index 000000000..1aa1140f7 --- /dev/null +++ b/tests/resources/bibles/opensong-dk1933.xml @@ -0,0 +1,46 @@ + + + + + + + + + Danish Version + + + The Holy Bible + The electronic edition of this Bible comes from the Danish 1933 edition. The Old Testament is an update from the 1931 edition, and the New Testament is an update from the 1907 edition. + Free Bible Software Group + + The Unbound Bible + Biola University: Administrative Computing + 13800 Biola Ave. + La Mirada, CA 90639 + United States of America + 562-903-4722 + + 2009-01-20 + Zefania XML Bible Markup Language + DAN + ftp://unboundftp.biola.edu/pub/danish1933_utf8.zip + DAN + provide the bible to the world + + + + + + I Begyndelsen skabte Gud Himmelen og Jorden. + Og Jorden var øde og tom, og der var Mørke over Verdensdybet. Men Guds Ånd svævede over Vandene. + Og Gud sagde: "Der blive Lys!" Og der blev Lys. + Og Gud så, at Lyset var godt, og Gud satte Skel mellem Lyset og Mørket, + og Gud kaldte Lyset Dag, og Mørket kaldte han Nat. Og det blev Aften, og det blev Morgen, første Dag. + Derpå sagde Gud: "Der blive en Hvælving midt i Vandene til at skille Vandene ad!" + Og således skete det: Gud gjorde Hvælvingen og skilte Vandet under Hvælvingen fra Vandet over Hvælvingen; + og Gud kaldte Hvælvingen Himmel. Og det blev Aften, og det blev Morgen, anden Dag. + Derpå sagde Gud: "Vandet under Himmelen samle sig på eet Sted, så det faste Land kommer til Syne!" Og således skete det; + og Gud kaldte det faste Land Jord, og Stedet, hvor Vandet samlede sig, kaldte han Hav. Og Gud så, at det var godt. + + + From b352c05ff065aa3844fb7207d67d7c20e7e07e60 Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Wed, 21 May 2014 11:33:23 +0200 Subject: [PATCH 144/212] Fix spelling in a comment --- openlp/plugins/songs/lib/ewimport.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/plugins/songs/lib/ewimport.py b/openlp/plugins/songs/lib/ewimport.py index b08193672..42f375e4b 100644 --- a/openlp/plugins/songs/lib/ewimport.py +++ b/openlp/plugins/songs/lib/ewimport.py @@ -344,7 +344,7 @@ class EasyWorshipSongImport(SongImport): try: decoded_words = words.decode() except UnicodeDecodeError: - # The unicode chars in the rtf was not escaped in the expected manor + # The unicode chars in the rtf was not escaped in the expected manner self.entry_error_log = translate('SongsPlugin.EasyWorshipSongImport', 'Unexpected data formatting.') return From d10507e250551c4af7c35278d8062599369cfd2a Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 21 May 2014 16:47:44 +0200 Subject: [PATCH 145/212] Small string fixes --- openlp/core/ui/themeform.py | 6 +++--- openlp/plugins/songs/lib/cclifileimport.py | 3 +-- openlp/plugins/songs/lib/db.py | 8 ++++---- openlp/plugins/songs/lib/xml.py | 2 +- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/openlp/core/ui/themeform.py b/openlp/core/ui/themeform.py index 321071c49..46fd227dd 100644 --- a/openlp/core/ui/themeform.py +++ b/openlp/core/ui/themeform.py @@ -18,11 +18,11 @@ # Software Foundation; version 2 of the License. # # # # This program is distributed in the hope that it will be useful, but WITHOUT # -# AN_y WARRANT_y; without even the implied warranty of MERCHANTABILIT_y or # +# 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 # +# 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 # ############################################################################### @@ -179,7 +179,7 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard, RegistryProperties): if self.page(self.currentId()) == self.background_page and \ 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 ' + translate('OpenLP.ThemeWizard', 'You have not selected a ' 'background image. Please select one before continuing.')) return False else: diff --git a/openlp/plugins/songs/lib/cclifileimport.py b/openlp/plugins/songs/lib/cclifileimport.py index 5c1fbdcb2..eda235ca1 100644 --- a/openlp/plugins/songs/lib/cclifileimport.py +++ b/openlp/plugins/songs/lib/cclifileimport.py @@ -63,7 +63,6 @@ class CCLIFileImport(SongImport): for filename in self.import_source: filename = str(filename) log.debug('Importing CCLI File: %s', filename) - lines = [] if os.path.isfile(filename): detect_file = open(filename, 'r') detect_content = detect_file.read(2048) @@ -250,7 +249,7 @@ class CCLIFileImport(SongImport): # e.g. For use solely with the SongSelect Terms of Use. All rights Reserved. www.ccli.com CCLI Licence number of user - # e.g. CCL-Liedlizenznummer: 14 / CCLI License No. 14 + # e.g. CCLI-Liedlizenznummer: 14 / CCLI License No. 14 """ log.debug('TXT file text: %s', text_list) diff --git a/openlp/plugins/songs/lib/db.py b/openlp/plugins/songs/lib/db.py index 91649c951..80b6580ba 100644 --- a/openlp/plugins/songs/lib/db.py +++ b/openlp/plugins/songs/lib/db.py @@ -74,10 +74,10 @@ class AuthorType(object): WordsAndMusic = 'words+music' Translation = 'translation' Types = { - Words: translate('OpenLP.Ui', 'Words'), - Music: translate('OpenLP.Ui', 'Music'), - WordsAndMusic: translate('OpenLP.Ui', 'Words and Music'), - Translation: translate('OpenLP.Ui', 'Translation') + Words: translate('SongsPlugin.AuthorType', 'Words'), + Music: translate('SongsPlugin.AuthorType', 'Music'), + WordsAndMusic: translate('SongsPlugin.AuthorType', 'Words and Music'), + Translation: translate('SongsPlugin.AuthorType', 'Translation') } diff --git a/openlp/plugins/songs/lib/xml.py b/openlp/plugins/songs/lib/xml.py index 87e5da21e..9219f6062 100644 --- a/openlp/plugins/songs/lib/xml.py +++ b/openlp/plugins/songs/lib/xml.py @@ -664,7 +664,7 @@ class OpenLyrics(object): # OpenLyrics 0.8 uses
for new lines. Append text from "lines" element to verse text. if version > '0.7': text = self._process_lines_mixed_content(element) - # OpenLyrics version <= 0.7 contais elements to represent lines. First child element is tested. + # OpenLyrics version <= 0.7 contains elements to represent lines. First child element is tested. else: # Loop over the "line" elements removing comments and chords. for line in element: From 2466a7d8fab3263d71e6e80fbe53ca7ac023d042 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 21 May 2014 17:09:44 +0200 Subject: [PATCH 146/212] Add hints for author types --- openlp/plugins/songs/lib/db.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openlp/plugins/songs/lib/db.py b/openlp/plugins/songs/lib/db.py index 80b6580ba..7b06d947d 100644 --- a/openlp/plugins/songs/lib/db.py +++ b/openlp/plugins/songs/lib/db.py @@ -74,10 +74,10 @@ class AuthorType(object): WordsAndMusic = 'words+music' Translation = 'translation' Types = { - Words: translate('SongsPlugin.AuthorType', 'Words'), - Music: translate('SongsPlugin.AuthorType', 'Music'), - WordsAndMusic: translate('SongsPlugin.AuthorType', 'Words and Music'), - Translation: translate('SongsPlugin.AuthorType', 'Translation') + Words: translate('SongsPlugin.AuthorType', 'Words', 'Author who wrote the lyrics of a song'), + Music: translate('SongsPlugin.AuthorType', 'Music', 'Author who wrote the music of a song'), + WordsAndMusic: translate('SongsPlugin.AuthorType', 'Author who wrote both lyrics and music of a song'), + Translation: translate('SongsPlugin.AuthorType', 'Translation', 'Author who translated the song') } From d1814f05dea6eb728570514ef444102ac2dc843b Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 21 May 2014 17:17:08 +0200 Subject: [PATCH 147/212] Add test --- tests/functional/openlp_core_lib/test_ui.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/functional/openlp_core_lib/test_ui.py b/tests/functional/openlp_core_lib/test_ui.py index f1b8ee17a..b017a60f0 100644 --- a/tests/functional/openlp_core_lib/test_ui.py +++ b/tests/functional/openlp_core_lib/test_ui.py @@ -197,3 +197,20 @@ class TestUi(TestCase): # THEN: The index should have changed self.assertEqual(2, combo.currentIndex()) + + def test_set_case_insensitive_completer(self): + """ + Test setting a case insensitive completer on a widget + """ + # GIVEN: A QComboBox and a list of completion items + line_edit = QtGui.QLineEdit() + suggestions = ['one', 'Two', 'THRee', 'FOUR'] + + # WHEN: We call the function + set_case_insensitive_completer(suggestions, line_edit) + + # THEN: The Combobox should have a completer which is case insensitive + completer = line_edit.completer() + self.assertIsInstance(completer, QtGui.QCompleter) + self.assertEqual(completer.caseSensitivity(), QtCore.Qt.CaseInsensitive) + From 2462fcf06e99efc9d9220a74e487e2891aba13d9 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 21 May 2014 17:18:04 +0200 Subject: [PATCH 148/212] Remove test --- tests/functional/openlp_core_lib/test_ui.py | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/tests/functional/openlp_core_lib/test_ui.py b/tests/functional/openlp_core_lib/test_ui.py index 87aaab701..f1b8ee17a 100644 --- a/tests/functional/openlp_core_lib/test_ui.py +++ b/tests/functional/openlp_core_lib/test_ui.py @@ -154,22 +154,6 @@ class TestUi(TestCase): self.assertEqual('my tooltip', action.toolTip()) self.assertEqual('my statustip', action.statusTip()) - def test_set_case_insensitive_completer(self): - """ - Test setting a case insensitive completer on a widget - """ - # GIVEN: A QComboBox and a list of completion items - line_edit = QtGui.QLineEdit() - suggestions = ['one', 'Two', 'THRee', 'FOUR'] - - # WHEN: We call the function - set_case_insensitive_completer(suggestions, line_edit) - - # THEN: The Combobox should have a completer which is case insensitive - completer = line_edit.completer() - self.assertIsInstance(completer, QtGui.QCompleter) - self.assertEqual(completer.caseSensitivity(), QtCore.Qt.CaseInsensitive) - def test_create_valign_selection_widgets(self): """ Test creating a combo box for valign selection From 4fadbef1964ff5a5ff753d7151345faa7e659724 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 21 May 2014 17:22:13 +0200 Subject: [PATCH 149/212] PEP8 --- tests/functional/openlp_core_lib/test_ui.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/functional/openlp_core_lib/test_ui.py b/tests/functional/openlp_core_lib/test_ui.py index b017a60f0..fa098ba84 100644 --- a/tests/functional/openlp_core_lib/test_ui.py +++ b/tests/functional/openlp_core_lib/test_ui.py @@ -197,7 +197,7 @@ class TestUi(TestCase): # THEN: The index should have changed self.assertEqual(2, combo.currentIndex()) - + def test_set_case_insensitive_completer(self): """ Test setting a case insensitive completer on a widget @@ -213,4 +213,3 @@ class TestUi(TestCase): completer = line_edit.completer() self.assertIsInstance(completer, QtGui.QCompleter) self.assertEqual(completer.caseSensitivity(), QtCore.Qt.CaseInsensitive) - From 5345f52cf41de9c86205c46b215707de461d2928 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 21 May 2014 17:22:49 +0200 Subject: [PATCH 150/212] chmod +x jenkins_script.py --- scripts/jenkins_script.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 scripts/jenkins_script.py diff --git a/scripts/jenkins_script.py b/scripts/jenkins_script.py old mode 100644 new mode 100755 From 5cd2f7ebff3494d58811c914c429c07777249955 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 21 May 2014 17:31:02 +0200 Subject: [PATCH 151/212] Remove tests --- tests/functional/openlp_core_lib/test_ui.py | 29 --------------------- 1 file changed, 29 deletions(-) diff --git a/tests/functional/openlp_core_lib/test_ui.py b/tests/functional/openlp_core_lib/test_ui.py index 604b75d62..f1b8ee17a 100644 --- a/tests/functional/openlp_core_lib/test_ui.py +++ b/tests/functional/openlp_core_lib/test_ui.py @@ -129,35 +129,6 @@ class TestUi(TestCase): self.assertEqual('my_btn', btn.objectName()) self.assertTrue(btn.isEnabled()) - 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()) - - def test_create_widget_action(self): - """ - Test creating an action for a widget - """ - # GIVEN: A button - button = QtGui.QPushButton() - - # WHEN: We call the function - action = create_widget_action(button, 'some action') - - # THEN: The action should be returned - self.assertIsInstance(action, QtGui.QAction) - self.assertEqual(action.objectName(), 'some action') - def test_create_action(self): """ Test creating an action From 854149408f5628b2cd195e4fccebdfedf53798d6 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 21 May 2014 17:31:25 +0200 Subject: [PATCH 152/212] Add test --- tests/functional/openlp_core_lib/test_ui.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/functional/openlp_core_lib/test_ui.py b/tests/functional/openlp_core_lib/test_ui.py index f1b8ee17a..7f9aba9ab 100644 --- a/tests/functional/openlp_core_lib/test_ui.py +++ b/tests/functional/openlp_core_lib/test_ui.py @@ -197,3 +197,17 @@ class TestUi(TestCase): # THEN: The index should have changed self.assertEqual(2, combo.currentIndex()) + + def test_create_widget_action(self): + """ + Test creating an action for a widget + """ + # GIVEN: A button + button = QtGui.QPushButton() + + # WHEN: We call the function + action = create_widget_action(button, 'some action') + + # THEN: The action should be returned + self.assertIsInstance(action, QtGui.QAction) + self.assertEqual(action.objectName(), 'some action') From 49a48c6463a13b747617025546c0f79e40e3728e Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 21 May 2014 19:20:44 +0200 Subject: [PATCH 153/212] more string fixes --- openlp/core/ui/formattingtagform.py | 7 +++---- openlp/plugins/bibles/lib/csvbible.py | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/openlp/core/ui/formattingtagform.py b/openlp/core/ui/formattingtagform.py index be4247bc1..4f3d5d251 100644 --- a/openlp/core/ui/formattingtagform.py +++ b/openlp/core/ui/formattingtagform.py @@ -91,10 +91,9 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog, FormattingTagCont """ new_row = self.tag_table_widget.rowCount() self.tag_table_widget.insertRow(new_row) - self.tag_table_widget.setItem(new_row, 0, - QtGui.QTableWidgetItem(translate('OpenLP.FormattingTagForm', 'New Tag%s') - % str(new_row))) - self.tag_table_widget.setItem(new_row, 1, QtGui.QTableWidgetItem('n%s' % str(new_row))) + self.tag_table_widget.setItem(new_row, 0, QtGui.QTableWidgetItem(translate('OpenLP.FormattingTagForm', + 'New Tag %d' % new_row))) + self.tag_table_widget.setItem(new_row, 1, QtGui.QTableWidgetItem('n%d' % new_row)) self.tag_table_widget.setItem(new_row, 2, QtGui.QTableWidgetItem(translate('OpenLP.FormattingTagForm', ''))) self.tag_table_widget.setItem(new_row, 3, QtGui.QTableWidgetItem('')) diff --git a/openlp/plugins/bibles/lib/csvbible.py b/openlp/plugins/bibles/lib/csvbible.py index c7d37cb7b..9bffb25fe 100644 --- a/openlp/plugins/bibles/lib/csvbible.py +++ b/openlp/plugins/bibles/lib/csvbible.py @@ -149,7 +149,7 @@ class CSVBible(BibleDB): book_ptr = book.name self.wizard.increment_progress_bar( translate('BiblesPlugin.CSVBible', - 'Importing verses from %s... Importing verses from ...') % book.name) + 'Importing verses from %s...' % book.name, 'Importing verses from ...')) self.session.commit() try: verse_text = str(line[3], details['encoding']) From b882f7e1f1f68d1d145491509038257d6df71e1f Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 21 May 2014 19:42:32 +0200 Subject: [PATCH 154/212] Typo --- scripts/translation_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/translation_utils.py b/scripts/translation_utils.py index ad3edcaa3..13e6a0ed3 100755 --- a/scripts/translation_utils.py +++ b/scripts/translation_utils.py @@ -194,7 +194,7 @@ def download_translations(): password = getpass(' Transifex password: ') # First get the list of languages url = SERVER_URL + 'resource/ents/' - base64string = base64.encodbytes('%s:%s' % (username, password))[:-1] + base64string = base64.encodebytes('%s:%s' % (username, password))[:-1] auth_header = 'Basic %s' % base64string request = urllib.request.Request(url + '?details') request.add_header('Authorization', auth_header) From dd9d535f81227adb5a6bc4179b1bca81b908df64 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 21 May 2014 21:55:16 +0200 Subject: [PATCH 155/212] Split test in two methods --- .../openlp_plugins/songs/test_mediaitem.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/tests/functional/openlp_plugins/songs/test_mediaitem.py b/tests/functional/openlp_plugins/songs/test_mediaitem.py index b3c097268..bc22a4577 100644 --- a/tests/functional/openlp_plugins/songs/test_mediaitem.py +++ b/tests/functional/openlp_plugins/songs/test_mediaitem.py @@ -154,7 +154,7 @@ class TestMediaItem(TestCase, TestMixin): # THEN: The songbook should be in the footer self.assertEqual(service_item.raw_footer, ['My Song', 'My copyright', 'My songbook #12']) - def match_authors_test(self): + def authors_match_test(self): """ Test the author matching when importing a song from a service """ @@ -180,6 +180,22 @@ class TestMediaItem(TestCase, TestMixin): # THEN: They should match self.assertTrue(result, "Authors should match") + def authors_dont_match_test(self): + # GIVEN: A song and a string with authors + song = MagicMock() + song.authors = [] + author = MagicMock() + author.display_name = "Hans Wurst" + song.authors.append(author) + author2 = MagicMock() + author2.display_name = "Max Mustermann" + song.authors.append(author2) + # There are occasions where an author appears twice in a song (with different types). + # We need to make sure that this case works (lp#1313538) + author3 = MagicMock() + author3.display_name = "Max Mustermann" + song.authors.append(author3) + # WHEN: An author is missing in the string authors_str = "Hans Wurst, Max Mustermann" result = self.media_item._authors_match(song, authors_str) From de528e7c41307580dffa06e1f369af163d2eeca4 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 21 May 2014 21:57:06 +0200 Subject: [PATCH 156/212] Add setup.cfg with pep8 config --- setup.cfg | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 setup.cfg diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 000000000..7b4bf5a3c --- /dev/null +++ b/setup.cfg @@ -0,0 +1,3 @@ +[pep8] +exclude=resources.py,vlc.py +max-line-length = 120 From eb5f0b7dd4817ce3026345dc731df9c197b309c4 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Thu, 22 May 2014 13:52:53 +0200 Subject: [PATCH 157/212] Fixes for translation utils --- scripts/translation_utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/translation_utils.py b/scripts/translation_utils.py index 13e6a0ed3..2e9dffb12 100755 --- a/scripts/translation_utils.py +++ b/scripts/translation_utils.py @@ -175,7 +175,7 @@ def run(command): process = QtCore.QProcess() process.start(command) while process.waitForReadyRead(): - print_verbose('ReadyRead: %s' % QtCore.QString(process.readAll())) + print_verbose('ReadyRead: %s' % process.readAll()) print_verbose('Error(s):\n%s' % process.readAllStandardError()) print_verbose('Output:\n%s' % process.readAllStandardOutput()) @@ -261,7 +261,7 @@ def prepare_project(): lines.append('TRANSLATIONS += %s' % line) lines.sort() file = open(os.path.join(start_dir, 'openlp.pro'), 'w') - file.write('\n'.join(lines).encode('utf8')) + file.write('\n'.join(lines)) file.close() print_quiet(' Done.') From e70a09c93e3a7ad48551b4f04dbff77a6aee63d6 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Thu, 22 May 2014 15:40:05 +0200 Subject: [PATCH 158/212] Detect language folder in source directory --- openlp/core/common/applocation.py | 29 +++++++++++++++------------- openlp/core/utils/languagemanager.py | 3 +-- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/openlp/core/common/applocation.py b/openlp/core/common/applocation.py index 2bc4027b6..f3abd5656 100644 --- a/openlp/core/common/applocation.py +++ b/openlp/core/common/applocation.py @@ -72,15 +72,15 @@ class AppLocation(object): :param dir_type: The directory type you want, for instance the data directory. Default *AppLocation.AppDir* """ if dir_type == AppLocation.AppDir: - return get_frozen_path(os.path.abspath(os.path.split(sys.argv[0])[0]), os.path.split(openlp.__file__)[0]) + return get_frozen_path(os.path.abspath(os.path.dirname(sys.argv[0])), os.path.dirname(openlp.__file__)) elif dir_type == AppLocation.PluginsDir: - app_path = os.path.abspath(os.path.split(sys.argv[0])[0]) + app_path = os.path.abspath(os.path.dirname(sys.argv[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.dirname(openlp.__file__), '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]) + return get_frozen_path(os.path.abspath(os.path.dirname(sys.argv[0])), os.path.dirname(openlp.__file__)) elif dir_type == AppLocation.LanguageDir: - app_path = get_frozen_path(os.path.abspath(os.path.split(sys.argv[0])[0]), _get_os_dir_path(dir_type)) + app_path = get_frozen_path(os.path.abspath(os.path.dirname(sys.argv[0])), _get_os_dir_path(dir_type)) return os.path.join(app_path, 'i18n') elif dir_type == AppLocation.DataDir and AppLocation.BaseDir: return os.path.join(AppLocation.BaseDir, 'data') @@ -140,25 +140,28 @@ def _get_os_dir_path(dir_type): """ Return a path based on which OS and environment we are running in. """ + # If running from source, return the language directory from the source directory + if dir_type == AppLocation.LanguageDir: + directory = os.path.abspath(os.path.join(os.path.dirname(openlp.__file__), '..', 'resources')) + if os.path.exists(directory): + return directory if sys.platform == 'win32': if dir_type == AppLocation.DataDir: return os.path.join(str(os.getenv('APPDATA')), 'openlp', 'data') elif dir_type == AppLocation.LanguageDir: - return os.path.split(openlp.__file__)[0] + return os.path.dirname(openlp.__file__) return os.path.join(str(os.getenv('APPDATA')), 'openlp') elif sys.platform == 'darwin': if dir_type == AppLocation.DataDir: - return os.path.join(str(os.getenv('HOME')), - 'Library', 'Application Support', 'openlp', 'Data') + return os.path.join(str(os.getenv('HOME')), 'Library', 'Application Support', 'openlp', 'Data') elif dir_type == AppLocation.LanguageDir: - return os.path.split(openlp.__file__)[0] + return os.path.dirname(openlp.__file__) return os.path.join(str(os.getenv('HOME')), 'Library', 'Application Support', 'openlp') else: if dir_type == AppLocation.LanguageDir: - for prefix in ['/usr/local', '/usr']: - directory = os.path.join(prefix, 'share', 'openlp') - if os.path.exists(directory): - return directory + directory = os.path.join('/usr', 'local', 'share', 'openlp') + if os.path.exists(directory): + return directory return os.path.join('/usr', 'share', 'openlp') if XDG_BASE_AVAILABLE: if dir_type == AppLocation.DataDir: diff --git a/openlp/core/utils/languagemanager.py b/openlp/core/utils/languagemanager.py index bb584f7bd..dd048e04c 100644 --- a/openlp/core/utils/languagemanager.py +++ b/openlp/core/utils/languagemanager.py @@ -71,8 +71,7 @@ class LanguageManager(object): """ Find all available language files in this OpenLP install """ - log.debug('Translation files: %s', AppLocation.get_directory( - AppLocation.LanguageDir)) + 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) # Remove qm files from the list which start with "qt_". From 873f18ad651e3b2ab4fc763a8c568009885252ea Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Thu, 22 May 2014 15:48:57 +0200 Subject: [PATCH 159/212] Add 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 f1b8ee17a..f6a27f020 100644 --- a/tests/functional/openlp_core_lib/test_ui.py +++ b/tests/functional/openlp_core_lib/test_ui.py @@ -154,6 +154,21 @@ class TestUi(TestCase): self.assertEqual('my tooltip', action.toolTip()) self.assertEqual('my statustip', action.statusTip()) + def test_create_action_2(self): + """ + Test creating an action + """ + # GIVEN: A dialog + dialog = QtGui.QDialog() + + # WHEN: We create an action with some properties + action = create_action(dialog, 'my_action', checked=True, enabled=False, visible=False) + + # THEN: These properties should be set + self.assertEqual(True, action.isChecked()) + self.assertEqual(False, action.isEnabled()) + self.assertEqual(False, action.isVisible()) + def test_create_valign_selection_widgets(self): """ Test creating a combo box for valign selection From ea30fb4bdc60061505b3ad08f0d9dac0c4df729e Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Thu, 22 May 2014 23:35:38 +0200 Subject: [PATCH 160/212] Accidentally removed string --- openlp/plugins/songs/lib/db.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openlp/plugins/songs/lib/db.py b/openlp/plugins/songs/lib/db.py index 7b06d947d..2ce7ced1d 100644 --- a/openlp/plugins/songs/lib/db.py +++ b/openlp/plugins/songs/lib/db.py @@ -76,7 +76,8 @@ class AuthorType(object): Types = { Words: translate('SongsPlugin.AuthorType', 'Words', 'Author who wrote the lyrics of a song'), Music: translate('SongsPlugin.AuthorType', 'Music', 'Author who wrote the music of a song'), - WordsAndMusic: translate('SongsPlugin.AuthorType', 'Author who wrote both lyrics and music of a song'), + WordsAndMusic: translate('SongsPlugin.AuthorType', 'Words and Music', + 'Author who wrote both lyrics and music of a song'), Translation: translate('SongsPlugin.AuthorType', 'Translation', 'Author who translated the song') } From 56ebf2de006d012000d95b8996491a4b36432d7f Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 28 May 2014 19:17:26 +0200 Subject: [PATCH 161/212] Revert change in check for language dir --- openlp/core/common/applocation.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/openlp/core/common/applocation.py b/openlp/core/common/applocation.py index f3abd5656..073d3c7f7 100644 --- a/openlp/core/common/applocation.py +++ b/openlp/core/common/applocation.py @@ -159,9 +159,10 @@ def _get_os_dir_path(dir_type): return os.path.join(str(os.getenv('HOME')), 'Library', 'Application Support', 'openlp') else: if dir_type == AppLocation.LanguageDir: - directory = os.path.join('/usr', 'local', 'share', 'openlp') - if os.path.exists(directory): - return directory + for prefix in ['/usr/local', '/usr']: + directory = os.path.join(prefix, 'share', 'openlp') + if os.path.exists(directory): + return directory return os.path.join('/usr', 'share', 'openlp') if XDG_BASE_AVAILABLE: if dir_type == AppLocation.DataDir: From d424270b7fd56b1ad02b6126886276ff0c96a56b Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Fri, 30 May 2014 11:21:26 +0200 Subject: [PATCH 162/212] Fixed locating mudraw on windows and mac, and enabled presentationplugin on mac. --- openlp/core/lib/pluginmanager.py | 5 ----- openlp/core/ui/firsttimeform.py | 5 +---- openlp/core/ui/firsttimewizard.py | 16 +++++----------- .../plugins/presentations/lib/pdfcontroller.py | 8 ++++---- .../openlp_core_lib/test_pluginmanager.py | 2 +- 5 files changed, 11 insertions(+), 25 deletions(-) diff --git a/openlp/core/lib/pluginmanager.py b/openlp/core/lib/pluginmanager.py index 474113c98..822d510b2 100644 --- a/openlp/core/lib/pluginmanager.py +++ b/openlp/core/lib/pluginmanager.py @@ -82,11 +82,6 @@ class PluginManager(RegistryMixin, OpenLPMixin, RegistryProperties): present_plugin_dir = os.path.join(self.base_path, 'presentations') self.log_debug('finding plugins in %s at depth %d' % (self.base_path, start_depth)) for root, dirs, files in os.walk(self.base_path): - if sys.platform == 'darwin' and root.startswith(present_plugin_dir): - # TODO Presentation plugin is not yet working on Mac OS X. - # For now just ignore it. The following code will ignore files from the presentation plugin directory - # and thereby never import the plugin. - continue for name in files: if name.endswith('.py') and not name.startswith('__'): path = os.path.abspath(os.path.join(root, name)) diff --git a/openlp/core/ui/firsttimeform.py b/openlp/core/ui/firsttimeform.py index 531c4b49f..d7c16f0d3 100644 --- a/openlp/core/ui/firsttimeform.py +++ b/openlp/core/ui/firsttimeform.py @@ -412,10 +412,7 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard, RegistryProperties): self._increment_progress_bar(translate('OpenLP.FirstTimeWizard', 'Enabling selected plugins...')) self._set_plugin_status(self.songs_check_box, 'songs/status') self._set_plugin_status(self.bible_check_box, 'bibles/status') - # TODO Presentation plugin is not yet working on Mac OS X. - # For now just ignore it. - if sys.platform != 'darwin': - self._set_plugin_status(self.presentation_check_box, 'presentations/status') + self._set_plugin_status(self.presentation_check_box, 'presentations/status') self._set_plugin_status(self.image_check_box, 'images/status') self._set_plugin_status(self.media_check_box, 'media/status') self._set_plugin_status(self.remote_check_box, 'remotes/status') diff --git a/openlp/core/ui/firsttimewizard.py b/openlp/core/ui/firsttimewizard.py index 35623ded2..ff1675ff5 100644 --- a/openlp/core/ui/firsttimewizard.py +++ b/openlp/core/ui/firsttimewizard.py @@ -95,13 +95,10 @@ class Ui_FirstTimeWizard(object): self.image_check_box.setChecked(True) self.image_check_box.setObjectName('image_check_box') self.plugin_layout.addWidget(self.image_check_box) - # TODO Presentation plugin is not yet working on Mac OS X. - # For now just ignore it. - if sys.platform != 'darwin': - self.presentation_check_box = QtGui.QCheckBox(self.plugin_page) - self.presentation_check_box.setChecked(True) - self.presentation_check_box.setObjectName('presentation_check_box') - self.plugin_layout.addWidget(self.presentation_check_box) + self.presentation_check_box = QtGui.QCheckBox(self.plugin_page) + self.presentation_check_box.setChecked(True) + self.presentation_check_box.setObjectName('presentation_check_box') + self.plugin_layout.addWidget(self.presentation_check_box) self.media_check_box = QtGui.QCheckBox(self.plugin_page) self.media_check_box.setChecked(True) self.media_check_box.setObjectName('media_check_box') @@ -222,10 +219,7 @@ class Ui_FirstTimeWizard(object): self.custom_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Custom Slides')) self.bible_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Bible')) self.image_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Images')) - # TODO Presentation plugin is not yet working on Mac OS X. - # For now just ignore it. - if sys.platform != 'darwin': - self.presentation_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Presentations')) + self.presentation_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Presentations')) self.media_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Media (Audio and Video)')) self.remote_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Allow remote access')) self.song_usage_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Monitor Song Usage')) diff --git a/openlp/plugins/presentations/lib/pdfcontroller.py b/openlp/plugins/presentations/lib/pdfcontroller.py index 5ed11d2ab..b98ae131a 100644 --- a/openlp/plugins/presentations/lib/pdfcontroller.py +++ b/openlp/plugins/presentations/lib/pdfcontroller.py @@ -126,8 +126,8 @@ class PdfController(PresentationController): if os.name == 'nt': # for windows we only accept mudraw.exe in the base folder application_path = AppLocation.get_directory(AppLocation.AppDir) - if os.path.isfile(application_path + '/../mudraw.exe'): - self.mudrawbin = application_path + '/../mudraw.exe' + if os.path.isfile(os.path.join(application_path, 'mudraw.exe')): + self.mudrawbin = os.path.join(application_path, 'mudraw.exe') else: DEVNULL = open(os.devnull, 'wb') # First try to find mupdf @@ -145,8 +145,8 @@ class PdfController(PresentationController): # Last option: check if mudraw is placed in OpenLP base folder if not self.mudrawbin and not self.gsbin: application_path = AppLocation.get_directory(AppLocation.AppDir) - if os.path.isfile(application_path + '/../mudraw'): - self.mudrawbin = application_path + '/../mudraw' + if os.path.isfile(os.path.join(application_path, 'mudraw')): + self.mudrawbin = os.path.join(application_path, 'mudraw') if self.mudrawbin: self.also_supports = ['xps'] return True diff --git a/tests/interfaces/openlp_core_lib/test_pluginmanager.py b/tests/interfaces/openlp_core_lib/test_pluginmanager.py index 262f9f2f3..ba81d708f 100644 --- a/tests/interfaces/openlp_core_lib/test_pluginmanager.py +++ b/tests/interfaces/openlp_core_lib/test_pluginmanager.py @@ -88,7 +88,7 @@ class TestPluginManager(TestCase, TestMixin): plugin_names = [plugin.name for plugin in plugin_manager.plugins] assert 'songs' in plugin_names, 'There should be a "songs" plugin.' assert 'bibles' in plugin_names, 'There should be a "bibles" plugin.' - assert 'presentations' not in plugin_names, 'There should NOT be a "presentations" plugin.' + assert 'presentations' in plugin_names, 'There should be a "presentations" plugin.' assert 'images' in plugin_names, 'There should be a "images" plugin.' assert 'media' in plugin_names, 'There should be a "media" plugin.' assert 'custom' in plugin_names, 'There should be a "custom" plugin.' From 0441f0e71b3f4d2e4031c5ca5b8cbb5a98cca8fb Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Fri, 30 May 2014 19:59:11 +0200 Subject: [PATCH 163/212] Better description --- 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 f6a27f020..854edcf88 100644 --- a/tests/functional/openlp_core_lib/test_ui.py +++ b/tests/functional/openlp_core_lib/test_ui.py @@ -156,7 +156,7 @@ class TestUi(TestCase): def test_create_action_2(self): """ - Test creating an action + Test creating an action with the 'checked', 'enabled' and 'visible' properties. """ # GIVEN: A dialog dialog = QtGui.QDialog() From 983db7b0f007db500fa3b08b58ec5e768ec857d6 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sat, 31 May 2014 21:07:46 +0100 Subject: [PATCH 164/212] fix pep --- tests/helpers/testmixin.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/helpers/testmixin.py b/tests/helpers/testmixin.py index 0b168d45e..757623027 100644 --- a/tests/helpers/testmixin.py +++ b/tests/helpers/testmixin.py @@ -67,4 +67,3 @@ class TestMixin(object): - From 7bea1ea7de2f86ff087ba1f234b14edd40a50fef Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sat, 31 May 2014 21:10:59 +0100 Subject: [PATCH 165/212] fix pep --- tests/helpers/testmixin.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests/helpers/testmixin.py b/tests/helpers/testmixin.py index 757623027..b4e8a5c59 100644 --- a/tests/helpers/testmixin.py +++ b/tests/helpers/testmixin.py @@ -62,8 +62,3 @@ class TestMixin(object): """ os.close(self.fd) os.unlink(Settings().fileName()) - - - - - From a912db6abf9a5a48d0dbd5cf5102be6dffb3e035 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sun, 1 Jun 2014 18:12:00 +0100 Subject: [PATCH 166/212] new bug --- 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 70cbd6141..9d4613c33 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -1081,6 +1081,7 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ServiceManage :param field: :param message: The data passed in from a remove message """ + self.log_debug(message) self.set_item(int(message)) def set_item(self, index): @@ -1089,7 +1090,7 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ServiceManage :param index: The index of the service item list to be actioned. """ - if 0 >= index < self.service_manager_list.topLevelItemCount(): + if 0 <= index < self.service_manager_list.topLevelItemCount(): item = self.service_manager_list.topLevelItem(index) self.service_manager_list.setCurrentItem(item) self.make_live() From f23e6033141a9115311cb5eab51af76f6c7a44de Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Thu, 5 Jun 2014 17:25:37 +0100 Subject: [PATCH 167/212] Fix up interface better --- openlp/core/ui/servicemanager.py | 9 ++++++--- openlp/plugins/presentations/lib/mediaitem.py | 2 +- tests/functional/openlp_core_ui/test_servicemanager.py | 7 +++++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 9d4613c33..52afb5edc 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -401,9 +401,12 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ServiceManage :param suffix_list: New Suffix's to be supported """ - for suffix in suffix_list: - if suffix not in self.suffixes: - self.suffixes.append(suffix) + if isinstance(suffix_list, str): + self.suffixes.append(suffix_list) + else: + for suffix in suffix_list: + if suffix not in self.suffixes: + self.suffixes.append(suffix) def on_new_service_clicked(self, field=None): """ diff --git a/openlp/plugins/presentations/lib/mediaitem.py b/openlp/plugins/presentations/lib/mediaitem.py index fe3246586..5b503d50f 100644 --- a/openlp/plugins/presentations/lib/mediaitem.py +++ b/openlp/plugins/presentations/lib/mediaitem.py @@ -92,7 +92,7 @@ class PresentationMediaItem(MediaManagerItem): for file_type in file_types: if file_type not in file_type_string: file_type_string += '*.%s ' % file_type - self.service_manager.supported_suffixes([file_type]) + self.service_manager.supported_suffixes(file_type) self.on_new_file_masks = translate('PresentationPlugin.MediaItem', 'Presentations (%s)') % file_type_string def required_icons(self): diff --git a/tests/functional/openlp_core_ui/test_servicemanager.py b/tests/functional/openlp_core_ui/test_servicemanager.py index f3deb56c7..d04c898a3 100644 --- a/tests/functional/openlp_core_ui/test_servicemanager.py +++ b/tests/functional/openlp_core_ui/test_servicemanager.py @@ -83,6 +83,9 @@ class TestServiceManager(TestCase): # GIVEN: A new service manager instance. service_manager = ServiceManager(None) # WHEN: a suffix is added. - service_manager.supported_suffixes(['txt']) + service_manager.supported_suffixes('txt') + service_manager.supported_suffixes(['pptx', 'ppt']) # THEN: The the controller should be registered in the registry. - self.assertEqual('txt' in service_manager.suffixes, True, 'The suffix should be in the list') + self.assertEqual('txt' in service_manager.suffixes, True, 'The suffix txt should be in the list') + self.assertEqual('ppt' in service_manager.suffixes, True, 'The suffix ppt should be in the list') + self.assertEqual('pptx' in service_manager.suffixes, True, 'The suffix pptx should be in the list') From 567b3f4b579b9c7b2c0430582aefb9f208a17699 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Thu, 5 Jun 2014 17:32:29 +0100 Subject: [PATCH 168/212] Fix up description --- tests/functional/openlp_core_ui/test_servicemanager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/functional/openlp_core_ui/test_servicemanager.py b/tests/functional/openlp_core_ui/test_servicemanager.py index d04c898a3..2c76ed965 100644 --- a/tests/functional/openlp_core_ui/test_servicemanager.py +++ b/tests/functional/openlp_core_ui/test_servicemanager.py @@ -82,10 +82,10 @@ class TestServiceManager(TestCase): """ # GIVEN: A new service manager instance. service_manager = ServiceManager(None) - # WHEN: a suffix is added. + # WHEN: a suffix is added as an individual or a list. service_manager.supported_suffixes('txt') service_manager.supported_suffixes(['pptx', 'ppt']) - # THEN: The the controller should be registered in the registry. + # THEN: The suffixes should be available to test. self.assertEqual('txt' in service_manager.suffixes, True, 'The suffix txt should be in the list') self.assertEqual('ppt' in service_manager.suffixes, True, 'The suffix ppt should be in the list') self.assertEqual('pptx' in service_manager.suffixes, True, 'The suffix pptx should be in the list') From 08e41672cb713e31afa6e883959a692c544de8a5 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Sun, 8 Jun 2014 16:27:03 +0200 Subject: [PATCH 169/212] Better name for test --- 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 7d1658968..591762947 100644 --- a/tests/functional/openlp_core_lib/test_ui.py +++ b/tests/functional/openlp_core_lib/test_ui.py @@ -154,7 +154,7 @@ class TestUi(TestCase): self.assertEqual('my tooltip', action.toolTip()) self.assertEqual('my statustip', action.statusTip()) - def test_create_action_2(self): + def test_create_checked_enabled_visible_action(self): """ Test creating an action with the 'checked', 'enabled' and 'visible' properties. """ From 0794a1e0c6b2618113a370e65d2b2fbd45a55ed9 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Sun, 8 Jun 2014 17:20:23 +0200 Subject: [PATCH 170/212] Give proper error messages in EasyWorship import Fixes: https://launchpad.net/bugs/1326664 --- openlp/plugins/songs/lib/ewimport.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/openlp/plugins/songs/lib/ewimport.py b/openlp/plugins/songs/lib/ewimport.py index 42f375e4b..ccd89e228 100644 --- a/openlp/plugins/songs/lib/ewimport.py +++ b/openlp/plugins/songs/lib/ewimport.py @@ -200,11 +200,20 @@ class EasyWorshipSongImport(SongImport): Import the songs from the database """ # Open the DB and MB files if they exist - import_source_mb = self.import_source.replace('.DB', '.MB') - if not os.path.isfile(self.import_source) or not os.path.isfile(import_source_mb): + import_source_mb = self.import_source.replace('.DB', '.MB').replace('.db', '.mb') + if not os.path.isfile(self.import_source): + self.log_error(self.import_source, translate('SongsPlugin.EasyWorshipSongImport', + 'This file does not exist.')) + return + if not os.path.isfile(import_source_mb): + self.log_error(self.import_source, translate('SongsPlugin.EasyWorshipSongImport', + 'Could not find the "Songs.MB" file. It must be in the same ' + 'folder as the "Songs.DB" file.')) return db_size = os.path.getsize(self.import_source) if db_size < 0x800: + self.log_error(self.import_source, translate('SongsPlugin.EasyWorshipSongImport', + 'This file is no valid EasyWorship Database.')) return db_file = open(self.import_source, 'rb') self.memo_file = open(import_source_mb, 'rb') @@ -213,6 +222,8 @@ class EasyWorshipSongImport(SongImport): if header_size != 0x800 or block_size < 1 or block_size > 4: db_file.close() self.memo_file.close() + self.log_error(self.import_source, translate('SongsPlugin.EasyWorshipSongImport', + 'This file is no valid EasyWorship Database.')) return # Take a stab at how text is encoded self.encoding = 'cp1252' @@ -240,6 +251,8 @@ class EasyWorshipSongImport(SongImport): self.encoding = 'cp874' self.encoding = retrieve_windows_encoding(self.encoding) if not self.encoding: + self.log_error(self.import_source, translate('SongsPlugin.EasyWorshipSongImport', + 'Could not retrieve encoding.')) return # Read the field description information db_file.seek(120) From b2e4c6f99f4abb96740741ec6239b9212bc1aff8 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Mon, 9 Jun 2014 09:55:48 +0200 Subject: [PATCH 171/212] Add test for missing songs.mb --- .../openlp_plugins/songs/test_ewimport.py | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/functional/openlp_plugins/songs/test_ewimport.py b/tests/functional/openlp_plugins/songs/test_ewimport.py index 8d9302015..ea557711b 100644 --- a/tests/functional/openlp_plugins/songs/test_ewimport.py +++ b/tests/functional/openlp_plugins/songs/test_ewimport.py @@ -314,6 +314,26 @@ class TestEasyWorshipSongImport(TestCase): mocked_os_path.isfile.assert_any_call('Songs.DB') mocked_os_path.isfile.assert_any_call('Songs.MB') + def do_import_source_invalid_test(self): + """ + Test the :mod:`do_import` module produces an error when Songs.MB not found. + """ + # 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: + mocked_manager = MagicMock() + importer = EasyWorshipSongImport(mocked_manager, filenames=[]) + importer.log_error = MagicMock() + mocked_os_path.isfile.side_effect = [True, False] + + # WHEN: do_import is supplied with an import source (Songs.MB missing) + importer.import_source = 'Songs.DB' + importer.do_import() + + # THEN: do_import should have logged an error that the Songs.MB file could not be found. + importer.log_error.assert_any_call(importer.import_source, 'Could not find the "Songs.MB" file. It must be ' + 'in the same folder as the "Songs.DB" file.') + def do_import_database_validity_test(self): """ Test the :mod:`do_import` module handles invalid database files correctly From e424c3328308eecf6c3a7306e88fda1d1a05de23 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Mon, 9 Jun 2014 10:59:52 +0200 Subject: [PATCH 172/212] This has been removed in trunk --- openlp/core/lib/filedialog.py | 2 +- openlp/plugins/songs/lib/__init__.py | 15 --------------- 2 files changed, 1 insertion(+), 16 deletions(-) diff --git a/openlp/core/lib/filedialog.py b/openlp/core/lib/filedialog.py index 5bf012ee5..3d1970dd5 100644 --- a/openlp/core/lib/filedialog.py +++ b/openlp/core/lib/filedialog.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 ############################################################################### # OpenLP - Open Source Lyrics Projection # diff --git a/openlp/plugins/songs/lib/__init__.py b/openlp/plugins/songs/lib/__init__.py index 7f8cd43dc..d03bdefd6 100644 --- a/openlp/plugins/songs/lib/__init__.py +++ b/openlp/plugins/songs/lib/__init__.py @@ -483,21 +483,6 @@ def strip_rtf(text, default_encoding=None): elif brace == '}' and len(stack) > 0: # Pop state ucskip, ignorable, font = stack.pop() - # \x (not a letter) - elif char: - curskip = 0 - if char == '~' and not ignorable: - out.append('\xA0') - elif char in '{}\\' and not ignorable: - out.append(char) - elif char in '\r\n' and not ignorable: - out.append(SPECIAL_CHARS['par']) - elif char == '-' and not ignorable: - out.append('\u00AD') - elif char == '_' and not ignorable: - out.append('\u2011') - elif char == '*': - ignorable = True # \command elif word: curskip = 0 From 6e9bba3a74494047b8a8725cff4bd1d72b018c1b Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Mon, 9 Jun 2014 11:08:27 +0200 Subject: [PATCH 173/212] Rename file --- openlp/plugins/songs/lib/importer.py | 2 +- openlp/plugins/songs/lib/{ppimport.py => propresenterimport.py} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename openlp/plugins/songs/lib/{ppimport.py => propresenterimport.py} (97%) diff --git a/openlp/plugins/songs/lib/importer.py b/openlp/plugins/songs/lib/importer.py index 1cfae2602..a95b19bad 100644 --- a/openlp/plugins/songs/lib/importer.py +++ b/openlp/plugins/songs/lib/importer.py @@ -49,7 +49,7 @@ from .songproimport import SongProImport from .sundayplusimport import SundayPlusImport from .foilpresenterimport import FoilPresenterImport from .zionworximport import ZionWorxImport -from .ppimport import ProPresenterImport +from .propresenterimport import ProPresenterImport # Imports that might fail diff --git a/openlp/plugins/songs/lib/ppimport.py b/openlp/plugins/songs/lib/propresenterimport.py similarity index 97% rename from openlp/plugins/songs/lib/ppimport.py rename to openlp/plugins/songs/lib/propresenterimport.py index 11ba9d2e7..a69c3cec0 100644 --- a/openlp/plugins/songs/lib/ppimport.py +++ b/openlp/plugins/songs/lib/propresenterimport.py @@ -27,7 +27,7 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### """ -The :mod:`ppimport` module provides the functionality for importing +The :mod:`propresenterimport` module provides the functionality for importing ProPresenter song files into the current installation database. """ From 543f4f37dd0838cb99cf5a8738dc6f58a64fedad Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Mon, 9 Jun 2014 12:27:17 +0200 Subject: [PATCH 174/212] Add test for ProPresenter Import --- openlp/plugins/songs/lib/importer.py | 3 +- .../plugins/songs/lib/propresenterimport.py | 20 +- .../songs/test_propresenterimport.py | 54 ++ .../songs/test_zionworximport.py | 2 +- .../propresentersongs/Amazing Grace.json | 121 +++++ .../propresentersongs/Amazing Grace.pro4 | 486 ++++++++++++++++++ 6 files changed, 674 insertions(+), 12 deletions(-) create mode 100644 tests/functional/openlp_plugins/songs/test_propresenterimport.py create mode 100644 tests/resources/propresentersongs/Amazing Grace.json create mode 100644 tests/resources/propresentersongs/Amazing Grace.pro4 diff --git a/openlp/plugins/songs/lib/importer.py b/openlp/plugins/songs/lib/importer.py index a95b19bad..7ce637285 100644 --- a/openlp/plugins/songs/lib/importer.py +++ b/openlp/plugins/songs/lib/importer.py @@ -276,8 +276,7 @@ class SongFormat(object): 'class': ProPresenterImport, 'name': 'ProPresenter', 'prefix': 'proPresenter', - 'filter': '%s (*.pro4)' % translate('SongsPlugin.ImportWizardForm', - 'ProPresenter Song Files') + 'filter': '%s (*.pro4)' % translate('SongsPlugin.ImportWizardForm', 'ProPresenter Song Files') }, SongBeamer: { 'class': SongBeamerImport, diff --git a/openlp/plugins/songs/lib/propresenterimport.py b/openlp/plugins/songs/lib/propresenterimport.py index a69c3cec0..6ce3c0819 100644 --- a/openlp/plugins/songs/lib/propresenterimport.py +++ b/openlp/plugins/songs/lib/propresenterimport.py @@ -39,35 +39,37 @@ from openlp.core.ui.wizard import WizardStrings from openlp.plugins.songs.lib import strip_rtf from .songimport import SongImport + class ProPresenterImport(SongImport): """ The :class:`ProPresenterImport` class provides OpenLP with the ability to import ProPresenter song files. """ - def doImport(self): + def do_import(self): self.import_wizard.progress_bar.setMaximum(len(self.import_source)) for file_path in self.import_source: if self.stop_import_flag: return - self.import_wizard.increment_progress_bar( - WizardStrings.ImportingType % os.path.basename(file_path)) + self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % os.path.basename(file_path)) root = objectify.parse(open(file_path, 'rb')).getroot() - self.processSong(root) + self.process_song(root) - def processSong(self, root): - self.setDefaults() + def process_song(self, root): + self.set_defaults() self.title = root.get('CCLISongTitle') self.copyright = root.get('CCLICopyrightInfo') self.comments = root.get('notes') - self.ccliNumber = root.get('CCLILicenseNumber') + self.ccli_number = root.get('CCLILicenseNumber') for author_key in ['author', 'artist', 'CCLIArtistCredits']: author = root.get(author_key) if len(author) > 0: self.parse_author(author) + count = 0 for slide in root.slides.RVDisplaySlide: + count += 1 RTFData = slide.displayElements.RVTextElement.get('RTFData') rtf = base64.standard_b64decode(RTFData) words, encoding = strip_rtf(rtf.decode()) - self.addVerse(words) + self.add_verse(words, "v%d" % count) if not self.finish(): - self.logError(self.import_source) + self.log_error(self.import_source) diff --git a/tests/functional/openlp_plugins/songs/test_propresenterimport.py b/tests/functional/openlp_plugins/songs/test_propresenterimport.py new file mode 100644 index 000000000..971ddbdf6 --- /dev/null +++ b/tests/functional/openlp_plugins/songs/test_propresenterimport.py @@ -0,0 +1,54 @@ +# -*- 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 # +# --------------------------------------------------------------------------- # +# This program is free software; you can redistribute it and/or modify it # +# under the terms of the GNU General Public License as published by the Free # +# Software Foundation; version 2 of the License. # +# # +# This program is distributed in the hope that it will be useful, but WITHOUT # +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # +# more details. # +# # +# You should have received a copy of the GNU General Public License along # +# with this program; if not, write to the Free Software Foundation, Inc., 59 # +# Temple Place, Suite 330, Boston, MA 02111-1307 USA # +############################################################################### +""" +The :mod:`propresenterimport` module provides the functionality for importing +ProPresenter song files into the current installation database. +""" + +import os + +from tests.helpers.songfileimport import SongImportTestHelper + +TEST_PATH = os.path.abspath( + os.path.join(os.path.dirname(__file__), '..', '..', '..', 'resources', 'propresentersongs')) + + +class TestProPresenterFileImport(SongImportTestHelper): + + def __init__(self, *args, **kwargs): + self.importer_class_name = 'ProPresenterImport' + self.importer_module_name = 'propresenterimport' + super(TestProPresenterFileImport, self).__init__(*args, **kwargs) + + def test_song_import(self): + """ + Test that loading an ProPresenter file works correctly + """ + self.file_import(os.path.join(TEST_PATH, 'Amazing Grace.pro4'), + self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace.json'))) diff --git a/tests/functional/openlp_plugins/songs/test_zionworximport.py b/tests/functional/openlp_plugins/songs/test_zionworximport.py index 2edc071c7..c5669e9c8 100644 --- a/tests/functional/openlp_plugins/songs/test_zionworximport.py +++ b/tests/functional/openlp_plugins/songs/test_zionworximport.py @@ -46,7 +46,7 @@ class TestZionWorxImport(TestCase): 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'): + with patch('openlp.plugins.songs.lib.zionworximport.SongImport'): mocked_manager = MagicMock() # WHEN: An importer object is created diff --git a/tests/resources/propresentersongs/Amazing Grace.json b/tests/resources/propresentersongs/Amazing Grace.json new file mode 100644 index 000000000..1746b7696 --- /dev/null +++ b/tests/resources/propresentersongs/Amazing Grace.json @@ -0,0 +1,121 @@ +{ + "authors": [ + "John Newton" + ], + "title": "Amazing Grace", + "verse_order_list": [], + "verses": [ + [ + "Amazing grace! How sweet the sound\n", + "v1" + ], + [ + "That saved a wretch like me!\n", + "v2" + ], + [ + "I once was lost, but now am found;\n", + "v3" + ], + [ + "Was blind, but now I see.\n", + "v4" + ], + [ + "'Twas grace that taught my heart to fear,\n", + "v5" + ], + [ + "And grace my fears relieved;\n", + "v6" + ], + [ + "How precious did that grace appear\n", + "v7" + ], + [ + "The hour I first believed.\n", + "v8" + ], + [ + "Through many dangers, toils and snares,\n", + "v9" + ], + [ + "I have already come;\n", + "v10" + ], + [ + "'Tis grace hath brought me safe thus far,\n", + "v11" + ], + [ + "And grace will lead me home.\n", + "v12" + ], + [ + "The Lord has promised good to me,\n", + "v13" + ], + [ + "His Word my hope secures;\n", + "v14" + ], + [ + "He will my Shield and Portion be,\n", + "v15" + ], + [ + "As long as life endures.\n", + "v16" + ], + [ + "Yea, when this flesh and heart shall fail,\n", + "v17" + ], + [ + "And mortal life shall cease,\n", + "v18" + ], + [ + "I shall possess, within the veil,\n", + "v19" + ], + [ + "A life of joy and peace.\n", + "v20" + ], + [ + "The earth shall soon dissolve like snow,\n", + "v21" + ], + [ + "The sun forbear to shine;\n", + "v22" + ], + [ + "But God, Who called me here below,\n", + "v23" + ], + [ + "Shall be forever mine.\n", + "v24" + ], + [ + "When we've been there ten thousand years,\n", + "v25" + ], + [ + "Bright shining as the sun,\n", + "v26" + ], + [ + "We've no less days to sing God's praise\n", + "v27" + ], + [ + "Than when we'd first begun.\n", + "v28" + ] + ] +} \ No newline at end of file diff --git a/tests/resources/propresentersongs/Amazing Grace.pro4 b/tests/resources/propresentersongs/Amazing Grace.pro4 new file mode 100644 index 000000000..dbe114a88 --- /dev/null +++ b/tests/resources/propresentersongs/Amazing Grace.pro4 @@ -0,0 +1,486 @@ + + + + + + + + <_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" /> + <_-D-_serializedShadow containerClass="NSMutableDictionary"> + + + + + + + + + + + + + + + + <_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" /> + <_-D-_serializedShadow containerClass="NSMutableDictionary"> + + + + + + + + + + + + + + + + <_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" /> + <_-D-_serializedShadow containerClass="NSMutableDictionary"> + + + + + + + + + + + + + + + + <_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" /> + <_-D-_serializedShadow containerClass="NSMutableDictionary"> + + + + + + + + + + + + + + + + <_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" /> + <_-D-_serializedShadow containerClass="NSMutableDictionary"> + + + + + + + + + + + + + + + + <_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" /> + <_-D-_serializedShadow containerClass="NSMutableDictionary"> + + + + + + + + + + + + + + + + <_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" /> + <_-D-_serializedShadow containerClass="NSMutableDictionary"> + + + + + + + + + + + + + + + + <_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" /> + <_-D-_serializedShadow containerClass="NSMutableDictionary"> + + + + + + + + + + + + + + + + <_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" /> + <_-D-_serializedShadow containerClass="NSMutableDictionary"> + + + + + + + + + + + + + + + + <_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" /> + <_-D-_serializedShadow containerClass="NSMutableDictionary"> + + + + + + + + + + + + + + + + <_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" /> + <_-D-_serializedShadow containerClass="NSMutableDictionary"> + + + + + + + + + + + + + + + + <_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" /> + <_-D-_serializedShadow containerClass="NSMutableDictionary"> + + + + + + + + + + + + + + + + <_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" /> + <_-D-_serializedShadow containerClass="NSMutableDictionary"> + + + + + + + + + + + + + + + + <_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" /> + <_-D-_serializedShadow containerClass="NSMutableDictionary"> + + + + + + + + + + + + + + + + <_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" /> + <_-D-_serializedShadow containerClass="NSMutableDictionary"> + + + + + + + + + + + + + + + + <_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" /> + <_-D-_serializedShadow containerClass="NSMutableDictionary"> + + + + + + + + + + + + + + + + <_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" /> + <_-D-_serializedShadow containerClass="NSMutableDictionary"> + + + + + + + + + + + + + + + + <_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" /> + <_-D-_serializedShadow containerClass="NSMutableDictionary"> + + + + + + + + + + + + + + + + <_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" /> + <_-D-_serializedShadow containerClass="NSMutableDictionary"> + + + + + + + + + + + + + + + + <_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" /> + <_-D-_serializedShadow containerClass="NSMutableDictionary"> + + + + + + + + + + + + + + + + <_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" /> + <_-D-_serializedShadow containerClass="NSMutableDictionary"> + + + + + + + + + + + + + + + + <_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" /> + <_-D-_serializedShadow containerClass="NSMutableDictionary"> + + + + + + + + + + + + + + + + <_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" /> + <_-D-_serializedShadow containerClass="NSMutableDictionary"> + + + + + + + + + + + + + + + + <_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" /> + <_-D-_serializedShadow containerClass="NSMutableDictionary"> + + + + + + + + + + + + + + + + <_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" /> + <_-D-_serializedShadow containerClass="NSMutableDictionary"> + + + + + + + + + + + + + + + + <_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" /> + <_-D-_serializedShadow containerClass="NSMutableDictionary"> + + + + + + + + + + + + + + + + <_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" /> + <_-D-_serializedShadow containerClass="NSMutableDictionary"> + + + + + + + + + + + + + + + + <_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" /> + <_-D-_serializedShadow containerClass="NSMutableDictionary"> + + + + + + + + + + + + + + + + + + \ No newline at end of file From 9f80c97deb9b3a527734476e26a9acc985c3dc4e Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Mon, 9 Jun 2014 12:33:09 +0200 Subject: [PATCH 175/212] Remove tag 2.2.2 From 8982839794623612ba97fd9bb62f35cc81f0d176 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Tue, 10 Jun 2014 10:33:02 +0200 Subject: [PATCH 176/212] fixed translation_util script for 2.2 --- scripts/translation_utils.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/scripts/translation_utils.py b/scripts/translation_utils.py index 2e9dffb12..d98fe61c9 100755 --- a/scripts/translation_utils.py +++ b/scripts/translation_utils.py @@ -63,7 +63,7 @@ import webbrowser from optparse import OptionParser from PyQt4 import QtCore -SERVER_URL = 'http://www.transifex.net/api/2/project/openlp/' +SERVER_URL = 'http://www.transifex.net/api/2/project/openlp/resource/openlp-22x/' IGNORED_PATHS = ['scripts'] IGNORED_FILES = ['setup.py'] @@ -193,12 +193,11 @@ def download_translations(): if not password: password = getpass(' Transifex password: ') # First get the list of languages - url = SERVER_URL + 'resource/ents/' base64string = base64.encodebytes('%s:%s' % (username, password))[:-1] auth_header = 'Basic %s' % base64string - request = urllib.request.Request(url + '?details') + request = urllib.request.Request(SERVER_URL + '?details') request.add_header('Authorization', auth_header) - print_verbose('Downloading list of languages from: %s' % url) + print_verbose('Downloading list of languages from: %s' % SERVER_URL) try: json_response = urllib.request.urlopen(request) except urllib.error.HTTPError: @@ -207,7 +206,7 @@ def download_translations(): json_dict = json.loads(json_response.read()) languages = [lang['code'] for lang in json_dict['available_languages']] for language in languages: - lang_url = url + 'translation/%s/?file' % language + lang_url = SERVER_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') From 108fabbae0fb791fbcabae02e84796fdc325e91a Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Tue, 10 Jun 2014 11:05:26 +0200 Subject: [PATCH 177/212] fixed str/byte errors --- scripts/translation_utils.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/translation_utils.py b/scripts/translation_utils.py index d98fe61c9..0074109dd 100755 --- a/scripts/translation_utils.py +++ b/scripts/translation_utils.py @@ -193,8 +193,8 @@ def download_translations(): if not password: password = getpass(' Transifex password: ') # First get the list of languages - base64string = base64.encodebytes('%s:%s' % (username, password))[:-1] - auth_header = 'Basic %s' % base64string + base64string = base64.encodebytes(('%s:%s' % (username, password)).encode())[:-1] + auth_header = 'Basic %s' % base64string.decode() request = urllib.request.Request(SERVER_URL + '?details') request.add_header('Authorization', auth_header) print_verbose('Downloading list of languages from: %s' % SERVER_URL) @@ -203,7 +203,7 @@ def download_translations(): except urllib.error.HTTPError: print_quiet('Username or password incorrect.') return False - json_dict = json.loads(json_response.read()) + json_dict = json.loads(json_response.read().decode()) languages = [lang['code'] for lang in json_dict['available_languages']] for language in languages: lang_url = SERVER_URL + 'translation/%s/?file' % language @@ -212,7 +212,7 @@ def download_translations(): 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') + fd = open(filename, 'wb') fd.write(response.read()) fd.close() print_quiet(' Done.') From 6d0ee83de18881135775259c5e8b1e13f04f72aa Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Tue, 10 Jun 2014 11:24:50 +0200 Subject: [PATCH 178/212] moved tests --- .../openlp_core_common/test_common.py | 61 ++++++++++++++++++- tests/functional/openlp_core_lib/test_lib.py | 59 ------------------ 2 files changed, 60 insertions(+), 60 deletions(-) diff --git a/tests/functional/openlp_core_common/test_common.py b/tests/functional/openlp_core_common/test_common.py index ab2d11b3a..3cb212a9c 100644 --- a/tests/functional/openlp_core_common/test_common.py +++ b/tests/functional/openlp_core_common/test_common.py @@ -32,14 +32,54 @@ 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 openlp.core.common import check_directory_exists, de_hump, trace_error_handler, translate from tests.functional import MagicMock, patch + class TestCommonFunctions(TestCase): """ A test suite to test out various functions in the openlp.core.common module. """ + def check_directory_exists_test(self): + """ + Test the check_directory_exists() function + """ + with patch('openlp.core.lib.os.path.exists') as mocked_exists, \ + patch('openlp.core.lib.os.makedirs') as mocked_makedirs: + # GIVEN: A directory to check and a mocked out os.makedirs and os.path.exists + directory_to_check = 'existing/directory' + + # WHEN: os.path.exists returns True and we check to see if the directory exists + mocked_exists.return_value = True + check_directory_exists(directory_to_check) + + # THEN: Only os.path.exists should have been called + mocked_exists.assert_called_with(directory_to_check) + self.assertIsNot(mocked_makedirs.called, 'os.makedirs should not have been called') + + # WHEN: os.path.exists returns False and we check the directory exists + mocked_exists.return_value = False + check_directory_exists(directory_to_check) + + # THEN: Both the mocked functions should have been called + mocked_exists.assert_called_with(directory_to_check) + mocked_makedirs.assert_called_with(directory_to_check) + + # WHEN: os.path.exists raises an IOError + mocked_exists.side_effect = IOError() + check_directory_exists(directory_to_check) + + # THEN: We shouldn't get an exception though the mocked exists has been called + mocked_exists.assert_called_with(directory_to_check) + + # WHEN: Some other exception is raised + mocked_exists.side_effect = ValueError() + + # THEN: check_directory_exists raises an exception + mocked_exists.assert_called_with(directory_to_check) + self.assertRaises(ValueError, check_directory_exists, directory_to_check) + def de_hump_conversion_test(self): """ Test the de_hump function with a class name @@ -81,3 +121,22 @@ class TestCommonFunctions(TestCase): # 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') + + def translate_test(self): + """ + Test the translate() function + """ + # GIVEN: A string to translate and a mocked Qt translate function + context = 'OpenLP.Tests' + text = 'Untranslated string' + comment = 'A comment' + encoding = 1 + n = 1 + mocked_translate = MagicMock(return_value='Translated string') + + # WHEN: we call the translate function + result = translate(context, text, comment, encoding, n, mocked_translate) + + # THEN: the translated string should be returned, and the mocked function should have been called + mocked_translate.assert_called_with(context, text, comment, encoding, n) + self.assertEqual('Translated string', result, 'The translated string should have been returned') \ No newline at end of file diff --git a/tests/functional/openlp_core_lib/test_lib.py b/tests/functional/openlp_core_lib/test_lib.py index 2e06e47fe..70d111520 100644 --- a/tests/functional/openlp_core_lib/test_lib.py +++ b/tests/functional/openlp_core_lib/test_lib.py @@ -36,7 +36,6 @@ from datetime import datetime, timedelta from PyQt4 import QtCore, QtGui -from openlp.core.common import check_directory_exists, translate from openlp.core.lib import str_to_bool, create_thumb, get_text_file_string, \ build_icon, image_to_byte, check_item_selected, validate_thumb, create_separated_list, clean_tags, expand_tags from tests.functional import MagicMock, patch @@ -152,64 +151,6 @@ class TestLib(TestCase): # THEN: we should get back a true self.assertTrue(str_result, 'The result should be True') - def translate_test(self): - """ - Test the translate() function - """ - # GIVEN: A string to translate and a mocked Qt translate function - context = 'OpenLP.Tests' - text = 'Untranslated string' - comment = 'A comment' - encoding = 1 - n = 1 - mocked_translate = MagicMock(return_value='Translated string') - - # WHEN: we call the translate function - result = translate(context, text, comment, encoding, n, mocked_translate) - - # THEN: the translated string should be returned, and the mocked function should have been called - mocked_translate.assert_called_with(context, text, comment, encoding, n) - self.assertEqual('Translated string', result, 'The translated string should have been returned') - - def check_directory_exists_test(self): - """ - Test the check_directory_exists() function - """ - with patch('openlp.core.lib.os.path.exists') as mocked_exists, \ - patch('openlp.core.lib.os.makedirs') as mocked_makedirs: - # GIVEN: A directory to check and a mocked out os.makedirs and os.path.exists - directory_to_check = 'existing/directory' - - # WHEN: os.path.exists returns True and we check to see if the directory exists - mocked_exists.return_value = True - check_directory_exists(directory_to_check) - - # THEN: Only os.path.exists should have been called - mocked_exists.assert_called_with(directory_to_check) - self.assertIsNot(mocked_makedirs.called, 'os.makedirs should not have been called') - - # WHEN: os.path.exists returns False and we check the directory exists - mocked_exists.return_value = False - check_directory_exists(directory_to_check) - - # THEN: Both the mocked functions should have been called - mocked_exists.assert_called_with(directory_to_check) - mocked_makedirs.assert_called_with(directory_to_check) - - # WHEN: os.path.exists raises an IOError - mocked_exists.side_effect = IOError() - check_directory_exists(directory_to_check) - - # THEN: We shouldn't get an exception though the mocked exists has been called - mocked_exists.assert_called_with(directory_to_check) - - # WHEN: Some other exception is raised - mocked_exists.side_effect = ValueError() - - # THEN: check_directory_exists raises an exception - mocked_exists.assert_called_with(directory_to_check) - self.assertRaises(ValueError, check_directory_exists, directory_to_check) - def get_text_file_string_no_file_test(self): """ Test the get_text_file_string() function when a file does not exist From 837d95ae419e4e2dc9e0b5b55cd458865b81a285 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Tue, 10 Jun 2014 11:39:24 +0200 Subject: [PATCH 179/212] added a test case --- tests/functional/openlp_core_lib/test_lib.py | 24 +++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/tests/functional/openlp_core_lib/test_lib.py b/tests/functional/openlp_core_lib/test_lib.py index 70d111520..528341a98 100644 --- a/tests/functional/openlp_core_lib/test_lib.py +++ b/tests/functional/openlp_core_lib/test_lib.py @@ -37,7 +37,8 @@ from datetime import datetime, timedelta from PyQt4 import QtCore, QtGui from openlp.core.lib import str_to_bool, create_thumb, get_text_file_string, \ - build_icon, image_to_byte, check_item_selected, validate_thumb, create_separated_list, clean_tags, expand_tags + build_icon, image_to_byte, check_item_selected, validate_thumb, create_separated_list, clean_tags, expand_tags, \ + resize_image from tests.functional import MagicMock, patch TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'resources')) @@ -449,6 +450,27 @@ class TestLib(TestCase): mocked_os.stat.assert_any_call(thumb_path) assert result is False, 'The result should be False' + def resize_thumb_test(self): + """ + Test the resize_thumb() function + """ + # GIVEN: A path to an image. + image_path = os.path.join(TEST_PATH, 'church.jpg') + wanted_width = 777 + wanted_height = 72 + # We want the background to be white. + wanted_background_hex = '#FFFFFF' + wanted_background_rgb = QtGui.QColor(wanted_background_hex).rgb() + + # WHEN: Resize the image and add a background. + image = resize_image(image_path, wanted_width, wanted_height, wanted_background_hex) + + # THEN: Check if the size is correct and the background was set. + result_size = image.size() + self.assertEqual(wanted_height, result_size.height(), 'The image should have the requested height.') + self.assertEqual(wanted_width, result_size.width(), 'The image should have the requested width.') + self.assertEqual(image.pixel(0, 0), wanted_background_rgb, 'The background should be white.') + def create_separated_list_qlocate_test(self): """ Test the create_separated_list function using the Qt provided method From 3235c334eb21e7789a219bf1f6fbe50be851a5b6 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Tue, 10 Jun 2014 11:42:11 +0200 Subject: [PATCH 180/212] sorted imports --- tests/functional/openlp_core_lib/test_lib.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/functional/openlp_core_lib/test_lib.py b/tests/functional/openlp_core_lib/test_lib.py index 528341a98..083698212 100644 --- a/tests/functional/openlp_core_lib/test_lib.py +++ b/tests/functional/openlp_core_lib/test_lib.py @@ -36,9 +36,8 @@ from datetime import datetime, timedelta from PyQt4 import QtCore, QtGui -from openlp.core.lib import str_to_bool, create_thumb, get_text_file_string, \ - build_icon, image_to_byte, check_item_selected, validate_thumb, create_separated_list, clean_tags, expand_tags, \ - resize_image +from openlp.core.lib import build_icon, check_item_selected, clean_tags, create_thumb, create_separated_list, \ + expand_tags, get_text_file_string, image_to_byte, resize_image, str_to_bool, validate_thumb from tests.functional import MagicMock, patch TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'resources')) From 2791b37525ff366a74baa46845adb0598f19b12f Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Tue, 10 Jun 2014 11:48:42 +0200 Subject: [PATCH 181/212] pep fixes --- tests/functional/openlp_core_common/test_common.py | 3 +-- tests/functional/openlp_core_lib/test_lib.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/functional/openlp_core_common/test_common.py b/tests/functional/openlp_core_common/test_common.py index 3cb212a9c..f52256c5c 100644 --- a/tests/functional/openlp_core_common/test_common.py +++ b/tests/functional/openlp_core_common/test_common.py @@ -36,7 +36,6 @@ from openlp.core.common import check_directory_exists, de_hump, trace_error_hand from tests.functional import MagicMock, patch - class TestCommonFunctions(TestCase): """ A test suite to test out various functions in the openlp.core.common module. @@ -139,4 +138,4 @@ class TestCommonFunctions(TestCase): # THEN: the translated string should be returned, and the mocked function should have been called mocked_translate.assert_called_with(context, text, comment, encoding, n) - self.assertEqual('Translated string', result, 'The translated string should have been returned') \ No newline at end of file + self.assertEqual('Translated string', result, 'The translated string should have been returned') diff --git a/tests/functional/openlp_core_lib/test_lib.py b/tests/functional/openlp_core_lib/test_lib.py index 083698212..fca6f371d 100644 --- a/tests/functional/openlp_core_lib/test_lib.py +++ b/tests/functional/openlp_core_lib/test_lib.py @@ -294,7 +294,7 @@ class TestLib(TestCase): Test that the check_item_selected() function returns True when there are selected indexes """ # GIVEN: A mocked out QtGui module and a list widget with selected indexes - MockedQtGui = patch('openlp.core.lib.QtGui') + mocked_QtGui = patch('openlp.core.lib.QtGui') mocked_list_widget = MagicMock() mocked_list_widget.selectedIndexes.return_value = True message = 'message' From 50a226d51bad1f1d2ebff082ce743cbae5f747ef Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 11 Jun 2014 07:18:32 +0200 Subject: [PATCH 182/212] Better wording --- openlp/plugins/songs/lib/ewimport.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openlp/plugins/songs/lib/ewimport.py b/openlp/plugins/songs/lib/ewimport.py index ccd89e228..c56e1dba1 100644 --- a/openlp/plugins/songs/lib/ewimport.py +++ b/openlp/plugins/songs/lib/ewimport.py @@ -213,7 +213,7 @@ class EasyWorshipSongImport(SongImport): db_size = os.path.getsize(self.import_source) if db_size < 0x800: self.log_error(self.import_source, translate('SongsPlugin.EasyWorshipSongImport', - 'This file is no valid EasyWorship Database.')) + 'This file is not a valid EasyWorship database.')) return db_file = open(self.import_source, 'rb') self.memo_file = open(import_source_mb, 'rb') @@ -223,7 +223,7 @@ class EasyWorshipSongImport(SongImport): db_file.close() self.memo_file.close() self.log_error(self.import_source, translate('SongsPlugin.EasyWorshipSongImport', - 'This file is no valid EasyWorship Database.')) + 'This file is not a valid EasyWorship database.')) return # Take a stab at how text is encoded self.encoding = 'cp1252' From 49342f4a6960fb55cca009704166cffb9cee82cd Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Sun, 22 Jun 2014 22:12:59 +0200 Subject: [PATCH 183/212] Improve Powerpoint error handling --- .../presentations/lib/powerpointcontroller.py | 137 ++++++++++++++---- 1 file changed, 107 insertions(+), 30 deletions(-) diff --git a/openlp/plugins/presentations/lib/powerpointcontroller.py b/openlp/plugins/presentations/lib/powerpointcontroller.py index 09fdf2fcc..05d25b98e 100644 --- a/openlp/plugins/presentations/lib/powerpointcontroller.py +++ b/openlp/plugins/presentations/lib/powerpointcontroller.py @@ -40,6 +40,7 @@ if os.name == 'nt': import pywintypes from openlp.core.lib import ScreenList +from openlp.core.lib.ui import UiStrings, critical_error_message_box from .presentationcontroller import PresentationController, PresentationDocument @@ -99,7 +100,7 @@ class PowerpointController(PresentationController): if self.process.Presentations.Count > 0: return self.process.Quit() - except pywintypes.com_error: + except (AttributeError, pywintypes.com_error): pass self.process = None @@ -126,16 +127,23 @@ class PowerpointDocument(PresentationDocument): earlier. """ log.debug('load_presentation') - if not self.controller.process or not self.controller.process.Visible: - self.controller.start_process() try: + if not self.controller.process or not self.controller.process.Visible: + self.controller.start_process() self.controller.process.Presentations.Open(self.file_path, False, False, True) - except pywintypes.com_error: - log.debug('PPT open failed') + self.presentation = self.controller.process.Presentations(self.controller.process.Presentations.Count) + self.create_thumbnails() + # Powerpoint 2013 pops up when loading a file, so we minimize it again + if self.presentation.Application.Version == u'15.0': + try: + self.presentation.Application.WindowState = 2 + except: + log.error('Failed to minimize main powerpoint window') + return True + except pywintypes.com_error as e: + log.error('PPT open failed') + log.error(e) return False - self.presentation = self.controller.process.Presentations(self.controller.process.Presentations.Count) - self.create_thumbnails() - return True def create_thumbnails(self): """ @@ -206,23 +214,33 @@ class PowerpointDocument(PresentationDocument): Unblanks (restores) the presentation. """ log.debug('unblank_screen') - self.presentation.SlideShowSettings.Run() - self.presentation.SlideShowWindow.View.State = 1 - self.presentation.SlideShowWindow.Activate() - if self.presentation.Application.Version == '14.0': - # Unblanking is broken in PowerPoint 2010, need to redisplay - slide = self.presentation.SlideShowWindow.View.CurrentShowPosition - click = self.presentation.SlideShowWindow.View.GetClickIndex() - self.presentation.SlideShowWindow.View.GotoSlide(slide) - if click: - self.presentation.SlideShowWindow.View.GotoClick(click) + try: + self.presentation.SlideShowSettings.Run() + self.presentation.SlideShowWindow.View.State = 1 + self.presentation.SlideShowWindow.Activate() + if self.presentation.Application.Version == '14.0': + # Unblanking is broken in PowerPoint 2010, need to redisplay + slide = self.presentation.SlideShowWindow.View.CurrentShowPosition + click = self.presentation.SlideShowWindow.View.GetClickIndex() + self.presentation.SlideShowWindow.View.GotoSlide(slide) + if click: + self.presentation.SlideShowWindow.View.GotoClick(click) + except pywintypes.com_error as e: + log.error('COM error while in unblank_screen') + log.error(e) + self.show_error_msg() def blank_screen(self): """ Blanks the screen. """ log.debug('blank_screen') - self.presentation.SlideShowWindow.View.State = 3 + try: + self.presentation.SlideShowWindow.View.State = 3 + except pywintypes.com_error as e: + log.error('COM error while in blank_screen') + log.error(e) + self.show_error_msg() def is_blank(self): """ @@ -230,7 +248,12 @@ class PowerpointDocument(PresentationDocument): """ log.debug('is_blank') if self.is_active(): - return self.presentation.SlideShowWindow.View.State == 3 + try: + return self.presentation.SlideShowWindow.View.State == 3 + except pywintypes.com_error as e: + log.error('COM error while in is_blank') + log.error(e) + self.show_error_msg() else: return False @@ -239,7 +262,12 @@ class PowerpointDocument(PresentationDocument): Stops the current presentation and hides the output. """ log.debug('stop_presentation') - self.presentation.SlideShowWindow.View.Exit() + try: + self.presentation.SlideShowWindow.View.Exit() + except pywintypes.com_error as e: + log.error('COM error while in stop_presentation') + log.error(e) + self.show_error_msg() if os.name == 'nt': def start_presentation(self): @@ -259,24 +287,48 @@ class PowerpointDocument(PresentationDocument): ppt_window = self.presentation.SlideShowSettings.Run() if not ppt_window: return - ppt_window.Top = size.y() * 72 / dpi - ppt_window.Height = size.height() * 72 / dpi - ppt_window.Left = size.x() * 72 / dpi - ppt_window.Width = size.width() * 72 / dpi + try: + ppt_window.Top = size.y() * 72 / dpi + ppt_window.Height = size.height() * 72 / dpi + ppt_window.Left = size.x() * 72 / dpi + ppt_window.Width = size.width() * 72 / dpi + except AttributeError as e: + log.error('AttributeError while in start_presentation') + log.error(e) + # Powerpoint 2013 pops up when starting a file, so we minimize it again + if self.presentation.Application.Version == u'15.0': + try: + self.presentation.Application.WindowState = 2 + except: + log.error('Failed to minimize main powerpoint window') def get_slide_number(self): """ Returns the current slide number. """ log.debug('get_slide_number') - return self.presentation.SlideShowWindow.View.CurrentShowPosition + ret = 0 + try: + ret = self.presentation.SlideShowWindow.View.CurrentShowPosition + except pywintypes.com_error as e: + log.error('COM error while in get_slide_number') + log.error(e) + self.show_error_msg() + return ret def get_slide_count(self): """ Returns total number of slides. """ log.debug('get_slide_count') - return self.presentation.Slides.Count + ret = 0 + try: + ret = self.presentation.Slides.Count + except pywintypes.com_error as e: + log.error('COM error while in get_slide_count') + log.error(e) + self.show_error_msg() + return ret def goto_slide(self, slide_no): """ @@ -285,14 +337,25 @@ class PowerpointDocument(PresentationDocument): :param slide_no: The slide the text is required for, starting at 1 """ log.debug('goto_slide') - self.presentation.SlideShowWindow.View.GotoSlide(slide_no) + try: + self.presentation.SlideShowWindow.View.GotoSlide(slide_no) + except pywintypes.com_error as e: + log.error('COM error while in goto_slide') + log.error(e) + self.show_error_msg() def next_step(self): """ Triggers the next effect of slide on the running presentation. """ log.debug('next_step') - self.presentation.SlideShowWindow.View.Next() + try: + self.presentation.SlideShowWindow.View.Next() + except pywintypes.com_error as e: + log.error('COM error while in next_step') + log.error(e) + self.show_error_msg() + return if self.get_slide_number() > self.get_slide_count(): self.previous_step() @@ -301,7 +364,12 @@ class PowerpointDocument(PresentationDocument): Triggers the previous slide on the running presentation. """ log.debug('previous_step') - self.presentation.SlideShowWindow.View.Previous() + try: + self.presentation.SlideShowWindow.View.Previous() + except pywintypes.com_error as e: + log.error('COM error while in previous_step') + log.error(e) + self.show_error_msg() def get_slide_text(self, slide_no): """ @@ -319,6 +387,15 @@ class PowerpointDocument(PresentationDocument): """ return _get_text_from_shapes(self.presentation.Slides(slide_no).NotesPage.Shapes) + def show_error_msg(self): + """ + Stop presentation and display an error message. + """ + self.stop_presentation() + critical_error_message_box(UiStrings().Error, translate('PresentationPlugin.PowerpointDocument', + 'An error occurred in the Powerpoint integration ' + 'and the presentation will be stopped. ' + 'Relstart the presentation if you wish to present it.')) def _get_text_from_shapes(shapes): """ From 248e703345e86ebfbcf5de882b9fea63b1215908 Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Tue, 24 Jun 2014 19:46:23 +0200 Subject: [PATCH 184/212] Fixed loading of libvlc.dll on windows --- 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 0326e4104..01aced404 100644 --- a/openlp/core/ui/media/vendor/vlc.py +++ b/openlp/core/ui/media/vendor/vlc.py @@ -110,7 +110,10 @@ def find_lib(): p = find_library('libvlc.dll') if p is None: try: # some registry settings - import _winreg as w # leaner than win32api, win32con + if PYTHON3: + import winreg as w # leaner than win32api, win32con + else: + 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') From f92afc296b862efe040f656c525cc9709680e9b8 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 25 Jun 2014 14:23:31 +0200 Subject: [PATCH 185/212] First go at an importer for Worship Assistant --- openlp/plugins/songs/lib/importer.py | 31 ++-- .../songs/lib/worshipassistantimport.py | 144 ++++++++++++++++++ 2 files changed, 166 insertions(+), 9 deletions(-) create mode 100644 openlp/plugins/songs/lib/worshipassistantimport.py diff --git a/openlp/plugins/songs/lib/importer.py b/openlp/plugins/songs/lib/importer.py index 7ce637285..6d01da309 100644 --- a/openlp/plugins/songs/lib/importer.py +++ b/openlp/plugins/songs/lib/importer.py @@ -50,6 +50,7 @@ from .sundayplusimport import SundayPlusImport from .foilpresenterimport import FoilPresenterImport from .zionworximport import ZionWorxImport from .propresenterimport import ProPresenterImport +from .worshipassistantimport import WorshipAssistantImport # Imports that might fail @@ -167,8 +168,9 @@ class SongFormat(object): SongsOfFellowship = 16 SundayPlus = 17 WordsOfWorship = 18 - WorshipCenterPro = 19 - ZionWorx = 20 + WorshipAssistant = 19 + WorshipCenterPro = 20 + ZionWorx = 21 # Set optional attribute defaults __defaults__ = { @@ -321,6 +323,16 @@ class SongFormat(object): 'prefix': 'wordsOfWorship', 'filter': '%s (*.wsg *.wow-song)' % translate('SongsPlugin.ImportWizardForm', 'Words Of Worship Song Files') }, + WorshipAssistant: { + 'class': WorshipAssistantImport, + 'name': 'Worship Assistant 0', + 'prefix': 'worshipAssistant', + 'selectMode': SongFormatSelect.SingleFile, + 'filter': '%s (*.csv)' % translate('SongsPlugin.ImportWizardForm', 'Worship Assistant Files'), + 'comboBoxText': translate('SongsPlugin.ImportWizardForm', 'Worship Assistant (CSV)'), + 'descriptionText': translate('SongsPlugin.ImportWizardForm', + 'In Worship Assistant, export your Database to a CSV file.') + }, WorshipCenterPro: { 'name': 'WorshipCenter Pro', 'prefix': 'worshipCenterPro', @@ -370,16 +382,17 @@ class SongFormat(object): SongFormat.SongsOfFellowship, SongFormat.SundayPlus, SongFormat.WordsOfWorship, + SongFormat.WorshipAssistant, SongFormat.WorshipCenterPro, SongFormat.ZionWorx ] @staticmethod - def get(format, *attributes): + def get(song_format, *attributes): """ Return requested song format attribute(s). - :param format: A song format from SongFormat. + :param song_format: A song format from SongFormat. :param attributes: Zero or more song format attributes from SongFormat. Return type depends on number of supplied attributes: @@ -389,23 +402,23 @@ class SongFormat(object): :>1: Return tuple of requested attribute values. """ if not attributes: - return SongFormat.__attributes__.get(format) + return SongFormat.__attributes__.get(song_format) elif len(attributes) == 1: default = SongFormat.__defaults__.get(attributes[0]) - return SongFormat.__attributes__[format].get(attributes[0], default) + return SongFormat.__attributes__[song_format].get(attributes[0], default) else: values = [] for attr in attributes: default = SongFormat.__defaults__.get(attr) - values.append(SongFormat.__attributes__[format].get(attr, default)) + values.append(SongFormat.__attributes__[song_format].get(attr, default)) return tuple(values) @staticmethod - def set(format, attribute, value): + def set(song_format, attribute, value): """ Set specified song format attribute to the supplied value. """ - SongFormat.__attributes__[format][attribute] = value + SongFormat.__attributes__[song_format][attribute] = value SongFormat.set(SongFormat.SongsOfFellowship, 'availability', HAS_SOF) diff --git a/openlp/plugins/songs/lib/worshipassistantimport.py b/openlp/plugins/songs/lib/worshipassistantimport.py new file mode 100644 index 000000000..683ec6456 --- /dev/null +++ b/openlp/plugins/songs/lib/worshipassistantimport.py @@ -0,0 +1,144 @@ +# -*- 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 # +############################################################################### +""" +The :mod:`worshipassistantimport` module provides the functionality for importing +Worship Assistant songs into the OpenLP database. +""" +import csv +import logging + +from openlp.core.common import translate +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]) + + +class WorshipAssistantImport(SongImport): + """ + The :class:`WorshipAssistantImport` class provides the ability to import songs + from Worship Assistant, via a dump of the database to a CSV file. + + The following fields are in the exported CSV file: + + * ``SONGNR`` Song ID (Discarded by importer) + * ``TITLE`` Song title + * ``AUTHOR`` Song author. May containt multiple authors. + * ``COPYRIGHT`` Copyright information + * ``FIRSTLINE`` Unknown (Discarded by importer) + * ``PRIKEY`` Primary chord key + * ``ALTKEY`` Alternate chord key + * ``TEMPO`` Tempo + * ``FOCUS`` Unknown (Discarded by importer) + * ``THEME`` Theme (Discarded by importer) + * ``SCRIPTURE`` Associated scripture (Discarded by importer) + * ``ACTIVE`` Boolean value (Discarded by importer) + * ``SONGBOOK`` Boolean value (Discarded by importer) + * ``TIMESIG`` Unknown (Discarded by importer) + * ``INTRODUCED`` Date the song was created (Discarded by importer) + * ``LASTUSED`` Date the song was last used (Discarded by importer) + * ``TIMESUSED`` How many times the song was used (Discarded by importer) + * ``TIMESUSED`` How many times the song was used (Discarded by importer) + * ``CCLINR`` CCLI Number + * ``USER1`` User Field 1 (Discarded by importer) + * ``USER2`` User Field 2 (Discarded by importer) + * ``USER3`` User Field 3 (Discarded by importer) + * ``USER4`` User Field 4 (Discarded by importer) + * ``USER5`` User Field 5 (Discarded by importer) + * ``ROADMAP`` Verse order + * ``FILELINK1`` Associated file 1 (Discarded by importer) + * ``OVERMAP`` Unknown (Discarded by importer) + * ``FILELINK2`` Associated file 2 (Discarded by importer) + * ``LYRICS`` The song lyrics as plain text (Discarded by importer) + * ``INFO`` Unknown (Discarded by importer) + * ``LYRICS2`` The song lyrics with verse numbers + * ``BACKGROUND`` Unknown (Discarded by importer) + """ + def do_import(self): + """ + Receive a CSV file to import. + """ + with open(self.import_source, 'r', encoding='latin-1') as songs_file: + field_names = ['SongNum', 'Title1', 'Title2', 'Lyrics', 'Writer', 'Copyright', 'Keywords', + 'DefaultStyle'] + songs_reader = csv.DictReader(songs_file)#, field_names) + try: + records = list(songs_reader) + except csv.Error as e: + self.log_error(translate('SongsPlugin.WorshipAssistantImport', 'Error reading CSV file.'), + translate('SongsPlugin.WorshipAssistantImport', 'Line %d: %s') % + (songs_reader.line_num, e)) + return + num_records = len(records) + log.info('%s records found in CSV file' % num_records) + self.import_wizard.progress_bar.setMaximum(num_records) + for index, record in enumerate(records, 1): + if self.stop_import_flag: + return + # The CSV file has a line in the middle of the file where the headers are repeated. + # We need to skip this line. + if record['TITLE'] == "TITLE" and record['COPYRIGHT'] == 'COPYRIGHT' and record['AUTHOR'] == 'AUTHOR': + continue + self.set_defaults() + try: + self.title = self._decode(record['TITLE']) + self.parse_author(self._decode(record['AUTHOR'])) + self.add_copyright(self._decode(record['COPYRIGHT'])) + lyrics = self._decode(record['LYRICS2']) + except UnicodeDecodeError as e: + self.log_error(translate('SongsPlugin.WorshipAssistantImport', 'Record %d' % index), + translate('SongsPlugin.WorshipAssistantImport', 'Decoding error: %s') % e) + continue + except TypeError as e: + self.log_error(translate('SongsPlugin.WorshipAssistantImport', + 'File not valid WorshipAssistant CSV format.'), 'TypeError: %s' % e) + return + verse = '' + for line in lyrics.splitlines(): + if line and not line.isspace(): + verse += line + '\n' + elif verse: + self.add_verse(verse) + verse = '' + if verse: + self.add_verse(verse) + if not self.finish(): + self.log_error(translate('SongsPlugin.ZionWorxImport', 'Record %d') % index + + (': "' + self.title + '"' if self.title else '')) + + def _decode(self, str): + """ + Decodes CSV input to unicode, stripping all control characters (except new lines). + """ + # This encoding choice seems OK. ZionWorx has no option for setting the + # encoding for its songs, so we assume encoding is always the same. + return str + #return str(str, 'cp1252').translate(CONTROL_CHARS_MAP) From 4ebfa33aac66b26eb106e2b2daf58edf6a40cec1 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 25 Jun 2014 15:49:03 +0200 Subject: [PATCH 186/212] Parse verse names --- .../songs/lib/worshipassistantimport.py | 30 ++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/openlp/plugins/songs/lib/worshipassistantimport.py b/openlp/plugins/songs/lib/worshipassistantimport.py index 683ec6456..ee9cd52cd 100644 --- a/openlp/plugins/songs/lib/worshipassistantimport.py +++ b/openlp/plugins/songs/lib/worshipassistantimport.py @@ -32,8 +32,10 @@ Worship Assistant songs into the OpenLP database. """ import csv import logging +import re from openlp.core.common import translate +from openlp.plugins.songs.lib import VerseType from openlp.plugins.songs.lib.songimport import SongImport log = logging.getLogger(__name__) @@ -87,9 +89,7 @@ class WorshipAssistantImport(SongImport): Receive a CSV file to import. """ with open(self.import_source, 'r', encoding='latin-1') as songs_file: - field_names = ['SongNum', 'Title1', 'Title2', 'Lyrics', 'Writer', 'Copyright', 'Keywords', - 'DefaultStyle'] - songs_reader = csv.DictReader(songs_file)#, field_names) + songs_reader = csv.DictReader(songs_file) try: records = list(songs_reader) except csv.Error as e: @@ -105,7 +105,7 @@ class WorshipAssistantImport(SongImport): return # The CSV file has a line in the middle of the file where the headers are repeated. # We need to skip this line. - if record['TITLE'] == "TITLE" and record['COPYRIGHT'] == 'COPYRIGHT' and record['AUTHOR'] == 'AUTHOR': + if record['TITLE'] == "TITLE" and record['AUTHOR'] == 'AUTHOR' and record['LYRICS2'] == 'LYRICS2': continue self.set_defaults() try: @@ -123,13 +123,29 @@ class WorshipAssistantImport(SongImport): return verse = '' for line in lyrics.splitlines(): - if line and not line.isspace(): + if line.startswith('['): # verse marker + # drop the square brackets + right_bracket = line.find(']') + content = line[1:right_bracket].lower() + # have we got any digits? If so, verse number is everything from the digits to the end (openlp does not + # have concept of part verses, so just ignore any non integers on the end (including floats)) + match = re.match('(\D*)(\d+)', content) + if match is not None: + verse_tag = match.group(1) + verse_num = match.group(2) + else: + # otherwise we assume number 1 and take the whole prefix as the verse tag + verse_tag = content + verse_num = '1' + verse_index = VerseType.from_loose_input(verse_tag) if verse_tag else 0 + verse_tag = VerseType.tags[verse_index] + elif line and not line.isspace(): verse += line + '\n' elif verse: - self.add_verse(verse) + self.add_verse(verse, verse_tag+verse_num) verse = '' if verse: - self.add_verse(verse) + self.add_verse(verse, verse_tag+verse_num) if not self.finish(): self.log_error(translate('SongsPlugin.ZionWorxImport', 'Record %d') % index + (': "' + self.title + '"' if self.title else '')) From c3e9d0c5b31a38dd23096c9192eb3b44d42ca1f2 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 25 Jun 2014 17:03:00 +0200 Subject: [PATCH 187/212] Add test --- openlp/plugins/songs/lib/cclifileimport.py | 2 +- .../songs/lib/worshipassistantimport.py | 162 +++++++++--------- .../songs/test_opensongimport.py | 6 +- .../songs/test_propresenterimport.py | 2 +- .../songs/test_songshowplusimport.py | 6 +- tests/helpers/songfileimport.py | 2 +- 6 files changed, 92 insertions(+), 88 deletions(-) diff --git a/openlp/plugins/songs/lib/cclifileimport.py b/openlp/plugins/songs/lib/cclifileimport.py index eda235ca1..69c20d1cc 100644 --- a/openlp/plugins/songs/lib/cclifileimport.py +++ b/openlp/plugins/songs/lib/cclifileimport.py @@ -64,7 +64,7 @@ class CCLIFileImport(SongImport): filename = str(filename) log.debug('Importing CCLI File: %s', filename) if os.path.isfile(filename): - detect_file = open(filename, 'r') + detect_file = open(filename, 'rb') detect_content = detect_file.read(2048) try: str(detect_content, 'utf-8') diff --git a/openlp/plugins/songs/lib/worshipassistantimport.py b/openlp/plugins/songs/lib/worshipassistantimport.py index ee9cd52cd..d371044b1 100644 --- a/openlp/plugins/songs/lib/worshipassistantimport.py +++ b/openlp/plugins/songs/lib/worshipassistantimport.py @@ -30,6 +30,7 @@ The :mod:`worshipassistantimport` module provides the functionality for importing Worship Assistant songs into the OpenLP database. """ +import chardet import csv import logging import re @@ -40,8 +41,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]) +EMPTY_STR = 'NULL' class WorshipAssistantImport(SongImport): @@ -53,12 +53,12 @@ class WorshipAssistantImport(SongImport): * ``SONGNR`` Song ID (Discarded by importer) * ``TITLE`` Song title - * ``AUTHOR`` Song author. May containt multiple authors. + * ``AUTHOR`` Song author. * ``COPYRIGHT`` Copyright information * ``FIRSTLINE`` Unknown (Discarded by importer) - * ``PRIKEY`` Primary chord key - * ``ALTKEY`` Alternate chord key - * ``TEMPO`` Tempo + * ``PRIKEY`` Primary chord key (Discarded by importer) + * ``ALTKEY`` Alternate chord key (Discarded by importer) + * ``TEMPO`` Tempo (Discarded by importer) * ``FOCUS`` Unknown (Discarded by importer) * ``THEME`` Theme (Discarded by importer) * ``SCRIPTURE`` Associated scripture (Discarded by importer) @@ -75,86 +75,90 @@ class WorshipAssistantImport(SongImport): * ``USER3`` User Field 3 (Discarded by importer) * ``USER4`` User Field 4 (Discarded by importer) * ``USER5`` User Field 5 (Discarded by importer) - * ``ROADMAP`` Verse order + * ``ROADMAP`` Verse order used for the presentation (Discarded by importer) * ``FILELINK1`` Associated file 1 (Discarded by importer) - * ``OVERMAP`` Unknown (Discarded by importer) + * ``OVERMAP`` Verse order used for printing (Discarded by importer) * ``FILELINK2`` Associated file 2 (Discarded by importer) - * ``LYRICS`` The song lyrics as plain text (Discarded by importer) + * ``LYRICS`` The song lyrics used for printing (Discarded by importer, LYRICS2 is used instead) * ``INFO`` Unknown (Discarded by importer) - * ``LYRICS2`` The song lyrics with verse numbers + * ``LYRICS2`` The song lyrics used for the presentation * ``BACKGROUND`` Unknown (Discarded by importer) """ def do_import(self): """ Receive a CSV file to import. """ - with open(self.import_source, 'r', encoding='latin-1') as songs_file: - songs_reader = csv.DictReader(songs_file) - try: - records = list(songs_reader) - except csv.Error as e: - self.log_error(translate('SongsPlugin.WorshipAssistantImport', 'Error reading CSV file.'), - translate('SongsPlugin.WorshipAssistantImport', 'Line %d: %s') % - (songs_reader.line_num, e)) - return - num_records = len(records) - log.info('%s records found in CSV file' % num_records) - self.import_wizard.progress_bar.setMaximum(num_records) - for index, record in enumerate(records, 1): - if self.stop_import_flag: - return - # The CSV file has a line in the middle of the file where the headers are repeated. - # We need to skip this line. - if record['TITLE'] == "TITLE" and record['AUTHOR'] == 'AUTHOR' and record['LYRICS2'] == 'LYRICS2': - continue - self.set_defaults() - try: - self.title = self._decode(record['TITLE']) - self.parse_author(self._decode(record['AUTHOR'])) - self.add_copyright(self._decode(record['COPYRIGHT'])) - lyrics = self._decode(record['LYRICS2']) - except UnicodeDecodeError as e: - self.log_error(translate('SongsPlugin.WorshipAssistantImport', 'Record %d' % index), - translate('SongsPlugin.WorshipAssistantImport', 'Decoding error: %s') % e) - continue - except TypeError as e: - self.log_error(translate('SongsPlugin.WorshipAssistantImport', - 'File not valid WorshipAssistant CSV format.'), 'TypeError: %s' % e) - return - verse = '' - for line in lyrics.splitlines(): - if line.startswith('['): # verse marker - # drop the square brackets - right_bracket = line.find(']') - content = line[1:right_bracket].lower() - # have we got any digits? If so, verse number is everything from the digits to the end (openlp does not - # have concept of part verses, so just ignore any non integers on the end (including floats)) - match = re.match('(\D*)(\d+)', content) - if match is not None: - verse_tag = match.group(1) - verse_num = match.group(2) - else: - # otherwise we assume number 1 and take the whole prefix as the verse tag - verse_tag = content - verse_num = '1' - verse_index = VerseType.from_loose_input(verse_tag) if verse_tag else 0 - verse_tag = VerseType.tags[verse_index] - elif line and not line.isspace(): - verse += line + '\n' - elif verse: - self.add_verse(verse, verse_tag+verse_num) - verse = '' - if verse: - self.add_verse(verse, verse_tag+verse_num) - if not self.finish(): - self.log_error(translate('SongsPlugin.ZionWorxImport', 'Record %d') % index - + (': "' + self.title + '"' if self.title else '')) + # Get encoding + detect_file = open(self.import_source, 'rb') + detect_content = detect_file.read() + details = chardet.detect(detect_content) + detect_file.close() + songs_file = open(self.import_source, 'r', encoding=details['encoding']) - def _decode(self, str): - """ - Decodes CSV input to unicode, stripping all control characters (except new lines). - """ - # This encoding choice seems OK. ZionWorx has no option for setting the - # encoding for its songs, so we assume encoding is always the same. - return str - #return str(str, 'cp1252').translate(CONTROL_CHARS_MAP) + songs_reader = csv.DictReader(songs_file) + try: + records = list(songs_reader) + except csv.Error as e: + self.log_error(translate('SongsPlugin.WorshipAssistantImport', 'Error reading CSV file.'), + translate('SongsPlugin.WorshipAssistantImport', 'Line %d: %s') % + (songs_reader.line_num, e)) + return + num_records = len(records) + log.info('%s records found in CSV file' % num_records) + self.import_wizard.progress_bar.setMaximum(num_records) + for index, record in enumerate(records, 1): + if self.stop_import_flag: + return + # Ensure that all keys are uppercase + record = dict((k.upper(), v) for k, v in record.items()) + # The CSV file has a line in the middle of the file where the headers are repeated. + # We need to skip this line. + if record['TITLE'] == "TITLE" and record['AUTHOR'] == 'AUTHOR' and record['LYRICS2'] == 'LYRICS2': + continue + self.set_defaults() + try: + self.title = record['TITLE'] + if record['AUTHOR'] != EMPTY_STR: + self.parse_author(record['AUTHOR']) + if record['COPYRIGHT']!= EMPTY_STR: + self.add_copyright(record['COPYRIGHT']) + if record['CCLINR'] != EMPTY_STR: + self.ccli_number = record['CCLINR'] + lyrics = record['LYRICS2'] + except UnicodeDecodeError as e: + self.log_error(translate('SongsPlugin.WorshipAssistantImport', 'Record %d' % index), + translate('SongsPlugin.WorshipAssistantImport', 'Decoding error: %s') % e) + continue + except TypeError as e: + self.log_error(translate('SongsPlugin.WorshipAssistantImport', + 'File not valid WorshipAssistant CSV format.'), 'TypeError: %s' % e) + return + verse = '' + for line in lyrics.splitlines(): + if line.startswith('['): # verse marker + # drop the square brackets + right_bracket = line.find(']') + content = line[1:right_bracket].lower() + # have we got any digits? If so, verse number is everything from the digits to the end (openlp does not + # have concept of part verses, so just ignore any non integers on the end (including floats)) + match = re.match('(\D*)(\d+)', content) + if match is not None: + verse_tag = match.group(1) + verse_num = match.group(2) + else: + # otherwise we assume number 1 and take the whole prefix as the verse tag + verse_tag = content + verse_num = '1' + verse_index = VerseType.from_loose_input(verse_tag) if verse_tag else 0 + verse_tag = VerseType.tags[verse_index] + elif line and not line.isspace(): + verse += line + '\n' + elif verse: + self.add_verse(verse, verse_tag+verse_num) + verse = '' + if verse: + self.add_verse(verse, verse_tag+verse_num) + if not self.finish(): + self.log_error(translate('SongsPlugin.ZionWorxImport', 'Record %d') % index + + (': "' + self.title + '"' if self.title else '')) + songs_file.close() diff --git a/tests/functional/openlp_plugins/songs/test_opensongimport.py b/tests/functional/openlp_plugins/songs/test_opensongimport.py index 70d3b342a..96d6bff0b 100644 --- a/tests/functional/openlp_plugins/songs/test_opensongimport.py +++ b/tests/functional/openlp_plugins/songs/test_opensongimport.py @@ -52,11 +52,11 @@ class TestOpenSongFileImport(SongImportTestHelper): """ Test that loading an OpenSong file works correctly on various files """ - self.file_import(os.path.join(TEST_PATH, 'Amazing Grace'), + self.file_import([os.path.join(TEST_PATH, 'Amazing Grace')], self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace.json'))) - self.file_import(os.path.join(TEST_PATH, 'Beautiful Garden Of Prayer'), + self.file_import([os.path.join(TEST_PATH, 'Beautiful Garden Of Prayer')], self.load_external_result_data(os.path.join(TEST_PATH, 'Beautiful Garden Of Prayer.json'))) - self.file_import(os.path.join(TEST_PATH, 'One, Two, Three, Four, Five'), + self.file_import([os.path.join(TEST_PATH, 'One, Two, Three, Four, Five')], self.load_external_result_data(os.path.join(TEST_PATH, 'One, Two, Three, Four, Five.json'))) diff --git a/tests/functional/openlp_plugins/songs/test_propresenterimport.py b/tests/functional/openlp_plugins/songs/test_propresenterimport.py index 971ddbdf6..10e2defc6 100644 --- a/tests/functional/openlp_plugins/songs/test_propresenterimport.py +++ b/tests/functional/openlp_plugins/songs/test_propresenterimport.py @@ -50,5 +50,5 @@ class TestProPresenterFileImport(SongImportTestHelper): """ Test that loading an ProPresenter file works correctly """ - self.file_import(os.path.join(TEST_PATH, 'Amazing Grace.pro4'), + self.file_import([os.path.join(TEST_PATH, 'Amazing Grace.pro4')], self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace.json'))) diff --git a/tests/functional/openlp_plugins/songs/test_songshowplusimport.py b/tests/functional/openlp_plugins/songs/test_songshowplusimport.py index 08400fdc5..dfee265e3 100644 --- a/tests/functional/openlp_plugins/songs/test_songshowplusimport.py +++ b/tests/functional/openlp_plugins/songs/test_songshowplusimport.py @@ -53,11 +53,11 @@ class TestSongShowPlusFileImport(SongImportTestHelper): """ Test that loading a SongShow Plus file works correctly on various files """ - self.file_import(os.path.join(TEST_PATH, 'Amazing Grace.sbsong'), + self.file_import([os.path.join(TEST_PATH, 'Amazing Grace.sbsong')], self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace.json'))) - self.file_import(os.path.join(TEST_PATH, 'Beautiful Garden Of Prayer.sbsong'), + self.file_import([os.path.join(TEST_PATH, 'Beautiful Garden Of Prayer.sbsong')], self.load_external_result_data(os.path.join(TEST_PATH, 'Beautiful Garden Of Prayer.json'))) - self.file_import(os.path.join(TEST_PATH, 'a mighty fortress is our god.sbsong'), + self.file_import([os.path.join(TEST_PATH, 'a mighty fortress is our god.sbsong')], self.load_external_result_data(os.path.join(TEST_PATH, 'a mighty fortress is our god.json'))) diff --git a/tests/helpers/songfileimport.py b/tests/helpers/songfileimport.py index 36beef6e5..80b2ee268 100644 --- a/tests/helpers/songfileimport.py +++ b/tests/helpers/songfileimport.py @@ -95,7 +95,7 @@ class SongImportTestHelper(TestCase): importer.topics = [] # WHEN: Importing the source file - importer.import_source = [source_file_name] + importer.import_source = source_file_name add_verse_calls = self._get_data(result_data, 'verses') author_calls = self._get_data(result_data, 'authors') ccli_number = self._get_data(result_data, 'ccli_number') From 5eb0ac618ecc733a00a0fdc1e16b7ffd0d5e32da Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 25 Jun 2014 17:04:13 +0200 Subject: [PATCH 188/212] PEP8 --- openlp/plugins/songs/lib/worshipassistantimport.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/openlp/plugins/songs/lib/worshipassistantimport.py b/openlp/plugins/songs/lib/worshipassistantimport.py index d371044b1..980f7a801 100644 --- a/openlp/plugins/songs/lib/worshipassistantimport.py +++ b/openlp/plugins/songs/lib/worshipassistantimport.py @@ -120,7 +120,7 @@ class WorshipAssistantImport(SongImport): self.title = record['TITLE'] if record['AUTHOR'] != EMPTY_STR: self.parse_author(record['AUTHOR']) - if record['COPYRIGHT']!= EMPTY_STR: + if record['COPYRIGHT'] != EMPTY_STR: self.add_copyright(record['COPYRIGHT']) if record['CCLINR'] != EMPTY_STR: self.ccli_number = record['CCLINR'] @@ -135,12 +135,10 @@ class WorshipAssistantImport(SongImport): return verse = '' for line in lyrics.splitlines(): - if line.startswith('['): # verse marker + if line.startswith('['): # verse marker # drop the square brackets right_bracket = line.find(']') content = line[1:right_bracket].lower() - # have we got any digits? If so, verse number is everything from the digits to the end (openlp does not - # have concept of part verses, so just ignore any non integers on the end (including floats)) match = re.match('(\D*)(\d+)', content) if match is not None: verse_tag = match.group(1) From 93c3a57ea9f3dcbce52ea925dc83190f25d33b6c Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 25 Jun 2014 17:04:28 +0200 Subject: [PATCH 189/212] Add missing files --- .../songs/test_worshipassistantimport.py | 54 +++++++++++++++++++ .../worshipassistantsongs/du_herr.csv | 30 +++++++++++ .../worshipassistantsongs/du_herr.json | 21 ++++++++ 3 files changed, 105 insertions(+) create mode 100644 tests/functional/openlp_plugins/songs/test_worshipassistantimport.py create mode 100644 tests/resources/worshipassistantsongs/du_herr.csv create mode 100644 tests/resources/worshipassistantsongs/du_herr.json diff --git a/tests/functional/openlp_plugins/songs/test_worshipassistantimport.py b/tests/functional/openlp_plugins/songs/test_worshipassistantimport.py new file mode 100644 index 000000000..9d968beaa --- /dev/null +++ b/tests/functional/openlp_plugins/songs/test_worshipassistantimport.py @@ -0,0 +1,54 @@ +# -*- 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 # +# --------------------------------------------------------------------------- # +# This program is free software; you can redistribute it and/or modify it # +# under the terms of the GNU General Public License as published by the Free # +# Software Foundation; version 2 of the License. # +# # +# This program is distributed in the hope that it will be useful, but WITHOUT # +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # +# more details. # +# # +# You should have received a copy of the GNU General Public License along # +# with this program; if not, write to the Free Software Foundation, Inc., 59 # +# Temple Place, Suite 330, Boston, MA 02111-1307 USA # +############################################################################### +""" +The :mod:`worshipassistantimport` module provides the functionality for importing +WorshipAssistant song files into the current installation database. +""" + +import os + +from tests.helpers.songfileimport import SongImportTestHelper + +TEST_PATH = os.path.abspath( + os.path.join(os.path.dirname(__file__), '..', '..', '..', 'resources', 'worshipassistantsongs')) + + +class TestWorshipAssistantFileImport(SongImportTestHelper): + + def __init__(self, *args, **kwargs): + self.importer_class_name = 'WorshipAssistantImport' + self.importer_module_name = 'worshipassistantimport' + super(TestWorshipAssistantFileImport, self).__init__(*args, **kwargs) + + def test_song_import(self): + """ + Test that loading an Worship Assistant file works correctly + """ + self.file_import(os.path.join(TEST_PATH, 'du_herr.csv'), + self.load_external_result_data(os.path.join(TEST_PATH, 'du_herr.json'))) diff --git a/tests/resources/worshipassistantsongs/du_herr.csv b/tests/resources/worshipassistantsongs/du_herr.csv new file mode 100644 index 000000000..72c5b4735 --- /dev/null +++ b/tests/resources/worshipassistantsongs/du_herr.csv @@ -0,0 +1,30 @@ +"SongID","SongNr","Title","Author","Copyright","FirstLine","PriKey","AltKey","Tempo","Focus","Theme","Scripture","Active","Songbook","TimeSig","Introduced","LastUsed","TimesUsed","CCLINr","User1","User2","User3","User4","User5","Roadmap","Overmap","FileLink1","FileLink2","Updated","Lyrics","Info","Lyrics2","Background" +"4ee399dc-edda-4aa9-891e-a859ca093c78","NULL","Du, Herr, verläßt mich nicht","Carl Brockhaus / Johann Georg Bäßler 1806","NULL","NULL","NULL","NULL","NULL","NULL","NULL","NULL","1","1","NULL","NULL","NULL","NULL","NULL","NULL","NULL","NULL","NULL","NULL","NULL","NULL","NULL","NULL","2014-06-25 12:15:28.317","","NULL","[1] +Du, Herr, verläßt mich nicht. +Auf Dich mein Herz allein vertraut, +Mein Auge glaubend auf Dich schaut. +Du bist mein Heil, mein Licht, +Mein Fels, mein sichrer Hort. +Bin ich versucht, gibt's Not und Leid, +Du bleibst mein Trost, mein Arm im Streit, +Mein Licht am dunklen Ort. + +[2] +Ich weiß, daß Du mich liebst. +Bist mir in jeder Lage nah', +Wohin ich gehe – Du bist da, +Ja, Du mir alles gibst. +Ich überlaß mich Dir; +Denn Du, Herr, kennst mich ganz und gar +Und führst mich sicher, wunderbar, +Und bist selbst alles mir. + +[3] +In dieser Wüste hier +Find't nirgend meine Seele Ruh', +Denn meine Ruh' bist, Jesu, Du. +Wohl mir, ich geh' zu Dir! +Bald werd' ich bei Dir sein, +Bald mit den Deinen ewiglich +Anbeten, loben, preisen Dich, +Mich Deiner stets erfreun.","NULL" diff --git a/tests/resources/worshipassistantsongs/du_herr.json b/tests/resources/worshipassistantsongs/du_herr.json new file mode 100644 index 000000000..56f3a7f0c --- /dev/null +++ b/tests/resources/worshipassistantsongs/du_herr.json @@ -0,0 +1,21 @@ +{ + "authors": [ + "Carl Brockhaus / Johann Georg Bäßler 1806" + ], + "title": "Du, Herr, verläßt mich nicht", + "verse_order_list": [], + "verses": [ + [ + "Du, Herr, verläßt mich nicht.\nAuf Dich mein Herz allein vertraut,\nMein Auge glaubend auf Dich schaut.\nDu bist mein Heil, mein Licht,\nMein Fels, mein sichrer Hort.\nBin ich versucht, gibt's Not und Leid,\nDu bleibst mein Trost, mein Arm im Streit,\nMein Licht am dunklen Ort.\n", + "v1" + ], + [ + "Ich weiß, daß Du mich liebst.\nBist mir in jeder Lage nah',\nWohin ich gehe – Du bist da,\nJa, Du mir alles gibst.\nIch überlaß mich Dir;\nDenn Du, Herr, kennst mich ganz und gar\nUnd führst mich sicher, wunderbar,\nUnd bist selbst alles mir.\n", + "v2" + ], + [ + "In dieser Wüste hier\nFind't nirgend meine Seele Ruh',\nDenn meine Ruh' bist, Jesu, Du.\nWohl mir, ich geh' zu Dir!\nBald werd' ich bei Dir sein,\nBald mit den Deinen ewiglich\nAnbeten, loben, preisen Dich,\nMich Deiner stets erfreun.\n", + "v3" + ] + ] +} \ No newline at end of file From e773d823417c21a405b395971ba3b99595d83d89 Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Thu, 26 Jun 2014 09:41:22 +0200 Subject: [PATCH 190/212] Updated VLC bindings to latest build --- openlp/core/ui/media/vendor/vlc.py | 523 ++++++++++++++++++++++++----- 1 file changed, 443 insertions(+), 80 deletions(-) diff --git a/openlp/core/ui/media/vendor/vlc.py b/openlp/core/ui/media/vendor/vlc.py index 01aced404..95f12ec85 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 = "Tue Jul 2 10:35:53 2013" +build_date = "Wed Jun 25 13:46:01 2014" if sys.version_info[0] > 2: str = str @@ -110,10 +110,11 @@ def find_lib(): p = find_library('libvlc.dll') if p is None: try: # some registry settings + # leaner than win32api, win32con if PYTHON3: - import winreg as w # leaner than win32api, win32con + import winreg as w else: - import _winreg as w # leaner than win32api, win32con + import _winreg as w for r in w.HKEY_LOCAL_MACHINE, w.HKEY_CURRENT_USER: try: r = w.OpenKey(r, 'Software\\VideoLAN\\VLC') @@ -368,6 +369,7 @@ class EventType(_Enum): 3: 'MediaParsedChanged', 4: 'MediaFreed', 5: 'MediaStateChanged', + 6: 'MediaSubItemTreeAdded', 0x100: 'MediaPlayerMediaChanged', 257: 'MediaPlayerNothingSpecial', 258: 'MediaPlayerOpening', @@ -387,6 +389,7 @@ class EventType(_Enum): 272: 'MediaPlayerSnapshotTaken', 273: 'MediaPlayerLengthChanged', 274: 'MediaPlayerVout', + 275: 'MediaPlayerScrambledChanged', 0x200: 'MediaListItemAdded', 513: 'MediaListWillAddItem', 514: 'MediaListItemDeleted', @@ -442,6 +445,7 @@ EventType.MediaPlayerPausableChanged = EventType(270) EventType.MediaPlayerPaused = EventType(261) EventType.MediaPlayerPlaying = EventType(260) EventType.MediaPlayerPositionChanged = EventType(268) +EventType.MediaPlayerScrambledChanged = EventType(275) EventType.MediaPlayerSeekableChanged = EventType(269) EventType.MediaPlayerSnapshotTaken = EventType(272) EventType.MediaPlayerStopped = EventType(262) @@ -450,6 +454,7 @@ EventType.MediaPlayerTitleChanged = EventType(271) EventType.MediaPlayerVout = EventType(274) EventType.MediaStateChanged = EventType(5) EventType.MediaSubItemAdded = EventType(1) +EventType.MediaSubItemTreeAdded = EventType(6) EventType.VlmMediaAdded = EventType(0x600) EventType.VlmMediaChanged = EventType(1538) EventType.VlmMediaInstanceStarted = EventType(1539) @@ -483,23 +488,35 @@ class Meta(_Enum): 14: 'EncodedBy', 15: 'ArtworkURL', 16: 'TrackID', + 17: 'TrackTotal', + 18: 'Director', + 19: 'Season', + 20: 'Episode', + 21: 'ShowName', + 22: 'Actors', } +Meta.Actors = Meta(22) Meta.Album = Meta(4) Meta.Artist = Meta(1) Meta.ArtworkURL = Meta(15) Meta.Copyright = Meta(3) Meta.Date = Meta(8) Meta.Description = Meta(6) +Meta.Director = Meta(18) Meta.EncodedBy = Meta(14) +Meta.Episode = Meta(20) Meta.Genre = Meta(2) Meta.Language = Meta(11) Meta.NowPlaying = Meta(12) Meta.Publisher = Meta(13) Meta.Rating = Meta(7) +Meta.Season = Meta(19) Meta.Setting = Meta(9) +Meta.ShowName = Meta(21) Meta.Title = Meta(0) Meta.TrackID = Meta(16) Meta.TrackNumber = Meta(5) +Meta.TrackTotal = Meta(17) Meta.URL = Meta(10) class State(_Enum): @@ -597,6 +614,32 @@ NavigateMode.left = NavigateMode(3) NavigateMode.right = NavigateMode(4) NavigateMode.up = NavigateMode(1) +class Position(_Enum): + '''Enumeration of values used to set position (e.g. of video title). + ''' + _enum_names_ = { + -1: 'disable', + 0: 'center', + 1: 'left', + 2: 'right', + 3: 'top', + 4: 'left', + 5: 'right', + 6: 'bottom', + 7: 'left', + 8: 'right', + } +Position.bottom = Position(6) +Position.center = Position(0) +Position.disable = Position(-1) +Position.left = Position(1) +Position.left = Position(4) +Position.left = Position(7) +Position.right = Position(2) +Position.right = Position(5) +Position.right = Position(8) +Position.top = Position(3) + class VideoLogoOption(_Enum): '''Option values for libvlc_video_{get,set}_logo_{int,string}. ''' @@ -688,7 +731,7 @@ class LogCb(ctypes.c_void_p): """Callback prototype for LibVLC log message handler. \param data data pointer as given to L{libvlc_log_set}() \param level message level (@ref enum libvlc_log_level) -\param ctx message context (meta-informations about the message) +\param ctx message context (meta-information about the message) \param fmt printf() format string (as defined by ISO C11) \param args variable argument list for the format \note Log message handlers must be thread-safe. @@ -826,18 +869,18 @@ 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}() \param level message level (@ref enum libvlc_log_level) -\param ctx message context (meta-informations about the message) +\param ctx message context (meta-information about the message) \param fmt printf() format string (as defined by ISO C11) \param args variable argument list for the format \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 @@ -849,7 +892,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. @@ -862,7 +905,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 @@ -870,7 +913,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 @@ -894,47 +937,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. @@ -944,12 +987,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 # @@ -1213,7 +1256,7 @@ class EventManager(_Ctype): @note: Only a single notification can be registered for each event type in an EventManager instance. - + ''' _callback_handler = None @@ -1290,7 +1333,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): @@ -1435,6 +1478,16 @@ class Instance(_Ctype): ''' return libvlc_set_user_agent(self, str_to_bytes(name), str_to_bytes(http)) + def set_app_id(self, id, version, icon): + '''Sets some meta-information about the application. + See also L{set_user_agent}(). + @param id: Java-style application identifier, e.g. "com.acme.foobar". + @param version: application version numbers, e.g. "1.2.3". + @param icon: application icon name, e.g. "foobar". + @version: LibVLC 2.1.0 or later. + ''' + return libvlc_set_app_id(self, str_to_bytes(id), str_to_bytes(version), str_to_bytes(icon)) + def log_unset(self): '''Unsets the logging callback for a LibVLC instance. This is rarely needed: the callback is implicitly unset when the instance is destroyed. @@ -1524,13 +1577,13 @@ class Instance(_Ctype): return libvlc_media_library_new(self) def audio_output_list_get(self): - '''Gets the list of available audio outputs. + '''Gets the list of available audio output modules. @return: list of available audio outputs. It must be freed it with In case of error, NULL is returned. ''' return libvlc_audio_output_list_get(self) def audio_output_device_list_get(self, aout): - '''Gets a list of audio output devices for a given audio output. + '''Gets a list of audio output devices for a given audio output module, See L{audio_output_device_set}(). @note: Not all audio outputs support this. In particular, an empty (NULL) list of devices does B{not} imply that the specified audio output does @@ -1756,11 +1809,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): @@ -1793,6 +1846,19 @@ class Media(_Ctype): for o in options: self.add_option(o) + def tracks_get(self): + """Get media descriptor's elementary streams description + Note, you need to call L{parse}() or play the media at least once + before calling this function. + Not doing this will result in an empty array. + The result must be freed with L{tracks_release}. + @version: LibVLC 2.1.0 and later. + """ + mediaTrack_pp = ctypes.POINTER(MediaTrack)() + n = libvlc_media_tracks_get(self, byref(mediaTrack_pp)) + info = cast(ctypes.mediaTrack_pp, ctypes.POINTER(ctypes.POINTER(MediaTrack) * n)) + return info + def add_option(self, psz_options): '''Add an option to the media. @@ -1965,17 +2031,6 @@ class Media(_Ctype): ''' return libvlc_media_get_user_data(self) - def tracks_get(self, tracks): - '''Get media descriptor's elementary streams description - Note, you need to call L{parse}() or play the media at least once - before calling this function. - Not doing this will result in an empty array. - @param tracks: address to store an allocated array of Elementary Streams descriptions (must be freed with L{tracks_release}. - @return: the number of Elementary Streams (zero on error). - @version: LibVLC 2.1.0 and later. - ''' - return libvlc_media_tracks_get(self, tracks) - def player_new_from_media(self): '''Create a Media Player object from a Media. @return: a new media player object, or NULL on error. @@ -2056,11 +2111,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): @@ -2076,10 +2131,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. @@ -2196,7 +2251,7 @@ class MediaListPlayer(_Ctype): It may take as parameter either: - a vlc.Instance - nothing - + ''' def __new__(cls, arg=None): @@ -2322,13 +2377,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:] @@ -2400,13 +2455,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. @@ -2559,12 +2614,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. @@ -2799,6 +2854,13 @@ class MediaPlayer(_Ctype): ''' return libvlc_media_player_can_pause(self) + def program_scrambled(self): + '''Check if the current program is scrambled. + @return: true if the current program is scrambled \libvlc_return_bool. + @version: LibVLC 2.2.0 or later. + ''' + return libvlc_media_player_program_scrambled(self) + def next_frame(self): '''Display the next frame (if supported). ''' @@ -2811,6 +2873,14 @@ class MediaPlayer(_Ctype): ''' return libvlc_media_player_navigate(self, navigate) + def set_video_title_display(self, position, timeout): + '''Set if, and how, the video title will be shown when media is played. + @param position: position at which to display the title, or libvlc_position_disable to prevent the title from being displayed. + @param timeout: title display timeout in milliseconds (ignored if libvlc_position_disable). + @version: libVLC 2.1.0 or later. + ''' + return libvlc_media_player_set_video_title_display(self, position, timeout) + def toggle_fullscreen(self): '''Toggle fullscreen status on non-embedded video outputs. @warning: The same limitations applies to this function @@ -3086,7 +3156,7 @@ class MediaPlayer(_Ctype): return libvlc_video_set_adjust_float(self, option, value) def audio_output_set(self, psz_name): - '''Sets the audio output. + '''Selects an audio output module. @note: Any change will take be effect only after playback is stopped and restarted. Audio output cannot be changed while playing. @param psz_name: name of audio output, use psz_name of See L{AudioOutput}. @@ -3094,21 +3164,46 @@ class MediaPlayer(_Ctype): ''' return libvlc_audio_output_set(self, str_to_bytes(psz_name)) - def audio_output_device_set(self, psz_audio_output, psz_device_id): - '''Configures an explicit audio output device for a given audio output plugin. - A list of possible devices can be obtained with + def audio_output_device_enum(self): + '''Gets a list of potential audio output devices, + See L{audio_output_device_set}(). + @note: Not all audio outputs support enumerating devices. + The audio output may be functional even if the list is empty (NULL). + @note: The list may not be exhaustive. + @warning: Some audio output devices in the list might not actually work in + some circumstances. By default, it is recommended to not specify any + explicit audio device. + @return: A NULL-terminated linked list of potential audio output devices. It must be freed it with L{audio_output_device_list_release}(). + @version: LibVLC 2.2.0 or later. + ''' + return libvlc_audio_output_device_enum(self) + + def audio_output_device_set(self, module, device_id): + '''Configures an explicit audio output device. + If the module paramater is NULL, audio output will be moved to the device + specified by the device identifier string immediately. This is the + recommended usage. + A list of adequate potential device strings can be obtained with + L{audio_output_device_enum}(). + However passing NULL is supported in LibVLC version 2.2.0 and later only; + in earlier versions, this function would have no effects when the module + parameter was NULL. + If the module parameter is not NULL, the device parameter of the + corresponding audio output, if it exists, will be set to the specified + string. Note that some audio output modules do not have such a parameter + (notably MMDevice and PulseAudio). + A list of adequate potential device strings can be obtained with L{audio_output_device_list_get}(). @note: This function does not select the specified audio output plugin. L{audio_output_set}() is used for that purpose. @warning: The syntax for the device parameter depends on the audio output. - This is not portable. Only use this function if you know what you are doing. - Some audio outputs do not support this function (e.g. PulseAudio, WASAPI). - Some audio outputs require further parameters (e.g. ALSA: channels map). - @param psz_audio_output: - name of audio output, See L{AudioOutput}. - @param psz_device_id: device. - @return: Nothing. Errors are ignored. + Some audio output modules require further parameters (e.g. a channels map + in the case of ALSA). + @param module: If NULL, current audio output module. if non-NULL, name of audio output module. + @param device_id: device identifier string. + @return: Nothing. Errors are ignored (this is a design bug). ''' - return libvlc_audio_output_device_set(self, str_to_bytes(psz_audio_output), str_to_bytes(psz_device_id)) + return libvlc_audio_output_device_set(self, str_to_bytes(module), str_to_bytes(device_id)) def audio_toggle_mute(self): '''Toggle mute status. @@ -3187,6 +3282,28 @@ class MediaPlayer(_Ctype): ''' return libvlc_audio_set_delay(self, i_delay) + def set_equalizer(self, p_equalizer): + '''Apply new equalizer settings to a media player. + The equalizer is first created by invoking L{audio_equalizer_new}() or + L{audio_equalizer_new_from_preset}(). + It is possible to apply new equalizer settings to a media player whether the media + player is currently playing media or not. + Invoking this method will immediately apply the new equalizer settings to the audio + output of the currently playing media if there is any. + If there is no currently playing media, the new equalizer settings will be applied + later if and when new media is played. + Equalizer settings will automatically be applied to subsequently played media. + To disable the equalizer for a media player invoke this method passing NULL for the + p_equalizer parameter. + The media player does not keep a reference to the supplied equalizer so it is safe + for an application to release the equalizer reference any time after this method + returns. + @param p_equalizer: opaque equalizer handle, or NULL to disable the equalizer for this media player. + @return: zero on success, -1 on error. + @version: LibVLC 2.2.0 or later. + ''' + return libvlc_media_player_set_equalizer(self, p_equalizer) + # LibVLC __version__ functions # @@ -3282,6 +3399,20 @@ def libvlc_set_user_agent(p_instance, name, http): None, Instance, ctypes.c_char_p, ctypes.c_char_p) return f(p_instance, name, http) +def libvlc_set_app_id(p_instance, id, version, icon): + '''Sets some meta-information about the application. + See also L{libvlc_set_user_agent}(). + @param p_instance: LibVLC instance. + @param id: Java-style application identifier, e.g. "com.acme.foobar". + @param version: application version numbers, e.g. "1.2.3". + @param icon: application icon name, e.g. "foobar". + @version: LibVLC 2.1.0 or later. + ''' + f = _Cfunctions.get('libvlc_set_app_id', None) or \ + _Cfunction('libvlc_set_app_id', ((1,), (1,), (1,), (1,),), None, + None, Instance, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p) + return f(p_instance, id, version, icon) + def libvlc_get_version(): '''Retrieve libvlc version. Example: "1.1.0-git The Luggage". @@ -3358,7 +3489,7 @@ def libvlc_event_type_name(event_type): return f(event_type) def libvlc_log_get_context(ctx): - '''Gets debugging informations about a log message: the name of the VLC module + '''Gets debugging information 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. @@ -3372,9 +3503,9 @@ def libvlc_log_get_context(ctx): return f(ctx) def libvlc_log_get_object(ctx, id): - '''Gets VLC object informations about a log message: the type name of the VLC + '''Gets VLC object information 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} + object identifier. This information is 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 @@ -4433,12 +4564,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. @@ -4817,6 +4948,17 @@ def libvlc_media_player_can_pause(p_mi): ctypes.c_int, MediaPlayer) return f(p_mi) +def libvlc_media_player_program_scrambled(p_mi): + '''Check if the current program is scrambled. + @param p_mi: the media player. + @return: true if the current program is scrambled \libvlc_return_bool. + @version: LibVLC 2.2.0 or later. + ''' + f = _Cfunctions.get('libvlc_media_player_program_scrambled', None) or \ + _Cfunction('libvlc_media_player_program_scrambled', ((1,),), None, + ctypes.c_int, MediaPlayer) + return f(p_mi) + def libvlc_media_player_next_frame(p_mi): '''Display the next frame (if supported). @param p_mi: the media player. @@ -4837,6 +4979,18 @@ def libvlc_media_player_navigate(p_mi, navigate): None, MediaPlayer, ctypes.c_uint) return f(p_mi, navigate) +def libvlc_media_player_set_video_title_display(p_mi, position, timeout): + '''Set if, and how, the video title will be shown when media is played. + @param p_mi: the media player. + @param position: position at which to display the title, or libvlc_position_disable to prevent the title from being displayed. + @param timeout: title display timeout in milliseconds (ignored if libvlc_position_disable). + @version: libVLC 2.1.0 or later. + ''' + f = _Cfunctions.get('libvlc_media_player_set_video_title_display', None) or \ + _Cfunction('libvlc_media_player_set_video_title_display', ((1,), (1,), (1,),), None, + None, MediaPlayer, Position, ctypes.c_int) + return f(p_mi, position, timeout) + def libvlc_track_description_list_release(p_track_description): '''Release (free) L{TrackDescription}. @param p_track_description: the structure to release. @@ -5338,7 +5492,7 @@ def libvlc_video_set_adjust_float(p_mi, option, value): return f(p_mi, option, value) def libvlc_audio_output_list_get(p_instance): - '''Gets the list of available audio outputs. + '''Gets the list of available audio output modules. @param p_instance: libvlc instance. @return: list of available audio outputs. It must be freed it with In case of error, NULL is returned. ''' @@ -5348,7 +5502,7 @@ def libvlc_audio_output_list_get(p_instance): return f(p_instance) def libvlc_audio_output_list_release(p_list): - '''Frees the list of available audio outputs. + '''Frees the list of available audio output modules. @param p_list: list with audio outputs for release. ''' f = _Cfunctions.get('libvlc_audio_output_list_release', None) or \ @@ -5357,7 +5511,7 @@ def libvlc_audio_output_list_release(p_list): return f(p_list) def libvlc_audio_output_set(p_mi, psz_name): - '''Sets the audio output. + '''Selects an audio output module. @note: Any change will take be effect only after playback is stopped and restarted. Audio output cannot be changed while playing. @param p_mi: media player. @@ -5369,8 +5523,26 @@ def libvlc_audio_output_set(p_mi, psz_name): ctypes.c_int, MediaPlayer, ctypes.c_char_p) return f(p_mi, psz_name) +def libvlc_audio_output_device_enum(mp): + '''Gets a list of potential audio output devices, + See L{libvlc_audio_output_device_set}(). + @note: Not all audio outputs support enumerating devices. + The audio output may be functional even if the list is empty (NULL). + @note: The list may not be exhaustive. + @warning: Some audio output devices in the list might not actually work in + some circumstances. By default, it is recommended to not specify any + explicit audio device. + @param mp: media player. + @return: A NULL-terminated linked list of potential audio output devices. It must be freed it with L{libvlc_audio_output_device_list_release}(). + @version: LibVLC 2.2.0 or later. + ''' + f = _Cfunctions.get('libvlc_audio_output_device_enum', None) or \ + _Cfunction('libvlc_audio_output_device_enum', ((1,),), None, + ctypes.POINTER(AudioOutputDevice), MediaPlayer) + return f(mp) + def libvlc_audio_output_device_list_get(p_instance, aout): - '''Gets a list of audio output devices for a given audio output. + '''Gets a list of audio output devices for a given audio output module, See L{libvlc_audio_output_device_set}(). @note: Not all audio outputs support this. In particular, an empty (NULL) list of devices does B{not} imply that the specified audio output does @@ -5399,25 +5571,36 @@ def libvlc_audio_output_device_list_release(p_list): None, ctypes.POINTER(AudioOutputDevice)) return f(p_list) -def libvlc_audio_output_device_set(p_mi, psz_audio_output, psz_device_id): - '''Configures an explicit audio output device for a given audio output plugin. - A list of possible devices can be obtained with +def libvlc_audio_output_device_set(mp, module, device_id): + '''Configures an explicit audio output device. + If the module paramater is NULL, audio output will be moved to the device + specified by the device identifier string immediately. This is the + recommended usage. + A list of adequate potential device strings can be obtained with + L{libvlc_audio_output_device_enum}(). + However passing NULL is supported in LibVLC version 2.2.0 and later only; + in earlier versions, this function would have no effects when the module + parameter was NULL. + If the module parameter is not NULL, the device parameter of the + corresponding audio output, if it exists, will be set to the specified + string. Note that some audio output modules do not have such a parameter + (notably MMDevice and PulseAudio). + A list of adequate potential device strings can be obtained with L{libvlc_audio_output_device_list_get}(). @note: This function does not select the specified audio output plugin. L{libvlc_audio_output_set}() is used for that purpose. @warning: The syntax for the device parameter depends on the audio output. - This is not portable. Only use this function if you know what you are doing. - Some audio outputs do not support this function (e.g. PulseAudio, WASAPI). - Some audio outputs require further parameters (e.g. ALSA: channels map). - @param p_mi: media player. - @param psz_audio_output: - name of audio output, See L{AudioOutput}. - @param psz_device_id: device. - @return: Nothing. Errors are ignored. + Some audio output modules require further parameters (e.g. a channels map + in the case of ALSA). + @param mp: media player. + @param module: If NULL, current audio output module. if non-NULL, name of audio output module. + @param device_id: device identifier string. + @return: Nothing. Errors are ignored (this is a design bug). ''' f = _Cfunctions.get('libvlc_audio_output_device_set', None) or \ _Cfunction('libvlc_audio_output_device_set', ((1,), (1,), (1,),), None, None, MediaPlayer, ctypes.c_char_p, ctypes.c_char_p) - return f(p_mi, psz_audio_output, psz_device_id) + return f(mp, module, device_id) def libvlc_audio_toggle_mute(p_mi): '''Toggle mute status. @@ -5554,6 +5737,175 @@ def libvlc_audio_set_delay(p_mi, i_delay): ctypes.c_int, MediaPlayer, ctypes.c_int64) return f(p_mi, i_delay) +def libvlc_audio_equalizer_get_preset_count(): + '''Get the number of equalizer presets. + @return: number of presets. + @version: LibVLC 2.2.0 or later. + ''' + f = _Cfunctions.get('libvlc_audio_equalizer_get_preset_count', None) or \ + _Cfunction('libvlc_audio_equalizer_get_preset_count', (), None, + ctypes.c_uint) + return f() + +def libvlc_audio_equalizer_get_preset_name(u_index): + '''Get the name of a particular equalizer preset. + This name can be used, for example, to prepare a preset label or menu in a user + interface. + @param u_index: index of the preset, counting from zero. + @return: preset name, or NULL if there is no such preset. + @version: LibVLC 2.2.0 or later. + ''' + f = _Cfunctions.get('libvlc_audio_equalizer_get_preset_name', None) or \ + _Cfunction('libvlc_audio_equalizer_get_preset_name', ((1,),), None, + ctypes.c_char_p, ctypes.c_uint) + return f(u_index) + +def libvlc_audio_equalizer_get_band_count(): + '''Get the number of distinct frequency bands for an equalizer. + @return: number of frequency bands. + @version: LibVLC 2.2.0 or later. + ''' + f = _Cfunctions.get('libvlc_audio_equalizer_get_band_count', None) or \ + _Cfunction('libvlc_audio_equalizer_get_band_count', (), None, + ctypes.c_uint) + return f() + +def libvlc_audio_equalizer_get_band_frequency(u_index): + '''Get a particular equalizer band frequency. + This value can be used, for example, to create a label for an equalizer band control + in a user interface. + @param u_index: index of the band, counting from zero. + @return: equalizer band frequency (Hz), or -1 if there is no such band. + @version: LibVLC 2.2.0 or later. + ''' + f = _Cfunctions.get('libvlc_audio_equalizer_get_band_frequency', None) or \ + _Cfunction('libvlc_audio_equalizer_get_band_frequency', ((1,),), None, + ctypes.c_float, ctypes.c_uint) + return f(u_index) + +def libvlc_audio_equalizer_new(): + '''Create a new default equalizer, with all frequency values zeroed. + The new equalizer can subsequently be applied to a media player by invoking + L{libvlc_media_player_set_equalizer}(). + The returned handle should be freed via L{libvlc_audio_equalizer_release}() when + it is no longer needed. + @return: opaque equalizer handle, or NULL on error. + @version: LibVLC 2.2.0 or later. + ''' + f = _Cfunctions.get('libvlc_audio_equalizer_new', None) or \ + _Cfunction('libvlc_audio_equalizer_new', (), None, + ctypes.c_void_p) + return f() + +def libvlc_audio_equalizer_new_from_preset(u_index): + '''Create a new equalizer, with initial frequency values copied from an existing + preset. + The new equalizer can subsequently be applied to a media player by invoking + L{libvlc_media_player_set_equalizer}(). + The returned handle should be freed via L{libvlc_audio_equalizer_release}() when + it is no longer needed. + @param u_index: index of the preset, counting from zero. + @return: opaque equalizer handle, or NULL on error. + @version: LibVLC 2.2.0 or later. + ''' + f = _Cfunctions.get('libvlc_audio_equalizer_new_from_preset', None) or \ + _Cfunction('libvlc_audio_equalizer_new_from_preset', ((1,),), None, + ctypes.c_void_p, ctypes.c_uint) + return f(u_index) + +def libvlc_audio_equalizer_release(p_equalizer): + '''Release a previously created equalizer instance. + The equalizer was previously created by using L{libvlc_audio_equalizer_new}() or + L{libvlc_audio_equalizer_new_from_preset}(). + It is safe to invoke this method with a NULL p_equalizer parameter for no effect. + @param p_equalizer: opaque equalizer handle, or NULL. + @version: LibVLC 2.2.0 or later. + ''' + f = _Cfunctions.get('libvlc_audio_equalizer_release', None) or \ + _Cfunction('libvlc_audio_equalizer_release', ((1,),), None, + None, ctypes.c_void_p) + return f(p_equalizer) + +def libvlc_audio_equalizer_set_preamp(p_equalizer, f_preamp): + '''Set a new pre-amplification value for an equalizer. + The new equalizer settings are subsequently applied to a media player by invoking + L{libvlc_media_player_set_equalizer}(). + The supplied amplification value will be clamped to the -20.0 to +20.0 range. + @param p_equalizer: valid equalizer handle, must not be NULL. + @param f_preamp: preamp value (-20.0 to 20.0 Hz). + @return: zero on success, -1 on error. + @version: LibVLC 2.2.0 or later. + ''' + f = _Cfunctions.get('libvlc_audio_equalizer_set_preamp', None) or \ + _Cfunction('libvlc_audio_equalizer_set_preamp', ((1,), (1,),), None, + ctypes.c_int, ctypes.c_void_p, ctypes.c_float) + return f(p_equalizer, f_preamp) + +def libvlc_audio_equalizer_get_preamp(p_equalizer): + '''Get the current pre-amplification value from an equalizer. + @param p_equalizer: valid equalizer handle, must not be NULL. + @return: preamp value (Hz). + @version: LibVLC 2.2.0 or later. + ''' + f = _Cfunctions.get('libvlc_audio_equalizer_get_preamp', None) or \ + _Cfunction('libvlc_audio_equalizer_get_preamp', ((1,),), None, + ctypes.c_float, ctypes.c_void_p) + return f(p_equalizer) + +def libvlc_audio_equalizer_set_amp_at_index(p_equalizer, f_amp, u_band): + '''Set a new amplification value for a particular equalizer frequency band. + The new equalizer settings are subsequently applied to a media player by invoking + L{libvlc_media_player_set_equalizer}(). + The supplied amplification value will be clamped to the -20.0 to +20.0 range. + @param p_equalizer: valid equalizer handle, must not be NULL. + @param f_amp: amplification value (-20.0 to 20.0 Hz). + @param u_band: index, counting from zero, of the frequency band to set. + @return: zero on success, -1 on error. + @version: LibVLC 2.2.0 or later. + ''' + f = _Cfunctions.get('libvlc_audio_equalizer_set_amp_at_index', None) or \ + _Cfunction('libvlc_audio_equalizer_set_amp_at_index', ((1,), (1,), (1,),), None, + ctypes.c_int, ctypes.c_void_p, ctypes.c_float, ctypes.c_uint) + return f(p_equalizer, f_amp, u_band) + +def libvlc_audio_equalizer_get_amp_at_index(p_equalizer, u_band): + '''Get the amplification value for a particular equalizer frequency band. + @param p_equalizer: valid equalizer handle, must not be NULL. + @param u_band: index, counting from zero, of the frequency band to get. + @return: amplification value (Hz); NaN if there is no such frequency band. + @version: LibVLC 2.2.0 or later. + ''' + f = _Cfunctions.get('libvlc_audio_equalizer_get_amp_at_index', None) or \ + _Cfunction('libvlc_audio_equalizer_get_amp_at_index', ((1,), (1,),), None, + ctypes.c_float, ctypes.c_void_p, ctypes.c_uint) + return f(p_equalizer, u_band) + +def libvlc_media_player_set_equalizer(p_mi, p_equalizer): + '''Apply new equalizer settings to a media player. + The equalizer is first created by invoking L{libvlc_audio_equalizer_new}() or + L{libvlc_audio_equalizer_new_from_preset}(). + It is possible to apply new equalizer settings to a media player whether the media + player is currently playing media or not. + Invoking this method will immediately apply the new equalizer settings to the audio + output of the currently playing media if there is any. + If there is no currently playing media, the new equalizer settings will be applied + later if and when new media is played. + Equalizer settings will automatically be applied to subsequently played media. + To disable the equalizer for a media player invoke this method passing NULL for the + p_equalizer parameter. + The media player does not keep a reference to the supplied equalizer so it is safe + for an application to release the equalizer reference any time after this method + returns. + @param p_mi: opaque media player handle. + @param p_equalizer: opaque equalizer handle, or NULL to disable the equalizer for this media player. + @return: zero on success, -1 on error. + @version: LibVLC 2.2.0 or later. + ''' + f = _Cfunctions.get('libvlc_media_player_set_equalizer', None) or \ + _Cfunction('libvlc_media_player_set_equalizer', ((1,), (1,),), None, + ctypes.c_int, MediaPlayer, ctypes.c_void_p) + return f(p_mi, p_equalizer) + def libvlc_vlm_release(p_instance): '''Release the vlm instance related to the given L{Instance}. @param p_instance: the instance. @@ -5866,7 +6218,18 @@ def libvlc_vlm_get_event_manager(p_instance): # libvlc_printerr # libvlc_set_exit_handler -# 17 function(s) not wrapped as methods: +# 28 function(s) not wrapped as methods: +# libvlc_audio_equalizer_get_amp_at_index +# libvlc_audio_equalizer_get_band_count +# libvlc_audio_equalizer_get_band_frequency +# libvlc_audio_equalizer_get_preamp +# libvlc_audio_equalizer_get_preset_count +# libvlc_audio_equalizer_get_preset_name +# libvlc_audio_equalizer_new +# libvlc_audio_equalizer_new_from_preset +# libvlc_audio_equalizer_release +# libvlc_audio_equalizer_set_amp_at_index +# libvlc_audio_equalizer_set_preamp # libvlc_audio_output_device_list_release # libvlc_audio_output_list_release # libvlc_clearerr From 66827a7676b1a7a9bce3966483430200cd424140 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Thu, 26 Jun 2014 09:58:59 +0200 Subject: [PATCH 191/212] Parse verse order --- openlp/plugins/songs/lib/songimport.py | 1 + .../songs/lib/worshipassistantimport.py | 18 ++++++++++++++---- .../songs/test_worshipassistantimport.py | 2 ++ .../worshipassistantsongs/du_herr.json | 2 +- 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/openlp/plugins/songs/lib/songimport.py b/openlp/plugins/songs/lib/songimport.py index b8fcc604b..18bb571d3 100644 --- a/openlp/plugins/songs/lib/songimport.py +++ b/openlp/plugins/songs/lib/songimport.py @@ -266,6 +266,7 @@ class SongImport(QtCore.QObject): """ Add an author to the list """ + print(author) if author in self.authors: return self.authors.append(author) diff --git a/openlp/plugins/songs/lib/worshipassistantimport.py b/openlp/plugins/songs/lib/worshipassistantimport.py index 980f7a801..6831ec450 100644 --- a/openlp/plugins/songs/lib/worshipassistantimport.py +++ b/openlp/plugins/songs/lib/worshipassistantimport.py @@ -68,21 +68,20 @@ class WorshipAssistantImport(SongImport): * ``INTRODUCED`` Date the song was created (Discarded by importer) * ``LASTUSED`` Date the song was last used (Discarded by importer) * ``TIMESUSED`` How many times the song was used (Discarded by importer) - * ``TIMESUSED`` How many times the song was used (Discarded by importer) * ``CCLINR`` CCLI Number * ``USER1`` User Field 1 (Discarded by importer) * ``USER2`` User Field 2 (Discarded by importer) * ``USER3`` User Field 3 (Discarded by importer) * ``USER4`` User Field 4 (Discarded by importer) * ``USER5`` User Field 5 (Discarded by importer) - * ``ROADMAP`` Verse order used for the presentation (Discarded by importer) + * ``ROADMAP`` Verse order used for the presentation * ``FILELINK1`` Associated file 1 (Discarded by importer) * ``OVERMAP`` Verse order used for printing (Discarded by importer) * ``FILELINK2`` Associated file 2 (Discarded by importer) * ``LYRICS`` The song lyrics used for printing (Discarded by importer, LYRICS2 is used instead) * ``INFO`` Unknown (Discarded by importer) * ``LYRICS2`` The song lyrics used for the presentation - * ``BACKGROUND`` Unknown (Discarded by importer) + * ``BACKGROUND`` Custom background (Discarded by importer) """ def do_import(self): """ @@ -116,14 +115,18 @@ class WorshipAssistantImport(SongImport): if record['TITLE'] == "TITLE" and record['AUTHOR'] == 'AUTHOR' and record['LYRICS2'] == 'LYRICS2': continue self.set_defaults() + verse_order_list = [] try: self.title = record['TITLE'] if record['AUTHOR'] != EMPTY_STR: self.parse_author(record['AUTHOR']) + print(record['AUTHOR']) if record['COPYRIGHT'] != EMPTY_STR: self.add_copyright(record['COPYRIGHT']) if record['CCLINR'] != EMPTY_STR: self.ccli_number = record['CCLINR'] + if record['ROADMAP'] != EMPTY_STR: + verse_order_list = record['ROADMAP'].split(',') lyrics = record['LYRICS2'] except UnicodeDecodeError as e: self.log_error(translate('SongsPlugin.WorshipAssistantImport', 'Record %d' % index), @@ -149,6 +152,11 @@ class WorshipAssistantImport(SongImport): verse_num = '1' verse_index = VerseType.from_loose_input(verse_tag) if verse_tag else 0 verse_tag = VerseType.tags[verse_index] + # Update verse order when the verse name has changed + if content != verse_tag + verse_num: + for i in range(len(verse_order_list)): + if verse_order_list[i].lower() == content.lower(): + verse_order_list[i] = verse_tag + verse_num elif line and not line.isspace(): verse += line + '\n' elif verse: @@ -156,7 +164,9 @@ class WorshipAssistantImport(SongImport): verse = '' if verse: self.add_verse(verse, verse_tag+verse_num) + if verse_order_list: + self.verse_order_list = verse_order_list if not self.finish(): - self.log_error(translate('SongsPlugin.ZionWorxImport', 'Record %d') % index + self.log_error(translate('SongsPlugin.WorshipAssistantImport', 'Record %d') % index + (': "' + self.title + '"' if self.title else '')) songs_file.close() diff --git a/tests/functional/openlp_plugins/songs/test_worshipassistantimport.py b/tests/functional/openlp_plugins/songs/test_worshipassistantimport.py index 9d968beaa..c0c5c7416 100644 --- a/tests/functional/openlp_plugins/songs/test_worshipassistantimport.py +++ b/tests/functional/openlp_plugins/songs/test_worshipassistantimport.py @@ -52,3 +52,5 @@ class TestWorshipAssistantFileImport(SongImportTestHelper): """ self.file_import(os.path.join(TEST_PATH, 'du_herr.csv'), self.load_external_result_data(os.path.join(TEST_PATH, 'du_herr.json'))) + self.file_import(os.path.join(TEST_PATH, 'would_you_be_free.csv'), + self.load_external_result_data(os.path.join(TEST_PATH, 'would_you_be_free.json'))) diff --git a/tests/resources/worshipassistantsongs/du_herr.json b/tests/resources/worshipassistantsongs/du_herr.json index 56f3a7f0c..1df700df8 100644 --- a/tests/resources/worshipassistantsongs/du_herr.json +++ b/tests/resources/worshipassistantsongs/du_herr.json @@ -18,4 +18,4 @@ "v3" ] ] -} \ No newline at end of file +} From c63883402383702fb2750e003f53e42de1707d7b Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Thu, 26 Jun 2014 09:59:16 +0200 Subject: [PATCH 192/212] Add missing files --- .../would_you_be_free.csv | 30 +++++++++++++++++++ .../would_you_be_free.json | 19 ++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 tests/resources/worshipassistantsongs/would_you_be_free.csv create mode 100644 tests/resources/worshipassistantsongs/would_you_be_free.json diff --git a/tests/resources/worshipassistantsongs/would_you_be_free.csv b/tests/resources/worshipassistantsongs/would_you_be_free.csv new file mode 100644 index 000000000..a454ddbf5 --- /dev/null +++ b/tests/resources/worshipassistantsongs/would_you_be_free.csv @@ -0,0 +1,30 @@ +SONGNR,TITLE,AUTHOR,COPYRIGHT,FIRSTLINE,PRIKEY,ALTKEY,TEMPO,FOCUS,THEME,SCRIPTURE,ACTIVE,SONGBOOK,TIMESIG,INTRODUCED,LASTUSED,TIMESUSED,CCLINR,USER1,USER2,USER3,USER4,USER5,ROADMAP,FILELINK1,OVERMAP,FILELINK2,LYRICS,INFO,LYRICS2,Background +"7","Would You Be Free","Jones, Lewis E.","Public Domain","Would you be free from your burden of sin?","G","","Moderate","Only To Others","","","N","Y","","1899-12-30","1899-12-30","","","","","","","","1,C,1","","","",".G C G + Would you be free from your burden of sin? +. D D7 G + There's power in the blood, power in the blood +. C G + Would you o'er evil a victory win? +. D D7 G + There's wonderful power in the blood + +.G C G + There is power, power, wonder working power +.D G + In the blood of the Lamb +. C G + There is power, power, wonder working power +. D G + In the precious blood of the Lamb +","","[1] +Would you be free from your burden of sin? +There's power in the blood, power in the blood +Would you o'er evil a victory win? +There's wonderful power in the blood + +[C] +There is power, power, wonder working power +In the blood of the Lamb +There is power, power, wonder working power +In the precious blood of the Lamb +","" diff --git a/tests/resources/worshipassistantsongs/would_you_be_free.json b/tests/resources/worshipassistantsongs/would_you_be_free.json new file mode 100644 index 000000000..96bc06a59 --- /dev/null +++ b/tests/resources/worshipassistantsongs/would_you_be_free.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "Jones", + "Lewis E" + ], + "title": "Would You Be Free", + "verse_order_list": ["v1", "c1", "v1"], + "copyright": "Public Domain", + "verses": [ + [ + "Would you be free from your burden of sin? \nThere's power in the blood, power in the blood \nWould you o'er evil a victory win? \nThere's wonderful power in the blood \n", + "v1" + ], + [ + "There is power, power, wonder working power \nIn the blood of the Lamb \nThere is power, power, wonder working power \nIn the precious blood of the Lamb \n", + "c1" + ] + ] +} From a52dc69c89f4e965f6224ba6022dd99a71f42215 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Thu, 26 Jun 2014 10:04:33 +0200 Subject: [PATCH 193/212] Remove debug output --- openlp/plugins/songs/lib/songimport.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openlp/plugins/songs/lib/songimport.py b/openlp/plugins/songs/lib/songimport.py index 18bb571d3..b8fcc604b 100644 --- a/openlp/plugins/songs/lib/songimport.py +++ b/openlp/plugins/songs/lib/songimport.py @@ -266,7 +266,6 @@ class SongImport(QtCore.QObject): """ Add an author to the list """ - print(author) if author in self.authors: return self.authors.append(author) From 78f1756380b4576a8c57ef98db16b7d95c507bcc Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Thu, 26 Jun 2014 10:35:53 +0200 Subject: [PATCH 194/212] Fix biblegateway url --- openlp/plugins/bibles/lib/http.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openlp/plugins/bibles/lib/http.py b/openlp/plugins/bibles/lib/http.py index 64b024639..6b26dfabe 100644 --- a/openlp/plugins/bibles/lib/http.py +++ b/openlp/plugins/bibles/lib/http.py @@ -225,7 +225,7 @@ class BGExtract(RegistryProperties): url_book_name = urllib.parse.quote(book_name.encode("utf-8")) url_params = 'search=%s+%s&version=%s' % (url_book_name, chapter, version) soup = get_soup_for_bible_ref( - 'http://www.biblegateway.com/passage/?%s' % url_params, + 'http://legacy.biblegateway.com/passage/?%s' % url_params, pre_parse_regex=r'', pre_parse_substitute='') if not soup: return None @@ -252,7 +252,7 @@ class BGExtract(RegistryProperties): """ log.debug('BGExtract.get_books_from_http("%s")', version) url_params = urllib.parse.urlencode({'action': 'getVersionInfo', 'vid': '%s' % version}) - reference_url = 'http://www.biblegateway.com/versions/?%s#books' % url_params + reference_url = 'http://legacy.biblegateway.com/versions/?%s#books' % url_params page = get_web_page(reference_url) if not page: send_error_message('download') From b4be73b616bad50b9fe732cddd45b092cdbaa1d6 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Thu, 26 Jun 2014 11:21:46 +0200 Subject: [PATCH 195/212] Fix bzr tags test --- tests/utils/test_bzr_tags.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/utils/test_bzr_tags.py b/tests/utils/test_bzr_tags.py index 393f4ce25..acadbd8c4 100644 --- a/tests/utils/test_bzr_tags.py +++ b/tests/utils/test_bzr_tags.py @@ -50,10 +50,6 @@ TAGS = [ ['1.9.11', '2039'], ['1.9.12', '2063'], ['2.0', '2118'], - ['2.0.1', '?'], - ['2.0.2', '?'], - ['2.0.3', '?'], - ['2.0.4', '?'], ['2.1.0', '2119'] ] From 7364d4962f7b70aa9f2536a2c347269e402a9a29 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Mon, 30 Jun 2014 09:18:26 +0200 Subject: [PATCH 196/212] Small fixes --- openlp/plugins/songs/lib/worshipassistantimport.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openlp/plugins/songs/lib/worshipassistantimport.py b/openlp/plugins/songs/lib/worshipassistantimport.py index 6831ec450..772fe8622 100644 --- a/openlp/plugins/songs/lib/worshipassistantimport.py +++ b/openlp/plugins/songs/lib/worshipassistantimport.py @@ -93,7 +93,6 @@ class WorshipAssistantImport(SongImport): details = chardet.detect(detect_content) detect_file.close() songs_file = open(self.import_source, 'r', encoding=details['encoding']) - songs_reader = csv.DictReader(songs_file) try: records = list(songs_reader) @@ -109,7 +108,7 @@ class WorshipAssistantImport(SongImport): if self.stop_import_flag: return # Ensure that all keys are uppercase - record = dict((k.upper(), v) for k, v in record.items()) + record = dict((field.upper(), value) for field, value in record.items()) # The CSV file has a line in the middle of the file where the headers are repeated. # We need to skip this line. if record['TITLE'] == "TITLE" and record['AUTHOR'] == 'AUTHOR' and record['LYRICS2'] == 'LYRICS2': From 7d2f8fa6cd02537fdf961c1c53da17b3bf75c603 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Tue, 1 Jul 2014 22:10:26 +0100 Subject: [PATCH 197/212] Fix setup.py --- openlp/.version | 2 +- openlp/core/utils/__init__.py | 4 ++-- resources/openlp.desktop | 6 ------ setup.py | 6 +++--- 4 files changed, 6 insertions(+), 12 deletions(-) diff --git a/openlp/.version b/openlp/.version index 3245d0c77..406870ece 100644 --- a/openlp/.version +++ b/openlp/.version @@ -1 +1 @@ -2.1.0-bzr2234 +2.1.0-bzr2389 diff --git a/openlp/core/utils/__init__.py b/openlp/core/utils/__init__.py index add663132..9b024eb84 100644 --- a/openlp/core/utils/__init__.py +++ b/openlp/core/utils/__init__.py @@ -149,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.decode('utf-8') else: - full_version = '%s-bzr%s' % (tag_version, tree_revision) + full_version = '%s-bzr%s' % (tag_version.decode('utf-8'), tree_revision.decode('utf-8')) else: # We're not running the development version, let's use the file. filepath = AppLocation.get_directory(AppLocation.VersionDir) diff --git a/resources/openlp.desktop b/resources/openlp.desktop index b17cbaf96..d585f4022 100755 --- a/resources/openlp.desktop +++ b/resources/openlp.desktop @@ -1,7 +1,5 @@ [Desktop Entry] Categories=AudioVideo; -Comment[de]= -Comment= Exec=openlp %F GenericName[de]=Church lyrics projection GenericName=Church lyrics projection @@ -9,11 +7,7 @@ Icon=openlp MimeType=application/x-openlp-service; Name[de]=OpenLP Name=OpenLP -Path= StartupNotify=true Terminal=false Type=Application -X-DBUS-ServiceName= -X-DBUS-StartupType= X-KDE-SubstituteUID=false -X-KDE-Username= diff --git a/setup.py b/setup.py index fc4a6f89b..fbf53d6d5 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.decode('utf-8') else: - version_string = '%s-bzr%s' % (tag_version, tree_revision) + version_string = '%s-bzr%s' % (tag_version.decode('utf-8'), tree_revision.decode('utf-8')) ver_file = open(VERSION_FILE, 'w') ver_file.write(version_string) except: @@ -152,7 +152,7 @@ using a computer and a data projector.""", 'Operating System :: POSIX :: BSD :: FreeBSD', 'Operating System :: POSIX :: Linux', 'Programming Language :: Python', - 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 3', 'Topic :: Desktop Environment :: Gnome', 'Topic :: Desktop Environment :: K Desktop Environment (KDE)', 'Topic :: Multimedia', From b3041a528764b7cdda0ce7fbfec7ee1f8f1c1135 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Tue, 1 Jul 2014 22:11:13 +0100 Subject: [PATCH 198/212] Fix version --- openlp/.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/.version b/openlp/.version index 406870ece..3245d0c77 100644 --- a/openlp/.version +++ b/openlp/.version @@ -1 +1 @@ -2.1.0-bzr2389 +2.1.0-bzr2234 From f649f0af3c3c252f4b0c8d310170f8a7ad27f352 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Wed, 2 Jul 2014 19:13:23 +0100 Subject: [PATCH 199/212] Fix initialisation --- openlp/core/lib/renderer.py | 1 - openlp/core/ui/maindisplay.py | 5 +---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/openlp/core/lib/renderer.py b/openlp/core/lib/renderer.py index 71a1f6058..ab4a5a4df 100644 --- a/openlp/core/lib/renderer.py +++ b/openlp/core/lib/renderer.py @@ -59,7 +59,6 @@ class Renderer(OpenLPMixin, RegistryMixin, RegistryProperties): """ super(Renderer, self).__init__(None) # Need live behaviour if this is also working as a pseudo MainDisplay. - self.is_live = True self.screens = ScreenList() self.theme_level = ThemeLevel.Global self.global_theme_name = '' diff --git a/openlp/core/ui/maindisplay.py b/openlp/core/ui/maindisplay.py index 946299aca..5c905c972 100644 --- a/openlp/core/ui/maindisplay.py +++ b/openlp/core/ui/maindisplay.py @@ -66,11 +66,8 @@ class Display(QtGui.QGraphicsView): if hasattr(parent, 'is_live') and parent.is_live: self.is_live = True if self.is_live: - super(Display, self).__init__() - # Overwrite the parent() method. self.parent = lambda: parent - else: - super(Display, self).__init__(parent) + super(Display, self).__init__() self.controller = parent self.screen = {} # FIXME: On Mac OS X (tested on 10.7) the display screen is corrupt with From 4ac921a2e7292f27f837dc264b31436c16bebc1b Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Wed, 2 Jul 2014 19:38:10 +0100 Subject: [PATCH 200/212] tests --- .../openlp_core_common/test_registrymixin.py | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 tests/functional/openlp_core_common/test_registrymixin.py diff --git a/tests/functional/openlp_core_common/test_registrymixin.py b/tests/functional/openlp_core_common/test_registrymixin.py new file mode 100644 index 000000000..75e134d5a --- /dev/null +++ b/tests/functional/openlp_core_common/test_registrymixin.py @@ -0,0 +1,76 @@ +# -*- 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.common package. +""" +import os +from unittest import TestCase + +from openlp.core.common import RegistryMixin, Registry + +TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '../', '..', 'resources')) + + +class TestRegistryMixin(TestCase): + + def registry_mixin_missing_test(self): + """ + Test the registry creation and its usage + """ + # GIVEN: A new registry + Registry.create() + + # WHEN: I create a new class + mock_1 = Test1() + + # THEN: The following methods are missing + self.assertEqual(len(Registry().functions_list), 0), 'The function should not be in the dict anymore.' + + def registry_mixin_present_test(self): + """ + Test the registry creation and its usage + """ + # GIVEN: A new registry + Registry.create() + + # WHEN: I create a new class + mock_2 = Test2() + + # THEN: The following bootstrap methods should be present + self.assertEqual(len(Registry().functions_list), 2), 'The bootstrap functions should be in the dict.' + + +class Test1(object): + def __init__(self): + pass + + +class Test2(RegistryMixin): + def __init__(self): + super(Test2, self).__init__(None) \ No newline at end of file From f71c4cc7af7e5544c5e021aff75daaa2ca1691a1 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Wed, 2 Jul 2014 19:58:52 +0100 Subject: [PATCH 201/212] remove nonvalid test --- tests/functional/openlp_core_lib/test_renderer.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/tests/functional/openlp_core_lib/test_renderer.py b/tests/functional/openlp_core_lib/test_renderer.py index 8814a21a0..8df4816bb 100644 --- a/tests/functional/openlp_core_lib/test_renderer.py +++ b/tests/functional/openlp_core_lib/test_renderer.py @@ -65,16 +65,6 @@ class TestRenderer(TestCase): """ del self.screens - def initial_renderer_test(self): - """ - Test the initial renderer state - """ - # GIVEN: A new renderer instance. - renderer = Renderer() - # WHEN: the default renderer is built. - # THEN: The renderer should be a live controller. - self.assertEqual(renderer.is_live, True, 'The base renderer should be a live controller') - def default_screen_layout_test(self): """ Test the default layout calculations From 3f889e08d4ea72ae940bcb5c174d93c6e423f0a8 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Wed, 2 Jul 2014 20:34:28 +0100 Subject: [PATCH 202/212] Fix setup.py --- setup.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/setup.py b/setup.py index fbf53d6d5..28f3658f1 100755 --- a/setup.py +++ b/setup.py @@ -105,6 +105,8 @@ try: tag_version, tag_revision = tags[-1].split() # 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. + tree_revision = tree_revision.strip() + tag_revision = tag_revision.strip() if tree_revision == tag_revision: version_string = tag_version.decode('utf-8') else: From 6847ee2e37e4f4db3a514eed3ad85ed113bf4b9d Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Wed, 2 Jul 2014 20:47:54 +0100 Subject: [PATCH 203/212] remove bl --- tests/functional/openlp_core_common/test_registrymixin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/openlp_core_common/test_registrymixin.py b/tests/functional/openlp_core_common/test_registrymixin.py index 75e134d5a..800445637 100644 --- a/tests/functional/openlp_core_common/test_registrymixin.py +++ b/tests/functional/openlp_core_common/test_registrymixin.py @@ -73,4 +73,4 @@ class Test1(object): class Test2(RegistryMixin): def __init__(self): - super(Test2, self).__init__(None) \ No newline at end of file + super(Test2, self).__init__(None) From 12c8663f8e2e6e151d0d5ec1f397fc893822ce64 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Wed, 2 Jul 2014 20:52:26 +0100 Subject: [PATCH 204/212] indent --- tests/functional/openlp_core_common/test_registrymixin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/openlp_core_common/test_registrymixin.py b/tests/functional/openlp_core_common/test_registrymixin.py index 800445637..d8636ac94 100644 --- a/tests/functional/openlp_core_common/test_registrymixin.py +++ b/tests/functional/openlp_core_common/test_registrymixin.py @@ -73,4 +73,4 @@ class Test1(object): class Test2(RegistryMixin): def __init__(self): - super(Test2, self).__init__(None) + super(Test2, self).__init__(None) From 79c7c583988c1f19f9183877a6fad6f635d15f58 Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Thu, 3 Jul 2014 13:21:12 +0200 Subject: [PATCH 205/212] Added test for powerpointcontroller --- .../presentations/lib/powerpointcontroller.py | 50 ++++--- .../test_powerpointcontroller.py | 131 ++++++++++++++++++ 2 files changed, 158 insertions(+), 23 deletions(-) create mode 100644 tests/functional/openlp_plugins/presentations/test_powerpointcontroller.py diff --git a/openlp/plugins/presentations/lib/powerpointcontroller.py b/openlp/plugins/presentations/lib/powerpointcontroller.py index 05d25b98e..0f9c2ff35 100644 --- a/openlp/plugins/presentations/lib/powerpointcontroller.py +++ b/openlp/plugins/presentations/lib/powerpointcontroller.py @@ -40,7 +40,8 @@ if os.name == 'nt': import pywintypes from openlp.core.lib import ScreenList -from openlp.core.lib.ui import UiStrings, critical_error_message_box +from openlp.core.lib.ui import UiStrings, critical_error_message_box, translate +from openlp.core.common import trace_error_handler from .presentationcontroller import PresentationController, PresentationDocument @@ -139,10 +140,11 @@ class PowerpointDocument(PresentationDocument): self.presentation.Application.WindowState = 2 except: log.error('Failed to minimize main powerpoint window') + trace_error_handler(log) return True - except pywintypes.com_error as e: + except pywintypes.com_error: log.error('PPT open failed') - log.error(e) + trace_error_handler(log) return False def create_thumbnails(self): @@ -225,9 +227,9 @@ class PowerpointDocument(PresentationDocument): self.presentation.SlideShowWindow.View.GotoSlide(slide) if click: self.presentation.SlideShowWindow.View.GotoClick(click) - except pywintypes.com_error as e: + except pywintypes.com_error: log.error('COM error while in unblank_screen') - log.error(e) + trace_error_handler(log) self.show_error_msg() def blank_screen(self): @@ -237,9 +239,9 @@ class PowerpointDocument(PresentationDocument): log.debug('blank_screen') try: self.presentation.SlideShowWindow.View.State = 3 - except pywintypes.com_error as e: + except pywintypes.com_error: log.error('COM error while in blank_screen') - log.error(e) + trace_error_handler(log) self.show_error_msg() def is_blank(self): @@ -250,9 +252,9 @@ class PowerpointDocument(PresentationDocument): if self.is_active(): try: return self.presentation.SlideShowWindow.View.State == 3 - except pywintypes.com_error as e: + except pywintypes.com_error: log.error('COM error while in is_blank') - log.error(e) + trace_error_handler(log) self.show_error_msg() else: return False @@ -264,9 +266,9 @@ class PowerpointDocument(PresentationDocument): log.debug('stop_presentation') try: self.presentation.SlideShowWindow.View.Exit() - except pywintypes.com_error as e: + except pywintypes.com_error: log.error('COM error while in stop_presentation') - log.error(e) + trace_error_handler(log) self.show_error_msg() if os.name == 'nt': @@ -301,6 +303,7 @@ class PowerpointDocument(PresentationDocument): self.presentation.Application.WindowState = 2 except: log.error('Failed to minimize main powerpoint window') + trace_error_handler(log) def get_slide_number(self): """ @@ -310,9 +313,9 @@ class PowerpointDocument(PresentationDocument): ret = 0 try: ret = self.presentation.SlideShowWindow.View.CurrentShowPosition - except pywintypes.com_error as e: + except pywintypes.com_error: log.error('COM error while in get_slide_number') - log.error(e) + trace_error_handler(log) self.show_error_msg() return ret @@ -324,9 +327,9 @@ class PowerpointDocument(PresentationDocument): ret = 0 try: ret = self.presentation.Slides.Count - except pywintypes.com_error as e: + except pywintypes.com_error: log.error('COM error while in get_slide_count') - log.error(e) + trace_error_handler(log) self.show_error_msg() return ret @@ -339,9 +342,9 @@ class PowerpointDocument(PresentationDocument): log.debug('goto_slide') try: self.presentation.SlideShowWindow.View.GotoSlide(slide_no) - except pywintypes.com_error as e: + except pywintypes.com_error: log.error('COM error while in goto_slide') - log.error(e) + trace_error_handler(log) self.show_error_msg() def next_step(self): @@ -351,9 +354,9 @@ class PowerpointDocument(PresentationDocument): log.debug('next_step') try: self.presentation.SlideShowWindow.View.Next() - except pywintypes.com_error as e: + except pywintypes.com_error: log.error('COM error while in next_step') - log.error(e) + trace_error_handler(log) self.show_error_msg() return if self.get_slide_number() > self.get_slide_count(): @@ -366,9 +369,9 @@ class PowerpointDocument(PresentationDocument): log.debug('previous_step') try: self.presentation.SlideShowWindow.View.Previous() - except pywintypes.com_error as e: + except pywintypes.com_error: log.error('COM error while in previous_step') - log.error(e) + trace_error_handler(log) self.show_error_msg() def get_slide_text(self, slide_no): @@ -392,10 +395,11 @@ class PowerpointDocument(PresentationDocument): Stop presentation and display an error message. """ self.stop_presentation() - critical_error_message_box(UiStrings().Error, translate('PresentationPlugin.PowerpointDocument', + critical_error_message_box(UiStrings().Error, translate('PresentationPlugin.PowerpointDocument', 'An error occurred in the Powerpoint integration ' 'and the presentation will be stopped. ' - 'Relstart the presentation if you wish to present it.')) + 'Restart the presentation if you wish to present it.')) + def _get_text_from_shapes(shapes): """ diff --git a/tests/functional/openlp_plugins/presentations/test_powerpointcontroller.py b/tests/functional/openlp_plugins/presentations/test_powerpointcontroller.py new file mode 100644 index 000000000..da58ef880 --- /dev/null +++ b/tests/functional/openlp_plugins/presentations/test_powerpointcontroller.py @@ -0,0 +1,131 @@ +# -*- 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 PowerPointController class and related methods. +""" +import os +if os.name == 'nt': + import pywintypes +import shutil +from unittest import TestCase +from tempfile import mkdtemp + +from tests.functional import patch, MagicMock +from tests.helpers.testmixin import TestMixin + +from openlp.plugins.presentations.lib.powerpointcontroller import PowerpointController, PowerpointDocument + + +class TestPowerpointController(TestCase, TestMixin): + """ + Test the PowerpointController Class + """ + + 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 PowerpointController + """ + # GIVEN: No presentation controller + controller = None + + # WHEN: The presentation controller object is created + controller = PowerpointController(plugin=self.mock_plugin) + + # THEN: The name of the presentation controller should be correct + self.assertEqual('Powerpoint', controller.name, + 'The name of the presentation controller should be correct') + + +class TestPowerpointDocument(TestCase): + """ + Test the PowerpointDocument Class + """ + + def setUp(self): + """ + Set up the patches and mocks need for all tests. + """ + self.powerpoint_document_stop_presentation_patcher = patch( + 'openlp.plugins.presentations.lib.powerpointcontroller.PowerpointDocument.stop_presentation') + self.presentation_document_get_temp_folder_patcher = patch( + 'openlp.plugins.presentations.lib.powerpointcontroller.PresentationDocument.get_temp_folder') + self.presentation_document_setup_patcher = patch( + 'openlp.plugins.presentations.lib.powerpointcontroller.PresentationDocument._setup') + self.mock_powerpoint_document_stop_presentation = self.powerpoint_document_stop_presentation_patcher.start() + self.mock_presentation_document_get_temp_folder = self.presentation_document_get_temp_folder_patcher.start() + self.mock_presentation_document_setup = self.presentation_document_setup_patcher.start() + self.mock_controller = MagicMock() + self.mock_presentation = MagicMock() + self.mock_presentation_document_get_temp_folder.return_value = 'temp folder' + + def tearDown(self): + """ + Stop the patches + """ + self.powerpoint_document_stop_presentation_patcher.stop() + self.presentation_document_get_temp_folder_patcher.stop() + self.presentation_document_setup_patcher.stop() + + def show_error_msg_test(self): + """ + Test the PowerpointDocument.show_error_msg() method gets called on com exception + """ + if os.name == 'nt': + # GIVEN: A PowerpointDocument with mocked controller and presentation + with patch('openlp.plugins.presentations.lib.powerpointcontroller.critical_error_message_box') as \ + mocked_critical_error_message_box: + instance = PowerpointDocument(self.mock_controller, self.mock_presentation) + instance.presentation = MagicMock() + instance.presentation.SlideShowWindow.View.GotoSlide = MagicMock(side_effect=pywintypes.com_error('1')) + + # WHEN: Calling goto_slide which will throw an exception + instance.goto_slide(42) + + # THEN: mocked_critical_error_message_box should have been called + mocked_critical_error_message_box.assert_called_with('Error', 'An error occurred in the Powerpoint ' + 'integration and the presentation will be stopped.' + ' Restart the presentation if you wish to ' + 'present it.') From d59be6ca822e8e1b4b64f62882e4c497f0954350 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Thu, 3 Jul 2014 18:54:51 +0200 Subject: [PATCH 206/212] Move song import plugins to subfolder --- openlp/plugins/songs/forms/editsongform.py | 2 +- .../plugins/songs/forms/songreviewwidget.py | 2 +- openlp/plugins/songs/lib/importer.py | 34 ++++++------ openlp/plugins/songs/lib/mediaitem.py | 2 +- openlp/plugins/songs/lib/openlyricsexport.py | 2 +- .../songs/lib/{xml.py => openlyricsxml.py} | 0 .../plugins/songs/lib/songimport/__init__.py | 31 +++++++++++ .../lib/{ => songimport}/cclifileimport.py | 0 .../lib/{ => songimport}/dreambeamimport.py | 2 +- .../lib/{ => songimport}/easyslidesimport.py | 2 +- .../songs/lib/{ => songimport}/ewimport.py | 0 .../{ => songimport}/foilpresenterimport.py | 4 +- .../lib/{ => songimport}/mediashoutimport.py | 2 +- .../songs/lib/{ => songimport}/olpimport.py | 0 .../songs/lib/{ => songimport}/oooimport.py | 0 .../lib/{ => songimport}/openlyricsimport.py | 4 +- .../lib/{ => songimport}/opensongimport.py | 2 +- .../lib/{ => songimport}/powersongimport.py | 2 +- .../{ => songimport}/propresenterimport.py | 0 .../songs/lib/{ => songimport}/sofimport.py | 0 .../lib/{ => songimport}/songbeamerimport.py | 2 +- .../songs/lib/{ => songimport}/songimport.py | 2 +- .../lib/{ => songimport}/songproimport.py | 2 +- .../{ => songimport}/songshowplusimport.py | 2 +- .../lib/{ => songimport}/sundayplusimport.py | 2 +- .../worshipassistantimport.py | 2 +- .../worshipcenterproimport.py | 2 +- .../songs/lib/{ => songimport}/wowimport.py | 2 +- .../lib/{ => songimport}/zionworximport.py | 2 +- openlp/plugins/songs/lib/songselect.py | 2 +- openlp/plugins/songs/songsplugin.py | 2 +- .../openlp_plugins/songs/test_ewimport.py | 52 +++++++++---------- .../songs/test_foilpresenterimport.py | 30 +++++------ .../songs/test_openlyricsimport.py | 6 +-- .../songs/test_opensongimport.py | 10 ++-- .../songs/test_songbeamerimport.py | 10 ++-- .../songs/test_songshowplusimport.py | 12 ++--- .../songs/test_worshipcenterproimport.py | 4 +- .../songs/test_zionworximport.py | 6 +-- tests/helpers/songfileimport.py | 12 ++--- 40 files changed, 143 insertions(+), 112 deletions(-) rename openlp/plugins/songs/lib/{xml.py => openlyricsxml.py} (100%) create mode 100644 openlp/plugins/songs/lib/songimport/__init__.py rename openlp/plugins/songs/lib/{ => songimport}/cclifileimport.py (100%) rename openlp/plugins/songs/lib/{ => songimport}/dreambeamimport.py (99%) rename openlp/plugins/songs/lib/{ => songimport}/easyslidesimport.py (99%) rename openlp/plugins/songs/lib/{ => songimport}/ewimport.py (100%) rename openlp/plugins/songs/lib/{ => songimport}/foilpresenterimport.py (99%) rename openlp/plugins/songs/lib/{ => songimport}/mediashoutimport.py (98%) rename openlp/plugins/songs/lib/{ => songimport}/olpimport.py (100%) rename openlp/plugins/songs/lib/{ => songimport}/oooimport.py (100%) rename openlp/plugins/songs/lib/{ => songimport}/openlyricsimport.py (96%) rename openlp/plugins/songs/lib/{ => songimport}/opensongimport.py (99%) rename openlp/plugins/songs/lib/{ => songimport}/powersongimport.py (99%) rename openlp/plugins/songs/lib/{ => songimport}/propresenterimport.py (100%) rename openlp/plugins/songs/lib/{ => songimport}/sofimport.py (100%) rename openlp/plugins/songs/lib/{ => songimport}/songbeamerimport.py (99%) rename openlp/plugins/songs/lib/{ => songimport}/songimport.py (99%) rename openlp/plugins/songs/lib/{ => songimport}/songproimport.py (98%) rename openlp/plugins/songs/lib/{ => songimport}/songshowplusimport.py (99%) rename openlp/plugins/songs/lib/{ => songimport}/sundayplusimport.py (99%) rename openlp/plugins/songs/lib/{ => songimport}/worshipassistantimport.py (99%) rename openlp/plugins/songs/lib/{ => songimport}/worshipcenterproimport.py (98%) rename openlp/plugins/songs/lib/{ => songimport}/wowimport.py (99%) rename openlp/plugins/songs/lib/{ => songimport}/zionworximport.py (98%) diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index 2125922fe..6b5ccb041 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -44,7 +44,7 @@ from openlp.core.lib.ui import set_case_insensitive_completer, critical_error_me from openlp.plugins.songs.lib import VerseType, clean_song from openlp.plugins.songs.lib.db import Book, Song, Author, AuthorType, Topic, MediaFile from openlp.plugins.songs.lib.ui import SongStrings -from openlp.plugins.songs.lib.xml import SongXML +from openlp.plugins.songs.lib.openlyricsxml import SongXML from openlp.plugins.songs.forms.editsongdialog import Ui_EditSongDialog from openlp.plugins.songs.forms.editverseform import EditVerseForm from openlp.plugins.songs.forms.mediafilesform import MediaFilesForm diff --git a/openlp/plugins/songs/forms/songreviewwidget.py b/openlp/plugins/songs/forms/songreviewwidget.py index 02d7b8774..7f5f9c0c6 100644 --- a/openlp/plugins/songs/forms/songreviewwidget.py +++ b/openlp/plugins/songs/forms/songreviewwidget.py @@ -33,7 +33,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import build_icon from openlp.plugins.songs.lib import VerseType -from openlp.plugins.songs.lib.xml import SongXML +from openlp.plugins.songs.lib.openlyricsxml import SongXML class SongReviewWidget(QtGui.QWidget): diff --git a/openlp/plugins/songs/lib/importer.py b/openlp/plugins/songs/lib/importer.py index 6d01da309..9c0b889ca 100644 --- a/openlp/plugins/songs/lib/importer.py +++ b/openlp/plugins/songs/lib/importer.py @@ -34,23 +34,23 @@ import logging from openlp.core.common import translate, UiStrings from openlp.core.ui.wizard import WizardStrings -from .opensongimport import OpenSongImport -from .easyslidesimport import EasySlidesImport -from .olpimport import OpenLPSongImport -from .openlyricsimport import OpenLyricsImport -from .wowimport import WowImport -from .cclifileimport import CCLIFileImport -from .dreambeamimport import DreamBeamImport -from .powersongimport import PowerSongImport -from .ewimport import EasyWorshipSongImport -from .songbeamerimport import SongBeamerImport -from .songshowplusimport import SongShowPlusImport -from .songproimport import SongProImport -from .sundayplusimport import SundayPlusImport -from .foilpresenterimport import FoilPresenterImport -from .zionworximport import ZionWorxImport -from .propresenterimport import ProPresenterImport -from .worshipassistantimport import WorshipAssistantImport +from .songimport.opensongimport import OpenSongImport +from .songimport.easyslidesimport import EasySlidesImport +from .songimport.olpimport import OpenLPSongImport +from .songimport.openlyricsimport import OpenLyricsImport +from .songimport.wowimport import WowImport +from .songimport.cclifileimport import CCLIFileImport +from .songimport.dreambeamimport import DreamBeamImport +from .songimport.powersongimport import PowerSongImport +from .songimport.ewimport import EasyWorshipSongImport +from .songimport.songbeamerimport import SongBeamerImport +from .songimport.songshowplusimport import SongShowPlusImport +from .songimport.songproimport import SongProImport +from .songimport.sundayplusimport import SundayPlusImport +from .songimport.foilpresenterimport import FoilPresenterImport +from .songimport.zionworximport import ZionWorxImport +from .songimport.propresenterimport import ProPresenterImport +from .songimport.worshipassistantimport import WorshipAssistantImport # Imports that might fail diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index d57c8fbcc..fde103c5f 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -46,7 +46,7 @@ from openlp.plugins.songs.forms.songexportform import SongExportForm from openlp.plugins.songs.lib import VerseType, clean_string, delete_song from openlp.plugins.songs.lib.db import Author, AuthorType, Song, Book, MediaFile from openlp.plugins.songs.lib.ui import SongStrings -from openlp.plugins.songs.lib.xml import OpenLyrics, SongXML +from openlp.plugins.songs.lib.openlyricsxml import OpenLyrics, SongXML log = logging.getLogger(__name__) diff --git a/openlp/plugins/songs/lib/openlyricsexport.py b/openlp/plugins/songs/lib/openlyricsexport.py index 72210e89f..0458b893b 100644 --- a/openlp/plugins/songs/lib/openlyricsexport.py +++ b/openlp/plugins/songs/lib/openlyricsexport.py @@ -37,7 +37,7 @@ from lxml import etree from openlp.core.common import RegistryProperties, check_directory_exists, translate from openlp.core.utils import clean_filename -from openlp.plugins.songs.lib.xml import OpenLyrics +from openlp.plugins.songs.lib.openlyricsxml import OpenLyrics log = logging.getLogger(__name__) diff --git a/openlp/plugins/songs/lib/xml.py b/openlp/plugins/songs/lib/openlyricsxml.py similarity index 100% rename from openlp/plugins/songs/lib/xml.py rename to openlp/plugins/songs/lib/openlyricsxml.py diff --git a/openlp/plugins/songs/lib/songimport/__init__.py b/openlp/plugins/songs/lib/songimport/__init__.py new file mode 100644 index 000000000..f57136ecf --- /dev/null +++ b/openlp/plugins/songs/lib/songimport/__init__.py @@ -0,0 +1,31 @@ +# -*- 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 # +############################################################################### +""" +The :mod:`~openlp.plugins.songs.lib.import` module contains a importers for the Songs plugin. +""" diff --git a/openlp/plugins/songs/lib/cclifileimport.py b/openlp/plugins/songs/lib/songimport/cclifileimport.py similarity index 100% rename from openlp/plugins/songs/lib/cclifileimport.py rename to openlp/plugins/songs/lib/songimport/cclifileimport.py diff --git a/openlp/plugins/songs/lib/dreambeamimport.py b/openlp/plugins/songs/lib/songimport/dreambeamimport.py similarity index 99% rename from openlp/plugins/songs/lib/dreambeamimport.py rename to openlp/plugins/songs/lib/songimport/dreambeamimport.py index 375867aac..e5afd65f4 100644 --- a/openlp/plugins/songs/lib/dreambeamimport.py +++ b/openlp/plugins/songs/lib/songimport/dreambeamimport.py @@ -35,7 +35,7 @@ import logging from lxml import etree, objectify from openlp.core.lib import translate -from openlp.plugins.songs.lib.songimport import SongImport +from openlp.plugins.songs.lib.songimport.songimport import SongImport from openlp.plugins.songs.lib.ui import SongStrings log = logging.getLogger(__name__) diff --git a/openlp/plugins/songs/lib/easyslidesimport.py b/openlp/plugins/songs/lib/songimport/easyslidesimport.py similarity index 99% rename from openlp/plugins/songs/lib/easyslidesimport.py rename to openlp/plugins/songs/lib/songimport/easyslidesimport.py index ca9a9b755..f30ae49e5 100644 --- a/openlp/plugins/songs/lib/easyslidesimport.py +++ b/openlp/plugins/songs/lib/songimport/easyslidesimport.py @@ -33,7 +33,7 @@ import re from lxml import etree, objectify from openlp.plugins.songs.lib import VerseType -from openlp.plugins.songs.lib.songimport import SongImport +from openlp.plugins.songs.lib.songimport.songimport import SongImport log = logging.getLogger(__name__) diff --git a/openlp/plugins/songs/lib/ewimport.py b/openlp/plugins/songs/lib/songimport/ewimport.py similarity index 100% rename from openlp/plugins/songs/lib/ewimport.py rename to openlp/plugins/songs/lib/songimport/ewimport.py diff --git a/openlp/plugins/songs/lib/foilpresenterimport.py b/openlp/plugins/songs/lib/songimport/foilpresenterimport.py similarity index 99% rename from openlp/plugins/songs/lib/foilpresenterimport.py rename to openlp/plugins/songs/lib/songimport/foilpresenterimport.py index 2b31718c2..67911de76 100644 --- a/openlp/plugins/songs/lib/foilpresenterimport.py +++ b/openlp/plugins/songs/lib/songimport/foilpresenterimport.py @@ -99,10 +99,10 @@ from lxml import etree, objectify from openlp.core.lib import translate from openlp.core.ui.wizard import WizardStrings from openlp.plugins.songs.lib import clean_song, VerseType -from openlp.plugins.songs.lib.songimport import SongImport +from openlp.plugins.songs.lib.songimport.songimport import SongImport from openlp.plugins.songs.lib.db import Author, Book, Song, Topic from openlp.plugins.songs.lib.ui import SongStrings -from openlp.plugins.songs.lib.xml import SongXML +from openlp.plugins.songs.lib.openlyricsxml import SongXML log = logging.getLogger(__name__) diff --git a/openlp/plugins/songs/lib/mediashoutimport.py b/openlp/plugins/songs/lib/songimport/mediashoutimport.py similarity index 98% rename from openlp/plugins/songs/lib/mediashoutimport.py rename to openlp/plugins/songs/lib/songimport/mediashoutimport.py index 99850e950..8eab61a96 100644 --- a/openlp/plugins/songs/lib/mediashoutimport.py +++ b/openlp/plugins/songs/lib/songimport/mediashoutimport.py @@ -33,7 +33,7 @@ a MediaShout database into the OpenLP database. import pyodbc from openlp.core.lib import translate -from openlp.plugins.songs.lib.songimport import SongImport +from openlp.plugins.songs.lib.songimport.songimport import SongImport VERSE_TAGS = ['V', 'C', 'B', 'O', 'P', 'I', 'E'] diff --git a/openlp/plugins/songs/lib/olpimport.py b/openlp/plugins/songs/lib/songimport/olpimport.py similarity index 100% rename from openlp/plugins/songs/lib/olpimport.py rename to openlp/plugins/songs/lib/songimport/olpimport.py diff --git a/openlp/plugins/songs/lib/oooimport.py b/openlp/plugins/songs/lib/songimport/oooimport.py similarity index 100% rename from openlp/plugins/songs/lib/oooimport.py rename to openlp/plugins/songs/lib/songimport/oooimport.py diff --git a/openlp/plugins/songs/lib/openlyricsimport.py b/openlp/plugins/songs/lib/songimport/openlyricsimport.py similarity index 96% rename from openlp/plugins/songs/lib/openlyricsimport.py rename to openlp/plugins/songs/lib/songimport/openlyricsimport.py index 031c5ba72..74329bfd8 100644 --- a/openlp/plugins/songs/lib/openlyricsimport.py +++ b/openlp/plugins/songs/lib/songimport/openlyricsimport.py @@ -37,9 +37,9 @@ import os from lxml import etree from openlp.core.ui.wizard import WizardStrings -from openlp.plugins.songs.lib.songimport import SongImport +from openlp.plugins.songs.lib.songimport.songimport import SongImport from openlp.plugins.songs.lib.ui import SongStrings -from openlp.plugins.songs.lib.xml import OpenLyrics, OpenLyricsError +from openlp.plugins.songs.lib.openlyricsxml import OpenLyrics, OpenLyricsError log = logging.getLogger(__name__) diff --git a/openlp/plugins/songs/lib/opensongimport.py b/openlp/plugins/songs/lib/songimport/opensongimport.py similarity index 99% rename from openlp/plugins/songs/lib/opensongimport.py rename to openlp/plugins/songs/lib/songimport/opensongimport.py index 3d9733dd8..a52b67b0f 100644 --- a/openlp/plugins/songs/lib/opensongimport.py +++ b/openlp/plugins/songs/lib/songimport/opensongimport.py @@ -35,7 +35,7 @@ from lxml.etree import Error, LxmlError from openlp.core.common import translate from openlp.plugins.songs.lib import VerseType -from openlp.plugins.songs.lib.songimport import SongImport +from openlp.plugins.songs.lib.songimport.songimport import SongImport from openlp.plugins.songs.lib.ui import SongStrings log = logging.getLogger(__name__) diff --git a/openlp/plugins/songs/lib/powersongimport.py b/openlp/plugins/songs/lib/songimport/powersongimport.py similarity index 99% rename from openlp/plugins/songs/lib/powersongimport.py rename to openlp/plugins/songs/lib/songimport/powersongimport.py index cd568bc2c..1cad5e33e 100644 --- a/openlp/plugins/songs/lib/powersongimport.py +++ b/openlp/plugins/songs/lib/songimport/powersongimport.py @@ -35,7 +35,7 @@ import fnmatch import os from openlp.core.common import translate -from openlp.plugins.songs.lib.songimport import SongImport +from openlp.plugins.songs.lib.songimport.songimport import SongImport log = logging.getLogger(__name__) diff --git a/openlp/plugins/songs/lib/propresenterimport.py b/openlp/plugins/songs/lib/songimport/propresenterimport.py similarity index 100% rename from openlp/plugins/songs/lib/propresenterimport.py rename to openlp/plugins/songs/lib/songimport/propresenterimport.py diff --git a/openlp/plugins/songs/lib/sofimport.py b/openlp/plugins/songs/lib/songimport/sofimport.py similarity index 100% rename from openlp/plugins/songs/lib/sofimport.py rename to openlp/plugins/songs/lib/songimport/sofimport.py diff --git a/openlp/plugins/songs/lib/songbeamerimport.py b/openlp/plugins/songs/lib/songimport/songbeamerimport.py similarity index 99% rename from openlp/plugins/songs/lib/songbeamerimport.py rename to openlp/plugins/songs/lib/songimport/songbeamerimport.py index 9c5e74c06..fbd016cda 100644 --- a/openlp/plugins/songs/lib/songbeamerimport.py +++ b/openlp/plugins/songs/lib/songimport/songbeamerimport.py @@ -36,7 +36,7 @@ import os import re from openlp.plugins.songs.lib import VerseType -from openlp.plugins.songs.lib.songimport import SongImport +from openlp.plugins.songs.lib.songimport.songimport import SongImport log = logging.getLogger(__name__) diff --git a/openlp/plugins/songs/lib/songimport.py b/openlp/plugins/songs/lib/songimport/songimport.py similarity index 99% rename from openlp/plugins/songs/lib/songimport.py rename to openlp/plugins/songs/lib/songimport/songimport.py index b8fcc604b..5382efbe5 100644 --- a/openlp/plugins/songs/lib/songimport.py +++ b/openlp/plugins/songs/lib/songimport/songimport.py @@ -39,7 +39,7 @@ from openlp.core.ui.wizard import WizardStrings from openlp.plugins.songs.lib import clean_song, VerseType from openlp.plugins.songs.lib.db import Song, Author, Topic, Book, MediaFile from openlp.plugins.songs.lib.ui import SongStrings -from openlp.plugins.songs.lib.xml import SongXML +from openlp.plugins.songs.lib.openlyricsxml import SongXML log = logging.getLogger(__name__) diff --git a/openlp/plugins/songs/lib/songproimport.py b/openlp/plugins/songs/lib/songimport/songproimport.py similarity index 98% rename from openlp/plugins/songs/lib/songproimport.py rename to openlp/plugins/songs/lib/songimport/songproimport.py index 86411a499..853a1d9eb 100644 --- a/openlp/plugins/songs/lib/songproimport.py +++ b/openlp/plugins/songs/lib/songimport/songproimport.py @@ -33,7 +33,7 @@ songs into the OpenLP database. import re from openlp.plugins.songs.lib import strip_rtf -from openlp.plugins.songs.lib.songimport import SongImport +from openlp.plugins.songs.lib.songimport.songimport import SongImport class SongProImport(SongImport): diff --git a/openlp/plugins/songs/lib/songshowplusimport.py b/openlp/plugins/songs/lib/songimport/songshowplusimport.py similarity index 99% rename from openlp/plugins/songs/lib/songshowplusimport.py rename to openlp/plugins/songs/lib/songimport/songshowplusimport.py index aebded029..352e55d96 100644 --- a/openlp/plugins/songs/lib/songshowplusimport.py +++ b/openlp/plugins/songs/lib/songimport/songshowplusimport.py @@ -38,7 +38,7 @@ import struct from openlp.core.ui.wizard import WizardStrings from openlp.plugins.songs.lib import VerseType, retrieve_windows_encoding -from openlp.plugins.songs.lib.songimport import SongImport +from openlp.plugins.songs.lib.songimport.songimport import SongImport TITLE = 1 AUTHOR = 2 diff --git a/openlp/plugins/songs/lib/sundayplusimport.py b/openlp/plugins/songs/lib/songimport/sundayplusimport.py similarity index 99% rename from openlp/plugins/songs/lib/sundayplusimport.py rename to openlp/plugins/songs/lib/songimport/sundayplusimport.py index 5c5f73047..fded3bca3 100644 --- a/openlp/plugins/songs/lib/sundayplusimport.py +++ b/openlp/plugins/songs/lib/songimport/sundayplusimport.py @@ -32,7 +32,7 @@ import re from openlp.plugins.songs.lib import VerseType, retrieve_windows_encoding from openlp.plugins.songs.lib import strip_rtf -from openlp.plugins.songs.lib.songimport import SongImport +from openlp.plugins.songs.lib.songimport.songimport import SongImport HOTKEY_TO_VERSE_TYPE = { '1': 'v1', diff --git a/openlp/plugins/songs/lib/worshipassistantimport.py b/openlp/plugins/songs/lib/songimport/worshipassistantimport.py similarity index 99% rename from openlp/plugins/songs/lib/worshipassistantimport.py rename to openlp/plugins/songs/lib/songimport/worshipassistantimport.py index 772fe8622..412a17c35 100644 --- a/openlp/plugins/songs/lib/worshipassistantimport.py +++ b/openlp/plugins/songs/lib/songimport/worshipassistantimport.py @@ -37,7 +37,7 @@ import re from openlp.core.common import translate from openlp.plugins.songs.lib import VerseType -from openlp.plugins.songs.lib.songimport import SongImport +from openlp.plugins.songs.lib.songimport.songimport import SongImport log = logging.getLogger(__name__) diff --git a/openlp/plugins/songs/lib/worshipcenterproimport.py b/openlp/plugins/songs/lib/songimport/worshipcenterproimport.py similarity index 98% rename from openlp/plugins/songs/lib/worshipcenterproimport.py rename to openlp/plugins/songs/lib/songimport/worshipcenterproimport.py index b24d2ae83..b6c32a8c0 100644 --- a/openlp/plugins/songs/lib/worshipcenterproimport.py +++ b/openlp/plugins/songs/lib/songimport/worshipcenterproimport.py @@ -35,7 +35,7 @@ import logging import pyodbc from openlp.core.common import translate -from openlp.plugins.songs.lib.songimport import SongImport +from openlp.plugins.songs.lib.songimport.songimport import SongImport log = logging.getLogger(__name__) diff --git a/openlp/plugins/songs/lib/wowimport.py b/openlp/plugins/songs/lib/songimport/wowimport.py similarity index 99% rename from openlp/plugins/songs/lib/wowimport.py rename to openlp/plugins/songs/lib/songimport/wowimport.py index c92b0ee2a..c135ca620 100644 --- a/openlp/plugins/songs/lib/wowimport.py +++ b/openlp/plugins/songs/lib/songimport/wowimport.py @@ -34,7 +34,7 @@ import os import logging from openlp.core.common import translate -from openlp.plugins.songs.lib.songimport import SongImport +from openlp.plugins.songs.lib.songimport.songimport import SongImport BLOCK_TYPES = ('V', 'C', 'B') diff --git a/openlp/plugins/songs/lib/zionworximport.py b/openlp/plugins/songs/lib/songimport/zionworximport.py similarity index 98% rename from openlp/plugins/songs/lib/zionworximport.py rename to openlp/plugins/songs/lib/songimport/zionworximport.py index dfdc2373d..8d939f244 100644 --- a/openlp/plugins/songs/lib/zionworximport.py +++ b/openlp/plugins/songs/lib/songimport/zionworximport.py @@ -34,7 +34,7 @@ import csv import logging from openlp.core.common import translate -from openlp.plugins.songs.lib.songimport import SongImport +from openlp.plugins.songs.lib.songimport.songimport import SongImport log = logging.getLogger(__name__) diff --git a/openlp/plugins/songs/lib/songselect.py b/openlp/plugins/songs/lib/songselect.py index 6fd084a47..61b02a66e 100644 --- a/openlp/plugins/songs/lib/songselect.py +++ b/openlp/plugins/songs/lib/songselect.py @@ -38,7 +38,7 @@ from html.parser import HTMLParser from bs4 import BeautifulSoup, NavigableString from openlp.plugins.songs.lib import Song, VerseType, clean_song, Author -from openlp.plugins.songs.lib.xml import SongXML +from openlp.plugins.songs.lib.openlyricsxml import SongXML USER_AGENT = 'Mozilla/5.0 (Linux; U; Android 4.0.3; en-us; GT-I9000 ' \ 'Build/IML74K) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 ' \ diff --git a/openlp/plugins/songs/songsplugin.py b/openlp/plugins/songs/songsplugin.py index 79fc282a6..c97e3835f 100644 --- a/openlp/plugins/songs/songsplugin.py +++ b/openlp/plugins/songs/songsplugin.py @@ -49,7 +49,7 @@ from openlp.plugins.songs.lib import clean_song, upgrade from openlp.plugins.songs.lib.db import init_schema, Song from openlp.plugins.songs.lib.mediaitem import SongSearch from openlp.plugins.songs.lib.importer import SongFormat -from openlp.plugins.songs.lib.olpimport import OpenLPSongImport +from openlp.plugins.songs.lib.songimport.olpimport import OpenLPSongImport from openlp.plugins.songs.lib.mediaitem import SongMediaItem from openlp.plugins.songs.lib.songstab import SongsTab diff --git a/tests/functional/openlp_plugins/songs/test_ewimport.py b/tests/functional/openlp_plugins/songs/test_ewimport.py index ea557711b..c28dc72aa 100644 --- a/tests/functional/openlp_plugins/songs/test_ewimport.py +++ b/tests/functional/openlp_plugins/songs/test_ewimport.py @@ -35,7 +35,7 @@ from unittest import TestCase from tests.functional import MagicMock, patch -from openlp.plugins.songs.lib.ewimport import EasyWorshipSongImport, FieldDescEntry, FieldType +from openlp.plugins.songs.lib.songimport.ewimport import EasyWorshipSongImport, FieldDescEntry, FieldType TEST_PATH = os.path.abspath( os.path.join(os.path.dirname(__file__), '..', '..', '..', 'resources', 'easyworshipsongs')) @@ -178,7 +178,7 @@ class TestEasyWorshipSongImport(TestCase): Test creating an instance of the EasyWorship file importer """ # GIVEN: A mocked out SongImport class, and a mocked out "manager" - with patch('openlp.plugins.songs.lib.ewimport.SongImport'): + with patch('openlp.plugins.songs.lib.songimport.ewimport.SongImport'): mocked_manager = MagicMock() # WHEN: An importer object is created @@ -192,7 +192,7 @@ class TestEasyWorshipSongImport(TestCase): Test finding an existing field in a given list using the :mod:`find_field` """ # GIVEN: A mocked out SongImport class, a mocked out "manager" and a list of field descriptions. - with patch('openlp.plugins.songs.lib.ewimport.SongImport'): + with patch('openlp.plugins.songs.lib.songimport.ewimport.SongImport'): mocked_manager = MagicMock() importer = EasyWorshipSongImport(mocked_manager, filenames=[]) importer.field_descriptions = TEST_FIELD_DESCS @@ -210,7 +210,7 @@ class TestEasyWorshipSongImport(TestCase): Test finding an non-existing field in a given list using the :mod:`find_field` """ # GIVEN: A mocked out SongImport class, a mocked out "manager" and a list of field descriptions - with patch('openlp.plugins.songs.lib.ewimport.SongImport'): + with patch('openlp.plugins.songs.lib.songimport.ewimport.SongImport'): mocked_manager = MagicMock() importer = EasyWorshipSongImport(mocked_manager, filenames=[]) importer.field_descriptions = TEST_FIELD_DESCS @@ -228,8 +228,8 @@ 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: + with patch('openlp.plugins.songs.lib.songimport.ewimport.SongImport'), \ + patch('openlp.plugins.songs.lib.songimport.ewimport.struct') as mocked_struct: mocked_manager = MagicMock() importer = EasyWorshipSongImport(mocked_manager, filenames=[]) @@ -246,7 +246,7 @@ class TestEasyWorshipSongImport(TestCase): Test the :mod:`get_field` module """ # 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'): + with patch('openlp.plugins.songs.lib.songimport.ewimport.SongImport'): mocked_manager = MagicMock() importer = EasyWorshipSongImport(mocked_manager, filenames=[]) importer.encoding = TEST_DATA_ENCODING @@ -269,7 +269,7 @@ class TestEasyWorshipSongImport(TestCase): """ for test_results in GET_MEMO_FIELD_TEST_RESULTS: # GIVEN: A mocked out SongImport class, a mocked out "manager", a mocked out memo_file and an encoding - with patch('openlp.plugins.songs.lib.ewimport.SongImport'): + with patch('openlp.plugins.songs.lib.songimport.ewimport.SongImport'): mocked_manager = MagicMock() mocked_memo_file = MagicMock() importer = EasyWorshipSongImport(mocked_manager, filenames=[]) @@ -300,8 +300,8 @@ class TestEasyWorshipSongImport(TestCase): Test the :mod:`do_import` module opens the correct files """ # 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: + with patch('openlp.plugins.songs.lib.songimport.ewimport.SongImport'), \ + patch('openlp.plugins.songs.lib.songimport.ewimport.os.path') as mocked_os_path: mocked_manager = MagicMock() importer = EasyWorshipSongImport(mocked_manager, filenames=[]) mocked_os_path.isfile.side_effect = [True, False] @@ -319,8 +319,8 @@ class TestEasyWorshipSongImport(TestCase): Test the :mod:`do_import` module produces an error when Songs.MB not found. """ # 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: + with patch('openlp.plugins.songs.lib.songimport.ewimport.SongImport'), \ + patch('openlp.plugins.songs.lib.songimport.ewimport.os.path') as mocked_os_path: mocked_manager = MagicMock() importer = EasyWorshipSongImport(mocked_manager, filenames=[]) importer.log_error = MagicMock() @@ -339,8 +339,8 @@ class TestEasyWorshipSongImport(TestCase): Test the :mod:`do_import` module handles invalid database files correctly """ # 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: + with patch('openlp.plugins.songs.lib.songimport.ewimport.SongImport'), \ + patch('openlp.plugins.songs.lib.songimport.ewimport.os.path') as mocked_os_path: mocked_manager = MagicMock() importer = EasyWorshipSongImport(mocked_manager, filenames=[]) mocked_os_path.isfile.return_value = True @@ -358,10 +358,10 @@ class TestEasyWorshipSongImport(TestCase): Test the :mod:`do_import` module handles invalid memo files correctly """ # 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, \ + with patch('openlp.plugins.songs.lib.songimport.ewimport.SongImport'), \ + patch('openlp.plugins.songs.lib.songimport.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.songimport.ewimport.struct') as mocked_struct: mocked_manager = MagicMock() importer = EasyWorshipSongImport(mocked_manager, filenames=[]) mocked_os_path.isfile.return_value = True @@ -385,10 +385,10 @@ class TestEasyWorshipSongImport(TestCase): Test the :mod:`do_import` converts the code page to the encoding correctly """ # 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('builtins.open'), patch('openlp.plugins.songs.lib.ewimport.struct') as mocked_struct, \ - patch('openlp.plugins.songs.lib.ewimport.retrieve_windows_encoding') as \ + with patch('openlp.plugins.songs.lib.songimport.ewimport.SongImport'), \ + patch('openlp.plugins.songs.lib.songimport.ewimport.os.path') as mocked_os_path, \ + patch('builtins.open'), patch('openlp.plugins.songs.lib.songimport.ewimport.struct') as mocked_struct, \ + patch('openlp.plugins.songs.lib.songimport.ewimport.retrieve_windows_encoding') as \ mocked_retrieve_windows_encoding: mocked_manager = MagicMock() importer = EasyWorshipSongImport(mocked_manager, filenames=[]) @@ -413,8 +413,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 \ + with patch('openlp.plugins.songs.lib.songimport.ewimport.SongImport'), \ + patch('openlp.plugins.songs.lib.songimport.ewimport.retrieve_windows_encoding') as \ mocked_retrieve_windows_encoding: mocked_retrieve_windows_encoding.return_value = 'cp1252' mocked_manager = MagicMock() @@ -469,8 +469,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') \ + with patch('openlp.plugins.songs.lib.songimport.ewimport.SongImport'), \ + patch('openlp.plugins.songs.lib.songimport.ewimport.retrieve_windows_encoding') \ as mocked_retrieve_windows_encoding: mocked_retrieve_windows_encoding.return_value = 'cp1252' mocked_manager = MagicMock() @@ -509,7 +509,7 @@ class TestEasyWorshipSongImport(TestCase): """ # GIVEN: A mocked out SongImport class, a mocked out "manager" and mocked out "author" method. - with patch('openlp.plugins.songs.lib.ewimport.SongImport'): + with patch('openlp.plugins.songs.lib.songimport.ewimport.SongImport'): mocked_manager = MagicMock() mocked_add_author = MagicMock() importer = EasyWorshipSongImportLogger(mocked_manager) diff --git a/tests/functional/openlp_plugins/songs/test_foilpresenterimport.py b/tests/functional/openlp_plugins/songs/test_foilpresenterimport.py index 61206b9fa..15247a361 100644 --- a/tests/functional/openlp_plugins/songs/test_foilpresenterimport.py +++ b/tests/functional/openlp_plugins/songs/test_foilpresenterimport.py @@ -34,7 +34,7 @@ import os from unittest import TestCase from tests.functional import patch, MagicMock -from openlp.plugins.songs.lib.foilpresenterimport import FoilPresenter +from openlp.plugins.songs.lib.songimport.foilpresenterimport import FoilPresenter TEST_PATH = os.path.abspath( os.path.join(os.path.dirname(__file__), '..', '..', '..', '/resources/foilpresentersongs')) @@ -57,27 +57,27 @@ class TestFoilPresenter(TestCase): # _process_topics def setUp(self): - self.child_patcher = patch('openlp.plugins.songs.lib.foilpresenterimport.FoilPresenter._child') - self.clean_song_patcher = patch('openlp.plugins.songs.lib.foilpresenterimport.clean_song') - self.objectify_patcher = patch('openlp.plugins.songs.lib.foilpresenterimport.objectify') + self.child_patcher = patch('openlp.plugins.songs.lib.songimport.foilpresenterimport.FoilPresenter._child') + self.clean_song_patcher = patch('openlp.plugins.songs.lib.songimport.foilpresenterimport.clean_song') + self.objectify_patcher = patch('openlp.plugins.songs.lib.songimport.foilpresenterimport.objectify') self.process_authors_patcher = \ - patch('openlp.plugins.songs.lib.foilpresenterimport.FoilPresenter._process_authors') + patch('openlp.plugins.songs.lib.songimport.foilpresenterimport.FoilPresenter._process_authors') self.process_cclinumber_patcher = \ - patch('openlp.plugins.songs.lib.foilpresenterimport.FoilPresenter._process_cclinumber') + patch('openlp.plugins.songs.lib.songimport.foilpresenterimport.FoilPresenter._process_cclinumber') self.process_comments_patcher = \ - patch('openlp.plugins.songs.lib.foilpresenterimport.FoilPresenter._process_comments') + patch('openlp.plugins.songs.lib.songimport.foilpresenterimport.FoilPresenter._process_comments') self.process_lyrics_patcher = \ - patch('openlp.plugins.songs.lib.foilpresenterimport.FoilPresenter._process_lyrics') + patch('openlp.plugins.songs.lib.songimport.foilpresenterimport.FoilPresenter._process_lyrics') self.process_songbooks_patcher = \ - patch('openlp.plugins.songs.lib.foilpresenterimport.FoilPresenter._process_songbooks') + patch('openlp.plugins.songs.lib.songimport.foilpresenterimport.FoilPresenter._process_songbooks') self.process_titles_patcher = \ - patch('openlp.plugins.songs.lib.foilpresenterimport.FoilPresenter._process_titles') + patch('openlp.plugins.songs.lib.songimport.foilpresenterimport.FoilPresenter._process_titles') self.process_topics_patcher = \ - patch('openlp.plugins.songs.lib.foilpresenterimport.FoilPresenter._process_topics') - self.re_patcher = patch('openlp.plugins.songs.lib.foilpresenterimport.re') - self.song_patcher = patch('openlp.plugins.songs.lib.foilpresenterimport.Song') - self.song_xml_patcher = patch('openlp.plugins.songs.lib.foilpresenterimport.SongXML') - self.translate_patcher = patch('openlp.plugins.songs.lib.foilpresenterimport.translate') + patch('openlp.plugins.songs.lib.songimport.foilpresenterimport.FoilPresenter._process_topics') + self.re_patcher = patch('openlp.plugins.songs.lib.songimport.foilpresenterimport.re') + self.song_patcher = patch('openlp.plugins.songs.lib.songimport.foilpresenterimport.Song') + self.song_xml_patcher = patch('openlp.plugins.songs.lib.songimport.foilpresenterimport.SongXML') + self.translate_patcher = patch('openlp.plugins.songs.lib.songimport.foilpresenterimport.translate') self.mocked_child = self.child_patcher.start() self.mocked_clean_song = self.clean_song_patcher.start() diff --git a/tests/functional/openlp_plugins/songs/test_openlyricsimport.py b/tests/functional/openlp_plugins/songs/test_openlyricsimport.py index 93ecafb78..2166b950c 100644 --- a/tests/functional/openlp_plugins/songs/test_openlyricsimport.py +++ b/tests/functional/openlp_plugins/songs/test_openlyricsimport.py @@ -34,8 +34,8 @@ 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 +from openlp.plugins.songs.lib.songimport.openlyricsimport import OpenLyricsImport +from openlp.plugins.songs.lib.songimport.songimport import SongImport TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..', 'resources', 'openlyricssongs')) @@ -69,7 +69,7 @@ class TestOpenLyricsImport(TestCase): 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'): + with patch('openlp.plugins.songs.lib.songimport.openlyricsimport.SongImport'): mocked_manager = MagicMock() # WHEN: An importer object is created diff --git a/tests/functional/openlp_plugins/songs/test_opensongimport.py b/tests/functional/openlp_plugins/songs/test_opensongimport.py index 96d6bff0b..8e7fee0af 100644 --- a/tests/functional/openlp_plugins/songs/test_opensongimport.py +++ b/tests/functional/openlp_plugins/songs/test_opensongimport.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- + # -*- coding: utf-8 -*- # vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 ############################################################################### @@ -34,7 +34,7 @@ import os from unittest import TestCase from tests.helpers.songfileimport import SongImportTestHelper -from openlp.plugins.songs.lib.opensongimport import OpenSongImport +from openlp.plugins.songs.lib.songimport.opensongimport import OpenSongImport from tests.functional import patch, MagicMock TEST_PATH = os.path.abspath( @@ -69,7 +69,7 @@ class TestOpenSongImport(TestCase): Test creating an instance of the OpenSong file importer """ # GIVEN: A mocked out SongImport class, and a mocked out "manager" - with patch('openlp.plugins.songs.lib.opensongimport.SongImport'): + with patch('openlp.plugins.songs.lib.songimport.opensongimport.SongImport'): mocked_manager = MagicMock() # WHEN: An importer object is created @@ -83,7 +83,7 @@ class TestOpenSongImport(TestCase): Test OpenSongImport.do_import handles different invalid import_source values """ # GIVEN: A mocked out SongImport class, and a mocked out "manager" - with patch('openlp.plugins.songs.lib.opensongimport.SongImport'): + with patch('openlp.plugins.songs.lib.songimport.opensongimport.SongImport'): mocked_manager = MagicMock() mocked_import_wizard = MagicMock() importer = OpenSongImport(mocked_manager, filenames=[]) @@ -104,7 +104,7 @@ class TestOpenSongImport(TestCase): Test OpenSongImport.do_import handles different invalid import_source values """ # GIVEN: A mocked out SongImport class, and a mocked out "manager" - with patch('openlp.plugins.songs.lib.opensongimport.SongImport'): + with patch('openlp.plugins.songs.lib.songimport.opensongimport.SongImport'): mocked_manager = MagicMock() mocked_import_wizard = MagicMock() importer = OpenSongImport(mocked_manager, filenames=[]) diff --git a/tests/functional/openlp_plugins/songs/test_songbeamerimport.py b/tests/functional/openlp_plugins/songs/test_songbeamerimport.py index a69d4a86c..9ca0ab790 100644 --- a/tests/functional/openlp_plugins/songs/test_songbeamerimport.py +++ b/tests/functional/openlp_plugins/songs/test_songbeamerimport.py @@ -34,7 +34,7 @@ import os from unittest import TestCase from tests.functional import MagicMock, patch -from openlp.plugins.songs.lib.songbeamerimport import SongBeamerImport +from openlp.plugins.songs.lib.songimport.songbeamerimport import SongBeamerImport from openlp.plugins.songs.lib import VerseType TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), @@ -64,7 +64,7 @@ class TestSongBeamerImport(TestCase): Test creating an instance of the SongBeamer file importer """ # GIVEN: A mocked out SongImport class, and a mocked out "manager" - with patch('openlp.plugins.songs.lib.songbeamerimport.SongImport'): + with patch('openlp.plugins.songs.lib.songimport.songbeamerimport.SongImport'): mocked_manager = MagicMock() # WHEN: An importer object is created @@ -78,7 +78,7 @@ class TestSongBeamerImport(TestCase): Test SongBeamerImport.do_import handles different invalid import_source values """ # GIVEN: A mocked out SongImport class, and a mocked out "manager" - with patch('openlp.plugins.songs.lib.songbeamerimport.SongImport'): + with patch('openlp.plugins.songs.lib.songimport.songbeamerimport.SongImport'): mocked_manager = MagicMock() mocked_import_wizard = MagicMock() importer = SongBeamerImport(mocked_manager, filenames=[]) @@ -99,7 +99,7 @@ class TestSongBeamerImport(TestCase): Test SongBeamerImport.do_import handles different invalid import_source values """ # GIVEN: A mocked out SongImport class, and a mocked out "manager" - with patch('openlp.plugins.songs.lib.songbeamerimport.SongImport'): + with patch('openlp.plugins.songs.lib.songimport.songbeamerimport.SongImport'): mocked_manager = MagicMock() mocked_import_wizard = MagicMock() importer = SongBeamerImport(mocked_manager, filenames=[]) @@ -122,7 +122,7 @@ class TestSongBeamerImport(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.songbeamerimport.SongImport'): + with patch('openlp.plugins.songs.lib.songimport.songbeamerimport.SongImport'): for song_file in SONG_TEST_DATA: mocked_manager = MagicMock() mocked_import_wizard = MagicMock() diff --git a/tests/functional/openlp_plugins/songs/test_songshowplusimport.py b/tests/functional/openlp_plugins/songs/test_songshowplusimport.py index dfee265e3..8d6a42287 100644 --- a/tests/functional/openlp_plugins/songs/test_songshowplusimport.py +++ b/tests/functional/openlp_plugins/songs/test_songshowplusimport.py @@ -35,7 +35,7 @@ from unittest import TestCase from tests.helpers.songfileimport import SongImportTestHelper from openlp.plugins.songs.lib import VerseType -from openlp.plugins.songs.lib.songshowplusimport import SongShowPlusImport +from openlp.plugins.songs.lib.songimport.songshowplusimport import SongShowPlusImport from tests.functional import patch, MagicMock TEST_PATH = os.path.abspath( @@ -70,7 +70,7 @@ class TestSongShowPlusImport(TestCase): Test creating an instance of the SongShow Plus file importer """ # GIVEN: A mocked out SongImport class, and a mocked out "manager" - with patch('openlp.plugins.songs.lib.songshowplusimport.SongImport'): + with patch('openlp.plugins.songs.lib.songimport.songshowplusimport.SongImport'): mocked_manager = MagicMock() # WHEN: An importer object is created @@ -84,7 +84,7 @@ class TestSongShowPlusImport(TestCase): Test SongShowPlusImport.do_import handles different invalid import_source values """ # GIVEN: A mocked out SongImport class, and a mocked out "manager" - with patch('openlp.plugins.songs.lib.songshowplusimport.SongImport'): + with patch('openlp.plugins.songs.lib.songimport.songshowplusimport.SongImport'): mocked_manager = MagicMock() mocked_import_wizard = MagicMock() importer = SongShowPlusImport(mocked_manager, filenames=[]) @@ -105,7 +105,7 @@ class TestSongShowPlusImport(TestCase): Test SongShowPlusImport.do_import handles different invalid import_source values """ # GIVEN: A mocked out SongImport class, and a mocked out "manager" - with patch('openlp.plugins.songs.lib.songshowplusimport.SongImport'): + with patch('openlp.plugins.songs.lib.songimport.songshowplusimport.SongImport'): mocked_manager = MagicMock() mocked_import_wizard = MagicMock() importer = SongShowPlusImport(mocked_manager, filenames=[]) @@ -126,7 +126,7 @@ class TestSongShowPlusImport(TestCase): Test to_openlp_verse_tag method by simulating adding a verse """ # GIVEN: A mocked out SongImport class, and a mocked out "manager" - with patch('openlp.plugins.songs.lib.songshowplusimport.SongImport'): + with patch('openlp.plugins.songs.lib.songimport.songshowplusimport.SongImport'): mocked_manager = MagicMock() importer = SongShowPlusImport(mocked_manager, filenames=[]) @@ -154,7 +154,7 @@ class TestSongShowPlusImport(TestCase): Test to_openlp_verse_tag method by simulating adding a verse to the verse order """ # GIVEN: A mocked out SongImport class, and a mocked out "manager" - with patch('openlp.plugins.songs.lib.songshowplusimport.SongImport'): + with patch('openlp.plugins.songs.lib.songimport.songshowplusimport.SongImport'): mocked_manager = MagicMock() importer = SongShowPlusImport(mocked_manager, filenames=[]) diff --git a/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py b/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py index 9a58a6c2b..263397cc9 100644 --- a/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py +++ b/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py @@ -37,7 +37,7 @@ if os.name != 'nt': import pyodbc -from openlp.plugins.songs.lib.worshipcenterproimport import WorshipCenterProImport +from openlp.plugins.songs.lib.songimport.worshipcenterproimport import WorshipCenterProImport from tests.functional import patch, MagicMock @@ -141,7 +141,7 @@ class TestWorshipCenterProSongImport(TestCase): Test creating an instance of the WorshipCenter Pro file importer """ # GIVEN: A mocked out SongImport class, and a mocked out "manager" - with patch('openlp.plugins.songs.lib.worshipcenterproimport.SongImport'): + with patch('openlp.plugins.songs.lib.songimport.worshipcenterproimport.SongImport'): mocked_manager = MagicMock() # WHEN: An importer object is created diff --git a/tests/functional/openlp_plugins/songs/test_zionworximport.py b/tests/functional/openlp_plugins/songs/test_zionworximport.py index c5669e9c8..a3f5bb20b 100644 --- a/tests/functional/openlp_plugins/songs/test_zionworximport.py +++ b/tests/functional/openlp_plugins/songs/test_zionworximport.py @@ -33,8 +33,8 @@ 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 +from openlp.plugins.songs.lib.songimport.zionworximport import ZionWorxImport +from openlp.plugins.songs.lib.songimport.songimport import SongImport class TestZionWorxImport(TestCase): @@ -46,7 +46,7 @@ class TestZionWorxImport(TestCase): 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.zionworximport.SongImport'): + with patch('openlp.plugins.songs.lib.songimport.zionworximport.SongImport'): mocked_manager = MagicMock() # WHEN: An importer object is created diff --git a/tests/helpers/songfileimport.py b/tests/helpers/songfileimport.py index 80b2ee268..0b0e3515d 100644 --- a/tests/helpers/songfileimport.py +++ b/tests/helpers/songfileimport.py @@ -43,7 +43,7 @@ class SongImportTestHelper(TestCase): def __init__(self, *args, **kwargs): super(SongImportTestHelper, self).__init__(*args, **kwargs) self.importer_module = __import__( - 'openlp.plugins.songs.lib.%s' % self.importer_module_name, fromlist=[self.importer_class_name]) + 'openlp.plugins.songs.lib.songimport.%s' % self.importer_module_name, fromlist=[self.importer_class_name]) self.importer_class = getattr(self.importer_module, self.importer_class_name) def setUp(self): @@ -51,14 +51,14 @@ class SongImportTestHelper(TestCase): Patch and set up the mocks required. """ self.add_copyright_patcher = patch( - 'openlp.plugins.songs.lib.%s.%s.add_copyright' % (self.importer_module_name, self.importer_class_name)) + 'openlp.plugins.songs.lib.songimport.%s.%s.add_copyright' % (self.importer_module_name, self.importer_class_name)) self.add_verse_patcher = patch( - 'openlp.plugins.songs.lib.%s.%s.add_verse' % (self.importer_module_name, self.importer_class_name)) + 'openlp.plugins.songs.lib.songimport.%s.%s.add_verse' % (self.importer_module_name, self.importer_class_name)) self.finish_patcher = patch( - 'openlp.plugins.songs.lib.%s.%s.finish' % (self.importer_module_name, self.importer_class_name)) + 'openlp.plugins.songs.lib.songimport.%s.%s.finish' % (self.importer_module_name, self.importer_class_name)) self.add_author_patcher = patch( - 'openlp.plugins.songs.lib.%s.%s.add_author' % (self.importer_module_name, self.importer_class_name)) - self.song_import_patcher = patch('openlp.plugins.songs.lib.%s.SongImport' % self.importer_module_name) + 'openlp.plugins.songs.lib.songimport.%s.%s.add_author' % (self.importer_module_name, self.importer_class_name)) + self.song_import_patcher = patch('openlp.plugins.songs.lib.songimport.%s.SongImport' % self.importer_module_name) self.mocked_add_copyright = self.add_copyright_patcher.start() self.mocked_add_verse = self.add_verse_patcher.start() self.mocked_finish = self.finish_patcher.start() From 3b7d5f53babe6856d1b51e7e566071ed8a026269 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Thu, 3 Jul 2014 19:57:13 +0200 Subject: [PATCH 207/212] PEP8 --- openlp/plugins/songs/lib/songimport/__init__.py | 2 +- .../openlp_plugins/songs/test_opensongimport.py | 2 +- .../songs/test_worshipcenterproimport.py | 12 ++++++------ tests/helpers/songfileimport.py | 12 ++++++++---- 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/openlp/plugins/songs/lib/songimport/__init__.py b/openlp/plugins/songs/lib/songimport/__init__.py index f57136ecf..da302572e 100644 --- a/openlp/plugins/songs/lib/songimport/__init__.py +++ b/openlp/plugins/songs/lib/songimport/__init__.py @@ -27,5 +27,5 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### """ -The :mod:`~openlp.plugins.songs.lib.import` module contains a importers for the Songs plugin. +The :mod:`~openlp.plugins.songs.lib.import` module contains importers for the Songs plugin. """ diff --git a/tests/functional/openlp_plugins/songs/test_opensongimport.py b/tests/functional/openlp_plugins/songs/test_opensongimport.py index 8e7fee0af..09fad7bc3 100644 --- a/tests/functional/openlp_plugins/songs/test_opensongimport.py +++ b/tests/functional/openlp_plugins/songs/test_opensongimport.py @@ -1,4 +1,4 @@ - # -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- # vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 ############################################################################### diff --git a/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py b/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py index 263397cc9..27c688aa0 100644 --- a/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py +++ b/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py @@ -156,9 +156,9 @@ class TestWorshipCenterProSongImport(TestCase): """ # GIVEN: A mocked out SongImport class, a mocked out pyodbc module, a mocked out translate method, # 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: + with patch('openlp.plugins.songs.lib.songimport.worshipcenterproimport.SongImport'), \ + patch('openlp.plugins.songs.lib.songimport.worshipcenterproimport.pyodbc.connect') as mocked_pyodbc_connect, \ + patch('openlp.plugins.songs.lib.songimport.worshipcenterproimport.translate') as mocked_translate: mocked_manager = MagicMock() mocked_log_error = MagicMock() mocked_translate.return_value = 'Translated Text' @@ -185,9 +185,9 @@ class TestWorshipCenterProSongImport(TestCase): """ # GIVEN: A mocked out SongImport class, a mocked out pyodbc module with a simulated recordset, a mocked out # 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: + with patch('openlp.plugins.songs.lib.songimport.worshipcenterproimport.SongImport'), \ + patch('openlp.plugins.songs.lib.songimport.worshipcenterproimport.pyodbc') as mocked_pyodbc, \ + patch('openlp.plugins.songs.lib.songimport.worshipcenterproimport.translate') as mocked_translate: mocked_manager = MagicMock() mocked_import_wizard = MagicMock() mocked_add_verse = MagicMock() diff --git a/tests/helpers/songfileimport.py b/tests/helpers/songfileimport.py index 0b0e3515d..003ade543 100644 --- a/tests/helpers/songfileimport.py +++ b/tests/helpers/songfileimport.py @@ -51,14 +51,18 @@ class SongImportTestHelper(TestCase): Patch and set up the mocks required. """ self.add_copyright_patcher = patch( - 'openlp.plugins.songs.lib.songimport.%s.%s.add_copyright' % (self.importer_module_name, self.importer_class_name)) + 'openlp.plugins.songs.lib.songimport.%s.%s.add_copyright' % (self.importer_module_name, + self.importer_class_name)) self.add_verse_patcher = patch( - 'openlp.plugins.songs.lib.songimport.%s.%s.add_verse' % (self.importer_module_name, self.importer_class_name)) + 'openlp.plugins.songs.lib.songimport.%s.%s.add_verse' % (self.importer_module_name, + self.importer_class_name)) self.finish_patcher = patch( 'openlp.plugins.songs.lib.songimport.%s.%s.finish' % (self.importer_module_name, self.importer_class_name)) self.add_author_patcher = patch( - 'openlp.plugins.songs.lib.songimport.%s.%s.add_author' % (self.importer_module_name, self.importer_class_name)) - self.song_import_patcher = patch('openlp.plugins.songs.lib.songimport.%s.SongImport' % self.importer_module_name) + 'openlp.plugins.songs.lib.songimport.%s.%s.add_author' % (self.importer_module_name, + self.importer_class_name)) + self.song_import_patcher = patch('openlp.plugins.songs.lib.songimport.%s.SongImport' % + self.importer_module_name) self.mocked_add_copyright = self.add_copyright_patcher.start() self.mocked_add_verse = self.add_verse_patcher.start() self.mocked_finish = self.finish_patcher.start() From c9b47e8e6f89ffd12905ee55cb50680557c22cb9 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Thu, 3 Jul 2014 19:57:46 +0200 Subject: [PATCH 208/212] PEP8 --- .../openlp_plugins/songs/test_worshipcenterproimport.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py b/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py index 27c688aa0..0558ad195 100644 --- a/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py +++ b/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py @@ -157,7 +157,8 @@ class TestWorshipCenterProSongImport(TestCase): # GIVEN: A mocked out SongImport class, a mocked out pyodbc module, a mocked out translate method, # a mocked "manager" and a mocked out log_error method. with patch('openlp.plugins.songs.lib.songimport.worshipcenterproimport.SongImport'), \ - patch('openlp.plugins.songs.lib.songimport.worshipcenterproimport.pyodbc.connect') as mocked_pyodbc_connect, \ + patch('openlp.plugins.songs.lib.songimport.worshipcenterproimport.pyodbc.connect') \ + as mocked_pyodbc_connect, \ patch('openlp.plugins.songs.lib.songimport.worshipcenterproimport.translate') as mocked_translate: mocked_manager = MagicMock() mocked_log_error = MagicMock() From 686f8d243746404c7e3dd70c979b25c531798a78 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Fri, 4 Jul 2014 11:31:06 +0200 Subject: [PATCH 209/212] More renaming --- openlp/plugins/songs/lib/importer.py | 54 +++++++++---------- .../lib/{songimport => importers}/__init__.py | 0 .../cclifile.py} | 0 .../dreambeam.py} | 2 +- .../easyslides.py} | 2 +- .../ewimport.py => importers/easyworship.py} | 0 .../foilpresenter.py} | 2 +- .../mediashout.py} | 2 +- .../olpimport.py => importers/openlp.py} | 0 .../openlyrics.py} | 2 +- .../oooimport.py => importers/openoffice.py} | 2 +- .../opensong.py} | 2 +- .../powersong.py} | 4 +- .../propresenter.py} | 0 .../songbeamer.py} | 2 +- .../{songimport => importers}/songimport.py | 0 .../songproimport.py => importers/songpro.py} | 2 +- .../songshowplus.py} | 2 +- .../songsoffellowship.py} | 8 +-- .../sundayplus.py} | 2 +- .../wordsofworship.py} | 4 +- .../worshipassistant.py} | 4 +- .../worshipcenterpro.py} | 2 +- .../zionworx.py} | 2 +- openlp/plugins/songs/songsplugin.py | 2 +- .../openlp_plugins/songs/test_ewimport.py | 52 +++++++++--------- .../songs/test_foilpresenterimport.py | 30 +++++------ .../songs/test_openlyricsimport.py | 6 +-- .../songs/test_opensongimport.py | 10 ++-- .../songs/test_propresenterimport.py | 2 +- .../songs/test_songbeamerimport.py | 10 ++-- .../songs/test_songshowplusimport.py | 14 ++--- .../songs/test_worshipassistantimport.py | 2 +- .../songs/test_worshipcenterproimport.py | 16 +++--- .../songs/test_zionworximport.py | 6 +-- tests/helpers/songfileimport.py | 12 ++--- 36 files changed, 131 insertions(+), 131 deletions(-) rename openlp/plugins/songs/lib/{songimport => importers}/__init__.py (100%) rename openlp/plugins/songs/lib/{songimport/cclifileimport.py => importers/cclifile.py} (100%) rename openlp/plugins/songs/lib/{songimport/dreambeamimport.py => importers/dreambeam.py} (99%) rename openlp/plugins/songs/lib/{songimport/easyslidesimport.py => importers/easyslides.py} (99%) rename openlp/plugins/songs/lib/{songimport/ewimport.py => importers/easyworship.py} (100%) rename openlp/plugins/songs/lib/{songimport/foilpresenterimport.py => importers/foilpresenter.py} (99%) rename openlp/plugins/songs/lib/{songimport/mediashoutimport.py => importers/mediashout.py} (98%) rename openlp/plugins/songs/lib/{songimport/olpimport.py => importers/openlp.py} (100%) rename openlp/plugins/songs/lib/{songimport/openlyricsimport.py => importers/openlyrics.py} (98%) rename openlp/plugins/songs/lib/{songimport/oooimport.py => importers/openoffice.py} (99%) rename openlp/plugins/songs/lib/{songimport/opensongimport.py => importers/opensong.py} (99%) rename openlp/plugins/songs/lib/{songimport/powersongimport.py => importers/powersong.py} (98%) rename openlp/plugins/songs/lib/{songimport/propresenterimport.py => importers/propresenter.py} (100%) rename openlp/plugins/songs/lib/{songimport/songbeamerimport.py => importers/songbeamer.py} (99%) rename openlp/plugins/songs/lib/{songimport => importers}/songimport.py (100%) rename openlp/plugins/songs/lib/{songimport/songproimport.py => importers/songpro.py} (98%) rename openlp/plugins/songs/lib/{songimport/songshowplusimport.py => importers/songshowplus.py} (99%) rename openlp/plugins/songs/lib/{songimport/sofimport.py => importers/songsoffellowship.py} (98%) rename openlp/plugins/songs/lib/{songimport/sundayplusimport.py => importers/sundayplus.py} (99%) rename openlp/plugins/songs/lib/{songimport/wowimport.py => importers/wordsofworship.py} (98%) rename openlp/plugins/songs/lib/{songimport/worshipassistantimport.py => importers/worshipassistant.py} (98%) rename openlp/plugins/songs/lib/{songimport/worshipcenterproimport.py => importers/worshipcenterpro.py} (98%) rename openlp/plugins/songs/lib/{songimport/zionworximport.py => importers/zionworx.py} (98%) diff --git a/openlp/plugins/songs/lib/importer.py b/openlp/plugins/songs/lib/importer.py index 9c0b889ca..bb622bcf9 100644 --- a/openlp/plugins/songs/lib/importer.py +++ b/openlp/plugins/songs/lib/importer.py @@ -34,23 +34,23 @@ import logging from openlp.core.common import translate, UiStrings from openlp.core.ui.wizard import WizardStrings -from .songimport.opensongimport import OpenSongImport -from .songimport.easyslidesimport import EasySlidesImport -from .songimport.olpimport import OpenLPSongImport -from .songimport.openlyricsimport import OpenLyricsImport -from .songimport.wowimport import WowImport -from .songimport.cclifileimport import CCLIFileImport -from .songimport.dreambeamimport import DreamBeamImport -from .songimport.powersongimport import PowerSongImport -from .songimport.ewimport import EasyWorshipSongImport -from .songimport.songbeamerimport import SongBeamerImport -from .songimport.songshowplusimport import SongShowPlusImport -from .songimport.songproimport import SongProImport -from .songimport.sundayplusimport import SundayPlusImport -from .songimport.foilpresenterimport import FoilPresenterImport -from .songimport.zionworximport import ZionWorxImport -from .songimport.propresenterimport import ProPresenterImport -from .songimport.worshipassistantimport import WorshipAssistantImport +from .importers.opensong import OpenSongImport +from .importers.easyslides import EasySlidesImport +from .importers.openlp import OpenLPSongImport +from .importers.openlyrics import OpenLyricsImport +from .importers.wordsofworship import WordsOfWorshipImport +from .importers.cclifile import CCLIFileImport +from .importers.dreambeam import DreamBeamImport +from .importers.powersong import PowerSongImport +from .importers.easyworship import EasyWorshipSongImport +from .importers.songbeamer import SongBeamerImport +from .importers.songshowplus import SongShowPlusImport +from .importers.songpro import SongProImport +from .importers.sundayplus import SundayPlusImport +from .importers.foilpresenter import FoilPresenterImport +from .importers.zionworx import ZionWorxImport +from .importers.propresenter import ProPresenterImport +from .importers.worshipassistant import WorshipAssistantImport # Imports that might fail @@ -58,13 +58,13 @@ log = logging.getLogger(__name__) try: - from .sofimport import SofImport + from .importers.songsoffellowship import SongsOfFellowshipImport HAS_SOF = True except ImportError: - log.exception('Error importing %s', 'SofImport') + log.exception('Error importing %s', 'SongsOfFellowshipImport') HAS_SOF = False try: - from .oooimport import OooImport + from .importers.openoffice import OpenOfficeImport HAS_OOO = True except ImportError: log.exception('Error importing %s', 'OooImport') @@ -72,14 +72,14 @@ except ImportError: HAS_MEDIASHOUT = False if os.name == 'nt': try: - from .mediashoutimport import MediaShoutImport + from .importers.mediashout import MediaShoutImport HAS_MEDIASHOUT = True except ImportError: log.exception('Error importing %s', 'MediaShoutImport') HAS_WORSHIPCENTERPRO = False if os.name == 'nt': try: - from .worshipcenterproimport import WorshipCenterProImport + from .importers.worshipcenterpro import WorshipCenterProImport HAS_WORSHIPCENTERPRO = True except ImportError: log.exception('Error importing %s', 'WorshipCenterProImport') @@ -109,7 +109,7 @@ class SongFormat(object): Name of the format, e.g. ``'OpenLyrics'`` ``'prefix'`` - Prefix for Qt objects. Use mixedCase, e.g. ``'open_lyrics'`` + Prefix for Qt objects. Use mixedCase, e.g. ``'openLyrics'`` See ``SongImportForm.add_file_select_item()`` Optional attributes for each song format: @@ -190,7 +190,7 @@ class SongFormat(object): OpenLyrics: { 'class': OpenLyricsImport, 'name': 'OpenLyrics', - 'prefix': 'open_lyrics', + 'prefix': 'openLyrics', 'filter': '%s (*.xml)' % translate('SongsPlugin.ImportWizardForm', 'OpenLyrics Files'), 'comboBoxText': translate('SongsPlugin.ImportWizardForm', 'OpenLyrics or OpenLP 2.0 Exported Song') }, @@ -318,7 +318,7 @@ class SongFormat(object): 'filter': '%s (*.ptf)' % translate('SongsPlugin.ImportWizardForm', 'SundayPlus Song Files') }, WordsOfWorship: { - 'class': WowImport, + 'class': WordsOfWorshipImport, 'name': 'Words of Worship', 'prefix': 'wordsOfWorship', 'filter': '%s (*.wsg *.wow-song)' % translate('SongsPlugin.ImportWizardForm', 'Words Of Worship Song Files') @@ -423,10 +423,10 @@ class SongFormat(object): SongFormat.set(SongFormat.SongsOfFellowship, 'availability', HAS_SOF) if HAS_SOF: - SongFormat.set(SongFormat.SongsOfFellowship, 'class', SofImport) + SongFormat.set(SongFormat.SongsOfFellowship, 'class', SongsOfFellowshipImport) SongFormat.set(SongFormat.Generic, 'availability', HAS_OOO) if HAS_OOO: - SongFormat.set(SongFormat.Generic, 'class', OooImport) + SongFormat.set(SongFormat.Generic, 'class', OpenOfficeImport) SongFormat.set(SongFormat.MediaShout, 'availability', HAS_MEDIASHOUT) if HAS_MEDIASHOUT: SongFormat.set(SongFormat.MediaShout, 'class', MediaShoutImport) diff --git a/openlp/plugins/songs/lib/songimport/__init__.py b/openlp/plugins/songs/lib/importers/__init__.py similarity index 100% rename from openlp/plugins/songs/lib/songimport/__init__.py rename to openlp/plugins/songs/lib/importers/__init__.py diff --git a/openlp/plugins/songs/lib/songimport/cclifileimport.py b/openlp/plugins/songs/lib/importers/cclifile.py similarity index 100% rename from openlp/plugins/songs/lib/songimport/cclifileimport.py rename to openlp/plugins/songs/lib/importers/cclifile.py diff --git a/openlp/plugins/songs/lib/songimport/dreambeamimport.py b/openlp/plugins/songs/lib/importers/dreambeam.py similarity index 99% rename from openlp/plugins/songs/lib/songimport/dreambeamimport.py rename to openlp/plugins/songs/lib/importers/dreambeam.py index e5afd65f4..38aab4ae7 100644 --- a/openlp/plugins/songs/lib/songimport/dreambeamimport.py +++ b/openlp/plugins/songs/lib/importers/dreambeam.py @@ -35,7 +35,7 @@ import logging from lxml import etree, objectify from openlp.core.lib import translate -from openlp.plugins.songs.lib.songimport.songimport import SongImport +from openlp.plugins.songs.lib.importers.songimport import SongImport from openlp.plugins.songs.lib.ui import SongStrings log = logging.getLogger(__name__) diff --git a/openlp/plugins/songs/lib/songimport/easyslidesimport.py b/openlp/plugins/songs/lib/importers/easyslides.py similarity index 99% rename from openlp/plugins/songs/lib/songimport/easyslidesimport.py rename to openlp/plugins/songs/lib/importers/easyslides.py index f30ae49e5..93e7fc4db 100644 --- a/openlp/plugins/songs/lib/songimport/easyslidesimport.py +++ b/openlp/plugins/songs/lib/importers/easyslides.py @@ -33,7 +33,7 @@ import re from lxml import etree, objectify from openlp.plugins.songs.lib import VerseType -from openlp.plugins.songs.lib.songimport.songimport import SongImport +from openlp.plugins.songs.lib.importers.songimport import SongImport log = logging.getLogger(__name__) diff --git a/openlp/plugins/songs/lib/songimport/ewimport.py b/openlp/plugins/songs/lib/importers/easyworship.py similarity index 100% rename from openlp/plugins/songs/lib/songimport/ewimport.py rename to openlp/plugins/songs/lib/importers/easyworship.py diff --git a/openlp/plugins/songs/lib/songimport/foilpresenterimport.py b/openlp/plugins/songs/lib/importers/foilpresenter.py similarity index 99% rename from openlp/plugins/songs/lib/songimport/foilpresenterimport.py rename to openlp/plugins/songs/lib/importers/foilpresenter.py index 67911de76..482fbc06a 100644 --- a/openlp/plugins/songs/lib/songimport/foilpresenterimport.py +++ b/openlp/plugins/songs/lib/importers/foilpresenter.py @@ -99,7 +99,7 @@ from lxml import etree, objectify from openlp.core.lib import translate from openlp.core.ui.wizard import WizardStrings from openlp.plugins.songs.lib import clean_song, VerseType -from openlp.plugins.songs.lib.songimport.songimport import SongImport +from openlp.plugins.songs.lib.importers.songimport import SongImport from openlp.plugins.songs.lib.db import Author, Book, Song, Topic from openlp.plugins.songs.lib.ui import SongStrings from openlp.plugins.songs.lib.openlyricsxml import SongXML diff --git a/openlp/plugins/songs/lib/songimport/mediashoutimport.py b/openlp/plugins/songs/lib/importers/mediashout.py similarity index 98% rename from openlp/plugins/songs/lib/songimport/mediashoutimport.py rename to openlp/plugins/songs/lib/importers/mediashout.py index 8eab61a96..d82304fae 100644 --- a/openlp/plugins/songs/lib/songimport/mediashoutimport.py +++ b/openlp/plugins/songs/lib/importers/mediashout.py @@ -33,7 +33,7 @@ a MediaShout database into the OpenLP database. import pyodbc from openlp.core.lib import translate -from openlp.plugins.songs.lib.songimport.songimport import SongImport +from openlp.plugins.songs.lib.importers.songimport import SongImport VERSE_TAGS = ['V', 'C', 'B', 'O', 'P', 'I', 'E'] diff --git a/openlp/plugins/songs/lib/songimport/olpimport.py b/openlp/plugins/songs/lib/importers/openlp.py similarity index 100% rename from openlp/plugins/songs/lib/songimport/olpimport.py rename to openlp/plugins/songs/lib/importers/openlp.py diff --git a/openlp/plugins/songs/lib/songimport/openlyricsimport.py b/openlp/plugins/songs/lib/importers/openlyrics.py similarity index 98% rename from openlp/plugins/songs/lib/songimport/openlyricsimport.py rename to openlp/plugins/songs/lib/importers/openlyrics.py index 74329bfd8..78fcbf2af 100644 --- a/openlp/plugins/songs/lib/songimport/openlyricsimport.py +++ b/openlp/plugins/songs/lib/importers/openlyrics.py @@ -37,7 +37,7 @@ import os from lxml import etree from openlp.core.ui.wizard import WizardStrings -from openlp.plugins.songs.lib.songimport.songimport import SongImport +from openlp.plugins.songs.lib.importers.songimport import SongImport from openlp.plugins.songs.lib.ui import SongStrings from openlp.plugins.songs.lib.openlyricsxml import OpenLyrics, OpenLyricsError diff --git a/openlp/plugins/songs/lib/songimport/oooimport.py b/openlp/plugins/songs/lib/importers/openoffice.py similarity index 99% rename from openlp/plugins/songs/lib/songimport/oooimport.py rename to openlp/plugins/songs/lib/importers/openoffice.py index 0e388b54f..0e499f7ae 100644 --- a/openlp/plugins/songs/lib/songimport/oooimport.py +++ b/openlp/plugins/songs/lib/importers/openoffice.py @@ -52,7 +52,7 @@ except ImportError: PAGE_BOTH = 6 -class OooImport(SongImport): +class OpenOfficeImport(SongImport): """ Import songs from Impress/Powerpoint docs using Impress """ diff --git a/openlp/plugins/songs/lib/songimport/opensongimport.py b/openlp/plugins/songs/lib/importers/opensong.py similarity index 99% rename from openlp/plugins/songs/lib/songimport/opensongimport.py rename to openlp/plugins/songs/lib/importers/opensong.py index a52b67b0f..e1502a903 100644 --- a/openlp/plugins/songs/lib/songimport/opensongimport.py +++ b/openlp/plugins/songs/lib/importers/opensong.py @@ -35,7 +35,7 @@ from lxml.etree import Error, LxmlError from openlp.core.common import translate from openlp.plugins.songs.lib import VerseType -from openlp.plugins.songs.lib.songimport.songimport import SongImport +from openlp.plugins.songs.lib.importers.songimport import SongImport from openlp.plugins.songs.lib.ui import SongStrings log = logging.getLogger(__name__) diff --git a/openlp/plugins/songs/lib/songimport/powersongimport.py b/openlp/plugins/songs/lib/importers/powersong.py similarity index 98% rename from openlp/plugins/songs/lib/songimport/powersongimport.py rename to openlp/plugins/songs/lib/importers/powersong.py index 1cad5e33e..89c847ab0 100644 --- a/openlp/plugins/songs/lib/songimport/powersongimport.py +++ b/openlp/plugins/songs/lib/importers/powersong.py @@ -35,7 +35,7 @@ import fnmatch import os from openlp.core.common import translate -from openlp.plugins.songs.lib.songimport.songimport import SongImport +from openlp.plugins.songs.lib.importers.songimport import SongImport log = logging.getLogger(__name__) @@ -90,7 +90,7 @@ class PowerSongImport(SongImport): """ Receive either a list of files or a folder (unicode) to import. """ - from .importer import SongFormat + from openlp.plugins.songs.lib.importer import SongFormat ps_string = SongFormat.get(SongFormat.PowerSong, 'name') if isinstance(self.import_source, str): if os.path.isdir(self.import_source): diff --git a/openlp/plugins/songs/lib/songimport/propresenterimport.py b/openlp/plugins/songs/lib/importers/propresenter.py similarity index 100% rename from openlp/plugins/songs/lib/songimport/propresenterimport.py rename to openlp/plugins/songs/lib/importers/propresenter.py diff --git a/openlp/plugins/songs/lib/songimport/songbeamerimport.py b/openlp/plugins/songs/lib/importers/songbeamer.py similarity index 99% rename from openlp/plugins/songs/lib/songimport/songbeamerimport.py rename to openlp/plugins/songs/lib/importers/songbeamer.py index fbd016cda..4c5873e2a 100644 --- a/openlp/plugins/songs/lib/songimport/songbeamerimport.py +++ b/openlp/plugins/songs/lib/importers/songbeamer.py @@ -36,7 +36,7 @@ import os import re from openlp.plugins.songs.lib import VerseType -from openlp.plugins.songs.lib.songimport.songimport import SongImport +from openlp.plugins.songs.lib.importers.songimport import SongImport log = logging.getLogger(__name__) diff --git a/openlp/plugins/songs/lib/songimport/songimport.py b/openlp/plugins/songs/lib/importers/songimport.py similarity index 100% rename from openlp/plugins/songs/lib/songimport/songimport.py rename to openlp/plugins/songs/lib/importers/songimport.py diff --git a/openlp/plugins/songs/lib/songimport/songproimport.py b/openlp/plugins/songs/lib/importers/songpro.py similarity index 98% rename from openlp/plugins/songs/lib/songimport/songproimport.py rename to openlp/plugins/songs/lib/importers/songpro.py index 853a1d9eb..52d702097 100644 --- a/openlp/plugins/songs/lib/songimport/songproimport.py +++ b/openlp/plugins/songs/lib/importers/songpro.py @@ -33,7 +33,7 @@ songs into the OpenLP database. import re from openlp.plugins.songs.lib import strip_rtf -from openlp.plugins.songs.lib.songimport.songimport import SongImport +from openlp.plugins.songs.lib.importers.songimport import SongImport class SongProImport(SongImport): diff --git a/openlp/plugins/songs/lib/songimport/songshowplusimport.py b/openlp/plugins/songs/lib/importers/songshowplus.py similarity index 99% rename from openlp/plugins/songs/lib/songimport/songshowplusimport.py rename to openlp/plugins/songs/lib/importers/songshowplus.py index 352e55d96..d53cc33f1 100644 --- a/openlp/plugins/songs/lib/songimport/songshowplusimport.py +++ b/openlp/plugins/songs/lib/importers/songshowplus.py @@ -38,7 +38,7 @@ import struct from openlp.core.ui.wizard import WizardStrings from openlp.plugins.songs.lib import VerseType, retrieve_windows_encoding -from openlp.plugins.songs.lib.songimport.songimport import SongImport +from openlp.plugins.songs.lib.importers.songimport import SongImport TITLE = 1 AUTHOR = 2 diff --git a/openlp/plugins/songs/lib/songimport/sofimport.py b/openlp/plugins/songs/lib/importers/songsoffellowship.py similarity index 98% rename from openlp/plugins/songs/lib/songimport/sofimport.py rename to openlp/plugins/songs/lib/importers/songsoffellowship.py index e44034648..c1ef8666f 100644 --- a/openlp/plugins/songs/lib/songimport/sofimport.py +++ b/openlp/plugins/songs/lib/importers/songsoffellowship.py @@ -37,13 +37,13 @@ import logging import os import re -from .oooimport import OooImport +from .openoffice import OpenOfficeImport log = logging.getLogger(__name__) if os.name == 'nt': - from .oooimport import PAGE_BEFORE, PAGE_AFTER, PAGE_BOTH + from .openoffice import PAGE_BEFORE, PAGE_AFTER, PAGE_BOTH RuntimeException = Exception else: try: @@ -62,7 +62,7 @@ except ImportError: ITALIC = 2 -class SofImport(OooImport): +class SongsOfFellowshipImport(OpenOfficeImport): """ Import songs provided on disks with the Songs of Fellowship music books VOLS1_2.RTF, sof3words.rtf and sof4words.rtf @@ -83,7 +83,7 @@ class SofImport(OooImport): Initialise the class. Requires a songmanager class which is passed to SongImport for writing song to disk """ - OooImport.__init__(self, manager, **kwargs) + OpenOfficeImport.__init__(self, manager, **kwargs) self.song = False def process_ooo_document(self): diff --git a/openlp/plugins/songs/lib/songimport/sundayplusimport.py b/openlp/plugins/songs/lib/importers/sundayplus.py similarity index 99% rename from openlp/plugins/songs/lib/songimport/sundayplusimport.py rename to openlp/plugins/songs/lib/importers/sundayplus.py index fded3bca3..b664efb54 100644 --- a/openlp/plugins/songs/lib/songimport/sundayplusimport.py +++ b/openlp/plugins/songs/lib/importers/sundayplus.py @@ -32,7 +32,7 @@ import re from openlp.plugins.songs.lib import VerseType, retrieve_windows_encoding from openlp.plugins.songs.lib import strip_rtf -from openlp.plugins.songs.lib.songimport.songimport import SongImport +from openlp.plugins.songs.lib.importers.songimport import SongImport HOTKEY_TO_VERSE_TYPE = { '1': 'v1', diff --git a/openlp/plugins/songs/lib/songimport/wowimport.py b/openlp/plugins/songs/lib/importers/wordsofworship.py similarity index 98% rename from openlp/plugins/songs/lib/songimport/wowimport.py rename to openlp/plugins/songs/lib/importers/wordsofworship.py index c135ca620..ce80c19ce 100644 --- a/openlp/plugins/songs/lib/songimport/wowimport.py +++ b/openlp/plugins/songs/lib/importers/wordsofworship.py @@ -34,14 +34,14 @@ import os import logging from openlp.core.common import translate -from openlp.plugins.songs.lib.songimport.songimport import SongImport +from openlp.plugins.songs.lib.importers.songimport import SongImport BLOCK_TYPES = ('V', 'C', 'B') log = logging.getLogger(__name__) -class WowImport(SongImport): +class WordsOfWorshipImport(SongImport): """ The :class:`WowImport` class provides the ability to import song files from Words of Worship. diff --git a/openlp/plugins/songs/lib/songimport/worshipassistantimport.py b/openlp/plugins/songs/lib/importers/worshipassistant.py similarity index 98% rename from openlp/plugins/songs/lib/songimport/worshipassistantimport.py rename to openlp/plugins/songs/lib/importers/worshipassistant.py index 412a17c35..6ddc71159 100644 --- a/openlp/plugins/songs/lib/songimport/worshipassistantimport.py +++ b/openlp/plugins/songs/lib/importers/worshipassistant.py @@ -27,7 +27,7 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### """ -The :mod:`worshipassistantimport` module provides the functionality for importing +The :mod:`worshipassistant` module provides the functionality for importing Worship Assistant songs into the OpenLP database. """ import chardet @@ -37,7 +37,7 @@ import re from openlp.core.common import translate from openlp.plugins.songs.lib import VerseType -from openlp.plugins.songs.lib.songimport.songimport import SongImport +from openlp.plugins.songs.lib.importers.songimport import SongImport log = logging.getLogger(__name__) diff --git a/openlp/plugins/songs/lib/songimport/worshipcenterproimport.py b/openlp/plugins/songs/lib/importers/worshipcenterpro.py similarity index 98% rename from openlp/plugins/songs/lib/songimport/worshipcenterproimport.py rename to openlp/plugins/songs/lib/importers/worshipcenterpro.py index b6c32a8c0..817bd8cae 100644 --- a/openlp/plugins/songs/lib/songimport/worshipcenterproimport.py +++ b/openlp/plugins/songs/lib/importers/worshipcenterpro.py @@ -35,7 +35,7 @@ import logging import pyodbc from openlp.core.common import translate -from openlp.plugins.songs.lib.songimport.songimport import SongImport +from openlp.plugins.songs.lib.importers.songimport import SongImport log = logging.getLogger(__name__) diff --git a/openlp/plugins/songs/lib/songimport/zionworximport.py b/openlp/plugins/songs/lib/importers/zionworx.py similarity index 98% rename from openlp/plugins/songs/lib/songimport/zionworximport.py rename to openlp/plugins/songs/lib/importers/zionworx.py index 8d939f244..0b97aee26 100644 --- a/openlp/plugins/songs/lib/songimport/zionworximport.py +++ b/openlp/plugins/songs/lib/importers/zionworx.py @@ -34,7 +34,7 @@ import csv import logging from openlp.core.common import translate -from openlp.plugins.songs.lib.songimport.songimport import SongImport +from openlp.plugins.songs.lib.importers.songimport import SongImport log = logging.getLogger(__name__) diff --git a/openlp/plugins/songs/songsplugin.py b/openlp/plugins/songs/songsplugin.py index c97e3835f..5cbedc994 100644 --- a/openlp/plugins/songs/songsplugin.py +++ b/openlp/plugins/songs/songsplugin.py @@ -49,7 +49,7 @@ from openlp.plugins.songs.lib import clean_song, upgrade from openlp.plugins.songs.lib.db import init_schema, Song from openlp.plugins.songs.lib.mediaitem import SongSearch from openlp.plugins.songs.lib.importer import SongFormat -from openlp.plugins.songs.lib.songimport.olpimport import OpenLPSongImport +from openlp.plugins.songs.lib.importers.openlp import OpenLPSongImport from openlp.plugins.songs.lib.mediaitem import SongMediaItem from openlp.plugins.songs.lib.songstab import SongsTab diff --git a/tests/functional/openlp_plugins/songs/test_ewimport.py b/tests/functional/openlp_plugins/songs/test_ewimport.py index c28dc72aa..f441084e7 100644 --- a/tests/functional/openlp_plugins/songs/test_ewimport.py +++ b/tests/functional/openlp_plugins/songs/test_ewimport.py @@ -35,7 +35,7 @@ from unittest import TestCase from tests.functional import MagicMock, patch -from openlp.plugins.songs.lib.songimport.ewimport import EasyWorshipSongImport, FieldDescEntry, FieldType +from openlp.plugins.songs.lib.importers.easyworship import EasyWorshipSongImport, FieldDescEntry, FieldType TEST_PATH = os.path.abspath( os.path.join(os.path.dirname(__file__), '..', '..', '..', 'resources', 'easyworshipsongs')) @@ -178,7 +178,7 @@ class TestEasyWorshipSongImport(TestCase): Test creating an instance of the EasyWorship file importer """ # GIVEN: A mocked out SongImport class, and a mocked out "manager" - with patch('openlp.plugins.songs.lib.songimport.ewimport.SongImport'): + with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'): mocked_manager = MagicMock() # WHEN: An importer object is created @@ -192,7 +192,7 @@ class TestEasyWorshipSongImport(TestCase): Test finding an existing field in a given list using the :mod:`find_field` """ # GIVEN: A mocked out SongImport class, a mocked out "manager" and a list of field descriptions. - with patch('openlp.plugins.songs.lib.songimport.ewimport.SongImport'): + with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'): mocked_manager = MagicMock() importer = EasyWorshipSongImport(mocked_manager, filenames=[]) importer.field_descriptions = TEST_FIELD_DESCS @@ -210,7 +210,7 @@ class TestEasyWorshipSongImport(TestCase): Test finding an non-existing field in a given list using the :mod:`find_field` """ # GIVEN: A mocked out SongImport class, a mocked out "manager" and a list of field descriptions - with patch('openlp.plugins.songs.lib.songimport.ewimport.SongImport'): + with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'): mocked_manager = MagicMock() importer = EasyWorshipSongImport(mocked_manager, filenames=[]) importer.field_descriptions = TEST_FIELD_DESCS @@ -228,8 +228,8 @@ 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.songimport.ewimport.SongImport'), \ - patch('openlp.plugins.songs.lib.songimport.ewimport.struct') as mocked_struct: + with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'), \ + patch('openlp.plugins.songs.lib.importers.easyworship.struct') as mocked_struct: mocked_manager = MagicMock() importer = EasyWorshipSongImport(mocked_manager, filenames=[]) @@ -246,7 +246,7 @@ class TestEasyWorshipSongImport(TestCase): Test the :mod:`get_field` module """ # 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.songimport.ewimport.SongImport'): + with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'): mocked_manager = MagicMock() importer = EasyWorshipSongImport(mocked_manager, filenames=[]) importer.encoding = TEST_DATA_ENCODING @@ -269,7 +269,7 @@ class TestEasyWorshipSongImport(TestCase): """ for test_results in GET_MEMO_FIELD_TEST_RESULTS: # GIVEN: A mocked out SongImport class, a mocked out "manager", a mocked out memo_file and an encoding - with patch('openlp.plugins.songs.lib.songimport.ewimport.SongImport'): + with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'): mocked_manager = MagicMock() mocked_memo_file = MagicMock() importer = EasyWorshipSongImport(mocked_manager, filenames=[]) @@ -300,8 +300,8 @@ class TestEasyWorshipSongImport(TestCase): Test the :mod:`do_import` module opens the correct files """ # GIVEN: A mocked out SongImport class, a mocked out "manager" - with patch('openlp.plugins.songs.lib.songimport.ewimport.SongImport'), \ - patch('openlp.plugins.songs.lib.songimport.ewimport.os.path') as mocked_os_path: + with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'), \ + patch('openlp.plugins.songs.lib.importers.easyworship.os.path') as mocked_os_path: mocked_manager = MagicMock() importer = EasyWorshipSongImport(mocked_manager, filenames=[]) mocked_os_path.isfile.side_effect = [True, False] @@ -319,8 +319,8 @@ class TestEasyWorshipSongImport(TestCase): Test the :mod:`do_import` module produces an error when Songs.MB not found. """ # GIVEN: A mocked out SongImport class, a mocked out "manager" - with patch('openlp.plugins.songs.lib.songimport.ewimport.SongImport'), \ - patch('openlp.plugins.songs.lib.songimport.ewimport.os.path') as mocked_os_path: + with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'), \ + patch('openlp.plugins.songs.lib.importers.easyworship.os.path') as mocked_os_path: mocked_manager = MagicMock() importer = EasyWorshipSongImport(mocked_manager, filenames=[]) importer.log_error = MagicMock() @@ -339,8 +339,8 @@ class TestEasyWorshipSongImport(TestCase): Test the :mod:`do_import` module handles invalid database files correctly """ # GIVEN: A mocked out SongImport class, os.path and a mocked out "manager" - with patch('openlp.plugins.songs.lib.songimport.ewimport.SongImport'), \ - patch('openlp.plugins.songs.lib.songimport.ewimport.os.path') as mocked_os_path: + with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'), \ + patch('openlp.plugins.songs.lib.importers.easyworship.os.path') as mocked_os_path: mocked_manager = MagicMock() importer = EasyWorshipSongImport(mocked_manager, filenames=[]) mocked_os_path.isfile.return_value = True @@ -358,10 +358,10 @@ class TestEasyWorshipSongImport(TestCase): Test the :mod:`do_import` module handles invalid memo files correctly """ # GIVEN: A mocked out SongImport class, a mocked out "manager" - with patch('openlp.plugins.songs.lib.songimport.ewimport.SongImport'), \ - patch('openlp.plugins.songs.lib.songimport.ewimport.os.path') as mocked_os_path, \ + with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'), \ + patch('openlp.plugins.songs.lib.importers.easyworship.os.path') as mocked_os_path, \ patch('builtins.open') as mocked_open, \ - patch('openlp.plugins.songs.lib.songimport.ewimport.struct') as mocked_struct: + patch('openlp.plugins.songs.lib.importers.easyworship.struct') as mocked_struct: mocked_manager = MagicMock() importer = EasyWorshipSongImport(mocked_manager, filenames=[]) mocked_os_path.isfile.return_value = True @@ -385,10 +385,10 @@ class TestEasyWorshipSongImport(TestCase): Test the :mod:`do_import` converts the code page to the encoding correctly """ # GIVEN: A mocked out SongImport class, a mocked out "manager" - with patch('openlp.plugins.songs.lib.songimport.ewimport.SongImport'), \ - patch('openlp.plugins.songs.lib.songimport.ewimport.os.path') as mocked_os_path, \ - patch('builtins.open'), patch('openlp.plugins.songs.lib.songimport.ewimport.struct') as mocked_struct, \ - patch('openlp.plugins.songs.lib.songimport.ewimport.retrieve_windows_encoding') as \ + with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'), \ + patch('openlp.plugins.songs.lib.importers.easyworship.os.path') as mocked_os_path, \ + patch('builtins.open'), patch('openlp.plugins.songs.lib.importers.easyworship.struct') as mocked_struct, \ + patch('openlp.plugins.songs.lib.importers.easyworship.retrieve_windows_encoding') as \ mocked_retrieve_windows_encoding: mocked_manager = MagicMock() importer = EasyWorshipSongImport(mocked_manager, filenames=[]) @@ -413,8 +413,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.songimport.ewimport.SongImport'), \ - patch('openlp.plugins.songs.lib.songimport.ewimport.retrieve_windows_encoding') as \ + with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'), \ + patch('openlp.plugins.songs.lib.importers.easyworship.retrieve_windows_encoding') as \ mocked_retrieve_windows_encoding: mocked_retrieve_windows_encoding.return_value = 'cp1252' mocked_manager = MagicMock() @@ -469,8 +469,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.songimport.ewimport.SongImport'), \ - patch('openlp.plugins.songs.lib.songimport.ewimport.retrieve_windows_encoding') \ + with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'), \ + patch('openlp.plugins.songs.lib.importers.easyworship.retrieve_windows_encoding') \ as mocked_retrieve_windows_encoding: mocked_retrieve_windows_encoding.return_value = 'cp1252' mocked_manager = MagicMock() @@ -509,7 +509,7 @@ class TestEasyWorshipSongImport(TestCase): """ # GIVEN: A mocked out SongImport class, a mocked out "manager" and mocked out "author" method. - with patch('openlp.plugins.songs.lib.songimport.ewimport.SongImport'): + with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'): mocked_manager = MagicMock() mocked_add_author = MagicMock() importer = EasyWorshipSongImportLogger(mocked_manager) diff --git a/tests/functional/openlp_plugins/songs/test_foilpresenterimport.py b/tests/functional/openlp_plugins/songs/test_foilpresenterimport.py index 15247a361..3886443ca 100644 --- a/tests/functional/openlp_plugins/songs/test_foilpresenterimport.py +++ b/tests/functional/openlp_plugins/songs/test_foilpresenterimport.py @@ -34,7 +34,7 @@ import os from unittest import TestCase from tests.functional import patch, MagicMock -from openlp.plugins.songs.lib.songimport.foilpresenterimport import FoilPresenter +from openlp.plugins.songs.lib.importers.foilpresenter import FoilPresenter TEST_PATH = os.path.abspath( os.path.join(os.path.dirname(__file__), '..', '..', '..', '/resources/foilpresentersongs')) @@ -57,27 +57,27 @@ class TestFoilPresenter(TestCase): # _process_topics def setUp(self): - self.child_patcher = patch('openlp.plugins.songs.lib.songimport.foilpresenterimport.FoilPresenter._child') - self.clean_song_patcher = patch('openlp.plugins.songs.lib.songimport.foilpresenterimport.clean_song') - self.objectify_patcher = patch('openlp.plugins.songs.lib.songimport.foilpresenterimport.objectify') + self.child_patcher = patch('openlp.plugins.songs.lib.importers.foilpresenter.FoilPresenter._child') + self.clean_song_patcher = patch('openlp.plugins.songs.lib.importers.foilpresenter.clean_song') + self.objectify_patcher = patch('openlp.plugins.songs.lib.importers.foilpresenter.objectify') self.process_authors_patcher = \ - patch('openlp.plugins.songs.lib.songimport.foilpresenterimport.FoilPresenter._process_authors') + patch('openlp.plugins.songs.lib.importers.foilpresenter.FoilPresenter._process_authors') self.process_cclinumber_patcher = \ - patch('openlp.plugins.songs.lib.songimport.foilpresenterimport.FoilPresenter._process_cclinumber') + patch('openlp.plugins.songs.lib.importers.foilpresenter.FoilPresenter._process_cclinumber') self.process_comments_patcher = \ - patch('openlp.plugins.songs.lib.songimport.foilpresenterimport.FoilPresenter._process_comments') + patch('openlp.plugins.songs.lib.importers.foilpresenter.FoilPresenter._process_comments') self.process_lyrics_patcher = \ - patch('openlp.plugins.songs.lib.songimport.foilpresenterimport.FoilPresenter._process_lyrics') + patch('openlp.plugins.songs.lib.importers.foilpresenter.FoilPresenter._process_lyrics') self.process_songbooks_patcher = \ - patch('openlp.plugins.songs.lib.songimport.foilpresenterimport.FoilPresenter._process_songbooks') + patch('openlp.plugins.songs.lib.importers.foilpresenter.FoilPresenter._process_songbooks') self.process_titles_patcher = \ - patch('openlp.plugins.songs.lib.songimport.foilpresenterimport.FoilPresenter._process_titles') + patch('openlp.plugins.songs.lib.importers.foilpresenter.FoilPresenter._process_titles') self.process_topics_patcher = \ - patch('openlp.plugins.songs.lib.songimport.foilpresenterimport.FoilPresenter._process_topics') - self.re_patcher = patch('openlp.plugins.songs.lib.songimport.foilpresenterimport.re') - self.song_patcher = patch('openlp.plugins.songs.lib.songimport.foilpresenterimport.Song') - self.song_xml_patcher = patch('openlp.plugins.songs.lib.songimport.foilpresenterimport.SongXML') - self.translate_patcher = patch('openlp.plugins.songs.lib.songimport.foilpresenterimport.translate') + patch('openlp.plugins.songs.lib.importers.foilpresenter.FoilPresenter._process_topics') + self.re_patcher = patch('openlp.plugins.songs.lib.importers.foilpresenter.re') + self.song_patcher = patch('openlp.plugins.songs.lib.importers.foilpresenter.Song') + self.song_xml_patcher = patch('openlp.plugins.songs.lib.importers.foilpresenter.SongXML') + self.translate_patcher = patch('openlp.plugins.songs.lib.importers.foilpresenter.translate') self.mocked_child = self.child_patcher.start() self.mocked_clean_song = self.clean_song_patcher.start() diff --git a/tests/functional/openlp_plugins/songs/test_openlyricsimport.py b/tests/functional/openlp_plugins/songs/test_openlyricsimport.py index 2166b950c..25db3e9e4 100644 --- a/tests/functional/openlp_plugins/songs/test_openlyricsimport.py +++ b/tests/functional/openlp_plugins/songs/test_openlyricsimport.py @@ -34,8 +34,8 @@ import os from unittest import TestCase from tests.functional import MagicMock, patch -from openlp.plugins.songs.lib.songimport.openlyricsimport import OpenLyricsImport -from openlp.plugins.songs.lib.songimport.songimport import SongImport +from openlp.plugins.songs.lib.importers.openlyrics import OpenLyricsImport +from openlp.plugins.songs.lib.importers.songimport import SongImport TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..', 'resources', 'openlyricssongs')) @@ -69,7 +69,7 @@ class TestOpenLyricsImport(TestCase): 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.songimport.openlyricsimport.SongImport'): + with patch('openlp.plugins.songs.lib.importers.openlyrics.SongImport'): mocked_manager = MagicMock() # WHEN: An importer object is created diff --git a/tests/functional/openlp_plugins/songs/test_opensongimport.py b/tests/functional/openlp_plugins/songs/test_opensongimport.py index 09fad7bc3..07b275f98 100644 --- a/tests/functional/openlp_plugins/songs/test_opensongimport.py +++ b/tests/functional/openlp_plugins/songs/test_opensongimport.py @@ -34,7 +34,7 @@ import os from unittest import TestCase from tests.helpers.songfileimport import SongImportTestHelper -from openlp.plugins.songs.lib.songimport.opensongimport import OpenSongImport +from openlp.plugins.songs.lib.importers.opensong import OpenSongImport from tests.functional import patch, MagicMock TEST_PATH = os.path.abspath( @@ -45,7 +45,7 @@ class TestOpenSongFileImport(SongImportTestHelper): def __init__(self, *args, **kwargs): self.importer_class_name = 'OpenSongImport' - self.importer_module_name = 'opensongimport' + self.importer_module_name = 'opensong' super(TestOpenSongFileImport, self).__init__(*args, **kwargs) def test_song_import(self): @@ -69,7 +69,7 @@ class TestOpenSongImport(TestCase): Test creating an instance of the OpenSong file importer """ # GIVEN: A mocked out SongImport class, and a mocked out "manager" - with patch('openlp.plugins.songs.lib.songimport.opensongimport.SongImport'): + with patch('openlp.plugins.songs.lib.importers.opensong.SongImport'): mocked_manager = MagicMock() # WHEN: An importer object is created @@ -83,7 +83,7 @@ class TestOpenSongImport(TestCase): Test OpenSongImport.do_import handles different invalid import_source values """ # GIVEN: A mocked out SongImport class, and a mocked out "manager" - with patch('openlp.plugins.songs.lib.songimport.opensongimport.SongImport'): + with patch('openlp.plugins.songs.lib.importers.opensong.SongImport'): mocked_manager = MagicMock() mocked_import_wizard = MagicMock() importer = OpenSongImport(mocked_manager, filenames=[]) @@ -104,7 +104,7 @@ class TestOpenSongImport(TestCase): Test OpenSongImport.do_import handles different invalid import_source values """ # GIVEN: A mocked out SongImport class, and a mocked out "manager" - with patch('openlp.plugins.songs.lib.songimport.opensongimport.SongImport'): + with patch('openlp.plugins.songs.lib.importers.opensong.SongImport'): mocked_manager = MagicMock() mocked_import_wizard = MagicMock() importer = OpenSongImport(mocked_manager, filenames=[]) diff --git a/tests/functional/openlp_plugins/songs/test_propresenterimport.py b/tests/functional/openlp_plugins/songs/test_propresenterimport.py index 10e2defc6..bc313e250 100644 --- a/tests/functional/openlp_plugins/songs/test_propresenterimport.py +++ b/tests/functional/openlp_plugins/songs/test_propresenterimport.py @@ -43,7 +43,7 @@ class TestProPresenterFileImport(SongImportTestHelper): def __init__(self, *args, **kwargs): self.importer_class_name = 'ProPresenterImport' - self.importer_module_name = 'propresenterimport' + self.importer_module_name = 'propresenter' super(TestProPresenterFileImport, self).__init__(*args, **kwargs) def test_song_import(self): diff --git a/tests/functional/openlp_plugins/songs/test_songbeamerimport.py b/tests/functional/openlp_plugins/songs/test_songbeamerimport.py index 9ca0ab790..3d872ae65 100644 --- a/tests/functional/openlp_plugins/songs/test_songbeamerimport.py +++ b/tests/functional/openlp_plugins/songs/test_songbeamerimport.py @@ -34,7 +34,7 @@ import os from unittest import TestCase from tests.functional import MagicMock, patch -from openlp.plugins.songs.lib.songimport.songbeamerimport import SongBeamerImport +from openlp.plugins.songs.lib.importers.songbeamer import SongBeamerImport from openlp.plugins.songs.lib import VerseType TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), @@ -64,7 +64,7 @@ class TestSongBeamerImport(TestCase): Test creating an instance of the SongBeamer file importer """ # GIVEN: A mocked out SongImport class, and a mocked out "manager" - with patch('openlp.plugins.songs.lib.songimport.songbeamerimport.SongImport'): + with patch('openlp.plugins.songs.lib.importers.songbeamer.SongImport'): mocked_manager = MagicMock() # WHEN: An importer object is created @@ -78,7 +78,7 @@ class TestSongBeamerImport(TestCase): Test SongBeamerImport.do_import handles different invalid import_source values """ # GIVEN: A mocked out SongImport class, and a mocked out "manager" - with patch('openlp.plugins.songs.lib.songimport.songbeamerimport.SongImport'): + with patch('openlp.plugins.songs.lib.importers.songbeamer.SongImport'): mocked_manager = MagicMock() mocked_import_wizard = MagicMock() importer = SongBeamerImport(mocked_manager, filenames=[]) @@ -99,7 +99,7 @@ class TestSongBeamerImport(TestCase): Test SongBeamerImport.do_import handles different invalid import_source values """ # GIVEN: A mocked out SongImport class, and a mocked out "manager" - with patch('openlp.plugins.songs.lib.songimport.songbeamerimport.SongImport'): + with patch('openlp.plugins.songs.lib.importers.songbeamer.SongImport'): mocked_manager = MagicMock() mocked_import_wizard = MagicMock() importer = SongBeamerImport(mocked_manager, filenames=[]) @@ -122,7 +122,7 @@ class TestSongBeamerImport(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.songimport.songbeamerimport.SongImport'): + with patch('openlp.plugins.songs.lib.importers.songbeamer.SongImport'): for song_file in SONG_TEST_DATA: mocked_manager = MagicMock() mocked_import_wizard = MagicMock() diff --git a/tests/functional/openlp_plugins/songs/test_songshowplusimport.py b/tests/functional/openlp_plugins/songs/test_songshowplusimport.py index 8d6a42287..77e1196bc 100644 --- a/tests/functional/openlp_plugins/songs/test_songshowplusimport.py +++ b/tests/functional/openlp_plugins/songs/test_songshowplusimport.py @@ -35,7 +35,7 @@ from unittest import TestCase from tests.helpers.songfileimport import SongImportTestHelper from openlp.plugins.songs.lib import VerseType -from openlp.plugins.songs.lib.songimport.songshowplusimport import SongShowPlusImport +from openlp.plugins.songs.lib.importers.songshowplus import SongShowPlusImport from tests.functional import patch, MagicMock TEST_PATH = os.path.abspath( @@ -46,7 +46,7 @@ class TestSongShowPlusFileImport(SongImportTestHelper): def __init__(self, *args, **kwargs): self.importer_class_name = 'SongShowPlusImport' - self.importer_module_name = 'songshowplusimport' + self.importer_module_name = 'songshowplus' super(TestSongShowPlusFileImport, self).__init__(*args, **kwargs) def test_song_import(self): @@ -70,7 +70,7 @@ class TestSongShowPlusImport(TestCase): Test creating an instance of the SongShow Plus file importer """ # GIVEN: A mocked out SongImport class, and a mocked out "manager" - with patch('openlp.plugins.songs.lib.songimport.songshowplusimport.SongImport'): + with patch('openlp.plugins.songs.lib.importers.songshowplus.SongImport'): mocked_manager = MagicMock() # WHEN: An importer object is created @@ -84,7 +84,7 @@ class TestSongShowPlusImport(TestCase): Test SongShowPlusImport.do_import handles different invalid import_source values """ # GIVEN: A mocked out SongImport class, and a mocked out "manager" - with patch('openlp.plugins.songs.lib.songimport.songshowplusimport.SongImport'): + with patch('openlp.plugins.songs.lib.importers.songshowplus.SongImport'): mocked_manager = MagicMock() mocked_import_wizard = MagicMock() importer = SongShowPlusImport(mocked_manager, filenames=[]) @@ -105,7 +105,7 @@ class TestSongShowPlusImport(TestCase): Test SongShowPlusImport.do_import handles different invalid import_source values """ # GIVEN: A mocked out SongImport class, and a mocked out "manager" - with patch('openlp.plugins.songs.lib.songimport.songshowplusimport.SongImport'): + with patch('openlp.plugins.songs.lib.importers.songshowplus.SongImport'): mocked_manager = MagicMock() mocked_import_wizard = MagicMock() importer = SongShowPlusImport(mocked_manager, filenames=[]) @@ -126,7 +126,7 @@ class TestSongShowPlusImport(TestCase): Test to_openlp_verse_tag method by simulating adding a verse """ # GIVEN: A mocked out SongImport class, and a mocked out "manager" - with patch('openlp.plugins.songs.lib.songimport.songshowplusimport.SongImport'): + with patch('openlp.plugins.songs.lib.importers.songshowplus.SongImport'): mocked_manager = MagicMock() importer = SongShowPlusImport(mocked_manager, filenames=[]) @@ -154,7 +154,7 @@ class TestSongShowPlusImport(TestCase): Test to_openlp_verse_tag method by simulating adding a verse to the verse order """ # GIVEN: A mocked out SongImport class, and a mocked out "manager" - with patch('openlp.plugins.songs.lib.songimport.songshowplusimport.SongImport'): + with patch('openlp.plugins.songs.lib.importers.songshowplus.SongImport'): mocked_manager = MagicMock() importer = SongShowPlusImport(mocked_manager, filenames=[]) diff --git a/tests/functional/openlp_plugins/songs/test_worshipassistantimport.py b/tests/functional/openlp_plugins/songs/test_worshipassistantimport.py index c0c5c7416..63ead5b30 100644 --- a/tests/functional/openlp_plugins/songs/test_worshipassistantimport.py +++ b/tests/functional/openlp_plugins/songs/test_worshipassistantimport.py @@ -43,7 +43,7 @@ class TestWorshipAssistantFileImport(SongImportTestHelper): def __init__(self, *args, **kwargs): self.importer_class_name = 'WorshipAssistantImport' - self.importer_module_name = 'worshipassistantimport' + self.importer_module_name = 'worshipassistant' super(TestWorshipAssistantFileImport, self).__init__(*args, **kwargs) def test_song_import(self): diff --git a/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py b/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py index 0558ad195..cd51e3384 100644 --- a/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py +++ b/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py @@ -37,7 +37,7 @@ if os.name != 'nt': import pyodbc -from openlp.plugins.songs.lib.songimport.worshipcenterproimport import WorshipCenterProImport +from openlp.plugins.songs.lib.importers.worshipcenterpro import WorshipCenterProImport from tests.functional import patch, MagicMock @@ -141,7 +141,7 @@ class TestWorshipCenterProSongImport(TestCase): Test creating an instance of the WorshipCenter Pro file importer """ # GIVEN: A mocked out SongImport class, and a mocked out "manager" - with patch('openlp.plugins.songs.lib.songimport.worshipcenterproimport.SongImport'): + with patch('openlp.plugins.songs.lib.importers.worshipcenterpro.SongImport'): mocked_manager = MagicMock() # WHEN: An importer object is created @@ -156,10 +156,10 @@ class TestWorshipCenterProSongImport(TestCase): """ # GIVEN: A mocked out SongImport class, a mocked out pyodbc module, a mocked out translate method, # a mocked "manager" and a mocked out log_error method. - with patch('openlp.plugins.songs.lib.songimport.worshipcenterproimport.SongImport'), \ - patch('openlp.plugins.songs.lib.songimport.worshipcenterproimport.pyodbc.connect') \ + with patch('openlp.plugins.songs.lib.importers.worshipcenterpro.SongImport'), \ + patch('openlp.plugins.songs.lib.importers.worshipcenterpro.pyodbc.connect') \ as mocked_pyodbc_connect, \ - patch('openlp.plugins.songs.lib.songimport.worshipcenterproimport.translate') as mocked_translate: + patch('openlp.plugins.songs.lib.importers.worshipcenterpro.translate') as mocked_translate: mocked_manager = MagicMock() mocked_log_error = MagicMock() mocked_translate.return_value = 'Translated Text' @@ -186,9 +186,9 @@ class TestWorshipCenterProSongImport(TestCase): """ # GIVEN: A mocked out SongImport class, a mocked out pyodbc module with a simulated recordset, a mocked out # translate method, a mocked "manager", add_verse method & mocked_finish method. - with patch('openlp.plugins.songs.lib.songimport.worshipcenterproimport.SongImport'), \ - patch('openlp.plugins.songs.lib.songimport.worshipcenterproimport.pyodbc') as mocked_pyodbc, \ - patch('openlp.plugins.songs.lib.songimport.worshipcenterproimport.translate') as mocked_translate: + with patch('openlp.plugins.songs.lib.importers.worshipcenterpro.SongImport'), \ + patch('openlp.plugins.songs.lib.importers.worshipcenterpro.pyodbc') as mocked_pyodbc, \ + patch('openlp.plugins.songs.lib.importers.worshipcenterpro.translate') as mocked_translate: mocked_manager = MagicMock() mocked_import_wizard = MagicMock() mocked_add_verse = MagicMock() diff --git a/tests/functional/openlp_plugins/songs/test_zionworximport.py b/tests/functional/openlp_plugins/songs/test_zionworximport.py index a3f5bb20b..faedc7005 100644 --- a/tests/functional/openlp_plugins/songs/test_zionworximport.py +++ b/tests/functional/openlp_plugins/songs/test_zionworximport.py @@ -33,8 +33,8 @@ This module contains tests for the ZionWorx song importer. from unittest import TestCase from tests.functional import MagicMock, patch -from openlp.plugins.songs.lib.songimport.zionworximport import ZionWorxImport -from openlp.plugins.songs.lib.songimport.songimport import SongImport +from openlp.plugins.songs.lib.importers.zionworx import ZionWorxImport +from openlp.plugins.songs.lib.importers.songimport import SongImport class TestZionWorxImport(TestCase): @@ -46,7 +46,7 @@ class TestZionWorxImport(TestCase): 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.songimport.zionworximport.SongImport'): + with patch('openlp.plugins.songs.lib.importers.zionworx.SongImport'): mocked_manager = MagicMock() # WHEN: An importer object is created diff --git a/tests/helpers/songfileimport.py b/tests/helpers/songfileimport.py index 003ade543..6fae4d31d 100644 --- a/tests/helpers/songfileimport.py +++ b/tests/helpers/songfileimport.py @@ -43,7 +43,7 @@ class SongImportTestHelper(TestCase): def __init__(self, *args, **kwargs): super(SongImportTestHelper, self).__init__(*args, **kwargs) self.importer_module = __import__( - 'openlp.plugins.songs.lib.songimport.%s' % self.importer_module_name, fromlist=[self.importer_class_name]) + 'openlp.plugins.songs.lib.importers.%s' % self.importer_module_name, fromlist=[self.importer_class_name]) self.importer_class = getattr(self.importer_module, self.importer_class_name) def setUp(self): @@ -51,17 +51,17 @@ class SongImportTestHelper(TestCase): Patch and set up the mocks required. """ self.add_copyright_patcher = patch( - 'openlp.plugins.songs.lib.songimport.%s.%s.add_copyright' % (self.importer_module_name, + 'openlp.plugins.songs.lib.importers.%s.%s.add_copyright' % (self.importer_module_name, self.importer_class_name)) self.add_verse_patcher = patch( - 'openlp.plugins.songs.lib.songimport.%s.%s.add_verse' % (self.importer_module_name, + 'openlp.plugins.songs.lib.importers.%s.%s.add_verse' % (self.importer_module_name, self.importer_class_name)) self.finish_patcher = patch( - 'openlp.plugins.songs.lib.songimport.%s.%s.finish' % (self.importer_module_name, self.importer_class_name)) + 'openlp.plugins.songs.lib.importers.%s.%s.finish' % (self.importer_module_name, self.importer_class_name)) self.add_author_patcher = patch( - 'openlp.plugins.songs.lib.songimport.%s.%s.add_author' % (self.importer_module_name, + 'openlp.plugins.songs.lib.importers.%s.%s.add_author' % (self.importer_module_name, self.importer_class_name)) - self.song_import_patcher = patch('openlp.plugins.songs.lib.songimport.%s.SongImport' % + self.song_import_patcher = patch('openlp.plugins.songs.lib.importers.%s.SongImport' % self.importer_module_name) self.mocked_add_copyright = self.add_copyright_patcher.start() self.mocked_add_verse = self.add_verse_patcher.start() From f089e7522e762f4ccb1a26f66bea19dd8c61801e Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Fri, 4 Jul 2014 11:35:10 +0200 Subject: [PATCH 210/212] Cleanups --- openlp/plugins/songs/lib/importers/dreambeam.py | 3 +-- openlp/plugins/songs/lib/importers/easyworship.py | 3 +-- openlp/plugins/songs/lib/importers/mediashout.py | 2 +- openlp/plugins/songs/lib/importers/openlp.py | 2 +- openlp/plugins/songs/lib/importers/openlyrics.py | 2 +- openlp/plugins/songs/lib/importers/powersong.py | 2 +- openlp/plugins/songs/lib/importers/propresenter.py | 2 +- openlp/plugins/songs/lib/importers/songbeamer.py | 2 +- openlp/plugins/songs/lib/importers/songpro.py | 2 +- openlp/plugins/songs/lib/importers/songshowplus.py | 2 +- openlp/plugins/songs/lib/importers/wordsofworship.py | 5 ++--- openlp/plugins/songs/lib/importers/zionworx.py | 3 +-- 12 files changed, 13 insertions(+), 17 deletions(-) diff --git a/openlp/plugins/songs/lib/importers/dreambeam.py b/openlp/plugins/songs/lib/importers/dreambeam.py index 38aab4ae7..458961df0 100644 --- a/openlp/plugins/songs/lib/importers/dreambeam.py +++ b/openlp/plugins/songs/lib/importers/dreambeam.py @@ -27,8 +27,7 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### """ -The :mod:`dreambeamimport` module provides the functionality for importing -DreamBeam songs into the OpenLP database. +The :mod:`dreambeam` module provides the functionality for importing DreamBeam songs into the OpenLP database. """ import logging diff --git a/openlp/plugins/songs/lib/importers/easyworship.py b/openlp/plugins/songs/lib/importers/easyworship.py index c56e1dba1..761f83f59 100644 --- a/openlp/plugins/songs/lib/importers/easyworship.py +++ b/openlp/plugins/songs/lib/importers/easyworship.py @@ -27,8 +27,7 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### """ -The :mod:`ewimport` module provides the functionality for importing -EasyWorship song databases into the current installation database. +The :mod:`easyworship` module provides the functionality for importing EasyWorship song databases into OpenLP. """ import os diff --git a/openlp/plugins/songs/lib/importers/mediashout.py b/openlp/plugins/songs/lib/importers/mediashout.py index d82304fae..19d1b1d9d 100644 --- a/openlp/plugins/songs/lib/importers/mediashout.py +++ b/openlp/plugins/songs/lib/importers/mediashout.py @@ -27,7 +27,7 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### """ -The :mod:`mediashoutimport` module provides the functionality for importing +The :mod:`mediashout` module provides the functionality for importing a MediaShout database into the OpenLP database. """ import pyodbc diff --git a/openlp/plugins/songs/lib/importers/openlp.py b/openlp/plugins/songs/lib/importers/openlp.py index f4b066ef0..1a27e8d69 100644 --- a/openlp/plugins/songs/lib/importers/openlp.py +++ b/openlp/plugins/songs/lib/importers/openlp.py @@ -27,7 +27,7 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### """ -The :mod:`olpimport` module provides the functionality for importing OpenLP +The :mod:`openlp` module provides the functionality for importing OpenLP song databases into the current installation database. """ import logging diff --git a/openlp/plugins/songs/lib/importers/openlyrics.py b/openlp/plugins/songs/lib/importers/openlyrics.py index 78fcbf2af..9bb20dbf4 100644 --- a/openlp/plugins/songs/lib/importers/openlyrics.py +++ b/openlp/plugins/songs/lib/importers/openlyrics.py @@ -27,7 +27,7 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### """ -The :mod:`openlyricsimport` module provides the functionality for importing +The :mod:`openlyrics` module provides the functionality for importing songs which are saved as OpenLyrics files. """ diff --git a/openlp/plugins/songs/lib/importers/powersong.py b/openlp/plugins/songs/lib/importers/powersong.py index 89c847ab0..5aa0038f4 100644 --- a/openlp/plugins/songs/lib/importers/powersong.py +++ b/openlp/plugins/songs/lib/importers/powersong.py @@ -27,7 +27,7 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### """ -The :mod:`powersongimport` module provides the functionality for importing +The :mod:`powersong` module provides the functionality for importing PowerSong songs into the OpenLP database. """ import logging diff --git a/openlp/plugins/songs/lib/importers/propresenter.py b/openlp/plugins/songs/lib/importers/propresenter.py index 6ce3c0819..3bf7f9cd8 100644 --- a/openlp/plugins/songs/lib/importers/propresenter.py +++ b/openlp/plugins/songs/lib/importers/propresenter.py @@ -27,7 +27,7 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### """ -The :mod:`propresenterimport` module provides the functionality for importing +The :mod:`propresenter` module provides the functionality for importing ProPresenter song files into the current installation database. """ diff --git a/openlp/plugins/songs/lib/importers/songbeamer.py b/openlp/plugins/songs/lib/importers/songbeamer.py index 4c5873e2a..9a7429f02 100644 --- a/openlp/plugins/songs/lib/importers/songbeamer.py +++ b/openlp/plugins/songs/lib/importers/songbeamer.py @@ -27,7 +27,7 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### """ -The :mod:`songbeamerimport` module provides the functionality for importing SongBeamer songs into the OpenLP database. +The :mod:`songbeamer` module provides the functionality for importing SongBeamer songs into the OpenLP database. """ import chardet import codecs diff --git a/openlp/plugins/songs/lib/importers/songpro.py b/openlp/plugins/songs/lib/importers/songpro.py index 52d702097..efe1a85ea 100644 --- a/openlp/plugins/songs/lib/importers/songpro.py +++ b/openlp/plugins/songs/lib/importers/songpro.py @@ -27,7 +27,7 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### """ -The :mod:`songproimport` module provides the functionality for importing SongPro +The :mod:`songpro` module provides the functionality for importing SongPro songs into the OpenLP database. """ import re diff --git a/openlp/plugins/songs/lib/importers/songshowplus.py b/openlp/plugins/songs/lib/importers/songshowplus.py index d53cc33f1..6c9feab68 100644 --- a/openlp/plugins/songs/lib/importers/songshowplus.py +++ b/openlp/plugins/songs/lib/importers/songshowplus.py @@ -27,7 +27,7 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### """ -The :mod:`songshowplusimport` module provides the functionality for importing SongShow Plus songs into the OpenLP +The :mod:`songshowplus` module provides the functionality for importing SongShow Plus songs into the OpenLP database. """ import chardet diff --git a/openlp/plugins/songs/lib/importers/wordsofworship.py b/openlp/plugins/songs/lib/importers/wordsofworship.py index ce80c19ce..1b398c604 100644 --- a/openlp/plugins/songs/lib/importers/wordsofworship.py +++ b/openlp/plugins/songs/lib/importers/wordsofworship.py @@ -27,7 +27,7 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### """ -The :mod:`wowimport` module provides the functionality for importing Words of +The :mod:`wordsofworship` module provides the functionality for importing Words of Worship songs into the OpenLP database. """ import os @@ -43,8 +43,7 @@ log = logging.getLogger(__name__) class WordsOfWorshipImport(SongImport): """ - The :class:`WowImport` class provides the ability to import song files from - Words of Worship. + The :class:`WordsOfWorshipImport` class provides the ability to import song files from Words of Worship. **Words Of Worship Song File Format:** diff --git a/openlp/plugins/songs/lib/importers/zionworx.py b/openlp/plugins/songs/lib/importers/zionworx.py index 0b97aee26..ed3c41f3a 100644 --- a/openlp/plugins/songs/lib/importers/zionworx.py +++ b/openlp/plugins/songs/lib/importers/zionworx.py @@ -27,8 +27,7 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### """ -The :mod:`zionworximport` module provides the functionality for importing -ZionWorx songs into the OpenLP database. +The :mod:`zionworx` module provides the functionality for importing ZionWorx songs into the OpenLP database. """ import csv import logging From 0fb445e1177e38d6082fd06814bed888f89735ef Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Fri, 4 Jul 2014 11:36:54 +0200 Subject: [PATCH 211/212] PEP8 --- tests/helpers/songfileimport.py | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/tests/helpers/songfileimport.py b/tests/helpers/songfileimport.py index 6fae4d31d..18e7914b9 100644 --- a/tests/helpers/songfileimport.py +++ b/tests/helpers/songfileimport.py @@ -42,25 +42,22 @@ class SongImportTestHelper(TestCase): """ def __init__(self, *args, **kwargs): super(SongImportTestHelper, self).__init__(*args, **kwargs) - self.importer_module = __import__( - 'openlp.plugins.songs.lib.importers.%s' % self.importer_module_name, fromlist=[self.importer_class_name]) + self.importer_module = __import__('openlp.plugins.songs.lib.importers.%s' % + self.importer_module_name, fromlist=[self.importer_class_name]) self.importer_class = getattr(self.importer_module, self.importer_class_name) def setUp(self): """ Patch and set up the mocks required. """ - self.add_copyright_patcher = patch( - 'openlp.plugins.songs.lib.importers.%s.%s.add_copyright' % (self.importer_module_name, - self.importer_class_name)) - self.add_verse_patcher = patch( - 'openlp.plugins.songs.lib.importers.%s.%s.add_verse' % (self.importer_module_name, - self.importer_class_name)) - self.finish_patcher = patch( - 'openlp.plugins.songs.lib.importers.%s.%s.finish' % (self.importer_module_name, self.importer_class_name)) - self.add_author_patcher = patch( - 'openlp.plugins.songs.lib.importers.%s.%s.add_author' % (self.importer_module_name, - self.importer_class_name)) + self.add_copyright_patcher = patch('openlp.plugins.songs.lib.importers.%s.%s.add_copyright' % + (self.importer_module_name, self.importer_class_name)) + self.add_verse_patcher = patch('openlp.plugins.songs.lib.importers.%s.%s.add_verse' % + (self.importer_module_name, self.importer_class_name)) + self.finish_patcher = patch('openlp.plugins.songs.lib.importers.%s.%s.finish' % + (self.importer_module_name, self.importer_class_name)) + self.add_author_patcher = patch('openlp.plugins.songs.lib.importers.%s.%s.add_author' % + (self.importer_module_name, self.importer_class_name)) self.song_import_patcher = patch('openlp.plugins.songs.lib.importers.%s.SongImport' % self.importer_module_name) self.mocked_add_copyright = self.add_copyright_patcher.start() From 2938df5e022ed3aa2f1240742196ec14c701fb3e Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sun, 6 Jul 2014 16:19:12 +0100 Subject: [PATCH 212/212] remove german --- resources/openlp.desktop | 2 -- 1 file changed, 2 deletions(-) diff --git a/resources/openlp.desktop b/resources/openlp.desktop index d585f4022..9f92f9d87 100755 --- a/resources/openlp.desktop +++ b/resources/openlp.desktop @@ -1,11 +1,9 @@ [Desktop Entry] Categories=AudioVideo; Exec=openlp %F -GenericName[de]=Church lyrics projection GenericName=Church lyrics projection Icon=openlp MimeType=application/x-openlp-service; -Name[de]=OpenLP Name=OpenLP StartupNotify=true Terminal=false