From 52f369533409064991f95efb6d7f2a3df91a9a3c Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Tue, 17 Feb 2015 22:58:29 +0000 Subject: [PATCH 1/8] Initial support for fetching list of web bibles. Not used yet. --- openlp/plugins/bibles/lib/http.py | 102 ++++++++++++++++++ .../openlp_plugins/bibles/test_lib_http.py | 32 +++++- 2 files changed, 133 insertions(+), 1 deletion(-) diff --git a/openlp/plugins/bibles/lib/http.py b/openlp/plugins/bibles/lib/http.py index 7334dd259..d93f645fa 100644 --- a/openlp/plugins/bibles/lib/http.py +++ b/openlp/plugins/bibles/lib/http.py @@ -50,6 +50,29 @@ UGLY_CHARS = { } VERSE_NUMBER_REGEX = re.compile(r'v(\d{1,2})(\d{3})(\d{3}) verse.*') +BIBLESERVER_LANGUAGE_CODE = { + 'fl_1': 'de', + 'fl_2': 'en', + 'fl_3': 'fr', + 'fl_4': 'it', + 'fl_5': 'es', + 'fl_6': 'pt', + 'fl_7': 'ru', + 'fl_8': 'sv', + 'fl_9': 'no', + 'fl_10': 'nl', + 'fl_11': 'cs', + 'fl_12': 'sk', + 'fl_13': 'ro', + 'fl_14': 'hr', + 'fl_15': 'hu', + 'fl_16': 'bg', + 'fl_17': 'ar', + 'fl_18': 'tr', + 'fl_19': 'pl', + 'fl_20': 'da', + 'fl_21': 'zh' +} log = logging.getLogger(__name__) @@ -278,6 +301,32 @@ class BGExtract(RegistryProperties): books.append(book.contents[0]) return books + def get_bibles_from_http(self): + """ + Load a list of bibles from BibleGateway website. + """ + log.debug('BGExtract.get_bibles_from_http') + bible_url = 'https://legacy.biblegateway.com/versions/' + soup = get_soup_for_bible_ref(bible_url) + if not soup: + return None + bible_select = soup.find('select', {'class': 'translation-dropdown'}) + option_tags = bible_select.find_all('option') + current_lang = '' + for ot in option_tags: + tag_class = '' + try: + tag_class = ot['class'][0] + except KeyError: + tag_class = '' + tag_text = ot.get_text() + if tag_class == 'lang': + current_lang = tag_text[tag_text.find('(') + 1:tag_text.find(')') + 1].lower() + elif tag_class == 'spacer': + continue + else: + print('biblename: %s, bible_key: %s, class: %s' % (tag_text, ot['value'], current_lang)) + class BSExtract(RegistryProperties): """ @@ -338,6 +387,33 @@ class BSExtract(RegistryProperties): content = content.find_all('li') return [book.contents[0].contents[0] for book in content if len(book.contents[0].contents)] + def get_bibles_from_http(self): + """ + Load a list of bibles from Bibleserver website. + """ + log.debug('BSExtract.get_bibles_from_http') + bible_url = 'http://www.bibleserver.com/index.php?language=2' + soup = get_soup_for_bible_ref(bible_url) + if not soup: + return None + bible_links = soup.find_all('a', {'class': 'trlCell'}) + for link in bible_links: + bible_name = link.get_text() + # Skip any audio + if 'audio' in bible_name.lower(): + continue + bible_link = link['href'] + bible_key = bible_link[bible_link.rfind('/') + 1:] + css_classes = link['class'] + language_code = '' + for css_class in css_classes: + if css_class.startswith('fl_'): + try: + language_code = BIBLESERVER_LANGUAGE_CODE[css_class] + except KeyError: + language_code = '' + print('biblename: %s, bible_key: %s, class: %s' % (bible_name, bible_key, language_code)) + class CWExtract(RegistryProperties): """ @@ -408,6 +484,32 @@ class CWExtract(RegistryProperties): books.append(book.contents[0]) return books + def get_bibles_from_http(self): + """ + Load a list of bibles from Crosswalk website. + """ + log.debug('CWExtract.get_bibles_from_http') + bible_url = 'http://www.biblestudytools.com/search/bible-search.part/' + soup = get_soup_for_bible_ref(bible_url) + if not soup: + return None + bible_select = soup.find('select') + option_tags = bible_select.find_all('option') + for ot in option_tags: + tag_text = ot.get_text() + tag_value = ot['value'] + if not tag_value: + continue + # The names of non-english bibles has their language in parentheses at the end + if tag_text.endswith(')'): + language_code = '' + # ... except for the latin vulgate + elif 'latin' in tag_text.lower(): + language_code = 'la' + else: + language_code = 'en' + print('biblename: %s, bible_key: %s, class: %s' % (tag_text, tag_value, language_code)) + class HTTPBible(BibleDB, RegistryProperties): log.info('%s HTTPBible loaded', __name__) diff --git a/tests/interfaces/openlp_plugins/bibles/test_lib_http.py b/tests/interfaces/openlp_plugins/bibles/test_lib_http.py index 673fb4455..c422f6e5f 100644 --- a/tests/interfaces/openlp_plugins/bibles/test_lib_http.py +++ b/tests/interfaces/openlp_plugins/bibles/test_lib_http.py @@ -25,7 +25,7 @@ from unittest import TestCase from openlp.core.common import Registry -from openlp.plugins.bibles.lib.http import BGExtract, CWExtract +from openlp.plugins.bibles.lib.http import BGExtract, CWExtract, BSExtract from tests.interfaces import MagicMock @@ -116,3 +116,33 @@ class TestBibleHTTP(TestCase): # THEN: We should get back a valid service item assert len(results.verse_list) == 36, 'The book of John should not have had any verses added or removed' + + def bibleserver_get_bibles_test(self): + """ + Test getting list of bibles from BibelServer.com + """ + # GIVEN: A new Bible Server extraction class + handler = BSExtract() + + handler.get_bibles_from_http() + self.assertTrue(False) + + def biblegateway_get_bibles_test(self): + """ + Test getting list of bibles from BibelGateway.com + """ + # GIVEN: A new Bible Gateway extraction class + handler = BGExtract() + + handler.get_bibles_from_http() + self.assertTrue(False) + + def crosswalk_get_bibles_test(self): + """ + Test getting list of bibles from Crosswalk.com + """ + # GIVEN: A new Crosswalk extraction class + handler = CWExtract() + + handler.get_bibles_from_http() + self.assertTrue(False) From b65e592df9a1165ae23460026234513f1ad35761 Mon Sep 17 00:00:00 2001 From: Raoul Snyman Date: Sat, 21 Feb 2015 11:59:37 +0200 Subject: [PATCH 2/8] Bug #1412216: Detect when a song has a problem, show a message to the user, and flip back to the previous screen. --- openlp/plugins/songs/forms/songselectform.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/openlp/plugins/songs/forms/songselectform.py b/openlp/plugins/songs/forms/songselectform.py index c208988f7..8482bee6b 100755 --- a/openlp/plugins/songs/forms/songselectform.py +++ b/openlp/plugins/songs/forms/songselectform.py @@ -187,6 +187,14 @@ class SongSelectForm(QtGui.QDialog, Ui_SongSelectDialog): self.application.process_events() # Get the full song song = self.song_select_importer.get_song(song, self._update_song_progress) + if not song: + QtGui.QMessageBox.critical( + self, translate('SongsPlugin.SongSelectForm', 'Incomplete song'), + translate('SongsPlugin.SongSelectForm', 'This song is missing some information, like the lyrics, ' + 'and cannot be imported.'), + QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok), QtGui.QMessageBox.OK) + self.stacked_widget.setCurrentIndex(1) + return # Update the UI self.title_edit.setText(song['title']) self.copyright_edit.setText(song['copyright']) From e78a0ba0dab521cb83c4ac538f29e4089e618e3b Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Mon, 23 Feb 2015 22:35:56 +0000 Subject: [PATCH 3/8] Implemented getting languages into combobox. --- .../plugins/bibles/forms/bibleimportform.py | 69 +++++++++++++----- openlp/plugins/bibles/lib/db.py | 5 +- openlp/plugins/bibles/lib/http.py | 37 ++++++++-- .../bibles/resources/bibles_resources.sqlite | Bin 104448 -> 112640 bytes 4 files changed, 85 insertions(+), 26 deletions(-) diff --git a/openlp/plugins/bibles/forms/bibleimportform.py b/openlp/plugins/bibles/forms/bibleimportform.py index 019286a68..ee4b01c35 100644 --- a/openlp/plugins/bibles/forms/bibleimportform.py +++ b/openlp/plugins/bibles/forms/bibleimportform.py @@ -34,6 +34,7 @@ from openlp.core.ui.wizard import OpenLPWizard, WizardStrings from openlp.core.utils import get_locale_key from openlp.plugins.bibles.lib.manager import BibleFormat from openlp.plugins.bibles.lib.db import BiblesResourcesDB, clean_filename +from openlp.plugins.bibles.lib.http import CWExtract, BGExtract, BSExtract log = logging.getLogger(__name__) @@ -98,12 +99,13 @@ class BibleImportForm(OpenLPWizard): """ Set up the signals used in the bible importer. """ - self.web_source_combo_box.currentIndexChanged.connect(self.on_web_source_combo_box_index_changed) + self.web_language_combo_box.currentIndexChanged.connect(self.on_web_language_combo_box_index_changed) self.osis_browse_button.clicked.connect(self.on_osis_browse_button_clicked) self.csv_books_button.clicked.connect(self.on_csv_books_browse_button_clicked) self.csv_verses_button.clicked.connect(self.on_csv_verses_browse_button_clicked) self.open_song_browse_button.clicked.connect(self.on_open_song_browse_button_clicked) self.zefania_browse_button.clicked.connect(self.on_zefania_browse_button_clicked) + self.web_update_button.clicked.connect(self.on_web_update_button_clicked) def add_custom_pages(self): """ @@ -202,20 +204,28 @@ class BibleImportForm(OpenLPWizard): self.web_bible_tab.setObjectName('WebBibleTab') self.web_bible_layout = QtGui.QFormLayout(self.web_bible_tab) self.web_bible_layout.setObjectName('WebBibleLayout') + + self.web_update_label = QtGui.QLabel(self.web_bible_tab) + self.web_update_label.setObjectName('WebUpdateLabel') + self.web_bible_layout.setWidget(0, QtGui.QFormLayout.LabelRole, self.web_update_label) + self.web_update_button = QtGui.QPushButton(self.web_bible_tab) + self.web_update_button.setObjectName('WebUpdateLabel') + self.web_bible_layout.setWidget(0, QtGui.QFormLayout.FieldRole, self.web_update_button) + self.web_source_label = QtGui.QLabel(self.web_bible_tab) self.web_source_label.setObjectName('WebSourceLabel') - self.web_bible_layout.setWidget(0, QtGui.QFormLayout.LabelRole, self.web_source_label) - self.web_source_combo_box = QtGui.QComboBox(self.web_bible_tab) - self.web_source_combo_box.setObjectName('WebSourceComboBox') - self.web_source_combo_box.addItems(['', '', '']) - self.web_bible_layout.setWidget(0, QtGui.QFormLayout.FieldRole, self.web_source_combo_box) + self.web_bible_layout.setWidget(1, QtGui.QFormLayout.LabelRole, self.web_source_label) + self.web_language_combo_box = QtGui.QComboBox(self.web_bible_tab) + self.web_language_combo_box.setObjectName('WebSourceComboBox') + self.web_language_combo_box.addItems(['', '', '']) + self.web_bible_layout.setWidget(1, QtGui.QFormLayout.FieldRole, self.web_language_combo_box) self.web_translation_label = QtGui.QLabel(self.web_bible_tab) self.web_translation_label.setObjectName('web_translation_label') - self.web_bible_layout.setWidget(1, QtGui.QFormLayout.LabelRole, self.web_translation_label) + self.web_bible_layout.setWidget(2, QtGui.QFormLayout.LabelRole, self.web_translation_label) self.web_translation_combo_box = QtGui.QComboBox(self.web_bible_tab) self.web_translation_combo_box.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToContents) self.web_translation_combo_box.setObjectName('WebTranslationComboBox') - self.web_bible_layout.setWidget(1, QtGui.QFormLayout.FieldRole, self.web_translation_combo_box) + self.web_bible_layout.setWidget(2, QtGui.QFormLayout.FieldRole, self.web_translation_combo_box) self.web_tab_widget.addTab(self.web_bible_tab, '') self.web_proxy_tab = QtGui.QWidget() self.web_proxy_tab.setObjectName('WebProxyTab') @@ -314,12 +324,9 @@ class BibleImportForm(OpenLPWizard): 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', - 'BibleGateway')) - self.web_source_combo_box.setItemText(WebDownload.Bibleserver, translate('BiblesPlugin.ImportWizardForm', - 'Bibleserver')) + self.web_update_label.setText(translate('BiblesPlugin.ImportWizardForm', 'Click to fetch bible list')) + self.web_update_button.setText(translate('BiblesPlugin.ImportWizardForm', 'Fetch list')) + #self.web_language_combo_box.setItemText(0, translate('BiblesPlugin.ImportWizardForm', 'Choose a language')) self.web_translation_label.setText(translate('BiblesPlugin.ImportWizardForm', 'Bible:')) self.web_tab_widget.setTabText(self.web_tab_widget.indexOf(self.web_bible_tab), translate('BiblesPlugin.ImportWizardForm', 'Download Options')) @@ -427,7 +434,7 @@ class BibleImportForm(OpenLPWizard): if self.currentPage() == self.progress_page: return True - def on_web_source_combo_box_index_changed(self, index): + def on_web_language_combo_box_index_changed(self, index): """ Setup the list of Bibles when you select a different source on the web download page. @@ -475,6 +482,32 @@ class BibleImportForm(OpenLPWizard): self.get_file_name(WizardStrings.OpenTypeFile % WizardStrings.ZEF, self.zefania_file_edit, 'last directory import') + def on_web_update_button_clicked(self): + """ + Download list of bibles from Crosswalk, BibleServer and BibleGateway, and fill data in comboboxes + """ + # Download from Crosswalk, BiblesGateway, BibleServer + self.web_bible_list = {} + for (source, extractor) in ((WebDownload.Crosswalk, CWExtract()), (WebDownload.BibleGateway, BGExtract()), + (WebDownload.Bibleserver, BSExtract())): + bibles = extractor.get_bibles_from_http() + for (bible_name, bible_key, language_code) in bibles: + if not language_code in self.web_bible_list: + self.web_bible_list[language_code] = {} + self.web_bible_list[language_code]['translations'] = [] + bible_language = BiblesResourcesDB.get_language(language_code) + if bible_language: + self.web_bible_list[language_code]['name'] = bible_language['name'] + self.web_bible_list[language_code]['native_name'] = bible_language['native_name'] + else: + self.web_bible_list[language_code]['name'] = language_code + self.web_bible_list[language_code]['native_name'] = language_code + self.web_bible_list[language_code]['translations'].append((source, bible_name, bible_key)) + # Update combo box + for key in self.web_bible_list.keys(): + self.web_language_combo_box.addItem(self.web_bible_list[key]['native_name'] + ' (' + + self.web_bible_list[key]['name'] + ')', userData=key) + def register_fields(self): """ Register the bible import wizard fields. @@ -485,7 +518,7 @@ class BibleImportForm(OpenLPWizard): 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_language', self.web_language_combo_box) self.select_page.registerField('web_biblename', self.web_translation_combo_box) self.select_page.registerField('proxy_server', self.web_server_edit) self.select_page.registerField('proxy_username', self.web_user_edit) @@ -509,7 +542,7 @@ class BibleImportForm(OpenLPWizard): self.setField('csv_versefile', '') self.setField('opensong_file', '') self.setField('zefania_file', '') - self.setField('web_location', WebDownload.Crosswalk) + self.setField('web_language', 0) self.setField('web_biblename', self.web_translation_combo_box.currentIndex()) self.setField('proxy_server', settings.value('proxy address')) self.setField('proxy_username', settings.value('proxy username')) @@ -517,7 +550,7 @@ class BibleImportForm(OpenLPWizard): self.setField('license_version', self.version_name_edit.text()) self.setField('license_copyright', self.copyright_edit.text()) self.setField('license_permissions', self.permissions_edit.text()) - self.on_web_source_combo_box_index_changed(WebDownload.Crosswalk) + #self.on_web_language_combo_box_index_changed(WebDownload.Crosswalk) settings.endGroup() def load_Web_Bibles(self): diff --git a/openlp/plugins/bibles/lib/db.py b/openlp/plugins/bibles/lib/db.py index 5c8443f23..858afdd63 100644 --- a/openlp/plugins/bibles/lib/db.py +++ b/openlp/plugins/bibles/lib/db.py @@ -791,12 +791,13 @@ class BiblesResourcesDB(QtCore.QObject, Manager): if not isinstance(name, str): name = str(name) language = BiblesResourcesDB.run_sql( - 'SELECT id, name, code FROM language WHERE name = ? OR code = ?', (name, name.lower())) + 'SELECT id, name, code, native_name FROM language WHERE name = ? OR code = ?', (name, name.lower())) if language: return { 'id': language[0][0], 'name': str(language[0][1]), - 'code': str(language[0][2]) + 'code': str(language[0][2]), + 'native_name': str(language[0][3]) } else: return None diff --git a/openlp/plugins/bibles/lib/http.py b/openlp/plugins/bibles/lib/http.py index d93f645fa..6b1d96b4e 100644 --- a/openlp/plugins/bibles/lib/http.py +++ b/openlp/plugins/bibles/lib/http.py @@ -74,6 +74,15 @@ BIBLESERVER_LANGUAGE_CODE = { 'fl_21': 'zh' } +CROSSWALK_LANGUAGES = { + 'Portuguese': 'pt', + 'German': 'de', + 'Italian': 'it', + 'Español': 'es', + 'French' : 'fr', + 'Dutch': 'nl' +} + log = logging.getLogger(__name__) @@ -304,6 +313,8 @@ class BGExtract(RegistryProperties): def get_bibles_from_http(self): """ Load a list of bibles from BibleGateway website. + + returns a list in the form [(biblename, biblekey, language_code)] """ log.debug('BGExtract.get_bibles_from_http') bible_url = 'https://legacy.biblegateway.com/versions/' @@ -313,6 +324,7 @@ class BGExtract(RegistryProperties): bible_select = soup.find('select', {'class': 'translation-dropdown'}) option_tags = bible_select.find_all('option') current_lang = '' + bibles = [] for ot in option_tags: tag_class = '' try: @@ -321,11 +333,12 @@ class BGExtract(RegistryProperties): tag_class = '' tag_text = ot.get_text() if tag_class == 'lang': - current_lang = tag_text[tag_text.find('(') + 1:tag_text.find(')') + 1].lower() + current_lang = tag_text[tag_text.find('(') + 1:tag_text.find(')')].lower() elif tag_class == 'spacer': continue else: - print('biblename: %s, bible_key: %s, class: %s' % (tag_text, ot['value'], current_lang)) + bibles.append((tag_text, ot['value'], current_lang)) + return bibles class BSExtract(RegistryProperties): @@ -390,6 +403,8 @@ class BSExtract(RegistryProperties): def get_bibles_from_http(self): """ Load a list of bibles from Bibleserver website. + + returns a list in the form [(biblename, biblekey, language_code)] """ log.debug('BSExtract.get_bibles_from_http') bible_url = 'http://www.bibleserver.com/index.php?language=2' @@ -397,6 +412,7 @@ class BSExtract(RegistryProperties): if not soup: return None bible_links = soup.find_all('a', {'class': 'trlCell'}) + bibles = [] for link in bible_links: bible_name = link.get_text() # Skip any audio @@ -412,7 +428,8 @@ class BSExtract(RegistryProperties): language_code = BIBLESERVER_LANGUAGE_CODE[css_class] except KeyError: language_code = '' - print('biblename: %s, bible_key: %s, class: %s' % (bible_name, bible_key, language_code)) + bibles.append((bible_name, bible_key, language_code)) + return bibles class CWExtract(RegistryProperties): @@ -487,6 +504,7 @@ class CWExtract(RegistryProperties): def get_bibles_from_http(self): """ Load a list of bibles from Crosswalk website. + returns a list in the form [(biblename, biblekey, language_code)] """ log.debug('CWExtract.get_bibles_from_http') bible_url = 'http://www.biblestudytools.com/search/bible-search.part/' @@ -495,20 +513,27 @@ class CWExtract(RegistryProperties): return None bible_select = soup.find('select') option_tags = bible_select.find_all('option') + bibles = [] for ot in option_tags: - tag_text = ot.get_text() + tag_text = ot.get_text().strip() tag_value = ot['value'] if not tag_value: continue # The names of non-english bibles has their language in parentheses at the end if tag_text.endswith(')'): - language_code = '' + language = tag_text[tag_text.rfind('(') + 1:-1] + if language in CROSSWALK_LANGUAGES: + language_code = CROSSWALK_LANGUAGES[language] + tag_text = tag_text[:tag_text.rfind('(')] + else: + language_code = '' # ... except for the latin vulgate elif 'latin' in tag_text.lower(): language_code = 'la' else: language_code = 'en' - print('biblename: %s, bible_key: %s, class: %s' % (tag_text, tag_value, language_code)) + bibles.append((tag_text, tag_value, language_code)) + return bibles class HTTPBible(BibleDB, RegistryProperties): diff --git a/openlp/plugins/bibles/resources/bibles_resources.sqlite b/openlp/plugins/bibles/resources/bibles_resources.sqlite index 8f1777124032ee11b547fd6e1897f7e9b8b717a9..4b6686055f5de7a9382625806e1231266581ee74 100644 GIT binary patch delta 10724 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 From ecc30163af2461e56e897b102ef047ef540264cd Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Tue, 24 Feb 2015 23:04:01 +0000 Subject: [PATCH 4/8] Reverted some things to decrease the changes. It is now working. --- .../plugins/bibles/forms/bibleimportform.py | 119 +++++++++--------- .../bibles/resources/bibles_resources.sqlite | Bin 112640 -> 104448 bytes 2 files changed, 57 insertions(+), 62 deletions(-) diff --git a/openlp/plugins/bibles/forms/bibleimportform.py b/openlp/plugins/bibles/forms/bibleimportform.py index ee4b01c35..ac80434ca 100644 --- a/openlp/plugins/bibles/forms/bibleimportform.py +++ b/openlp/plugins/bibles/forms/bibleimportform.py @@ -24,6 +24,7 @@ The bible import functions for OpenLP """ import logging import os +import urllib.error from PyQt4 import QtGui @@ -91,7 +92,6 @@ class BibleImportForm(OpenLPWizard): Perform any custom initialisation for bible importing. """ self.manager.set_process_dialog(self) - self.load_Web_Bibles() self.restart() self.select_stack.setCurrentIndex(0) @@ -99,7 +99,7 @@ class BibleImportForm(OpenLPWizard): """ Set up the signals used in the bible importer. """ - self.web_language_combo_box.currentIndexChanged.connect(self.on_web_language_combo_box_index_changed) + self.web_source_combo_box.currentIndexChanged.connect(self.on_web_source_combo_box_index_changed) self.osis_browse_button.clicked.connect(self.on_osis_browse_button_clicked) self.csv_books_button.clicked.connect(self.on_csv_books_browse_button_clicked) self.csv_verses_button.clicked.connect(self.on_csv_verses_browse_button_clicked) @@ -204,28 +204,33 @@ class BibleImportForm(OpenLPWizard): self.web_bible_tab.setObjectName('WebBibleTab') self.web_bible_layout = QtGui.QFormLayout(self.web_bible_tab) self.web_bible_layout.setObjectName('WebBibleLayout') - self.web_update_label = QtGui.QLabel(self.web_bible_tab) self.web_update_label.setObjectName('WebUpdateLabel') self.web_bible_layout.setWidget(0, QtGui.QFormLayout.LabelRole, self.web_update_label) self.web_update_button = QtGui.QPushButton(self.web_bible_tab) - self.web_update_button.setObjectName('WebUpdateLabel') + self.web_update_button.setObjectName('WebUpdateButton') self.web_bible_layout.setWidget(0, QtGui.QFormLayout.FieldRole, self.web_update_button) - self.web_source_label = QtGui.QLabel(self.web_bible_tab) self.web_source_label.setObjectName('WebSourceLabel') self.web_bible_layout.setWidget(1, QtGui.QFormLayout.LabelRole, self.web_source_label) - self.web_language_combo_box = QtGui.QComboBox(self.web_bible_tab) - self.web_language_combo_box.setObjectName('WebSourceComboBox') - self.web_language_combo_box.addItems(['', '', '']) - self.web_bible_layout.setWidget(1, QtGui.QFormLayout.FieldRole, self.web_language_combo_box) + self.web_source_combo_box = QtGui.QComboBox(self.web_bible_tab) + self.web_source_combo_box.setObjectName('WebSourceComboBox') + self.web_source_combo_box.addItems(['', '', '']) + self.web_source_combo_box.setEnabled(False) + self.web_bible_layout.setWidget(1, QtGui.QFormLayout.FieldRole, self.web_source_combo_box) self.web_translation_label = QtGui.QLabel(self.web_bible_tab) self.web_translation_label.setObjectName('web_translation_label') self.web_bible_layout.setWidget(2, QtGui.QFormLayout.LabelRole, self.web_translation_label) self.web_translation_combo_box = QtGui.QComboBox(self.web_bible_tab) self.web_translation_combo_box.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToContents) self.web_translation_combo_box.setObjectName('WebTranslationComboBox') + self.web_translation_combo_box.setEnabled(False) self.web_bible_layout.setWidget(2, QtGui.QFormLayout.FieldRole, self.web_translation_combo_box) + self.web_progress_bar = QtGui.QProgressBar(self) + self.web_progress_bar.setRange(0, 3) + self.web_progress_bar.setObjectName('WebTranslationProgressBar') + self.web_progress_bar.setVisible(False) + self.web_bible_layout.setWidget(3, QtGui.QFormLayout.SpanningRole, self.web_progress_bar) self.web_tab_widget.addTab(self.web_bible_tab, '') self.web_proxy_tab = QtGui.QWidget() self.web_proxy_tab.setObjectName('WebProxyTab') @@ -326,7 +331,12 @@ class BibleImportForm(OpenLPWizard): self.zefania_file_label.setText(translate('BiblesPlugin.ImportWizardForm', 'Bible file:')) self.web_update_label.setText(translate('BiblesPlugin.ImportWizardForm', 'Click to fetch bible list')) self.web_update_button.setText(translate('BiblesPlugin.ImportWizardForm', 'Fetch list')) - #self.web_language_combo_box.setItemText(0, translate('BiblesPlugin.ImportWizardForm', 'Choose a language')) + self.web_source_combo_box.setItemText(WebDownload.Crosswalk, translate('BiblesPlugin.ImportWizardForm', + 'Crosswalk')) + self.web_source_combo_box.setItemText(WebDownload.BibleGateway, translate('BiblesPlugin.ImportWizardForm', + 'BibleGateway')) + self.web_source_combo_box.setItemText(WebDownload.Bibleserver, translate('BiblesPlugin.ImportWizardForm', + 'Bibleserver')) self.web_translation_label.setText(translate('BiblesPlugin.ImportWizardForm', 'Bible:')) self.web_tab_widget.setTabText(self.web_tab_widget.indexOf(self.web_bible_tab), translate('BiblesPlugin.ImportWizardForm', 'Download Options')) @@ -395,8 +405,11 @@ class BibleImportForm(OpenLPWizard): 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 + # If count is 0 the bible list has not yet been downloaded + if self.web_translation_combo_box.count() == 0: + return False + else: + self.version_name_edit.setText(self.web_translation_combo_box.currentText()) return True elif self.currentPage() == self.license_details_page: license_version = self.field('license_version') @@ -434,16 +447,17 @@ class BibleImportForm(OpenLPWizard): if self.currentPage() == self.progress_page: return True - def on_web_language_combo_box_index_changed(self, index): + def on_web_source_combo_box_index_changed(self, index): """ Setup the list of Bibles when you select a different source on the web download page. :param index: The index of the combo box. """ self.web_translation_combo_box.clear() - bibles = list(self.web_bible_list[index].keys()) - bibles.sort(key=get_locale_key) - self.web_translation_combo_box.addItems(bibles) + if self.web_bible_list: + bibles = list(self.web_bible_list[index].keys()) + bibles.sort(key=get_locale_key) + self.web_translation_combo_box.addItems(bibles) def on_osis_browse_button_clicked(self): """ @@ -484,29 +498,34 @@ class BibleImportForm(OpenLPWizard): def on_web_update_button_clicked(self): """ - Download list of bibles from Crosswalk, BibleServer and BibleGateway, and fill data in comboboxes + Download list of bibles from Crosswalk, BibleServer and BibleGateway. """ # Download from Crosswalk, BiblesGateway, BibleServer self.web_bible_list = {} - for (source, extractor) in ((WebDownload.Crosswalk, CWExtract()), (WebDownload.BibleGateway, BGExtract()), - (WebDownload.Bibleserver, BSExtract())): - bibles = extractor.get_bibles_from_http() + self.web_source_combo_box.setEnabled(False) + self.web_translation_combo_box.setEnabled(False) + self.web_progress_bar.setVisible(True) + self.web_progress_bar.setValue(0) + proxy_server = self.field('proxy_server') + for (download_type, extractor) in ((WebDownload.Crosswalk, CWExtract(proxy_server)), + (WebDownload.BibleGateway, BGExtract(proxy_server)), + (WebDownload.Bibleserver, BSExtract(proxy_server))): + try: + bibles = extractor.get_bibles_from_http() + except (urllib.error.URLError, ConnectionError) as err: + critical_error_message_box(translate('BiblesPlugin.ImportWizardForm', 'Error during download'), + translate('BiblesPlugin.ImportWizardForm', + 'An error occurred while downloading the list of bibles from %s.')) + self.web_bible_list[download_type] = {} for (bible_name, bible_key, language_code) in bibles: - if not language_code in self.web_bible_list: - self.web_bible_list[language_code] = {} - self.web_bible_list[language_code]['translations'] = [] - bible_language = BiblesResourcesDB.get_language(language_code) - if bible_language: - self.web_bible_list[language_code]['name'] = bible_language['name'] - self.web_bible_list[language_code]['native_name'] = bible_language['native_name'] - else: - self.web_bible_list[language_code]['name'] = language_code - self.web_bible_list[language_code]['native_name'] = language_code - self.web_bible_list[language_code]['translations'].append((source, bible_name, bible_key)) - # Update combo box - for key in self.web_bible_list.keys(): - self.web_language_combo_box.addItem(self.web_bible_list[key]['native_name'] + ' (' + - self.web_bible_list[key]['name'] + ')', userData=key) + self.web_bible_list[download_type][bible_name] = bible_key + self.web_progress_bar.setValue(download_type + 1) + # Update combo box if something got into the list + if self.web_bible_list: + self.on_web_source_combo_box_index_changed(0) + self.web_source_combo_box.setEnabled(True) + self.web_translation_combo_box.setEnabled(True) + self.web_progress_bar.setVisible(False) def register_fields(self): """ @@ -518,7 +537,7 @@ class BibleImportForm(OpenLPWizard): 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_language', self.web_language_combo_box) + self.select_page.registerField('web_location', self.web_source_combo_box) self.select_page.registerField('web_biblename', self.web_translation_combo_box) self.select_page.registerField('proxy_server', self.web_server_edit) self.select_page.registerField('proxy_username', self.web_user_edit) @@ -542,7 +561,7 @@ class BibleImportForm(OpenLPWizard): self.setField('csv_versefile', '') self.setField('opensong_file', '') self.setField('zefania_file', '') - self.setField('web_language', 0) + self.setField('web_location', WebDownload.Crosswalk) self.setField('web_biblename', self.web_translation_combo_box.currentIndex()) self.setField('proxy_server', settings.value('proxy address')) self.setField('proxy_username', settings.value('proxy username')) @@ -550,33 +569,9 @@ class BibleImportForm(OpenLPWizard): self.setField('license_version', self.version_name_edit.text()) self.setField('license_copyright', self.copyright_edit.text()) self.setField('license_permissions', self.permissions_edit.text()) - #self.on_web_language_combo_box_index_changed(WebDownload.Crosswalk) + self.on_web_source_combo_box_index_changed(WebDownload.Crosswalk) settings.endGroup() - def load_Web_Bibles(self): - """ - Load the lists of Crosswalk, BibleGateway and Bibleserver bibles. - """ - # Load Crosswalk Bibles. - self.load_Bible_Resource(WebDownload.Crosswalk) - # Load BibleGateway Bibles. - self.load_Bible_Resource(WebDownload.BibleGateway) - # Load and Bibleserver Bibles. - self.load_Bible_Resource(WebDownload.Bibleserver) - - def load_Bible_Resource(self, download_type): - """ - Loads a web bible from bible_resources.sqlite. - - :param download_type: The WebDownload type e.g. bibleserver. - """ - self.web_bible_list[download_type] = {} - bibles = BiblesResourcesDB.get_webbibles(WebDownload.Names[download_type]) - for bible in bibles: - version = bible['name'] - name = bible['abbreviation'] - self.web_bible_list[download_type][version] = name.strip() - def pre_wizard(self): """ Prepare the UI for the import. diff --git a/openlp/plugins/bibles/resources/bibles_resources.sqlite b/openlp/plugins/bibles/resources/bibles_resources.sqlite index 4b6686055f5de7a9382625806e1231266581ee74..8f1777124032ee11b547fd6e1897f7e9b8b717a9 100644 GIT binary patch delta 4349 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 delta 10724 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 Date: Thu, 26 Feb 2015 20:58:54 +0000 Subject: [PATCH 5/8] Fixes for code and tests. --- .../plugins/bibles/forms/bibleimportform.py | 13 +++-- openlp/plugins/bibles/lib/db.py | 5 +- openlp/plugins/bibles/lib/http.py | 49 ++++++++++++++----- .../openlp_plugins/bibles/test_lib_http.py | 25 +++++++--- 4 files changed, 66 insertions(+), 26 deletions(-) diff --git a/openlp/plugins/bibles/forms/bibleimportform.py b/openlp/plugins/bibles/forms/bibleimportform.py index ac80434ca..94d8e6567 100644 --- a/openlp/plugins/bibles/forms/bibleimportform.py +++ b/openlp/plugins/bibles/forms/bibleimportform.py @@ -329,8 +329,8 @@ class BibleImportForm(OpenLPWizard): 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_update_label.setText(translate('BiblesPlugin.ImportWizardForm', 'Click to fetch bible list')) - self.web_update_button.setText(translate('BiblesPlugin.ImportWizardForm', 'Fetch list')) + self.web_update_label.setText(translate('BiblesPlugin.ImportWizardForm', 'Click to download bible list')) + self.web_update_button.setText(translate('BiblesPlugin.ImportWizardForm', 'Download bible list')) self.web_source_combo_box.setItemText(WebDownload.Crosswalk, translate('BiblesPlugin.ImportWizardForm', 'Crosswalk')) self.web_source_combo_box.setItemText(WebDownload.BibleGateway, translate('BiblesPlugin.ImportWizardForm', @@ -504,6 +504,7 @@ class BibleImportForm(OpenLPWizard): self.web_bible_list = {} self.web_source_combo_box.setEnabled(False) self.web_translation_combo_box.setEnabled(False) + self.web_update_button.setEnabled(False) self.web_progress_bar.setVisible(True) self.web_progress_bar.setValue(0) proxy_server = self.field('proxy_server') @@ -518,13 +519,14 @@ class BibleImportForm(OpenLPWizard): 'An error occurred while downloading the list of bibles from %s.')) self.web_bible_list[download_type] = {} for (bible_name, bible_key, language_code) in bibles: - self.web_bible_list[download_type][bible_name] = bible_key + self.web_bible_list[download_type][bible_name] = (bible_key, language_code) self.web_progress_bar.setValue(download_type + 1) # Update combo box if something got into the list if self.web_bible_list: self.on_web_source_combo_box_index_changed(0) self.web_source_combo_box.setEnabled(True) self.web_translation_combo_box.setEnabled(True) + self.web_update_button.setEnabled(True) self.web_progress_bar.setVisible(False) def register_fields(self): @@ -611,14 +613,15 @@ class BibleImportForm(OpenLPWizard): self.progress_bar.setMaximum(1) download_location = self.field('web_location') bible_version = self.web_translation_combo_box.currentText() - bible = self.web_bible_list[download_location][bible_version] + (bible, language_id) = self.web_bible_list[download_location][bible_version] importer = self.manager.import_bible( BibleFormat.WebDownload, name=license_version, download_source=WebDownload.Names[download_location], download_name=bible, proxy_server=self.field('proxy_server'), proxy_username=self.field('proxy_username'), - proxy_password=self.field('proxy_password') + proxy_password=self.field('proxy_password'), + language_id=language_id ) elif bible_type == BibleFormat.Zefania: # Import an Zefania bible. diff --git a/openlp/plugins/bibles/lib/db.py b/openlp/plugins/bibles/lib/db.py index 858afdd63..5c8443f23 100644 --- a/openlp/plugins/bibles/lib/db.py +++ b/openlp/plugins/bibles/lib/db.py @@ -791,13 +791,12 @@ class BiblesResourcesDB(QtCore.QObject, Manager): if not isinstance(name, str): name = str(name) language = BiblesResourcesDB.run_sql( - 'SELECT id, name, code, native_name FROM language WHERE name = ? OR code = ?', (name, name.lower())) + 'SELECT id, name, code FROM language WHERE name = ? OR code = ?', (name, name.lower())) if language: return { 'id': language[0][0], 'name': str(language[0][1]), - 'code': str(language[0][2]), - 'native_name': str(language[0][3]) + 'code': str(language[0][2]) } else: return None diff --git a/openlp/plugins/bibles/lib/http.py b/openlp/plugins/bibles/lib/http.py index 6b1d96b4e..e2df05c90 100644 --- a/openlp/plugins/bibles/lib/http.py +++ b/openlp/plugins/bibles/lib/http.py @@ -254,6 +254,8 @@ class BGExtract(RegistryProperties): if not soup: return None div = soup.find('div', 'result-text-style-normal') + if not div: + return None self._clean_soup(div) span_list = div.find_all('span', 'text') log.debug('Span list: %s', span_list) @@ -322,7 +324,13 @@ class BGExtract(RegistryProperties): if not soup: return None bible_select = soup.find('select', {'class': 'translation-dropdown'}) + if not bible_select: + log.debug('No select tags found - did site change?') + return None option_tags = bible_select.find_all('option') + if not option_tags: + log.debug('No option tags found - did site change?') + return None current_lang = '' bibles = [] for ot in option_tags: @@ -412,15 +420,21 @@ class BSExtract(RegistryProperties): if not soup: return None bible_links = soup.find_all('a', {'class': 'trlCell'}) + if not bible_links: + log.debug('No a tags found - did site change?') + return None bibles = [] for link in bible_links: bible_name = link.get_text() # Skip any audio if 'audio' in bible_name.lower(): continue - bible_link = link['href'] - bible_key = bible_link[bible_link.rfind('/') + 1:] - css_classes = link['class'] + try: + bible_link = link['href'] + bible_key = bible_link[bible_link.rfind('/') + 1:] + css_classes = link['class'] + except KeyError: + log.debug('No href/class attribute found - did site change?') language_code = '' for css_class in css_classes: if css_class.startswith('fl_'): @@ -512,11 +526,21 @@ class CWExtract(RegistryProperties): if not soup: return None bible_select = soup.find('select') + if not bible_select: + log.debug('No select tags found - did site change?') + return None option_tags = bible_select.find_all('option') + if not option_tags: + log.debug('No option tags found - did site change?') + return None bibles = [] for ot in option_tags: tag_text = ot.get_text().strip() - tag_value = ot['value'] + try: + tag_value = ot['value'] + except KeyError: + log.exception('No value attribute found - did site change?') + return None if not tag_value: continue # The names of non-english bibles has their language in parentheses at the end @@ -524,7 +548,6 @@ class CWExtract(RegistryProperties): language = tag_text[tag_text.rfind('(') + 1:-1] if language in CROSSWALK_LANGUAGES: language_code = CROSSWALK_LANGUAGES[language] - tag_text = tag_text[:tag_text.rfind('(')] else: language_code = '' # ... except for the latin vulgate @@ -555,6 +578,7 @@ class HTTPBible(BibleDB, RegistryProperties): self.proxy_server = None self.proxy_username = None self.proxy_password = None + self.language_id = None if 'path' in kwargs: self.path = kwargs['path'] if 'proxy_server' in kwargs: @@ -563,6 +587,8 @@ class HTTPBible(BibleDB, RegistryProperties): self.proxy_username = kwargs['proxy_username'] if 'proxy_password' in kwargs: self.proxy_password = kwargs['proxy_password'] + if 'language_id' in kwargs: + self.language_id = kwargs['language_id'] def do_import(self, bible_name=None): """ @@ -595,13 +621,12 @@ class HTTPBible(BibleDB, RegistryProperties): return False self.wizard.progress_bar.setMaximum(len(books) + 2) self.wizard.increment_progress_bar(translate('BiblesPlugin.HTTPBible', 'Registering Language...')) - bible = BiblesResourcesDB.get_webbible(self.download_name, self.download_source.lower()) - if bible['language_id']: - language_id = bible['language_id'] - self.save_meta('language_id', language_id) + bible = None #BiblesResourcesDB.get_webbible(self.download_name, self.download_source.lower()) + if self.language_id: + self.save_meta('language_id', self.language_id) else: - language_id = self.get_language(bible_name) - if not language_id: + self.language_id = self.get_language(bible_name) + if not self.language_id: log.error('Importing books from %s failed' % self.filename) return False for book in books: @@ -609,7 +634,7 @@ class HTTPBible(BibleDB, RegistryProperties): break self.wizard.increment_progress_bar(translate( 'BiblesPlugin.HTTPBible', 'Importing %s...', 'Importing ...') % book) - book_ref_id = self.get_book_ref_id_by_name(book, len(books), language_id) + book_ref_id = self.get_book_ref_id_by_name(book, len(books), self.language_id) if not book_ref_id: log.error('Importing books from %s - download name: "%s" failed' % (self.download_source, self.download_name)) diff --git a/tests/interfaces/openlp_plugins/bibles/test_lib_http.py b/tests/interfaces/openlp_plugins/bibles/test_lib_http.py index c422f6e5f..ff9ce0382 100644 --- a/tests/interfaces/openlp_plugins/bibles/test_lib_http.py +++ b/tests/interfaces/openlp_plugins/bibles/test_lib_http.py @@ -124,8 +124,13 @@ class TestBibleHTTP(TestCase): # GIVEN: A new Bible Server extraction class handler = BSExtract() - handler.get_bibles_from_http() - self.assertTrue(False) + # WHEN: downloading bible list from bibleserver + bibles = handler.get_bibles_from_http() + + # THEN: The list should not be None, and some known bibles should be there + self.assertIsNotNone(bibles) + self.assertIn(('New Int. Readers Version', 'NIRV', 'en'), bibles) + self.assertIn(('Българската Библия', 'BLG', 'bg'), bibles) def biblegateway_get_bibles_test(self): """ @@ -134,8 +139,12 @@ class TestBibleHTTP(TestCase): # GIVEN: A new Bible Gateway extraction class handler = BGExtract() - handler.get_bibles_from_http() - self.assertTrue(False) + # WHEN: downloading bible list from Crosswalk + bibles = handler.get_bibles_from_http() + + # THEN: The list should not be None, and some known bibles should be there + self.assertIsNotNone(bibles) + self.assertIn(('Holman Christian Standard Bible', 'HCSB', 'en'), bibles) def crosswalk_get_bibles_test(self): """ @@ -144,5 +153,9 @@ class TestBibleHTTP(TestCase): # GIVEN: A new Crosswalk extraction class handler = CWExtract() - handler.get_bibles_from_http() - self.assertTrue(False) + # WHEN: downloading bible list from Crosswalk + bibles = handler.get_bibles_from_http() + + # THEN: The list should not be None, and some known bibles should be there + self.assertIsNotNone(bibles) + self.assertIn(('Giovanni Diodati 1649 (Italian)', 'gdb', 'it'), bibles) From 7a5787797e75e011a31f7f77b8148049e3bdd41c Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Thu, 26 Feb 2015 21:02:26 +0000 Subject: [PATCH 6/8] Pep8 fixes --- openlp/plugins/bibles/lib/http.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openlp/plugins/bibles/lib/http.py b/openlp/plugins/bibles/lib/http.py index e2df05c90..fbf18d98e 100644 --- a/openlp/plugins/bibles/lib/http.py +++ b/openlp/plugins/bibles/lib/http.py @@ -79,7 +79,7 @@ CROSSWALK_LANGUAGES = { 'German': 'de', 'Italian': 'it', 'Español': 'es', - 'French' : 'fr', + 'French': 'fr', 'Dutch': 'nl' } @@ -621,7 +621,6 @@ class HTTPBible(BibleDB, RegistryProperties): return False self.wizard.progress_bar.setMaximum(len(books) + 2) self.wizard.increment_progress_bar(translate('BiblesPlugin.HTTPBible', 'Registering Language...')) - bible = None #BiblesResourcesDB.get_webbible(self.download_name, self.download_source.lower()) if self.language_id: self.save_meta('language_id', self.language_id) else: From 7d7fec194e827c3d4322242c81a9d0e82c0599b7 Mon Sep 17 00:00:00 2001 From: Raoul Snyman Date: Sat, 28 Feb 2015 00:28:24 +0200 Subject: [PATCH 7/8] Fix bug #1423699 so that you're not logged out when you want to import more songs. Fixes: https://launchpad.net/bugs/1423699 --- .bzrignore | 2 + openlp/plugins/songs/forms/songselectform.py | 16 +- .../openlp_plugins/songs/test_songselect.py | 898 +++++++++++------- 3 files changed, 575 insertions(+), 341 deletions(-) diff --git a/.bzrignore b/.bzrignore index 6b7b989a6..5fd2beb79 100644 --- a/.bzrignore +++ b/.bzrignore @@ -40,3 +40,5 @@ __pycache__ # Rejected diff's *.rej *.~\?~ +.coverage +cover diff --git a/openlp/plugins/songs/forms/songselectform.py b/openlp/plugins/songs/forms/songselectform.py index 8482bee6b..e885d0269 100755 --- a/openlp/plugins/songs/forms/songselectform.py +++ b/openlp/plugins/songs/forms/songselectform.py @@ -192,7 +192,7 @@ class SongSelectForm(QtGui.QDialog, Ui_SongSelectDialog): self, translate('SongsPlugin.SongSelectForm', 'Incomplete song'), translate('SongsPlugin.SongSelectForm', 'This song is missing some information, like the lyrics, ' 'and cannot be imported.'), - QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok), QtGui.QMessageBox.OK) + QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok), QtGui.QMessageBox.Ok) self.stacked_widget.setCurrentIndex(1) return # Update the UI @@ -367,15 +367,11 @@ class SongSelectForm(QtGui.QDialog, Ui_SongSelectDialog): Import a song from SongSelect. """ self.song_select_importer.save_song(self.song) - question_dialog = QtGui.QMessageBox() - question_dialog.setWindowTitle(translate('SongsPlugin.SongSelectForm', 'Song Imported')) - question_dialog.setText(translate('SongsPlugin.SongSelectForm', 'Your song has been imported, would you like ' - 'to exit now, or import more songs?')) - question_dialog.addButton(QtGui.QPushButton(translate('SongsPlugin.SongSelectForm', 'Import More Songs')), - QtGui.QMessageBox.YesRole) - question_dialog.addButton(QtGui.QPushButton(translate('SongsPlugin.SongSelectForm', 'Exit Now')), - QtGui.QMessageBox.NoRole) - if question_dialog.exec_() == QtGui.QMessageBox.Yes: + if QtGui.QMessageBox.question(self, translate('SongsPlugin.SongSelectForm', 'Song Imported'), + translate('SongsPlugin.SongSelectForm', 'Your song has been imported, would you ' + 'like to import more songs?'), + QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, + QtGui.QMessageBox.Yes) == QtGui.QMessageBox.Yes: self.on_back_button_clicked() else: self.application.process_events() diff --git a/tests/functional/openlp_plugins/songs/test_songselect.py b/tests/functional/openlp_plugins/songs/test_songselect.py index 700933465..2fda9bd3b 100644 --- a/tests/functional/openlp_plugins/songs/test_songselect.py +++ b/tests/functional/openlp_plugins/songs/test_songselect.py @@ -23,361 +23,363 @@ This module contains tests for the CCLI SongSelect importer. """ import os - from unittest import TestCase from urllib.error import URLError -from tests.functional import MagicMock, patch, call -from tests.helpers.testmixin import TestMixin + +from PyQt4 import QtGui from openlp.core import Registry -from openlp.plugins.songs.forms.songselectform import SongSelectForm -from openlp.plugins.songs.lib import Author, Song, VerseType +from openlp.plugins.songs.forms.songselectform import SongSelectForm, SearchWorker +from openlp.plugins.songs.lib import Song from openlp.plugins.songs.lib.songselect import SongSelectImport, LOGOUT_URL, BASE_URL from openlp.plugins.songs.lib.importers.cclifile import CCLIFileImport +from tests.functional import MagicMock, patch, call +from tests.helpers.testmixin import TestMixin + class TestSongSelectImport(TestCase, TestMixin): """ Test the :class:`~openlp.plugins.songs.lib.songselect.SongSelectImport` class """ - def constructor_test(self): + @patch('openlp.plugins.songs.lib.songselect.build_opener') + def constructor_test(self, mocked_build_opener): """ Test that constructing a basic SongSelectImport object works correctly """ # GIVEN: The SongSelectImporter class and a mocked out build_opener - with patch('openlp.plugins.songs.lib.songselect.build_opener') as mocked_build_opener: - # WHEN: An object is instantiated - importer = SongSelectImport(None) + # WHEN: An object is instantiated + importer = SongSelectImport(None) - # THEN: The object should have the correct properties - self.assertIsNone(importer.db_manager, 'The db_manager should be None') - self.assertIsNotNone(importer.html_parser, 'There should be a valid html_parser object') - self.assertIsNotNone(importer.opener, 'There should be a valid opener object') - self.assertEqual(1, mocked_build_opener.call_count, 'The build_opener method should have been called once') + # THEN: The object should have the correct properties + self.assertIsNone(importer.db_manager, 'The db_manager should be None') + self.assertIsNotNone(importer.html_parser, 'There should be a valid html_parser object') + self.assertIsNotNone(importer.opener, 'There should be a valid opener object') + self.assertEqual(1, mocked_build_opener.call_count, 'The build_opener method should have been called once') - def login_fails_test(self): + @patch('openlp.plugins.songs.lib.songselect.build_opener') + @patch('openlp.plugins.songs.lib.songselect.BeautifulSoup') + def login_fails_test(self, MockedBeautifulSoup, mocked_build_opener): """ Test that when logging in to SongSelect fails, the login method returns False """ # GIVEN: A bunch of mocked out stuff and an importer object - with patch('openlp.plugins.songs.lib.songselect.build_opener') as mocked_build_opener, \ - patch('openlp.plugins.songs.lib.songselect.BeautifulSoup') as MockedBeautifulSoup: - mocked_opener = MagicMock() - mocked_build_opener.return_value = mocked_opener - mocked_login_page = MagicMock() - mocked_login_page.find.return_value = {'value': 'blah'} - MockedBeautifulSoup.return_value = mocked_login_page - mock_callback = MagicMock() - importer = SongSelectImport(None) + mocked_opener = MagicMock() + mocked_build_opener.return_value = mocked_opener + mocked_login_page = MagicMock() + mocked_login_page.find.return_value = {'value': 'blah'} + MockedBeautifulSoup.return_value = mocked_login_page + mock_callback = MagicMock() + importer = SongSelectImport(None) - # WHEN: The login method is called after being rigged to fail - result = importer.login('username', 'password', mock_callback) + # WHEN: The login method is called after being rigged to fail + result = importer.login('username', 'password', mock_callback) - # THEN: callback was called 3 times, open was called twice, find was called twice, and False was returned - self.assertEqual(3, mock_callback.call_count, 'callback should have been called 3 times') - self.assertEqual(2, mocked_login_page.find.call_count, 'find should have been called twice') - self.assertEqual(2, mocked_opener.open.call_count, 'opener should have been called twice') - self.assertFalse(result, 'The login method should have returned False') + # THEN: callback was called 3 times, open was called twice, find was called twice, and False was returned + self.assertEqual(3, mock_callback.call_count, 'callback should have been called 3 times') + self.assertEqual(2, mocked_login_page.find.call_count, 'find should have been called twice') + self.assertEqual(2, mocked_opener.open.call_count, 'opener should have been called twice') + self.assertFalse(result, 'The login method should have returned False') - def login_succeeds_test(self): + @patch('openlp.plugins.songs.lib.songselect.build_opener') + @patch('openlp.plugins.songs.lib.songselect.BeautifulSoup') + def login_succeeds_test(self, MockedBeautifulSoup, mocked_build_opener): """ Test that when logging in to SongSelect succeeds, the login method returns True """ # GIVEN: A bunch of mocked out stuff and an importer object - with patch('openlp.plugins.songs.lib.songselect.build_opener') as mocked_build_opener, \ - patch('openlp.plugins.songs.lib.songselect.BeautifulSoup') as MockedBeautifulSoup: - mocked_opener = MagicMock() - mocked_build_opener.return_value = mocked_opener - mocked_login_page = MagicMock() - mocked_login_page.find.side_effect = [{'value': 'blah'}, None] - MockedBeautifulSoup.return_value = mocked_login_page - mock_callback = MagicMock() - importer = SongSelectImport(None) + mocked_opener = MagicMock() + mocked_build_opener.return_value = mocked_opener + mocked_login_page = MagicMock() + mocked_login_page.find.side_effect = [{'value': 'blah'}, None] + MockedBeautifulSoup.return_value = mocked_login_page + mock_callback = MagicMock() + importer = SongSelectImport(None) - # WHEN: The login method is called after being rigged to fail - result = importer.login('username', 'password', mock_callback) + # WHEN: The login method is called after being rigged to fail + result = importer.login('username', 'password', mock_callback) - # THEN: callback was called 3 times, open was called twice, find was called twice, and True was returned - self.assertEqual(3, mock_callback.call_count, 'callback should have been called 3 times') - self.assertEqual(2, mocked_login_page.find.call_count, 'find should have been called twice') - self.assertEqual(2, mocked_opener.open.call_count, 'opener should have been called twice') - self.assertTrue(result, 'The login method should have returned True') + # THEN: callback was called 3 times, open was called twice, find was called twice, and True was returned + self.assertEqual(3, mock_callback.call_count, 'callback should have been called 3 times') + self.assertEqual(2, mocked_login_page.find.call_count, 'find should have been called twice') + self.assertEqual(2, mocked_opener.open.call_count, 'opener should have been called twice') + self.assertTrue(result, 'The login method should have returned True') - def logout_test(self): + @patch('openlp.plugins.songs.lib.songselect.build_opener') + def logout_test(self, mocked_build_opener): """ Test that when the logout method is called, it logs the user out of SongSelect """ # GIVEN: A bunch of mocked out stuff and an importer object - with patch('openlp.plugins.songs.lib.songselect.build_opener') as mocked_build_opener: - mocked_opener = MagicMock() - mocked_build_opener.return_value = mocked_opener - importer = SongSelectImport(None) + mocked_opener = MagicMock() + mocked_build_opener.return_value = mocked_opener + importer = SongSelectImport(None) - # WHEN: The login method is called after being rigged to fail - importer.logout() + # WHEN: The login method is called after being rigged to fail + importer.logout() - # THEN: The opener is called once with the logout url - self.assertEqual(1, mocked_opener.open.call_count, 'opener should have been called once') - mocked_opener.open.assert_called_with(LOGOUT_URL) + # THEN: The opener is called once with the logout url + self.assertEqual(1, mocked_opener.open.call_count, 'opener should have been called once') + mocked_opener.open.assert_called_with(LOGOUT_URL) - def search_returns_no_results_test(self): + @patch('openlp.plugins.songs.lib.songselect.build_opener') + @patch('openlp.plugins.songs.lib.songselect.BeautifulSoup') + def search_returns_no_results_test(self, MockedBeautifulSoup, mocked_build_opener): """ Test that when the search finds no results, it simply returns an empty list """ # GIVEN: A bunch of mocked out stuff and an importer object - with patch('openlp.plugins.songs.lib.songselect.build_opener') as mocked_build_opener, \ - patch('openlp.plugins.songs.lib.songselect.BeautifulSoup') as MockedBeautifulSoup: - mocked_opener = MagicMock() - mocked_build_opener.return_value = mocked_opener - mocked_results_page = MagicMock() - mocked_results_page.find_all.return_value = [] - MockedBeautifulSoup.return_value = mocked_results_page - mock_callback = MagicMock() - importer = SongSelectImport(None) + mocked_opener = MagicMock() + mocked_build_opener.return_value = mocked_opener + mocked_results_page = MagicMock() + mocked_results_page.find_all.return_value = [] + MockedBeautifulSoup.return_value = mocked_results_page + mock_callback = MagicMock() + importer = SongSelectImport(None) - # WHEN: The login method is called after being rigged to fail - results = importer.search('text', 1000, mock_callback) + # WHEN: The login method is called after being rigged to fail + results = importer.search('text', 1000, mock_callback) - # THEN: callback was never called, open was called once, find_all was called once, an empty list returned - self.assertEqual(0, mock_callback.call_count, 'callback should not have been called') - self.assertEqual(1, mocked_opener.open.call_count, 'open should have been called once') - self.assertEqual(1, mocked_results_page.find_all.call_count, 'find_all should have been called once') - mocked_results_page.find_all.assert_called_with('li', 'result pane') - self.assertEqual([], results, 'The search method should have returned an empty list') + # THEN: callback was never called, open was called once, find_all was called once, an empty list returned + self.assertEqual(0, mock_callback.call_count, 'callback should not have been called') + self.assertEqual(1, mocked_opener.open.call_count, 'open should have been called once') + self.assertEqual(1, mocked_results_page.find_all.call_count, 'find_all should have been called once') + mocked_results_page.find_all.assert_called_with('li', 'result pane') + self.assertEqual([], results, 'The search method should have returned an empty list') - def search_returns_two_results_test(self): + @patch('openlp.plugins.songs.lib.songselect.build_opener') + @patch('openlp.plugins.songs.lib.songselect.BeautifulSoup') + def search_returns_two_results_test(self, MockedBeautifulSoup, mocked_build_opener): """ Test that when the search finds 2 results, it simply returns a list with 2 results """ # GIVEN: A bunch of mocked out stuff and an importer object - with patch('openlp.plugins.songs.lib.songselect.build_opener') as mocked_build_opener, \ - patch('openlp.plugins.songs.lib.songselect.BeautifulSoup') as MockedBeautifulSoup: - # first search result - mocked_result1 = MagicMock() - mocked_result1.find.side_effect = [MagicMock(string='Title 1'), {'href': '/url1'}] - mocked_result1.find_all.return_value = [MagicMock(string='Author 1-1'), MagicMock(string='Author 1-2')] - # second search result - mocked_result2 = MagicMock() - mocked_result2.find.side_effect = [MagicMock(string='Title 2'), {'href': '/url2'}] - mocked_result2.find_all.return_value = [MagicMock(string='Author 2-1'), MagicMock(string='Author 2-2')] - # rest of the stuff - mocked_opener = MagicMock() - mocked_build_opener.return_value = mocked_opener - mocked_results_page = MagicMock() - mocked_results_page.find_all.side_effect = [[mocked_result1, mocked_result2], []] - MockedBeautifulSoup.return_value = mocked_results_page - mock_callback = MagicMock() - importer = SongSelectImport(None) + # first search result + mocked_result1 = MagicMock() + mocked_result1.find.side_effect = [MagicMock(string='Title 1'), {'href': '/url1'}] + mocked_result1.find_all.return_value = [MagicMock(string='Author 1-1'), MagicMock(string='Author 1-2')] + # second search result + mocked_result2 = MagicMock() + mocked_result2.find.side_effect = [MagicMock(string='Title 2'), {'href': '/url2'}] + mocked_result2.find_all.return_value = [MagicMock(string='Author 2-1'), MagicMock(string='Author 2-2')] + # rest of the stuff + mocked_opener = MagicMock() + mocked_build_opener.return_value = mocked_opener + mocked_results_page = MagicMock() + mocked_results_page.find_all.side_effect = [[mocked_result1, mocked_result2], []] + MockedBeautifulSoup.return_value = mocked_results_page + mock_callback = MagicMock() + importer = SongSelectImport(None) - # WHEN: The login method is called after being rigged to fail - results = importer.search('text', 1000, mock_callback) + # WHEN: The login method is called after being rigged to fail + results = importer.search('text', 1000, mock_callback) - # THEN: callback was never called, open was called once, find_all was called once, an empty list returned - self.assertEqual(2, mock_callback.call_count, 'callback should have been called twice') - self.assertEqual(2, mocked_opener.open.call_count, 'open should have been called twice') - self.assertEqual(2, mocked_results_page.find_all.call_count, 'find_all should have been called twice') - mocked_results_page.find_all.assert_called_with('li', 'result pane') - expected_list = [ - {'title': 'Title 1', 'authors': ['Author 1-1', 'Author 1-2'], 'link': BASE_URL + '/url1'}, - {'title': 'Title 2', 'authors': ['Author 2-1', 'Author 2-2'], 'link': BASE_URL + '/url2'} - ] - self.assertListEqual(expected_list, results, 'The search method should have returned two songs') + # THEN: callback was never called, open was called once, find_all was called once, an empty list returned + self.assertEqual(2, mock_callback.call_count, 'callback should have been called twice') + self.assertEqual(2, mocked_opener.open.call_count, 'open should have been called twice') + self.assertEqual(2, mocked_results_page.find_all.call_count, 'find_all should have been called twice') + mocked_results_page.find_all.assert_called_with('li', 'result pane') + expected_list = [ + {'title': 'Title 1', 'authors': ['Author 1-1', 'Author 1-2'], 'link': BASE_URL + '/url1'}, + {'title': 'Title 2', 'authors': ['Author 2-1', 'Author 2-2'], 'link': BASE_URL + '/url2'} + ] + self.assertListEqual(expected_list, results, 'The search method should have returned two songs') - def search_reaches_max_results_test(self): + @patch('openlp.plugins.songs.lib.songselect.build_opener') + @patch('openlp.plugins.songs.lib.songselect.BeautifulSoup') + def search_reaches_max_results_test(self, MockedBeautifulSoup, mocked_build_opener): """ Test that when the search finds MAX (2) results, it simply returns a list with those (2) """ # GIVEN: A bunch of mocked out stuff and an importer object - with patch('openlp.plugins.songs.lib.songselect.build_opener') as mocked_build_opener, \ - patch('openlp.plugins.songs.lib.songselect.BeautifulSoup') as MockedBeautifulSoup: - # first search result - mocked_result1 = MagicMock() - mocked_result1.find.side_effect = [MagicMock(string='Title 1'), {'href': '/url1'}] - mocked_result1.find_all.return_value = [MagicMock(string='Author 1-1'), MagicMock(string='Author 1-2')] - # second search result - mocked_result2 = MagicMock() - mocked_result2.find.side_effect = [MagicMock(string='Title 2'), {'href': '/url2'}] - mocked_result2.find_all.return_value = [MagicMock(string='Author 2-1'), MagicMock(string='Author 2-2')] - # third search result - mocked_result3 = MagicMock() - mocked_result3.find.side_effect = [MagicMock(string='Title 3'), {'href': '/url3'}] - mocked_result3.find_all.return_value = [MagicMock(string='Author 3-1'), MagicMock(string='Author 3-2')] - # rest of the stuff - mocked_opener = MagicMock() - mocked_build_opener.return_value = mocked_opener - mocked_results_page = MagicMock() - mocked_results_page.find_all.side_effect = [[mocked_result1, mocked_result2, mocked_result3], []] - MockedBeautifulSoup.return_value = mocked_results_page - mock_callback = MagicMock() - importer = SongSelectImport(None) + # first search result + mocked_result1 = MagicMock() + mocked_result1.find.side_effect = [MagicMock(string='Title 1'), {'href': '/url1'}] + mocked_result1.find_all.return_value = [MagicMock(string='Author 1-1'), MagicMock(string='Author 1-2')] + # second search result + mocked_result2 = MagicMock() + mocked_result2.find.side_effect = [MagicMock(string='Title 2'), {'href': '/url2'}] + mocked_result2.find_all.return_value = [MagicMock(string='Author 2-1'), MagicMock(string='Author 2-2')] + # third search result + mocked_result3 = MagicMock() + mocked_result3.find.side_effect = [MagicMock(string='Title 3'), {'href': '/url3'}] + mocked_result3.find_all.return_value = [MagicMock(string='Author 3-1'), MagicMock(string='Author 3-2')] + # rest of the stuff + mocked_opener = MagicMock() + mocked_build_opener.return_value = mocked_opener + mocked_results_page = MagicMock() + mocked_results_page.find_all.side_effect = [[mocked_result1, mocked_result2, mocked_result3], []] + MockedBeautifulSoup.return_value = mocked_results_page + mock_callback = MagicMock() + importer = SongSelectImport(None) - # WHEN: The login method is called after being rigged to fail - results = importer.search('text', 2, mock_callback) + # WHEN: The login method is called after being rigged to fail + results = importer.search('text', 2, mock_callback) - # THEN: callback was never called, open was called once, find_all was called once, an empty list returned - self.assertEqual(2, mock_callback.call_count, 'callback should have been called twice') - self.assertEqual(2, mocked_opener.open.call_count, 'open should have been called twice') - self.assertEqual(2, mocked_results_page.find_all.call_count, 'find_all should have been called twice') - mocked_results_page.find_all.assert_called_with('li', 'result pane') - expected_list = [{'title': 'Title 1', 'authors': ['Author 1-1', 'Author 1-2'], 'link': BASE_URL + '/url1'}, - {'title': 'Title 2', 'authors': ['Author 2-1', 'Author 2-2'], 'link': BASE_URL + '/url2'}] - self.assertListEqual(expected_list, results, 'The search method should have returned two songs') + # THEN: callback was never called, open was called once, find_all was called once, an empty list returned + self.assertEqual(2, mock_callback.call_count, 'callback should have been called twice') + self.assertEqual(2, mocked_opener.open.call_count, 'open should have been called twice') + self.assertEqual(2, mocked_results_page.find_all.call_count, 'find_all should have been called twice') + mocked_results_page.find_all.assert_called_with('li', 'result pane') + expected_list = [{'title': 'Title 1', 'authors': ['Author 1-1', 'Author 1-2'], 'link': BASE_URL + '/url1'}, + {'title': 'Title 2', 'authors': ['Author 2-1', 'Author 2-2'], 'link': BASE_URL + '/url2'}] + self.assertListEqual(expected_list, results, 'The search method should have returned two songs') - def get_song_page_raises_exception_test(self): + @patch('openlp.plugins.songs.lib.songselect.build_opener') + def get_song_page_raises_exception_test(self, mocked_build_opener): """ Test that when BeautifulSoup gets a bad song page the get_song() method returns None """ # GIVEN: A bunch of mocked out stuff and an importer object - with patch('openlp.plugins.songs.lib.songselect.build_opener') as mocked_build_opener: - mocked_opener = MagicMock() - mocked_build_opener.return_value = mocked_opener - mocked_opener.open.read.side_effect = URLError('[Errno -2] Name or service not known') - mocked_callback = MagicMock() - importer = SongSelectImport(None) + mocked_opener = MagicMock() + mocked_build_opener.return_value = mocked_opener + mocked_opener.open.read.side_effect = URLError('[Errno -2] Name or service not known') + mocked_callback = MagicMock() + importer = SongSelectImport(None) - # WHEN: get_song is called - result = importer.get_song({'link': 'link'}, callback=mocked_callback) + # WHEN: get_song is called + result = importer.get_song({'link': 'link'}, callback=mocked_callback) - # THEN: The callback should have been called once and None should be returned - mocked_callback.assert_called_with() - self.assertIsNone(result, 'The get_song() method should have returned None') + # THEN: The callback should have been called once and None should be returned + mocked_callback.assert_called_with() + self.assertIsNone(result, 'The get_song() method should have returned None') - def get_song_lyrics_raise_exception_test(self): + @patch('openlp.plugins.songs.lib.songselect.build_opener') + @patch('openlp.plugins.songs.lib.songselect.BeautifulSoup') + def get_song_lyrics_raise_exception_test(self, MockedBeautifulSoup, mocked_build_opener): """ Test that when BeautifulSoup gets a bad lyrics page the get_song() method returns None """ # GIVEN: A bunch of mocked out stuff and an importer object - with patch('openlp.plugins.songs.lib.songselect.build_opener'), \ - patch('openlp.plugins.songs.lib.songselect.BeautifulSoup') as MockedBeautifulSoup: - MockedBeautifulSoup.side_effect = [None, TypeError('Test Error')] - mocked_callback = MagicMock() - importer = SongSelectImport(None) + MockedBeautifulSoup.side_effect = [None, TypeError('Test Error')] + mocked_callback = MagicMock() + importer = SongSelectImport(None) - # WHEN: get_song is called - result = importer.get_song({'link': 'link'}, callback=mocked_callback) + # WHEN: get_song is called + result = importer.get_song({'link': 'link'}, callback=mocked_callback) - # THEN: The callback should have been called twice and None should be returned - self.assertEqual(2, mocked_callback.call_count, 'The callback should have been called twice') - self.assertIsNone(result, 'The get_song() method should have returned None') + # THEN: The callback should have been called twice and None should be returned + self.assertEqual(2, mocked_callback.call_count, 'The callback should have been called twice') + self.assertIsNone(result, 'The get_song() method should have returned None') - def get_song_test(self): + @patch('openlp.plugins.songs.lib.songselect.build_opener') + @patch('openlp.plugins.songs.lib.songselect.BeautifulSoup') + def get_song_test(self, MockedBeautifulSoup, mocked_build_opener): """ Test that the get_song() method returns the correct song details """ # GIVEN: A bunch of mocked out stuff and an importer object - with patch('openlp.plugins.songs.lib.songselect.build_opener'), \ - patch('openlp.plugins.songs.lib.songselect.BeautifulSoup') as MockedBeautifulSoup: - mocked_song_page = MagicMock() - mocked_copyright = MagicMock() - mocked_copyright.find_all.return_value = [MagicMock(string='Copyright 1'), MagicMock(string='Copyright 2')] - mocked_song_page.find.side_effect = [ - mocked_copyright, - MagicMock(find=MagicMock(string='CCLI: 123456')) - ] - mocked_lyrics_page = MagicMock() - mocked_find_all = MagicMock() - mocked_find_all.side_effect = [ - [ - MagicMock(contents='The Lord told Noah: there\'s gonna be a floody, floody'), - MagicMock(contents='So, rise and shine, and give God the glory, glory'), - MagicMock(contents='The Lord told Noah to build him an arky, arky') - ], - [MagicMock(string='Verse 1'), MagicMock(string='Chorus'), MagicMock(string='Verse 2')] - ] - mocked_lyrics_page.find.return_value = MagicMock(find_all=mocked_find_all) - MockedBeautifulSoup.side_effect = [mocked_song_page, mocked_lyrics_page] - mocked_callback = MagicMock() - importer = SongSelectImport(None) - fake_song = {'title': 'Title', 'authors': ['Author 1', 'Author 2'], 'link': 'url'} + mocked_song_page = MagicMock() + mocked_copyright = MagicMock() + mocked_copyright.find_all.return_value = [MagicMock(string='Copyright 1'), MagicMock(string='Copyright 2')] + mocked_song_page.find.side_effect = [ + mocked_copyright, + MagicMock(find=MagicMock(string='CCLI: 123456')) + ] + mocked_lyrics_page = MagicMock() + mocked_find_all = MagicMock() + mocked_find_all.side_effect = [ + [ + MagicMock(contents='The Lord told Noah: there\'s gonna be a floody, floody'), + MagicMock(contents='So, rise and shine, and give God the glory, glory'), + MagicMock(contents='The Lord told Noah to build him an arky, arky') + ], + [MagicMock(string='Verse 1'), MagicMock(string='Chorus'), MagicMock(string='Verse 2')] + ] + mocked_lyrics_page.find.return_value = MagicMock(find_all=mocked_find_all) + MockedBeautifulSoup.side_effect = [mocked_song_page, mocked_lyrics_page] + mocked_callback = MagicMock() + importer = SongSelectImport(None) + fake_song = {'title': 'Title', 'authors': ['Author 1', 'Author 2'], 'link': 'url'} - # WHEN: get_song is called - result = importer.get_song(fake_song, callback=mocked_callback) + # WHEN: get_song is called + result = importer.get_song(fake_song, callback=mocked_callback) - # THEN: The callback should have been called three times and the song should be returned - self.assertEqual(3, mocked_callback.call_count, 'The callback should have been called twice') - self.assertIsNotNone(result, 'The get_song() method should have returned a song dictionary') - self.assertEqual(2, mocked_lyrics_page.find.call_count, 'The find() method should have been called twice') - self.assertEqual(2, mocked_find_all.call_count, 'The find_all() method should have been called twice') - self.assertEqual([call('section', 'lyrics'), call('section', 'lyrics')], - mocked_lyrics_page.find.call_args_list, - 'The find() method should have been called with the right arguments') - self.assertEqual([call('p'), call('h3')], mocked_find_all.call_args_list, - 'The find_all() method should have been called with the right arguments') - self.assertIn('copyright', result, 'The returned song should have a copyright') - self.assertIn('ccli_number', result, 'The returned song should have a CCLI number') - self.assertIn('verses', result, 'The returned song should have verses') - self.assertEqual(3, len(result['verses']), 'Three verses should have been returned') + # THEN: The callback should have been called three times and the song should be returned + self.assertEqual(3, mocked_callback.call_count, 'The callback should have been called twice') + self.assertIsNotNone(result, 'The get_song() method should have returned a song dictionary') + self.assertEqual(2, mocked_lyrics_page.find.call_count, 'The find() method should have been called twice') + self.assertEqual(2, mocked_find_all.call_count, 'The find_all() method should have been called twice') + self.assertEqual([call('section', 'lyrics'), call('section', 'lyrics')], + mocked_lyrics_page.find.call_args_list, + 'The find() method should have been called with the right arguments') + self.assertEqual([call('p'), call('h3')], mocked_find_all.call_args_list, + 'The find_all() method should have been called with the right arguments') + self.assertIn('copyright', result, 'The returned song should have a copyright') + self.assertIn('ccli_number', result, 'The returned song should have a CCLI number') + self.assertIn('verses', result, 'The returned song should have verses') + self.assertEqual(3, len(result['verses']), 'Three verses should have been returned') - def save_song_new_author_test(self): + @patch('openlp.plugins.songs.lib.songselect.clean_song') + @patch('openlp.plugins.songs.lib.songselect.Author') + def save_song_new_author_test(self, MockedAuthor, mocked_clean_song): """ Test that saving a song with a new author performs the correct actions """ # GIVEN: A song to save, and some mocked out objects - with patch('openlp.plugins.songs.lib.songselect.clean_song') as mocked_clean_song, \ - patch('openlp.plugins.songs.lib.songselect.Author') as MockedAuthor: - song_dict = { - 'title': 'Arky Arky', - 'authors': ['Public Domain'], - 'verses': [ - {'label': 'Verse 1', 'lyrics': 'The Lord told Noah: there\'s gonna be a floody, floody'}, - {'label': 'Chorus 1', 'lyrics': 'So, rise and shine, and give God the glory, glory'}, - {'label': 'Verse 2', 'lyrics': 'The Lord told Noah to build him an arky, arky'} - ], - 'copyright': 'Public Domain', - 'ccli_number': '123456' - } - MockedAuthor.display_name.__eq__.return_value = False - mocked_db_manager = MagicMock() - mocked_db_manager.get_object_filtered.return_value = None - importer = SongSelectImport(mocked_db_manager) + song_dict = { + 'title': 'Arky Arky', + 'authors': ['Public Domain'], + 'verses': [ + {'label': 'Verse 1', 'lyrics': 'The Lord told Noah: there\'s gonna be a floody, floody'}, + {'label': 'Chorus 1', 'lyrics': 'So, rise and shine, and give God the glory, glory'}, + {'label': 'Verse 2', 'lyrics': 'The Lord told Noah to build him an arky, arky'} + ], + 'copyright': 'Public Domain', + 'ccli_number': '123456' + } + MockedAuthor.display_name.__eq__.return_value = False + mocked_db_manager = MagicMock() + mocked_db_manager.get_object_filtered.return_value = None + importer = SongSelectImport(mocked_db_manager) - # WHEN: The song is saved to the database - result = importer.save_song(song_dict) + # WHEN: The song is saved to the database + result = importer.save_song(song_dict) - # THEN: The return value should be a Song class and the mocked_db_manager should have been called - self.assertIsInstance(result, Song, 'The returned value should be a Song object') - mocked_clean_song.assert_called_with(mocked_db_manager, result) - self.assertEqual(2, mocked_db_manager.save_object.call_count, - 'The save_object() method should have been called twice') - 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_songs), 'There should only be one author') + # THEN: The return value should be a Song class and the mocked_db_manager should have been called + self.assertIsInstance(result, Song, 'The returned value should be a Song object') + mocked_clean_song.assert_called_with(mocked_db_manager, result) + self.assertEqual(2, mocked_db_manager.save_object.call_count, + 'The save_object() method should have been called twice') + 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_songs), 'There should only be one author') - def save_song_existing_author_test(self): + @patch('openlp.plugins.songs.lib.songselect.clean_song') + @patch('openlp.plugins.songs.lib.songselect.Author') + def save_song_existing_author_test(self, MockedAuthor, mocked_clean_song): """ Test that saving a song with an existing author performs the correct actions """ # GIVEN: A song to save, and some mocked out objects - with patch('openlp.plugins.songs.lib.songselect.clean_song') as mocked_clean_song, \ - patch('openlp.plugins.songs.lib.songselect.Author') as MockedAuthor: - song_dict = { - 'title': 'Arky Arky', - 'authors': ['Public Domain'], - 'verses': [ - {'label': 'Verse 1', 'lyrics': 'The Lord told Noah: there\'s gonna be a floody, floody'}, - {'label': 'Chorus 1', 'lyrics': 'So, rise and shine, and give God the glory, glory'}, - {'label': 'Verse 2', 'lyrics': 'The Lord told Noah to build him an arky, arky'} - ], - 'copyright': 'Public Domain', - 'ccli_number': '123456' - } - MockedAuthor.display_name.__eq__.return_value = False - mocked_db_manager = MagicMock() - mocked_db_manager.get_object_filtered.return_value = MagicMock() - importer = SongSelectImport(mocked_db_manager) + song_dict = { + 'title': 'Arky Arky', + 'authors': ['Public Domain'], + 'verses': [ + {'label': 'Verse 1', 'lyrics': 'The Lord told Noah: there\'s gonna be a floody, floody'}, + {'label': 'Chorus 1', 'lyrics': 'So, rise and shine, and give God the glory, glory'}, + {'label': 'Verse 2', 'lyrics': 'The Lord told Noah to build him an arky, arky'} + ], + 'copyright': 'Public Domain', + 'ccli_number': '123456' + } + MockedAuthor.display_name.__eq__.return_value = False + mocked_db_manager = MagicMock() + mocked_db_manager.get_object_filtered.return_value = MagicMock() + importer = SongSelectImport(mocked_db_manager) - # WHEN: The song is saved to the database - result = importer.save_song(song_dict) + # WHEN: The song is saved to the database + result = importer.save_song(song_dict) - # THEN: The return value should be a Song class and the mocked_db_manager should have been called - self.assertIsInstance(result, Song, 'The returned value should be a Song object') - mocked_clean_song.assert_called_with(mocked_db_manager, result) - self.assertEqual(2, mocked_db_manager.save_object.call_count, - '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_songs), 'There should only be one author') + # THEN: The return value should be a Song class and the mocked_db_manager should have been called + self.assertIsInstance(result, Song, 'The returned value should be a Song object') + mocked_clean_song.assert_called_with(mocked_db_manager, result) + self.assertEqual(2, mocked_db_manager.save_object.call_count, + '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_songs), 'There should only be one author') class TestSongSelectForm(TestCase, TestMixin): @@ -409,59 +411,219 @@ class TestSongSelectForm(TestCase, TestMixin): self.assertEqual(mocked_plugin, ssform.plugin, 'The correct plugin should have been assigned') self.assertEqual(mocked_db_manager, ssform.db_manager, 'The correct db_manager should have been assigned') - def login_fails_test(self): + @patch('openlp.plugins.songs.forms.songselectform.SongSelectImport') + @patch('openlp.plugins.songs.forms.songselectform.QtGui.QMessageBox.critical') + @patch('openlp.plugins.songs.forms.songselectform.translate') + def login_fails_test(self, mocked_translate, mocked_critical, MockedSongSelectImport): """ Test that when the login fails, the form returns to the correct state """ # GIVEN: A valid SongSelectForm with a mocked out SongSelectImport, and a bunch of mocked out controls - with patch('openlp.plugins.songs.forms.songselectform.SongSelectImport') as MockedSongSelectImport, \ - patch('openlp.plugins.songs.forms.songselectform.QtGui.QMessageBox.critical') as mocked_critical, \ - patch('openlp.plugins.songs.forms.songselectform.translate') as mocked_translate: - mocked_song_select_import = MagicMock() - mocked_song_select_import.login.return_value = False - MockedSongSelectImport.return_value = mocked_song_select_import - mocked_translate.side_effect = lambda *args: args[1] - ssform = SongSelectForm(None, MagicMock(), MagicMock()) - ssform.initialise() - with patch.object(ssform, 'username_edit') as mocked_username_edit, \ - patch.object(ssform, 'password_edit') as mocked_password_edit, \ - patch.object(ssform, 'save_password_checkbox') as mocked_save_password_checkbox, \ - patch.object(ssform, 'login_button') as mocked_login_button, \ - patch.object(ssform, 'login_spacer') as mocked_login_spacer, \ - patch.object(ssform, 'login_progress_bar') as mocked_login_progress_bar, \ - patch.object(ssform.application, 'process_events') as mocked_process_events: + mocked_song_select_import = MagicMock() + mocked_song_select_import.login.return_value = False + MockedSongSelectImport.return_value = mocked_song_select_import + mocked_translate.side_effect = lambda *args: args[1] + ssform = SongSelectForm(None, MagicMock(), MagicMock()) + ssform.initialise() + with patch.object(ssform, 'username_edit') as mocked_username_edit, \ + patch.object(ssform, 'password_edit') as mocked_password_edit, \ + patch.object(ssform, 'save_password_checkbox') as mocked_save_password_checkbox, \ + patch.object(ssform, 'login_button') as mocked_login_button, \ + patch.object(ssform, 'login_spacer') as mocked_login_spacer, \ + patch.object(ssform, 'login_progress_bar') as mocked_login_progress_bar, \ + patch.object(ssform.application, 'process_events') as mocked_process_events: - # WHEN: The login button is clicked, and the login is rigged to fail - ssform.on_login_button_clicked() + # WHEN: The login button is clicked, and the login is rigged to fail + ssform.on_login_button_clicked() - # THEN: The right things should have happened in the right order - expected_username_calls = [call(False), call(True)] - expected_password_calls = [call(False), call(True)] - expected_save_password_calls = [call(False), call(True)] - expected_login_btn_calls = [call(False), call(True)] - expected_login_spacer_calls = [call(False), call(True)] - expected_login_progress_visible_calls = [call(True), call(False)] - expected_login_progress_value_calls = [call(0), call(0)] - self.assertEqual(expected_username_calls, mocked_username_edit.setEnabled.call_args_list, - 'The username edit should be disabled then enabled') - self.assertEqual(expected_password_calls, mocked_password_edit.setEnabled.call_args_list, - 'The password edit should be disabled then enabled') - self.assertEqual(expected_save_password_calls, mocked_save_password_checkbox.setEnabled.call_args_list, - 'The save password checkbox should be disabled then enabled') - self.assertEqual(expected_login_btn_calls, mocked_login_button.setEnabled.call_args_list, - 'The login button should be disabled then enabled') - self.assertEqual(expected_login_spacer_calls, mocked_login_spacer.setVisible.call_args_list, - 'Thee login spacer should be make invisible, then visible') - self.assertEqual(expected_login_progress_visible_calls, - mocked_login_progress_bar.setVisible.call_args_list, - 'Thee login progress bar should be make visible, then invisible') - self.assertEqual(expected_login_progress_value_calls, mocked_login_progress_bar.setValue.call_args_list, - 'Thee login progress bar should have the right values set') - self.assertEqual(2, mocked_process_events.call_count, - 'The process_events() method should be called twice') - mocked_critical.assert_called_with(ssform, 'Error Logging In', 'There was a problem logging in, ' - 'perhaps your username or password is ' - 'incorrect?') + # THEN: The right things should have happened in the right order + expected_username_calls = [call(False), call(True)] + expected_password_calls = [call(False), call(True)] + expected_save_password_calls = [call(False), call(True)] + expected_login_btn_calls = [call(False), call(True)] + expected_login_spacer_calls = [call(False), call(True)] + expected_login_progress_visible_calls = [call(True), call(False)] + expected_login_progress_value_calls = [call(0), call(0)] + self.assertEqual(expected_username_calls, mocked_username_edit.setEnabled.call_args_list, + 'The username edit should be disabled then enabled') + self.assertEqual(expected_password_calls, mocked_password_edit.setEnabled.call_args_list, + 'The password edit should be disabled then enabled') + self.assertEqual(expected_save_password_calls, mocked_save_password_checkbox.setEnabled.call_args_list, + 'The save password checkbox should be disabled then enabled') + self.assertEqual(expected_login_btn_calls, mocked_login_button.setEnabled.call_args_list, + 'The login button should be disabled then enabled') + self.assertEqual(expected_login_spacer_calls, mocked_login_spacer.setVisible.call_args_list, + 'Thee login spacer should be make invisible, then visible') + self.assertEqual(expected_login_progress_visible_calls, + mocked_login_progress_bar.setVisible.call_args_list, + 'Thee login progress bar should be make visible, then invisible') + self.assertEqual(expected_login_progress_value_calls, mocked_login_progress_bar.setValue.call_args_list, + 'Thee login progress bar should have the right values set') + self.assertEqual(2, mocked_process_events.call_count, + 'The process_events() method should be called twice') + mocked_critical.assert_called_with(ssform, 'Error Logging In', 'There was a problem logging in, ' + 'perhaps your username or password is ' + 'incorrect?') + + @patch('openlp.plugins.songs.forms.songselectform.QtGui.QMessageBox.question') + @patch('openlp.plugins.songs.forms.songselectform.translate') + def on_import_yes_clicked_test(self, mocked_translate, mocked_question): + """ + Test that when a song is imported and the user clicks the "yes" button, the UI goes back to the previous page + """ + # GIVEN: A valid SongSelectForm with a mocked out QMessageBox.question() method + mocked_translate.side_effect = lambda *args: args[1] + mocked_question.return_value = QtGui.QMessageBox.Yes + ssform = SongSelectForm(None, MagicMock(), MagicMock()) + mocked_song_select_importer = MagicMock() + ssform.song_select_importer = mocked_song_select_importer + ssform.song = None + + # WHEN: The import button is clicked, and the user clicks Yes + with patch.object(ssform, 'on_back_button_clicked') as mocked_on_back_button_clicked: + ssform.on_import_button_clicked() + + # THEN: The on_back_button_clicked() method should have been called + mocked_song_select_importer.save_song.assert_called_with(None) + mocked_question.assert_called_with(ssform, 'Song Imported', + 'Your song has been imported, would you like to import more songs?', + QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, QtGui.QMessageBox.Yes) + mocked_on_back_button_clicked.assert_called_with() + + @patch('openlp.plugins.songs.forms.songselectform.QtGui.QMessageBox.question') + @patch('openlp.plugins.songs.forms.songselectform.translate') + def on_import_no_clicked_test(self, mocked_translate, mocked_question): + """ + Test that when a song is imported and the user clicks the "no" button, the UI exits + """ + # GIVEN: A valid SongSelectForm with a mocked out QMessageBox.question() method + mocked_translate.side_effect = lambda *args: args[1] + mocked_question.return_value = QtGui.QMessageBox.No + ssform = SongSelectForm(None, MagicMock(), MagicMock()) + mocked_song_select_importer = MagicMock() + ssform.song_select_importer = mocked_song_select_importer + ssform.song = None + + # WHEN: The import button is clicked, and the user clicks Yes + with patch.object(ssform, 'done') as mocked_done: + ssform.on_import_button_clicked() + + # THEN: The on_back_button_clicked() method should have been called + mocked_song_select_importer.save_song.assert_called_with(None) + mocked_question.assert_called_with(ssform, 'Song Imported', + 'Your song has been imported, would you like to import more songs?', + QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, QtGui.QMessageBox.Yes) + mocked_done.assert_called_with(QtGui.QDialog.Accepted) + + def on_back_button_clicked_test(self): + """ + Test that when the back button is clicked, the stacked widget is set back one page + """ + # GIVEN: A SongSelect form + ssform = SongSelectForm(None, MagicMock(), MagicMock()) + + # WHEN: The back button is clicked + with patch.object(ssform, 'stacked_widget') as mocked_stacked_widget, \ + patch.object(ssform, 'search_combobox') as mocked_search_combobox: + ssform.on_back_button_clicked() + + # THEN: The stacked widget should be set back one page + mocked_stacked_widget.setCurrentIndex.assert_called_with(1) + mocked_search_combobox.setFocus.assert_called_with() + + @patch('openlp.plugins.songs.forms.songselectform.QtGui.QMessageBox.information') + def on_search_show_info_test(self, mocked_information): + """ + Test that when the search_show_info signal is emitted, the on_search_show_info() method shows a dialog + """ + # GIVEN: A SongSelect form + ssform = SongSelectForm(None, MagicMock(), MagicMock()) + expected_title = 'Test Title' + expected_text = 'This is a test' + + # WHEN: on_search_show_info is called + ssform.on_search_show_info(expected_title, expected_text) + + # THEN: An information dialog should be shown + mocked_information.assert_called_with(ssform, expected_title, expected_text) + + def update_login_progress_test(self): + """ + Test the _update_login_progress() method + """ + # GIVEN: A SongSelect form + ssform = SongSelectForm(None, MagicMock(), MagicMock()) + + # WHEN: _update_login_progress() is called + with patch.object(ssform, 'login_progress_bar') as mocked_login_progress_bar: + mocked_login_progress_bar.value.return_value = 3 + ssform._update_login_progress() + + # THEN: The login progress bar should be updated + mocked_login_progress_bar.setValue.assert_called_with(4) + + def update_song_progress_test(self): + """ + Test the _update_song_progress() method + """ + # GIVEN: A SongSelect form + ssform = SongSelectForm(None, MagicMock(), MagicMock()) + + # WHEN: _update_song_progress() is called + with patch.object(ssform, 'song_progress_bar') as mocked_song_progress_bar: + mocked_song_progress_bar.value.return_value = 2 + ssform._update_song_progress() + + # THEN: The song progress bar should be updated + mocked_song_progress_bar.setValue.assert_called_with(3) + + def on_search_results_widget_double_clicked_test(self): + """ + Test that a song is retrieved when a song in the results list is double-clicked + """ + # GIVEN: A SongSelect form + ssform = SongSelectForm(None, MagicMock(), MagicMock()) + expected_song = {'title': 'Amazing Grace'} + + # WHEN: A song result is double-clicked + with patch.object(ssform, '_view_song') as mocked_view_song: + ssform.on_search_results_widget_double_clicked(expected_song) + + # THEN: The song is fetched and shown to the user + mocked_view_song.assert_called_with(expected_song) + + def on_view_button_clicked_test(self): + """ + Test that a song is retrieved when the view button is clicked + """ + # GIVEN: A SongSelect form + ssform = SongSelectForm(None, MagicMock(), MagicMock()) + expected_song = {'title': 'Amazing Grace'} + + # WHEN: A song result is double-clicked + with patch.object(ssform, '_view_song') as mocked_view_song, \ + patch.object(ssform, 'search_results_widget') as mocked_search_results_widget: + mocked_search_results_widget.currentItem.return_value = expected_song + ssform.on_view_button_clicked() + + # THEN: The song is fetched and shown to the user + mocked_view_song.assert_called_with(expected_song) + + def on_search_results_widget_selection_changed_test(self): + """ + Test that the view button is updated when the search results list is changed + """ + # GIVEN: A SongSelect form + ssform = SongSelectForm(None, MagicMock(), MagicMock()) + + # WHEN: There is at least 1 item selected + with patch.object(ssform, 'search_results_widget') as mocked_search_results_widget, \ + patch.object(ssform, 'view_button') as mocked_view_button: + mocked_search_results_widget.selectedItems.return_value = [1] + ssform.on_search_results_widget_selection_changed() + + # THEN: The view button should be enabled + mocked_view_button.setEnabled.assert_called_with(True) class TestSongSelectFileImport(TestCase, TestMixin): @@ -481,53 +643,45 @@ class TestSongSelectFileImport(TestCase, TestMixin): self.authors = ['Author One', 'Author Two'] self.topics = ['Adoration', 'Praise'] - def tearDown(self): + def songselect_import_bin_file_test(self): """ - Test cleanups - :return: - """ - pass - - def songselect_import_usr_file_test(self): - """ - Verify import SongSelect USR file parses file properly + Verify import SongSelect BIN file parses file properly :return: """ # GIVEN: Text file to import and mocks - copyright = '2011 OpenLP Programmer One (Admin. by OpenLP One) | ' \ + copyright_bin = '2011 OpenLP Programmer One (Admin. by OpenLP One) | ' \ 'Openlp Programmer Two (Admin. by OpenLP Two)' - verses = [ + verses_bin = [ ['v1', 'Line One Verse One\nLine Two Verse One\nLine Three Verse One\nLine Four Verse One', None], ['v2', 'Line One Verse Two\nLine Two Verse Two\nLine Three Verse Two\nLine Four Verse Two', None] ] + song_import = CCLIFileImport(manager=None, filename=['{}.bin'.format(self.file_name)]) - song_import = CCLIFileImport(manager=None, filename=['{}.bin'.format(self.file_name)], ) with patch.object(song_import, 'import_wizard'), patch.object(song_import, 'finish'): - # WHEN: We call the song importer song_import.do_import() # THEN: Song values should be equal to test values in setUp self.assertEquals(song_import.title, self.title, 'Song title should match') self.assertEquals(song_import.ccli_number, self.ccli_number, 'CCLI Song Number should match') self.assertEquals(song_import.authors, self.authors, 'Author(s) should match') - self.assertEquals(song_import.copyright, self.copyright_usr, 'Copyright should match') + self.assertEquals(song_import.copyright, copyright_bin, 'Copyright should match') self.assertEquals(song_import.topics, self.topics, 'Theme(s) should match') - self.assertEquals(song_import.verses, self.verses, 'Verses should match with test verses') + self.assertEquals(song_import.verses, verses_bin, 'Verses should match with test verses') - def songselect_import_usr_file_test(self): + def songselect_import_txt_file_test(self): """ - Verify import SongSelect USR file parses file properly + Verify import SongSelect TXT file parses file properly :return: """ # GIVEN: Text file to import and mocks - copyright = '© 2011 OpenLP Programmer One (Admin. by OpenLP One)' - verses = [ + copyright_txt = '© 2011 OpenLP Programmer One (Admin. by OpenLP One)' + verses_txt = [ ['v1', 'Line One Verse One\r\nLine Two Verse One\r\nLine Three Verse One\r\nLine Four Verse One', None], ['v2', 'Line One Verse Two\r\nLine Two Verse Two\r\nLine Three Verse Two\r\nLine Four Verse Two', None] ] - song_import = CCLIFileImport(manager=None, filename=['{}.txt'.format(self.file_name)], ) - with patch.object(song_import, 'import_wizard'), patch.object(song_import, 'finish'): + song_import = CCLIFileImport(manager=None, filename=['{}.txt'.format(self.file_name)]) + with patch.object(song_import, 'import_wizard'), patch.object(song_import, 'finish'): # WHEN: We call the song importer song_import.do_import() @@ -535,5 +689,87 @@ class TestSongSelectFileImport(TestCase, TestMixin): self.assertEquals(song_import.title, self.title, 'Song title should match') self.assertEquals(song_import.ccli_number, self.ccli_number, 'CCLI Song Number should match') self.assertEquals(song_import.authors, self.authors, 'Author(s) should match') - self.assertEquals(song_import.copyright, copyright, 'Copyright should match') - self.assertEquals(song_import.verses, verses, 'Verses should match with test verses') + self.assertEquals(song_import.copyright, copyright_txt, 'Copyright should match') + self.assertEquals(song_import.verses, verses_txt, 'Verses should match with test verses') + + +class TestSearchWorker(TestCase, TestMixin): + """ + Test the SearchWorker class + """ + def constructor_test(self): + """ + Test the SearchWorker constructor + """ + # GIVEN: An importer mock object and some search text + importer = MagicMock() + search_text = 'Jesus' + + # WHEN: The search worker is created + worker = SearchWorker(importer, search_text) + + # THEN: The correct values should be set + self.assertIs(importer, worker.importer, 'The importer should be the right object') + self.assertEqual(search_text, worker.search_text, 'The search text should be correct') + + def start_test(self): + """ + Test the start() method of the SearchWorker class + """ + # GIVEN: An importer mock object, some search text and an initialised SearchWorker + importer = MagicMock() + importer.search.return_value = ['song1', 'song2'] + search_text = 'Jesus' + worker = SearchWorker(importer, search_text) + + # WHEN: The start() method is called + with patch.object(worker, 'finished') as mocked_finished, patch.object(worker, 'quit') as mocked_quit: + worker.start() + + # THEN: The "finished" and "quit" signals should be emitted + importer.search.assert_called_with(search_text, 1000, worker._found_song_callback) + mocked_finished.emit.assert_called_with() + mocked_quit.emit.assert_called_with() + + @patch('openlp.plugins.songs.forms.songselectform.translate') + def start_over_1000_songs_test(self, mocked_translate): + """ + Test the start() method of the SearchWorker class when it finds over 1000 songs + """ + # GIVEN: An importer mock object, some search text and an initialised SearchWorker + mocked_translate.side_effect = lambda x, y: y + importer = MagicMock() + importer.search.return_value = ['song%s' % num for num in range(1050)] + search_text = 'Jesus' + worker = SearchWorker(importer, search_text) + + # WHEN: The start() method is called + with patch.object(worker, 'finished') as mocked_finished, patch.object(worker, 'quit') as mocked_quit, \ + patch.object(worker, 'show_info') as mocked_show_info: + worker.start() + + # THEN: The "finished" and "quit" signals should be emitted + importer.search.assert_called_with(search_text, 1000, worker._found_song_callback) + mocked_show_info.emit.assert_called_with('More than 1000 results', 'Your search has returned more than 1000 ' + 'results, it has been stopped. Please ' + 'refine your search to fetch better ' + 'results.') + mocked_finished.emit.assert_called_with() + mocked_quit.emit.assert_called_with() + + def found_song_callback_test(self): + """ + Test that when the _found_song_callback() function is called, the "found_song" signal is emitted + """ + # GIVEN: An importer mock object, some search text and an initialised SearchWorker + importer = MagicMock() + search_text = 'Jesus' + song = {'title': 'Amazing Grace'} + worker = SearchWorker(importer, search_text) + + # WHEN: The start() method is called + with patch.object(worker, 'found_song') as mocked_found_song: + worker._found_song_callback(song) + + # THEN: The "found_song" signal should have been emitted + mocked_found_song.emit.assert_called_with(song) From e14c03166dd64ae4bd90b40450356bd9c3fc4c3a Mon Sep 17 00:00:00 2001 From: Raoul Snyman Date: Sat, 28 Feb 2015 01:02:19 +0200 Subject: [PATCH 8/8] Minor style fixes --- openlp/plugins/alerts/lib/alertsmanager.py | 2 +- tests/functional/openlp_plugins/songs/test_songselect.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openlp/plugins/alerts/lib/alertsmanager.py b/openlp/plugins/alerts/lib/alertsmanager.py index 2e3b46f9e..d4089c6c1 100644 --- a/openlp/plugins/alerts/lib/alertsmanager.py +++ b/openlp/plugins/alerts/lib/alertsmanager.py @@ -70,7 +70,7 @@ class AlertsManager(OpenLPMixin, RegistryMixin, QtCore.QObject, RegistryProperti """ Format and request the Alert and start the timer. """ - if not self.alert_list or (self.live_controller.display.screens.display_count == 1 + if not self.alert_list or (self.live_controller.display.screens.display_count == 1 and not Settings().value('core/display on monitor')): return text = self.alert_list.pop(0) diff --git a/tests/functional/openlp_plugins/songs/test_songselect.py b/tests/functional/openlp_plugins/songs/test_songselect.py index 1e6bd85ef..a086d15fd 100644 --- a/tests/functional/openlp_plugins/songs/test_songselect.py +++ b/tests/functional/openlp_plugins/songs/test_songselect.py @@ -648,7 +648,7 @@ class TestSongSelectFileImport(TestCase, TestMixin): """ # GIVEN: Text file to import and mocks copyright_bin = '2011 OpenLP Programmer One (Admin. by OpenLP One) | ' \ - 'Openlp Programmer Two (Admin. by OpenLP Two)' + 'Openlp Programmer Two (Admin. by OpenLP Two)' verses_bin = [ ['v1', 'Line One Verse One\nLine Two Verse One\nLine Three Verse One\nLine Four Verse One', None], ['v2', 'Line One Verse Two\nLine Two Verse Two\nLine Three Verse Two\nLine Four Verse Two', None]