diff --git a/openlp/plugins/songs/lib/importer.py b/openlp/plugins/songs/lib/importer.py index ef4acaad0..4dab8c86d 100644 --- a/openlp/plugins/songs/lib/importer.py +++ b/openlp/plugins/songs/lib/importer.py @@ -166,28 +166,29 @@ class SongFormat(object): EasyWorshipDB = 7 EasyWorshipSqliteDB = 8 EasyWorshipService = 9 - FoilPresenter = 10 - LiveWorship = 11 - Lyrix = 12 - MediaShout = 13 - OpenSong = 14 - OPSPro = 15 - PowerPraise = 16 - PowerSong = 17 - PresentationManager = 18 - ProPresenter = 19 - SingingTheFaith = 20 - SongBeamer = 21 - SongPro = 22 - SongShowPlus = 23 - SongsOfFellowship = 24 - SundayPlus = 25 - VideoPsalm = 26 - WordsOfWorship = 27 - WorshipAssistant = 28 - WorshipCenterPro = 29 - ZionWorx = 30 - Datasoul = 31 + EasyWorshipServiceSqliteDB = 10 + FoilPresenter = 11 + LiveWorship = 12 + Lyrix = 13 + MediaShout = 14 + OpenSong = 15 + OPSPro = 16 + PowerPraise = 17 + PowerSong = 18 + PresentationManager = 19 + ProPresenter = 20 + SingingTheFaith = 21 + SongBeamer = 22 + SongPro = 23 + SongShowPlus = 24 + SongsOfFellowship = 25 + SundayPlus = 26 + VideoPsalm = 27 + WordsOfWorship = 28 + WorshipAssistant = 29 + WorshipCenterPro = 30 + ZionWorx = 31 + Datasoul = 32 # Set optional attribute defaults __defaults__ = { @@ -278,6 +279,14 @@ class SongFormat(object): 'filter': '{text} (*.ews)'.format(text=translate('SongsPlugin.ImportWizardForm', 'EasyWorship 2007/2009 Service File')) }, + EasyWorshipServiceSqliteDB: { + 'class': EasyWorshipSongImport, + 'name': 'EasyWorship 6/7 Service File', + 'prefix': 'ew', + 'selectMode': SongFormatSelect.SingleFile, + 'filter': '{text} (*.ewsx)'.format(text=translate('SongsPlugin.ImportWizardForm', + 'EasyWorship 6/7 Service File')) + }, FoilPresenter: { 'class': FoilPresenterImport, 'name': 'Foilpresenter', @@ -487,6 +496,7 @@ class SongFormat(object): SongFormat.EasyWorshipDB, SongFormat.EasyWorshipSqliteDB, SongFormat.EasyWorshipService, + SongFormat.EasyWorshipServiceSqliteDB, SongFormat.FoilPresenter, SongFormat.LiveWorship, SongFormat.Lyrix, diff --git a/openlp/plugins/songs/lib/importers/easyworship.py b/openlp/plugins/songs/lib/importers/easyworship.py index 0d0d2ff03..55e83fd12 100644 --- a/openlp/plugins/songs/lib/importers/easyworship.py +++ b/openlp/plugins/songs/lib/importers/easyworship.py @@ -28,6 +28,8 @@ import sqlite3 import struct import zlib from pathlib import Path +from tempfile import NamedTemporaryFile +from zipfile import ZipFile from openlp.core.common.i18n import translate from openlp.plugins.songs.lib import VerseType, retrieve_windows_encoding, strip_rtf @@ -83,6 +85,8 @@ class EasyWorshipSongImport(SongImport): self.import_ews() elif ext == '.db': self.import_db() + elif ext == '.ewsx': + self.import_ewsx() else: self.import_sqlite_db() except Exception: @@ -346,6 +350,65 @@ class EasyWorshipSongImport(SongImport): db_file.close() self.memo_file.close() + def import_ewsx(self): + """ + Imports songs from an EasyWorship 6/7 service file, which is just a zip file with an Sqlite DB with text + resources. Non-text recources is also in the zip file, but is ignored. + """ + invalid_ewsx_msg = translate('SongsPlugin.EasyWorshipSongImport', + 'This is not a valid Easy Worship 6/7 service file.') + # Open ewsx file if it exists + if not self.import_source.is_file(): + log.debug('Given ewsx file does not exists.') + return + tmp_db_file = NamedTemporaryFile(delete=False) + with ZipFile(self.import_source, 'r') as eswx_file: + db_zfile = eswx_file.open('main.db') + # eswx has bad CRC for the database for some reason (custom CRC?), so skip the CRC + db_zfile._expected_crc = None + db_data = db_zfile.read() + tmp_db_file.write(db_data) + tmp_db_file.close() + ewsx_conn = sqlite3.connect(tmp_db_file.file.name) + if ewsx_conn is None: + self.log_error(self.import_source, invalid_ewsx_msg) + return + ewsx_db = ewsx_conn.cursor() + # Take a stab at how text is encoded + self.encoding = 'cp1252' + self.encoding = retrieve_windows_encoding(self.encoding) + if not self.encoding: + log.debug('No encoding set.') + return + # get list of songs in service file, presentation_type=6 means songs + songs_exec = ewsx_db.execute('SELECT rowid, title, author, copyright, reference_number ' + 'FROM presentation WHERE presentation_type=6;') + songs = songs_exec.fetchall() + for song in songs: + self.title = title = song[1] + self.author = song[2] + self.copyright = song[3] + self.ccli_number = song[4] + # get slides for the song, element_type=6 means songs, element_style_type=4 means song text + slides = ewsx_db.execute('SELECT rt.rtf ' + 'FROM element as e ' + 'JOIN slide as s ON e.slide_id = s.rowid ' + 'JOIN resource_text as rt ON rt.resource_id = e.foreground_resource_id ' + 'WHERE e.element_type=6 AND e.element_style_type=4 AND s.presentation_id = ? ' + 'ORDER BY s.order_index;', (song[0],)) + for slide in slides: + if slide: + self.set_song_import_object(self.author, slide[0].encode()) + # save song + if not self.finish(): + self.log_error(self.import_source, + translate('SongsPlugin.EasyWorshipSongImport', + '"{title}" could not be imported. {entry}'). + format(title=title, entry=self.entry_error_log)) + # close database handles + ewsx_conn.close() + Path(tmp_db_file.file.name).unlink() + def import_sqlite_db(self): """ Import the songs from an EasyWorship 6 SQLite database diff --git a/tests/openlp_plugins/songs/test_ewimport.py b/tests/openlp_plugins/songs/test_ewimport.py index 30aaf2533..c7b41365f 100644 --- a/tests/openlp_plugins/songs/test_ewimport.py +++ b/tests/openlp_plugins/songs/test_ewimport.py @@ -498,6 +498,48 @@ def test_ews_file_import(mocked_retrieve_windows_encoding: MagicMock, MockSongIm mocked_finish.assert_called_with() +@patch('openlp.plugins.songs.lib.importers.easyworship.SongImport') +@patch('openlp.plugins.songs.lib.importers.easyworship.retrieve_windows_encoding') +def test_ewsx_file_import(mocked_retrieve_windows_encoding: MagicMock, MockSongImport: MagicMock, + registry: Registry, settings: Settings): + """ + Test the actual import of song from ewsx file and check that the imported data is correct. + """ + + # GIVEN: Test files with a mocked out SongImport class, a mocked out "manager", a mocked out "import_wizard", + # and mocked out "author", "add_copyright", "add_verse", "finish" methods. + mocked_retrieve_windows_encoding.return_value = 'cp1252' + mocked_manager = MagicMock() + mocked_import_wizard = MagicMock() + mocked_add_author = MagicMock() + mocked_add_verse = MagicMock() + mocked_finish = MagicMock() + mocked_title = MagicMock() + mocked_finish.return_value = True + importer = EasyWorshipSongImportLogger(mocked_manager) + importer.import_wizard = mocked_import_wizard + importer.stop_import_flag = False + importer.add_author = mocked_add_author + importer.add_verse = mocked_add_verse + importer.title = mocked_title + importer.finish = mocked_finish + importer.topics = [] + + # WHEN: Importing ews file + importer.import_source = str(TEST_PATH / 'test1.ewsx') + import_result = importer.do_import() + + # THEN: do_import should return none, the song data should be as expected, and finish should have been + # called. + title = EWS_SONG_TEST_DATA['title'] + assert import_result is None, 'do_import should return None when it has completed' + assert title in importer._title_assignment_list, 'title for should be "%s"' % title + mocked_add_author.assert_any_call(EWS_SONG_TEST_DATA['authors'][0]) + for verse_text, verse_tag in EWS_SONG_TEST_DATA['verses']: + mocked_add_verse.assert_any_call(verse_text, verse_tag) + mocked_finish.assert_called_with() + + @patch('openlp.plugins.songs.lib.importers.easyworship.SongImport') def test_import_rtf_unescaped_unicode(MockSongImport: MagicMock, registry: Registry, settings: Settings): """ diff --git a/tests/resources/songs/easyworship/test1.ewsx b/tests/resources/songs/easyworship/test1.ewsx new file mode 100644 index 000000000..c8833eb00 Binary files /dev/null and b/tests/resources/songs/easyworship/test1.ewsx differ