From 5aee3198aebd632890e4333d2e6f8f7ac7696bd6 Mon Sep 17 00:00:00 2001 From: phill-ridout Date: Sat, 13 Apr 2013 23:01:36 +0100 Subject: [PATCH 1/8] Added WorshipCenter Pro song importer. Also added associated tests. --- openlp/plugins/songs/lib/importer.py | 26 ++- .../songs/lib/worshipcenterproimport.py | 79 ++++++++ .../songs/test_worshipcenterproimport.py | 170 ++++++++++++++++++ 3 files changed, 273 insertions(+), 2 deletions(-) create mode 100644 openlp/plugins/songs/lib/worshipcenterproimport.py create mode 100644 tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py diff --git a/openlp/plugins/songs/lib/importer.py b/openlp/plugins/songs/lib/importer.py index ff018abf3..9db34bed6 100644 --- a/openlp/plugins/songs/lib/importer.py +++ b/openlp/plugins/songs/lib/importer.py @@ -76,7 +76,13 @@ if os.name == u'nt': HAS_MEDIASHOUT = True except ImportError: log.exception('Error importing %s', 'MediaShoutImport') - +HAS_WORSHIPCENTERPRO = False +if os.name == u'nt': + try: + from worshipcenterproimport import WorshipCenterProImport + HAS_WORSHIPCENTERPRO = True + except ImportError: + log.exception('Error importing %s', 'WorshipCenterProImport') class SongFormatSelect(object): """ @@ -160,7 +166,8 @@ class SongFormat(object): SongsOfFellowship = 15 SundayPlus = 16 WordsOfWorship = 17 - ZionWorx = 18 + WorshipCenterPro = 18 + ZionWorx = 19 # Set optional attribute defaults __defaults__ = { @@ -311,6 +318,16 @@ class SongFormat(object): u'filter': u'%s (*.wsg *.wow-song)' % translate('SongsPlugin.ImportWizardForm', 'Words Of Worship Song Files') }, + WorshipCenterPro: { + u'name': u'WorshipCenter Pro', + u'prefix': u'worshipCenterPro', + u'canDisable': True, + u'selectMode': SongFormatSelect.SingleFile, + u'filter': u'%s (*.mdb)' % translate('SongsPlugin.ImportWizardForm', 'WorshipCenter Pro Song Files'), + u'disabledLabelText': translate('SongsPlugin.ImportWizardForm', + 'The WorshipCenter Pro importer is only supported on Windows. It has been disabled due to a missing ' + 'Python module. If you want to use this importer, you will need to install the "pyodbc" module.') + }, ZionWorx: { u'class': ZionWorxImport, u'name': u'ZionWorx', @@ -348,6 +365,7 @@ class SongFormat(object): SongFormat.SongsOfFellowship, SongFormat.SundayPlus, SongFormat.WordsOfWorship, + SongFormat.WorshipCenterPro, SongFormat.ZionWorx ] @@ -399,5 +417,9 @@ if HAS_OOO: SongFormat.set(SongFormat.MediaShout, u'availability', HAS_MEDIASHOUT) if HAS_MEDIASHOUT: SongFormat.set(SongFormat.MediaShout, u'class', MediaShoutImport) +SongFormat.set(SongFormat.WorshipCenterPro, u'availability', HAS_WORSHIPCENTERPRO) +if HAS_WORSHIPCENTERPRO: + SongFormat.set(SongFormat.WorshipCenterPro, u'class', WorshipCenterProImport) + __all__ = [u'SongFormat', u'SongFormatSelect'] diff --git a/openlp/plugins/songs/lib/worshipcenterproimport.py b/openlp/plugins/songs/lib/worshipcenterproimport.py new file mode 100644 index 000000000..47ce8029d --- /dev/null +++ b/openlp/plugins/songs/lib/worshipcenterproimport.py @@ -0,0 +1,79 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-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:`worshipcenterpro` module provides the functionality for importing +a WorshipCenter Pro database into the OpenLP database. +""" +import pyodbc + +from openlp.core.lib import translate +from openlp.plugins.songs.lib.songimport import SongImport + +class WorshipCenterProImport(SongImport): + """ + The :class:`WorshipCenterProImport` class provides the ability to import the + WorshipCenter Pro Access Database + """ + def __init__(self, manager, **kwargs): + """ + Initialise the WorshipCenter Pro importer. + """ + SongImport.__init__(self, manager, **kwargs) + + def doImport(self): + """ + Receive a single file to import. + """ + try: + conn = pyodbc.connect(u'DRIVER={Microsoft Access Driver (*.mdb)};DBQ=%s' % self.import_source) + except: + # Unfortunately no specific exception type + self.logError(self.import_source, + translate('SongsPlugin.WorshipCenterProImport', 'Unable to connect the WorshipCenter Pro database.')) + return + cursor = conn.cursor() + cursor.execute(u'SELECT ID, Field, Value FROM __SONGDATA') + records = cursor.fetchall() + songs = {} + for record in records: + id = record.ID + if not songs.has_key(id): + songs[id] = {} + songs[id][record.Field] = record.Value + self.import_wizard.progress_bar.setMaximum(len(songs)) + for song in songs: + if self.stop_import_flag: + break + self.setDefaults() + self.title = songs[song][u'TITLE'] + lyrics = songs[song][u'LYRICS'].strip(u'&crlf;&crlf;') + for verse in lyrics.split(u'&crlf;&crlf;'): + verse = verse.replace(u'&crlf;', u'\n') + self.addVerse(verse) + self.finish() diff --git a/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py b/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py new file mode 100644 index 000000000..920b403ff --- /dev/null +++ b/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py @@ -0,0 +1,170 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +""" +This module contains tests for the WorshipCenter/ Pro song importer. +""" + +from unittest import TestCase +from mock import patch, MagicMock + +from openlp.plugins.songs.lib.worshipcenterproimport import WorshipCenterProImport + +class TestRecord: + """ + Microsoft Access Driver is not available on non Microsoft Systems for this reason the :class:`TestRecord` is used + to simulate a recordset that would be returned by pyobdc. + """ + def __init__(self, id, field, value): + # The case of the following instance variables is important as it needs to be the same as the ones in use in the + # WorshipCenter Pro database. + self.ID = id + self.Field = field + self.Value = value + + +RECORDSET_TEST_DATA = [TestRecord(1, u'TITLE', u'Amazing Grace'), + TestRecord(1, u'LYRICS', + u'Amazing grace! How&crlf;sweet the sound&crlf;That saved a wretch like me!&crlf;' + u'I once was lost,&crlf;but now am found;&crlf;Was blind, but now I see.&crlf;&crlf;' + u'\'Twas grace that&crlf;taught my heart to fear,&crlf;And grace my fears relieved;&crlf;' + u'How precious did&crlf;that grace appear&crlf;The hour I first believed.&crlf;&crlf;' + u'Through many dangers,&crlf;toils and snares,&crlf;I have already come;&crlf;' + u'\'Tis grace hath brought&crlf;me safe thus far,&crlf;' + u'And grace will lead me home.&crlf;&crlf;The Lord has&crlf;promised good to me,&crlf;' + u'His Word my hope secures;&crlf;He will my Shield&crlf;and Portion be,&crlf;' + u'As long as life endures.&crlf;&crlf;Yea, when this flesh&crlf;and heart shall fail,&crlf;' + u'And mortal life shall cease,&crlf;I shall possess,&crlf;within the veil,&crlf;' + u'A life of joy and peace.&crlf;&crlf;The earth shall soon&crlf;dissolve like snow,&crlf;' + u'The sun forbear to shine;&crlf;But God, Who called&crlf;me here below,&crlf;' + u'Shall be forever mine.&crlf;&crlf;When we\'ve been there&crlf;ten thousand years,&crlf;' + u'Bright shining as the sun,&crlf;We\'ve no less days to&crlf;sing God\'s praise&crlf;' + u'Than when we\'d first begun.&crlf;&crlf;'), + TestRecord(2, u'TITLE', u'Beautiful Garden Of Prayer, The'), + TestRecord(2, u'LYRICS', + u'There\'s a garden where&crlf;Jesus is waiting,&crlf;' + u'There\'s a place that&crlf;is wondrously fair,&crlf;For it glows with the&crlf;' + u'light of His presence.&crlf;\'Tis the beautiful&crlf;garden of prayer.&crlf;&crlf;' + u'Oh, the beautiful garden,&crlf;the garden of prayer!&crlf;Oh, the beautiful&crlf;' + u'garden of prayer!&crlf;There my Savior awaits,&crlf;and He opens the gates&crlf;' + u'To the beautiful&crlf;garden of prayer.&crlf;&crlf;There\'s a garden where&crlf;' + u'Jesus is waiting,&crlf;And I go with my&crlf;burden and care,&crlf;' + u'Just to learn from His&crlf;lips words of comfort&crlf;In the beautiful&crlf;' + u'garden of prayer.&crlf;&crlf;There\'s a garden where&crlf;Jesus is waiting,&crlf;' + u'And He bids you to come,&crlf;meet Him there;&crlf;Just to bow and&crlf;' + u'receive a new blessing&crlf;In the beautiful&crlf;garden of prayer.&crlf;&crlf;')] +SONG_TEST_DATA = [{u'title': u'Amazing Grace', + u'verses': [ + (u'Amazing grace! How\nsweet the sound\nThat saved a wretch like me!\nI once was lost,\n' + u'but now am found;\nWas blind, but now I see.'), + (u'\'Twas grace that\ntaught my heart to fear,\nAnd grace my fears relieved;\nHow precious did\n' + u'that grace appear\nThe hour I first believed.'), + (u'Through many dangers,\ntoils and snares,\nI have already come;\n\'Tis grace hath brought\n' + u'me safe thus far,\nAnd grace will lead me home.'), + (u'The Lord has\npromised good to me,\nHis Word my hope secures;\n' + u'He will my Shield\nand Portion be,\nAs long as life endures.'), + (u'Yea, when this flesh\nand heart shall fail,\nAnd mortal life shall cease,\nI shall possess,\n' + u'within the veil,\nA life of joy and peace.'), + (u'The earth shall soon\ndissolve like snow,\nThe sun forbear to shine;\nBut God, Who called\n' + u'me here below,\nShall be forever mine.'), + (u'When we\'ve been there\nten thousand years,\nBright shining as the sun,\n' + u'We\'ve no less days to\nsing God\'s praise\nThan when we\'d first begun.')]}, + {u'title': u'Beautiful Garden Of Prayer, The', + u'verses': [ + (u'There\'s a garden where\nJesus is waiting,\nThere\'s a place that\nis wondrously fair,\n' + u'For it glows with the\nlight of His presence.\n\'Tis the beautiful\ngarden of prayer.'), + (u'Oh, the beautiful garden,\nthe garden of prayer!\nOh, the beautiful\ngarden of prayer!\n' + u'There my Savior awaits,\nand He opens the gates\nTo the beautiful\ngarden of prayer.'), + (u'There\'s a garden where\nJesus is waiting,\nAnd I go with my\nburden and care,\n' + u'Just to learn from His\nlips words of comfort\nIn the beautiful\ngarden of prayer.'), + (u'There\'s a garden where\nJesus is waiting,\nAnd He bids you to come,\nmeet Him there;\n' + u'Just to bow and\nreceive a new blessing\nIn the beautiful\ngarden of prayer.')]}] + +class TestWorshipCenterProSongImport(TestCase): + """ + Test the functions in the :mod:`worshipcenterproimport` module. + """ + def create_importer_test(self): + """ + Test creating an instance of the WorshipCenter Pro file importer + """ + # GIVEN: A mocked out SongImport class, and a mocked out "manager" + with patch(u'openlp.plugins.songs.lib.worshipcenterproimport.SongImport'): + mocked_manager = MagicMock() + + # WHEN: An importer object is created + importer = WorshipCenterProImport(mocked_manager) + + # THEN: The importer object should not be None + self.assertIsNotNone(importer, u'Import should not be none') + + def pyodbc_exception_test(self): + """ + Test that exceptions raised by pyodbc are handled + """ + # GIVEN: A mocked out SongImport class, a mocked out pyodbc module, a mocked out translate method, + # a mocked "manager" and a mocked out logError method. + with patch(u'openlp.plugins.songs.lib.worshipcenterproimport.SongImport'), \ + patch(u'openlp.plugins.songs.lib.worshipcenterproimport.pyodbc') as mocked_pyodbc, \ + patch(u'openlp.plugins.songs.lib.worshipcenterproimport.translate') as mocked_translate: + mocked_manager = MagicMock() + mocked_log_error = MagicMock() + mocked_translate.return_value = u'Translated Text' + importer = WorshipCenterProImport(mocked_manager) + importer.logError = mocked_log_error + importer.import_source = u'import_source' + + # WHEN: pyodbc raises and unspecified exception + mocked_pyodbc.connect.side_effect = Exception() + + # THEN: doImport should return None, and pyodbc, translate & logError are called with known calls + self.assertIsNone(importer.doImport(), u'doImport should return None when pyodbc raises an exception.') + mocked_pyodbc.connect.assert_called_with( u'DRIVER={Microsoft Access Driver (*.mdb)};DBQ=import_source') + mocked_translate.assert_called_with('SongsPlugin.WorshipCenterProImport', + 'Unable to connect the WorshipCenter Pro database.') + mocked_log_error.assert_called_with(u'import_source', u'Translated Text') + + def song_import_test(self): + """ + Test that a simulated WorshipCenter Pro recordset is imported correctly + """ + # GIVEN: A mocked out SongImport class, a mocked out pyodbc module, a mocked out translate method, + # a mocked "manager", logError method, addVerse method & mocked_finish method. + with patch(u'openlp.plugins.songs.lib.worshipcenterproimport.SongImport'), \ + patch(u'openlp.plugins.songs.lib.worshipcenterproimport.pyodbc') as mocked_pyodbc, \ + patch(u'openlp.plugins.songs.lib.worshipcenterproimport.translate') as mocked_translate: + mocked_manager = MagicMock() + mocked_log_error = MagicMock() + mocked_import_wizard = MagicMock() + mocked_add_verse = MagicMock() + mocked_finish = MagicMock() + mocked_translate.return_value = u'Translated Text' + importer = WorshipCenterProImport(mocked_manager) + importer.logError = mocked_log_error + importer.import_source = u'import_source' + importer.import_wizard = mocked_import_wizard + importer.addVerse = mocked_add_verse + importer.stop_import_flag = False + importer.finish = mocked_finish + + # WHEN: the RECORDSET_TEST_DATA is simulated as fetched + mocked_pyodbc.connect().cursor().fetchall.return_value = RECORDSET_TEST_DATA + + # THEN: doImport should return None, and pyodbc, import_wizard, importer.title and addVerse are called with + # known calls + self.assertIsNone(importer.doImport(), u'doImport should return None when pyodbc raises an exception.') + mocked_pyodbc.connect.assert_called_with(u'DRIVER={Microsoft Access Driver (*.mdb)};DBQ=import_source') + mocked_pyodbc.connect().cursor.assert_any_call() + mocked_pyodbc.connect().cursor().execute.assert_called_with(u'SELECT ID, Field, Value FROM __SONGDATA') + mocked_pyodbc.connect().cursor().fetchall.assert_any_call() + mocked_import_wizard.progress_bar.setMaximum.assert_called_with(2) + for song_data in SONG_TEST_DATA: + title_value = song_data[u'title'] + # Here is where I'm stuck. + # importer.title, should have been first set to 'Amazing Grace' and then + # 'Beautiful Garden Of Prayer, The' how can I capture past values? mohij suggested using properties, + # I know very little about these, how would I be able to apply it from the test case? + self.assertEqual(importer.title, title_value) + verse_calls = song_data[u'verses'] + for call in verse_calls: + mocked_add_verse.assert_any_call(call) From f5213d315d242ce703be02d9621012cca2a1b433 Mon Sep 17 00:00:00 2001 From: phill-ridout Date: Sun, 14 Apr 2013 20:16:31 +0100 Subject: [PATCH 2/8] Commented out failing test --- .../openlp_plugins/songs/test_worshipcenterproimport.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py b/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py index 920b403ff..317e0ddd8 100644 --- a/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py +++ b/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py @@ -164,7 +164,7 @@ class TestWorshipCenterProSongImport(TestCase): # importer.title, should have been first set to 'Amazing Grace' and then # 'Beautiful Garden Of Prayer, The' how can I capture past values? mohij suggested using properties, # I know very little about these, how would I be able to apply it from the test case? - self.assertEqual(importer.title, title_value) + #self.assertEqual(importer.title, title_value) verse_calls = song_data[u'verses'] for call in verse_calls: mocked_add_verse.assert_any_call(call) From 4d20ced4212980e6f844c1db1e03256885e76a94 Mon Sep 17 00:00:00 2001 From: phill-ridout Date: Sun, 14 Apr 2013 21:10:08 +0100 Subject: [PATCH 3/8] Added logger class with help from mohij. --- .../songs/test_worshipcenterproimport.py | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py b/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py index 317e0ddd8..11f304221 100644 --- a/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py +++ b/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py @@ -22,6 +22,23 @@ class TestRecord: self.Field = field self.Value = value +class WorshipCenterProImportLogger(WorshipCenterProImport): + """ + This class logs changes in the title instance variable + """ + _title_assignment_list = [] + + def __init__(self, manager): + WorshipCenterProImport.__init__(self, manager) + + @property + def title(self): + return self._title_assignment_list[-1] + + @title.setter + def title(self, title): + self._title_assignment_list.append(title) + RECORDSET_TEST_DATA = [TestRecord(1, u'TITLE', u'Amazing Grace'), TestRecord(1, u'LYRICS', @@ -139,7 +156,7 @@ class TestWorshipCenterProSongImport(TestCase): mocked_add_verse = MagicMock() mocked_finish = MagicMock() mocked_translate.return_value = u'Translated Text' - importer = WorshipCenterProImport(mocked_manager) + importer = WorshipCenterProImportLogger(mocked_manager) importer.logError = mocked_log_error importer.import_source = u'import_source' importer.import_wizard = mocked_import_wizard @@ -160,11 +177,7 @@ class TestWorshipCenterProSongImport(TestCase): mocked_import_wizard.progress_bar.setMaximum.assert_called_with(2) for song_data in SONG_TEST_DATA: title_value = song_data[u'title'] - # Here is where I'm stuck. - # importer.title, should have been first set to 'Amazing Grace' and then - # 'Beautiful Garden Of Prayer, The' how can I capture past values? mohij suggested using properties, - # I know very little about these, how would I be able to apply it from the test case? - #self.assertEqual(importer.title, title_value) + self.assertIn(title_value, importer._title_assignment_list) verse_calls = song_data[u'verses'] for call in verse_calls: mocked_add_verse.assert_any_call(call) From 6e688961d0c166770ee355f8be84c99405300307 Mon Sep 17 00:00:00 2001 From: phill-ridout Date: Mon, 15 Apr 2013 17:35:36 +0100 Subject: [PATCH 4/8] Removed logError from file import test as it was not needed. Added check to see that addVerse has been called the correct ammount of times --- .../songs/test_worshipcenterproimport.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py b/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py index 11f304221..db0b12e99 100644 --- a/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py +++ b/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py @@ -146,18 +146,16 @@ class TestWorshipCenterProSongImport(TestCase): Test that a simulated WorshipCenter Pro recordset is imported correctly """ # GIVEN: A mocked out SongImport class, a mocked out pyodbc module, a mocked out translate method, - # a mocked "manager", logError method, addVerse method & mocked_finish method. + # a mocked "manager", addVerse method & mocked_finish method. with patch(u'openlp.plugins.songs.lib.worshipcenterproimport.SongImport'), \ - patch(u'openlp.plugins.songs.lib.worshipcenterproimport.pyodbc') as mocked_pyodbc, \ + patch(u'openlp.plugins.songs.lib.worshipcenterproimport.pyodbc') as mocked_pyodbc, \ patch(u'openlp.plugins.songs.lib.worshipcenterproimport.translate') as mocked_translate: mocked_manager = MagicMock() - mocked_log_error = MagicMock() mocked_import_wizard = MagicMock() mocked_add_verse = MagicMock() mocked_finish = MagicMock() mocked_translate.return_value = u'Translated Text' importer = WorshipCenterProImportLogger(mocked_manager) - importer.logError = mocked_log_error importer.import_source = u'import_source' importer.import_wizard = mocked_import_wizard importer.addVerse = mocked_add_verse @@ -175,9 +173,14 @@ class TestWorshipCenterProSongImport(TestCase): mocked_pyodbc.connect().cursor().execute.assert_called_with(u'SELECT ID, Field, Value FROM __SONGDATA') mocked_pyodbc.connect().cursor().fetchall.assert_any_call() mocked_import_wizard.progress_bar.setMaximum.assert_called_with(2) + add_verse_call_count = 0 for song_data in SONG_TEST_DATA: title_value = song_data[u'title'] - self.assertIn(title_value, importer._title_assignment_list) + self.assertIn(title_value, importer._title_assignment_list, + u'title should have been set to %s' % title_value) verse_calls = song_data[u'verses'] + add_verse_call_count += len(verse_calls) for call in verse_calls: mocked_add_verse.assert_any_call(call) + self.assertEqual(mocked_add_verse.call_count, add_verse_call_count, + u'Inccorect number of calls made to addVerse') \ No newline at end of file From 5512435c0cfefb966484c2ced7812c1b2bf1696d Mon Sep 17 00:00:00 2001 From: phill-ridout Date: Wed, 24 Apr 2013 18:37:15 +0100 Subject: [PATCH 5/8] Changed then Givens, Whens and Thens arround --- .../songs/lib/worshipcenterproimport.py | 8 ++++++- .../songs/test_worshipcenterproimport.py | 24 +++++++++++-------- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/openlp/plugins/songs/lib/worshipcenterproimport.py b/openlp/plugins/songs/lib/worshipcenterproimport.py index 47ce8029d..80237d38b 100644 --- a/openlp/plugins/songs/lib/worshipcenterproimport.py +++ b/openlp/plugins/songs/lib/worshipcenterproimport.py @@ -30,11 +30,16 @@ The :mod:`worshipcenterpro` module provides the functionality for importing a WorshipCenter Pro database into the OpenLP database. """ +import logging + import pyodbc from openlp.core.lib import translate from openlp.plugins.songs.lib.songimport import SongImport +log = logging.getLogger(__name__) + + class WorshipCenterProImport(SongImport): """ The :class:`WorshipCenterProImport` class provides the ability to import the @@ -52,7 +57,8 @@ class WorshipCenterProImport(SongImport): """ try: conn = pyodbc.connect(u'DRIVER={Microsoft Access Driver (*.mdb)};DBQ=%s' % self.import_source) - except: + except (pyodbc.DatabaseError, pyodbc.IntegrityError, pyodbc.InternalError, pyodbc.OperationalError), e: + log.warn(u'Unable to connect the WorshipCenter Pro database %s. %s', self.import_source, unicode(e)) # Unfortunately no specific exception type self.logError(self.import_source, translate('SongsPlugin.WorshipCenterProImport', 'Unable to connect the WorshipCenter Pro database.')) diff --git a/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py b/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py index db0b12e99..e1e90d0f8 100644 --- a/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py +++ b/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py @@ -2,11 +2,12 @@ # vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 """ -This module contains tests for the WorshipCenter/ Pro song importer. +This module contains tests for the WorshipCenter Pro song importer. """ from unittest import TestCase from mock import patch, MagicMock +import pyodbc from openlp.plugins.songs.lib.worshipcenterproimport import WorshipCenterProImport @@ -130,12 +131,13 @@ class TestWorshipCenterProSongImport(TestCase): importer = WorshipCenterProImport(mocked_manager) importer.logError = mocked_log_error importer.import_source = u'import_source' + mocked_pyodbc.connect.side_effect = Exception(pyodbc.DatabaseError) - # WHEN: pyodbc raises and unspecified exception - mocked_pyodbc.connect.side_effect = Exception() + # WHEN: Calling the doImport method + return_value = importer.doImport() # THEN: doImport should return None, and pyodbc, translate & logError are called with known calls - self.assertIsNone(importer.doImport(), u'doImport should return None when pyodbc raises an exception.') + self.assertIsNone(return_value, u'doImport should return None when pyodbc raises an exception.') mocked_pyodbc.connect.assert_called_with( u'DRIVER={Microsoft Access Driver (*.mdb)};DBQ=import_source') mocked_translate.assert_called_with('SongsPlugin.WorshipCenterProImport', 'Unable to connect the WorshipCenter Pro database.') @@ -145,8 +147,8 @@ class TestWorshipCenterProSongImport(TestCase): """ Test that a simulated WorshipCenter Pro recordset is imported correctly """ - # GIVEN: A mocked out SongImport class, a mocked out pyodbc module, a mocked out translate method, - # a mocked "manager", addVerse method & mocked_finish method. + # GIVEN: A mocked out SongImport class, a mocked out pyodbc module with a simulated recordset, a mocked out + # translate method, a mocked "manager", addVerse method & mocked_finish method. with patch(u'openlp.plugins.songs.lib.worshipcenterproimport.SongImport'), \ patch(u'openlp.plugins.songs.lib.worshipcenterproimport.pyodbc') as mocked_pyodbc, \ patch(u'openlp.plugins.songs.lib.worshipcenterproimport.translate') as mocked_translate: @@ -154,6 +156,7 @@ class TestWorshipCenterProSongImport(TestCase): mocked_import_wizard = MagicMock() mocked_add_verse = MagicMock() mocked_finish = MagicMock() + mocked_pyodbc.connect().cursor().fetchall.return_value = RECORDSET_TEST_DATA mocked_translate.return_value = u'Translated Text' importer = WorshipCenterProImportLogger(mocked_manager) importer.import_source = u'import_source' @@ -162,12 +165,13 @@ class TestWorshipCenterProSongImport(TestCase): importer.stop_import_flag = False importer.finish = mocked_finish - # WHEN: the RECORDSET_TEST_DATA is simulated as fetched - mocked_pyodbc.connect().cursor().fetchall.return_value = RECORDSET_TEST_DATA + # WHEN: Calling the doImport method + return_value = importer.doImport() + # THEN: doImport should return None, and pyodbc, import_wizard, importer.title and addVerse are called with # known calls - self.assertIsNone(importer.doImport(), u'doImport should return None when pyodbc raises an exception.') + self.assertIsNone(return_value, u'doImport should return None when pyodbc raises an exception.') mocked_pyodbc.connect.assert_called_with(u'DRIVER={Microsoft Access Driver (*.mdb)};DBQ=import_source') mocked_pyodbc.connect().cursor.assert_any_call() mocked_pyodbc.connect().cursor().execute.assert_called_with(u'SELECT ID, Field, Value FROM __SONGDATA') @@ -183,4 +187,4 @@ class TestWorshipCenterProSongImport(TestCase): for call in verse_calls: mocked_add_verse.assert_any_call(call) self.assertEqual(mocked_add_verse.call_count, add_verse_call_count, - u'Inccorect number of calls made to addVerse') \ No newline at end of file + u'Incorrect number of calls made to addVerse') \ No newline at end of file From 9550405778b9b37575f74fd5cd785d1e06ffb51a Mon Sep 17 00:00:00 2001 From: phill-ridout Date: Wed, 24 Apr 2013 20:30:39 +0100 Subject: [PATCH 6/8] Fixed mock raising errors --- .../songs/test_worshipcenterproimport.py | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py b/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py index e1e90d0f8..da75a0137 100644 --- a/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py +++ b/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py @@ -123,7 +123,7 @@ class TestWorshipCenterProSongImport(TestCase): # GIVEN: A mocked out SongImport class, a mocked out pyodbc module, a mocked out translate method, # a mocked "manager" and a mocked out logError method. with patch(u'openlp.plugins.songs.lib.worshipcenterproimport.SongImport'), \ - patch(u'openlp.plugins.songs.lib.worshipcenterproimport.pyodbc') as mocked_pyodbc, \ + patch(u'openlp.plugins.songs.lib.worshipcenterproimport.pyodbc.connect') as mocked_pyodbc_connect, \ patch(u'openlp.plugins.songs.lib.worshipcenterproimport.translate') as mocked_translate: mocked_manager = MagicMock() mocked_log_error = MagicMock() @@ -131,17 +131,19 @@ class TestWorshipCenterProSongImport(TestCase): importer = WorshipCenterProImport(mocked_manager) importer.logError = mocked_log_error importer.import_source = u'import_source' - mocked_pyodbc.connect.side_effect = Exception(pyodbc.DatabaseError) + pyodbc_errors = [pyodbc.DatabaseError, pyodbc.IntegrityError, pyodbc.InternalError, pyodbc.OperationalError] + mocked_pyodbc_connect.side_effect = pyodbc_errors # WHEN: Calling the doImport method - return_value = importer.doImport() + for effect in pyodbc_errors: + return_value = importer.doImport() - # THEN: doImport should return None, and pyodbc, translate & logError are called with known calls - self.assertIsNone(return_value, u'doImport should return None when pyodbc raises an exception.') - mocked_pyodbc.connect.assert_called_with( u'DRIVER={Microsoft Access Driver (*.mdb)};DBQ=import_source') - mocked_translate.assert_called_with('SongsPlugin.WorshipCenterProImport', - 'Unable to connect the WorshipCenter Pro database.') - mocked_log_error.assert_called_with(u'import_source', u'Translated Text') + # THEN: doImport should return None, and pyodbc, translate & logError are called with known calls + self.assertIsNone(return_value, u'doImport should return None when pyodbc raises an exception.') + mocked_pyodbc_connect.assert_called_with( u'DRIVER={Microsoft Access Driver (*.mdb)};DBQ=import_source') + mocked_translate.assert_called_with('SongsPlugin.WorshipCenterProImport', + 'Unable to connect the WorshipCenter Pro database.') + mocked_log_error.assert_called_with(u'import_source', u'Translated Text') def song_import_test(self): """ From 8713bba796fb8c690922264b782b605e27460fc6 Mon Sep 17 00:00:00 2001 From: phill-ridout Date: Tue, 2 Jul 2013 22:24:18 +0100 Subject: [PATCH 7/8] added pyodbc module to the check_dependencies script --- scripts/check_dependencies.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/check_dependencies.py b/scripts/check_dependencies.py index e3849d449..bd7786b01 100755 --- a/scripts/check_dependencies.py +++ b/scripts/check_dependencies.py @@ -62,6 +62,7 @@ WIN32_MODULES = [ 'win32com', 'win32ui', 'pywintypes', + 'pyodbc', ] MODULES = [ From d73664e54ac678fe8f9d9dcd090fee32ed31fd70 Mon Sep 17 00:00:00 2001 From: phill-ridout Date: Sat, 6 Jul 2013 15:22:30 +0100 Subject: [PATCH 8/8] Changed TestRecord ot inhearit from object --- .../openlp_plugins/songs/test_worshipcenterproimport.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py b/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py index da75a0137..655eff900 100644 --- a/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py +++ b/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py @@ -11,7 +11,7 @@ import pyodbc from openlp.plugins.songs.lib.worshipcenterproimport import WorshipCenterProImport -class TestRecord: +class TestRecord(object): """ Microsoft Access Driver is not available on non Microsoft Systems for this reason the :class:`TestRecord` is used to simulate a recordset that would be returned by pyobdc.