From 13374f1002d9014f2471e0749ace17e6049983df Mon Sep 17 00:00:00 2001 From: "Jeffrey S. Smith" Date: Mon, 2 Sep 2013 23:07:29 -0500 Subject: [PATCH 01/73] 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 d62b92718d544c17f6e2fa0f5a85b6f573669cc1 Mon Sep 17 00:00:00 2001 From: Stewart Becker Date: Sat, 19 Apr 2014 22:57:49 +0100 Subject: [PATCH 02/73] 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 e6162e033f0d3b1039c468ea687e6c40bbea8c8b Mon Sep 17 00:00:00 2001 From: Stewart Becker Date: Mon, 21 Apr 2014 16:49:41 +0100 Subject: [PATCH 03/73] 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 04/73] 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 05/73] 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 06/73] 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 07/73] 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 08/73] 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 d39e1fd4a28faf55d2f9727653ea75bd25ca39ac Mon Sep 17 00:00:00 2001 From: Stewart Becker Date: Tue, 22 Apr 2014 22:43:12 +0100 Subject: [PATCH 09/73] 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 10/73] 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 4022cff5e58a2e634b23d22e639b529a2982e62a Mon Sep 17 00:00:00 2001 From: Stewart Becker Date: Wed, 30 Apr 2014 21:39:40 +0100 Subject: [PATCH 11/73] 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 12/73] 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 0d48c09180b0648ca476c9977ef3392a48b4211d Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Tue, 6 May 2014 21:53:08 +0200 Subject: [PATCH 13/73] 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 14/73] 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 15/73] 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 16/73] 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 17/73] 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 18/73] 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 19/73] 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 20/73] 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 21/73] 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 ec7a7b74fee96699a01ec1a13e4638bb75adb36a Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sat, 10 May 2014 08:38:38 +0100 Subject: [PATCH 22/73] 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 23/73] 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 24/73] 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 25/73] 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 26/73] 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 27/73] 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 28/73] 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 29/73] 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 30/73] 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 31/73] 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 32/73] 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 33/73] 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 34/73] 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 35/73] 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 36/73] 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 37/73] 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 38/73] 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 39/73] 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 40/73] 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 41/73] 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 42/73] 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 43/73] 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 44/73] 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 45/73] 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 46/73] 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 47/73] 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 48/73] 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 49/73] 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 50/73] 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 51/73] 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 52/73] 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 53/73] 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 54/73] 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 55/73] 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 56/73] 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 57/73] 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 58/73] 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 59/73] 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 60/73] 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 61/73] 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 62/73] 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 63/73] 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 64/73] 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 65/73] 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 8982839794623612ba97fd9bb62f35cc81f0d176 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Tue, 10 Jun 2014 10:33:02 +0200 Subject: [PATCH 67/73] 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 68/73] 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 69/73] 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 70/73] 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 71/73] 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 72/73] 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 73/73] 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'