forked from openlp/openlp
head
This commit is contained in:
commit
e9be4a9143
|
@ -40,3 +40,5 @@ __pycache__
|
||||||
# Rejected diff's
|
# Rejected diff's
|
||||||
*.rej
|
*.rej
|
||||||
*.~\?~
|
*.~\?~
|
||||||
|
.coverage
|
||||||
|
cover
|
||||||
|
|
|
@ -24,6 +24,7 @@ The bible import functions for OpenLP
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
import urllib.error
|
||||||
|
|
||||||
from PyQt4 import QtGui
|
from PyQt4 import QtGui
|
||||||
|
|
||||||
|
@ -34,6 +35,7 @@ from openlp.core.ui.wizard import OpenLPWizard, WizardStrings
|
||||||
from openlp.core.utils import get_locale_key
|
from openlp.core.utils import get_locale_key
|
||||||
from openlp.plugins.bibles.lib.manager import BibleFormat
|
from openlp.plugins.bibles.lib.manager import BibleFormat
|
||||||
from openlp.plugins.bibles.lib.db import BiblesResourcesDB, clean_filename
|
from openlp.plugins.bibles.lib.db import BiblesResourcesDB, clean_filename
|
||||||
|
from openlp.plugins.bibles.lib.http import CWExtract, BGExtract, BSExtract
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -90,7 +92,6 @@ class BibleImportForm(OpenLPWizard):
|
||||||
Perform any custom initialisation for bible importing.
|
Perform any custom initialisation for bible importing.
|
||||||
"""
|
"""
|
||||||
self.manager.set_process_dialog(self)
|
self.manager.set_process_dialog(self)
|
||||||
self.load_Web_Bibles()
|
|
||||||
self.restart()
|
self.restart()
|
||||||
self.select_stack.setCurrentIndex(0)
|
self.select_stack.setCurrentIndex(0)
|
||||||
|
|
||||||
|
@ -104,6 +105,7 @@ class BibleImportForm(OpenLPWizard):
|
||||||
self.csv_verses_button.clicked.connect(self.on_csv_verses_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.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.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):
|
def add_custom_pages(self):
|
||||||
"""
|
"""
|
||||||
|
@ -202,20 +204,33 @@ class BibleImportForm(OpenLPWizard):
|
||||||
self.web_bible_tab.setObjectName('WebBibleTab')
|
self.web_bible_tab.setObjectName('WebBibleTab')
|
||||||
self.web_bible_layout = QtGui.QFormLayout(self.web_bible_tab)
|
self.web_bible_layout = QtGui.QFormLayout(self.web_bible_tab)
|
||||||
self.web_bible_layout.setObjectName('WebBibleLayout')
|
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('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 = QtGui.QLabel(self.web_bible_tab)
|
||||||
self.web_source_label.setObjectName('WebSourceLabel')
|
self.web_source_label.setObjectName('WebSourceLabel')
|
||||||
self.web_bible_layout.setWidget(0, QtGui.QFormLayout.LabelRole, self.web_source_label)
|
self.web_bible_layout.setWidget(1, QtGui.QFormLayout.LabelRole, self.web_source_label)
|
||||||
self.web_source_combo_box = QtGui.QComboBox(self.web_bible_tab)
|
self.web_source_combo_box = QtGui.QComboBox(self.web_bible_tab)
|
||||||
self.web_source_combo_box.setObjectName('WebSourceComboBox')
|
self.web_source_combo_box.setObjectName('WebSourceComboBox')
|
||||||
self.web_source_combo_box.addItems(['', '', ''])
|
self.web_source_combo_box.addItems(['', '', ''])
|
||||||
self.web_bible_layout.setWidget(0, QtGui.QFormLayout.FieldRole, self.web_source_combo_box)
|
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 = QtGui.QLabel(self.web_bible_tab)
|
||||||
self.web_translation_label.setObjectName('web_translation_label')
|
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 = QtGui.QComboBox(self.web_bible_tab)
|
||||||
self.web_translation_combo_box.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToContents)
|
self.web_translation_combo_box.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToContents)
|
||||||
self.web_translation_combo_box.setObjectName('WebTranslationComboBox')
|
self.web_translation_combo_box.setObjectName('WebTranslationComboBox')
|
||||||
self.web_bible_layout.setWidget(1, QtGui.QFormLayout.FieldRole, self.web_translation_combo_box)
|
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_tab_widget.addTab(self.web_bible_tab, '')
|
||||||
self.web_proxy_tab = QtGui.QWidget()
|
self.web_proxy_tab = QtGui.QWidget()
|
||||||
self.web_proxy_tab.setObjectName('WebProxyTab')
|
self.web_proxy_tab.setObjectName('WebProxyTab')
|
||||||
|
@ -314,6 +329,8 @@ class BibleImportForm(OpenLPWizard):
|
||||||
self.open_song_file_label.setText(translate('BiblesPlugin.ImportWizardForm', 'Bible file:'))
|
self.open_song_file_label.setText(translate('BiblesPlugin.ImportWizardForm', 'Bible file:'))
|
||||||
self.web_source_label.setText(translate('BiblesPlugin.ImportWizardForm', 'Location:'))
|
self.web_source_label.setText(translate('BiblesPlugin.ImportWizardForm', 'Location:'))
|
||||||
self.zefania_file_label.setText(translate('BiblesPlugin.ImportWizardForm', 'Bible file:'))
|
self.zefania_file_label.setText(translate('BiblesPlugin.ImportWizardForm', 'Bible file:'))
|
||||||
|
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',
|
self.web_source_combo_box.setItemText(WebDownload.Crosswalk, translate('BiblesPlugin.ImportWizardForm',
|
||||||
'Crosswalk'))
|
'Crosswalk'))
|
||||||
self.web_source_combo_box.setItemText(WebDownload.BibleGateway, translate('BiblesPlugin.ImportWizardForm',
|
self.web_source_combo_box.setItemText(WebDownload.BibleGateway, translate('BiblesPlugin.ImportWizardForm',
|
||||||
|
@ -388,9 +405,12 @@ class BibleImportForm(OpenLPWizard):
|
||||||
self.zefania_file_edit.setFocus()
|
self.zefania_file_edit.setFocus()
|
||||||
return False
|
return False
|
||||||
elif self.field('source_format') == BibleFormat.WebDownload:
|
elif self.field('source_format') == BibleFormat.WebDownload:
|
||||||
|
# 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())
|
self.version_name_edit.setText(self.web_translation_combo_box.currentText())
|
||||||
return True
|
return True
|
||||||
return True
|
|
||||||
elif self.currentPage() == self.license_details_page:
|
elif self.currentPage() == self.license_details_page:
|
||||||
license_version = self.field('license_version')
|
license_version = self.field('license_version')
|
||||||
license_copyright = self.field('license_copyright')
|
license_copyright = self.field('license_copyright')
|
||||||
|
@ -434,6 +454,7 @@ class BibleImportForm(OpenLPWizard):
|
||||||
:param index: The index of the combo box.
|
:param index: The index of the combo box.
|
||||||
"""
|
"""
|
||||||
self.web_translation_combo_box.clear()
|
self.web_translation_combo_box.clear()
|
||||||
|
if self.web_bible_list:
|
||||||
bibles = list(self.web_bible_list[index].keys())
|
bibles = list(self.web_bible_list[index].keys())
|
||||||
bibles.sort(key=get_locale_key)
|
bibles.sort(key=get_locale_key)
|
||||||
self.web_translation_combo_box.addItems(bibles)
|
self.web_translation_combo_box.addItems(bibles)
|
||||||
|
@ -475,6 +496,39 @@ class BibleImportForm(OpenLPWizard):
|
||||||
self.get_file_name(WizardStrings.OpenTypeFile % WizardStrings.ZEF, self.zefania_file_edit,
|
self.get_file_name(WizardStrings.OpenTypeFile % WizardStrings.ZEF, self.zefania_file_edit,
|
||||||
'last directory import')
|
'last directory import')
|
||||||
|
|
||||||
|
def on_web_update_button_clicked(self):
|
||||||
|
"""
|
||||||
|
Download list of bibles from Crosswalk, BibleServer and BibleGateway.
|
||||||
|
"""
|
||||||
|
# Download from Crosswalk, BiblesGateway, BibleServer
|
||||||
|
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')
|
||||||
|
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:
|
||||||
|
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):
|
def register_fields(self):
|
||||||
"""
|
"""
|
||||||
Register the bible import wizard fields.
|
Register the bible import wizard fields.
|
||||||
|
@ -520,30 +574,6 @@ class BibleImportForm(OpenLPWizard):
|
||||||
self.on_web_source_combo_box_index_changed(WebDownload.Crosswalk)
|
self.on_web_source_combo_box_index_changed(WebDownload.Crosswalk)
|
||||||
settings.endGroup()
|
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):
|
def pre_wizard(self):
|
||||||
"""
|
"""
|
||||||
Prepare the UI for the import.
|
Prepare the UI for the import.
|
||||||
|
@ -583,14 +613,15 @@ class BibleImportForm(OpenLPWizard):
|
||||||
self.progress_bar.setMaximum(1)
|
self.progress_bar.setMaximum(1)
|
||||||
download_location = self.field('web_location')
|
download_location = self.field('web_location')
|
||||||
bible_version = self.web_translation_combo_box.currentText()
|
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(
|
importer = self.manager.import_bible(
|
||||||
BibleFormat.WebDownload, name=license_version,
|
BibleFormat.WebDownload, name=license_version,
|
||||||
download_source=WebDownload.Names[download_location],
|
download_source=WebDownload.Names[download_location],
|
||||||
download_name=bible,
|
download_name=bible,
|
||||||
proxy_server=self.field('proxy_server'),
|
proxy_server=self.field('proxy_server'),
|
||||||
proxy_username=self.field('proxy_username'),
|
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:
|
elif bible_type == BibleFormat.Zefania:
|
||||||
# Import an Zefania bible.
|
# Import an Zefania bible.
|
||||||
|
|
|
@ -50,6 +50,38 @@ UGLY_CHARS = {
|
||||||
}
|
}
|
||||||
VERSE_NUMBER_REGEX = re.compile(r'v(\d{1,2})(\d{3})(\d{3}) verse.*')
|
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'
|
||||||
|
}
|
||||||
|
|
||||||
|
CROSSWALK_LANGUAGES = {
|
||||||
|
'Portuguese': 'pt',
|
||||||
|
'German': 'de',
|
||||||
|
'Italian': 'it',
|
||||||
|
'Español': 'es',
|
||||||
|
'French': 'fr',
|
||||||
|
'Dutch': 'nl'
|
||||||
|
}
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -222,6 +254,8 @@ class BGExtract(RegistryProperties):
|
||||||
if not soup:
|
if not soup:
|
||||||
return None
|
return None
|
||||||
div = soup.find('div', 'result-text-style-normal')
|
div = soup.find('div', 'result-text-style-normal')
|
||||||
|
if not div:
|
||||||
|
return None
|
||||||
self._clean_soup(div)
|
self._clean_soup(div)
|
||||||
span_list = div.find_all('span', 'text')
|
span_list = div.find_all('span', 'text')
|
||||||
log.debug('Span list: %s', span_list)
|
log.debug('Span list: %s', span_list)
|
||||||
|
@ -278,6 +312,42 @@ class BGExtract(RegistryProperties):
|
||||||
books.append(book.contents[0])
|
books.append(book.contents[0])
|
||||||
return books
|
return books
|
||||||
|
|
||||||
|
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/'
|
||||||
|
soup = get_soup_for_bible_ref(bible_url)
|
||||||
|
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:
|
||||||
|
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(')')].lower()
|
||||||
|
elif tag_class == 'spacer':
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
bibles.append((tag_text, ot['value'], current_lang))
|
||||||
|
return bibles
|
||||||
|
|
||||||
|
|
||||||
class BSExtract(RegistryProperties):
|
class BSExtract(RegistryProperties):
|
||||||
"""
|
"""
|
||||||
|
@ -338,6 +408,43 @@ class BSExtract(RegistryProperties):
|
||||||
content = content.find_all('li')
|
content = content.find_all('li')
|
||||||
return [book.contents[0].contents[0] for book in content if len(book.contents[0].contents)]
|
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.
|
||||||
|
|
||||||
|
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'
|
||||||
|
soup = get_soup_for_bible_ref(bible_url)
|
||||||
|
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
|
||||||
|
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_'):
|
||||||
|
try:
|
||||||
|
language_code = BIBLESERVER_LANGUAGE_CODE[css_class]
|
||||||
|
except KeyError:
|
||||||
|
language_code = ''
|
||||||
|
bibles.append((bible_name, bible_key, language_code))
|
||||||
|
return bibles
|
||||||
|
|
||||||
|
|
||||||
class CWExtract(RegistryProperties):
|
class CWExtract(RegistryProperties):
|
||||||
"""
|
"""
|
||||||
|
@ -408,6 +515,49 @@ class CWExtract(RegistryProperties):
|
||||||
books.append(book.contents[0])
|
books.append(book.contents[0])
|
||||||
return books
|
return books
|
||||||
|
|
||||||
|
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/'
|
||||||
|
soup = get_soup_for_bible_ref(bible_url)
|
||||||
|
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()
|
||||||
|
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
|
||||||
|
if tag_text.endswith(')'):
|
||||||
|
language = tag_text[tag_text.rfind('(') + 1:-1]
|
||||||
|
if language in CROSSWALK_LANGUAGES:
|
||||||
|
language_code = CROSSWALK_LANGUAGES[language]
|
||||||
|
else:
|
||||||
|
language_code = ''
|
||||||
|
# ... except for the latin vulgate
|
||||||
|
elif 'latin' in tag_text.lower():
|
||||||
|
language_code = 'la'
|
||||||
|
else:
|
||||||
|
language_code = 'en'
|
||||||
|
bibles.append((tag_text, tag_value, language_code))
|
||||||
|
return bibles
|
||||||
|
|
||||||
|
|
||||||
class HTTPBible(BibleDB, RegistryProperties):
|
class HTTPBible(BibleDB, RegistryProperties):
|
||||||
log.info('%s HTTPBible loaded', __name__)
|
log.info('%s HTTPBible loaded', __name__)
|
||||||
|
@ -428,6 +578,7 @@ class HTTPBible(BibleDB, RegistryProperties):
|
||||||
self.proxy_server = None
|
self.proxy_server = None
|
||||||
self.proxy_username = None
|
self.proxy_username = None
|
||||||
self.proxy_password = None
|
self.proxy_password = None
|
||||||
|
self.language_id = None
|
||||||
if 'path' in kwargs:
|
if 'path' in kwargs:
|
||||||
self.path = kwargs['path']
|
self.path = kwargs['path']
|
||||||
if 'proxy_server' in kwargs:
|
if 'proxy_server' in kwargs:
|
||||||
|
@ -436,6 +587,8 @@ class HTTPBible(BibleDB, RegistryProperties):
|
||||||
self.proxy_username = kwargs['proxy_username']
|
self.proxy_username = kwargs['proxy_username']
|
||||||
if 'proxy_password' in kwargs:
|
if 'proxy_password' in kwargs:
|
||||||
self.proxy_password = kwargs['proxy_password']
|
self.proxy_password = kwargs['proxy_password']
|
||||||
|
if 'language_id' in kwargs:
|
||||||
|
self.language_id = kwargs['language_id']
|
||||||
|
|
||||||
def do_import(self, bible_name=None):
|
def do_import(self, bible_name=None):
|
||||||
"""
|
"""
|
||||||
|
@ -468,13 +621,11 @@ class HTTPBible(BibleDB, RegistryProperties):
|
||||||
return False
|
return False
|
||||||
self.wizard.progress_bar.setMaximum(len(books) + 2)
|
self.wizard.progress_bar.setMaximum(len(books) + 2)
|
||||||
self.wizard.increment_progress_bar(translate('BiblesPlugin.HTTPBible', 'Registering Language...'))
|
self.wizard.increment_progress_bar(translate('BiblesPlugin.HTTPBible', 'Registering Language...'))
|
||||||
bible = BiblesResourcesDB.get_webbible(self.download_name, self.download_source.lower())
|
if self.language_id:
|
||||||
if bible['language_id']:
|
self.save_meta('language_id', self.language_id)
|
||||||
language_id = bible['language_id']
|
|
||||||
self.save_meta('language_id', language_id)
|
|
||||||
else:
|
else:
|
||||||
language_id = self.get_language(bible_name)
|
self.language_id = self.get_language(bible_name)
|
||||||
if not language_id:
|
if not self.language_id:
|
||||||
log.error('Importing books from %s failed' % self.filename)
|
log.error('Importing books from %s failed' % self.filename)
|
||||||
return False
|
return False
|
||||||
for book in books:
|
for book in books:
|
||||||
|
@ -482,7 +633,7 @@ class HTTPBible(BibleDB, RegistryProperties):
|
||||||
break
|
break
|
||||||
self.wizard.increment_progress_bar(translate(
|
self.wizard.increment_progress_bar(translate(
|
||||||
'BiblesPlugin.HTTPBible', 'Importing %s...', 'Importing <book name>...') % book)
|
'BiblesPlugin.HTTPBible', 'Importing %s...', 'Importing <book name>...') % 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:
|
if not book_ref_id:
|
||||||
log.error('Importing books from %s - download name: "%s" failed' %
|
log.error('Importing books from %s - download name: "%s" failed' %
|
||||||
(self.download_source, self.download_name))
|
(self.download_source, self.download_name))
|
||||||
|
|
|
@ -187,6 +187,14 @@ class SongSelectForm(QtGui.QDialog, Ui_SongSelectDialog):
|
||||||
self.application.process_events()
|
self.application.process_events()
|
||||||
# Get the full song
|
# Get the full song
|
||||||
song = self.song_select_importer.get_song(song, self._update_song_progress)
|
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
|
# Update the UI
|
||||||
self.title_edit.setText(song['title'])
|
self.title_edit.setText(song['title'])
|
||||||
self.copyright_edit.setText(song['copyright'])
|
self.copyright_edit.setText(song['copyright'])
|
||||||
|
@ -359,15 +367,11 @@ class SongSelectForm(QtGui.QDialog, Ui_SongSelectDialog):
|
||||||
Import a song from SongSelect.
|
Import a song from SongSelect.
|
||||||
"""
|
"""
|
||||||
self.song_select_importer.save_song(self.song)
|
self.song_select_importer.save_song(self.song)
|
||||||
question_dialog = QtGui.QMessageBox()
|
if QtGui.QMessageBox.question(self, translate('SongsPlugin.SongSelectForm', 'Song Imported'),
|
||||||
question_dialog.setWindowTitle(translate('SongsPlugin.SongSelectForm', 'Song Imported'))
|
translate('SongsPlugin.SongSelectForm', 'Your song has been imported, would you '
|
||||||
question_dialog.setText(translate('SongsPlugin.SongSelectForm', 'Your song has been imported, would you like '
|
'like to import more songs?'),
|
||||||
'to exit now, or import more songs?'))
|
QtGui.QMessageBox.Yes | QtGui.QMessageBox.No,
|
||||||
question_dialog.addButton(QtGui.QPushButton(translate('SongsPlugin.SongSelectForm', 'Import More Songs')),
|
QtGui.QMessageBox.Yes) == QtGui.QMessageBox.Yes:
|
||||||
QtGui.QMessageBox.YesRole)
|
|
||||||
question_dialog.addButton(QtGui.QPushButton(translate('SongsPlugin.SongSelectForm', 'Exit Now')),
|
|
||||||
QtGui.QMessageBox.NoRole)
|
|
||||||
if question_dialog.exec_() == QtGui.QMessageBox.Yes:
|
|
||||||
self.on_back_button_clicked()
|
self.on_back_button_clicked()
|
||||||
else:
|
else:
|
||||||
self.application.process_events()
|
self.application.process_events()
|
||||||
|
|
|
@ -23,29 +23,31 @@
|
||||||
This module contains tests for the CCLI SongSelect importer.
|
This module contains tests for the CCLI SongSelect importer.
|
||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
from urllib.error import URLError
|
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.core import Registry
|
||||||
from openlp.plugins.songs.forms.songselectform import SongSelectForm
|
from openlp.plugins.songs.forms.songselectform import SongSelectForm, SearchWorker
|
||||||
from openlp.plugins.songs.lib import Author, Song, VerseType
|
from openlp.plugins.songs.lib import Song
|
||||||
from openlp.plugins.songs.lib.songselect import SongSelectImport, LOGOUT_URL, BASE_URL
|
from openlp.plugins.songs.lib.songselect import SongSelectImport, LOGOUT_URL, BASE_URL
|
||||||
from openlp.plugins.songs.lib.importers.cclifile import CCLIFileImport
|
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):
|
class TestSongSelectImport(TestCase, TestMixin):
|
||||||
"""
|
"""
|
||||||
Test the :class:`~openlp.plugins.songs.lib.songselect.SongSelectImport` class
|
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
|
Test that constructing a basic SongSelectImport object works correctly
|
||||||
"""
|
"""
|
||||||
# GIVEN: The SongSelectImporter class and a mocked out build_opener
|
# 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
|
# WHEN: An object is instantiated
|
||||||
importer = SongSelectImport(None)
|
importer = SongSelectImport(None)
|
||||||
|
|
||||||
|
@ -55,13 +57,13 @@ class TestSongSelectImport(TestCase, TestMixin):
|
||||||
self.assertIsNotNone(importer.opener, 'There should be a valid opener 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')
|
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
|
Test that when logging in to SongSelect fails, the login method returns False
|
||||||
"""
|
"""
|
||||||
# GIVEN: A bunch of mocked out stuff and an importer object
|
# 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_opener = MagicMock()
|
||||||
mocked_build_opener.return_value = mocked_opener
|
mocked_build_opener.return_value = mocked_opener
|
||||||
mocked_login_page = MagicMock()
|
mocked_login_page = MagicMock()
|
||||||
|
@ -79,13 +81,13 @@ class TestSongSelectImport(TestCase, TestMixin):
|
||||||
self.assertEqual(2, mocked_opener.open.call_count, 'opener 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')
|
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
|
Test that when logging in to SongSelect succeeds, the login method returns True
|
||||||
"""
|
"""
|
||||||
# GIVEN: A bunch of mocked out stuff and an importer object
|
# 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_opener = MagicMock()
|
||||||
mocked_build_opener.return_value = mocked_opener
|
mocked_build_opener.return_value = mocked_opener
|
||||||
mocked_login_page = MagicMock()
|
mocked_login_page = MagicMock()
|
||||||
|
@ -103,12 +105,12 @@ class TestSongSelectImport(TestCase, TestMixin):
|
||||||
self.assertEqual(2, mocked_opener.open.call_count, 'opener 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')
|
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
|
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
|
# 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_opener = MagicMock()
|
||||||
mocked_build_opener.return_value = mocked_opener
|
mocked_build_opener.return_value = mocked_opener
|
||||||
importer = SongSelectImport(None)
|
importer = SongSelectImport(None)
|
||||||
|
@ -120,13 +122,13 @@ class TestSongSelectImport(TestCase, TestMixin):
|
||||||
self.assertEqual(1, mocked_opener.open.call_count, 'opener should have been called once')
|
self.assertEqual(1, mocked_opener.open.call_count, 'opener should have been called once')
|
||||||
mocked_opener.open.assert_called_with(LOGOUT_URL)
|
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
|
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
|
# 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_opener = MagicMock()
|
||||||
mocked_build_opener.return_value = mocked_opener
|
mocked_build_opener.return_value = mocked_opener
|
||||||
mocked_results_page = MagicMock()
|
mocked_results_page = MagicMock()
|
||||||
|
@ -145,13 +147,13 @@ class TestSongSelectImport(TestCase, TestMixin):
|
||||||
mocked_results_page.find_all.assert_called_with('li', 'result pane')
|
mocked_results_page.find_all.assert_called_with('li', 'result pane')
|
||||||
self.assertEqual([], results, 'The search method should have returned an empty list')
|
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
|
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
|
# 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
|
# first search result
|
||||||
mocked_result1 = MagicMock()
|
mocked_result1 = MagicMock()
|
||||||
mocked_result1.find.side_effect = [MagicMock(string='Title 1'), {'href': '/url1'}]
|
mocked_result1.find.side_effect = [MagicMock(string='Title 1'), {'href': '/url1'}]
|
||||||
|
@ -183,13 +185,13 @@ class TestSongSelectImport(TestCase, TestMixin):
|
||||||
]
|
]
|
||||||
self.assertListEqual(expected_list, results, 'The search method should have returned two songs')
|
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)
|
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
|
# 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
|
# first search result
|
||||||
mocked_result1 = MagicMock()
|
mocked_result1 = MagicMock()
|
||||||
mocked_result1.find.side_effect = [MagicMock(string='Title 1'), {'href': '/url1'}]
|
mocked_result1.find.side_effect = [MagicMock(string='Title 1'), {'href': '/url1'}]
|
||||||
|
@ -223,12 +225,12 @@ class TestSongSelectImport(TestCase, TestMixin):
|
||||||
{'title': 'Title 2', 'authors': ['Author 2-1', 'Author 2-2'], 'link': BASE_URL + '/url2'}]
|
{'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')
|
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
|
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
|
# 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_opener = MagicMock()
|
||||||
mocked_build_opener.return_value = mocked_opener
|
mocked_build_opener.return_value = mocked_opener
|
||||||
mocked_opener.open.read.side_effect = URLError('[Errno -2] Name or service not known')
|
mocked_opener.open.read.side_effect = URLError('[Errno -2] Name or service not known')
|
||||||
|
@ -242,13 +244,13 @@ class TestSongSelectImport(TestCase, TestMixin):
|
||||||
mocked_callback.assert_called_with()
|
mocked_callback.assert_called_with()
|
||||||
self.assertIsNone(result, 'The get_song() method should have returned None')
|
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
|
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
|
# 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')]
|
MockedBeautifulSoup.side_effect = [None, TypeError('Test Error')]
|
||||||
mocked_callback = MagicMock()
|
mocked_callback = MagicMock()
|
||||||
importer = SongSelectImport(None)
|
importer = SongSelectImport(None)
|
||||||
|
@ -260,13 +262,13 @@ class TestSongSelectImport(TestCase, TestMixin):
|
||||||
self.assertEqual(2, mocked_callback.call_count, 'The callback should have been called twice')
|
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')
|
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
|
Test that the get_song() method returns the correct song details
|
||||||
"""
|
"""
|
||||||
# GIVEN: A bunch of mocked out stuff and an importer object
|
# 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_song_page = MagicMock()
|
||||||
mocked_copyright = MagicMock()
|
mocked_copyright = MagicMock()
|
||||||
mocked_copyright.find_all.return_value = [MagicMock(string='Copyright 1'), MagicMock(string='Copyright 2')]
|
mocked_copyright.find_all.return_value = [MagicMock(string='Copyright 1'), MagicMock(string='Copyright 2')]
|
||||||
|
@ -308,13 +310,13 @@ class TestSongSelectImport(TestCase, TestMixin):
|
||||||
self.assertIn('verses', result, 'The returned song should have verses')
|
self.assertIn('verses', result, 'The returned song should have verses')
|
||||||
self.assertEqual(3, len(result['verses']), 'Three verses should have been returned')
|
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
|
Test that saving a song with a new author performs the correct actions
|
||||||
"""
|
"""
|
||||||
# GIVEN: A song to save, and some mocked out objects
|
# 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 = {
|
song_dict = {
|
||||||
'title': 'Arky Arky',
|
'title': 'Arky Arky',
|
||||||
'authors': ['Public Domain'],
|
'authors': ['Public Domain'],
|
||||||
|
@ -344,13 +346,13 @@ class TestSongSelectImport(TestCase, TestMixin):
|
||||||
display_name='Public Domain')
|
display_name='Public Domain')
|
||||||
self.assertEqual(1, len(result.authors_songs), 'There should only be one author')
|
self.assertEqual(1, len(result.authors_songs), 'There should only be one author')
|
||||||
|
|
||||||
def save_song_existing_author_test(self):
|
@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
|
Test that saving a song with an existing author performs the correct actions
|
||||||
"""
|
"""
|
||||||
# GIVEN: A song to save, and some mocked out objects
|
# 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 = {
|
song_dict = {
|
||||||
'title': 'Arky Arky',
|
'title': 'Arky Arky',
|
||||||
'authors': ['Public Domain'],
|
'authors': ['Public Domain'],
|
||||||
|
@ -409,14 +411,14 @@ class TestSongSelectForm(TestCase, TestMixin):
|
||||||
self.assertEqual(mocked_plugin, ssform.plugin, 'The correct plugin should have been assigned')
|
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')
|
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
|
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
|
# 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 = MagicMock()
|
||||||
mocked_song_select_import.login.return_value = False
|
mocked_song_select_import.login.return_value = False
|
||||||
MockedSongSelectImport.return_value = mocked_song_select_import
|
MockedSongSelectImport.return_value = mocked_song_select_import
|
||||||
|
@ -463,6 +465,166 @@ class TestSongSelectForm(TestCase, TestMixin):
|
||||||
'perhaps your username or password is '
|
'perhaps your username or password is '
|
||||||
'incorrect?')
|
'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):
|
class TestSongSelectFileImport(TestCase, TestMixin):
|
||||||
"""
|
"""
|
||||||
|
@ -480,50 +642,43 @@ class TestSongSelectFileImport(TestCase, TestMixin):
|
||||||
self.authors = ['Author One', 'Author Two']
|
self.authors = ['Author One', 'Author Two']
|
||||||
self.topics = ['Adoration', 'Praise']
|
self.topics = ['Adoration', 'Praise']
|
||||||
|
|
||||||
def tearDown(self):
|
def songselect_import_bin_file_test(self):
|
||||||
"""
|
"""
|
||||||
Test cleanups
|
Verify import SongSelect BIN file parses file properly
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def songselect_import_usr_file_test(self):
|
|
||||||
"""
|
|
||||||
Verify import SongSelect USR file parses file properly
|
|
||||||
"""
|
"""
|
||||||
# GIVEN: Text file to import and mocks
|
# 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)'
|
'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],
|
['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]
|
['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'):
|
with patch.object(song_import, 'import_wizard'), patch.object(song_import, 'finish'):
|
||||||
|
|
||||||
# WHEN: We call the song importer
|
# WHEN: We call the song importer
|
||||||
song_import.do_import()
|
song_import.do_import()
|
||||||
# THEN: Song values should be equal to test values in setUp
|
# 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.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.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.authors, self.authors, 'Author(s) should match')
|
||||||
self.assertEquals(song_import.copyright, copyright, '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.topics, self.topics, 'Theme(s) should match')
|
||||||
self.assertEquals(song_import.verses, verses, 'Verses should match with test verses')
|
self.assertEquals(song_import.verses, verses_bin, 'Verses should match with test verses')
|
||||||
|
|
||||||
def songselect_import_text_file_test(self):
|
def songselect_import_text_file_test(self):
|
||||||
"""
|
"""
|
||||||
Verify import SongSelect TEXT file parses file properly
|
Verify import SongSelect TEXT file parses file properly
|
||||||
"""
|
"""
|
||||||
# GIVEN: Text file to import and mocks
|
# GIVEN: Text file to import and mocks
|
||||||
copyright = '© 2011 OpenLP Programmer One (Admin. by OpenLP One)'
|
copyright_txt = '© 2011 OpenLP Programmer One (Admin. by OpenLP One)'
|
||||||
verses = [
|
verses_txt = [
|
||||||
['v1', 'Line One Verse One\r\nLine Two Verse One\r\nLine Three Verse One\r\nLine Four Verse One', None],
|
['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]
|
['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)], )
|
song_import = CCLIFileImport(manager=None, filename=['{}.txt'.format(self.file_name)])
|
||||||
with patch.object(song_import, 'import_wizard'), patch.object(song_import, 'finish'):
|
|
||||||
|
|
||||||
|
with patch.object(song_import, 'import_wizard'), patch.object(song_import, 'finish'):
|
||||||
# WHEN: We call the song importer
|
# WHEN: We call the song importer
|
||||||
song_import.do_import()
|
song_import.do_import()
|
||||||
|
|
||||||
|
@ -531,5 +686,87 @@ class TestSongSelectFileImport(TestCase, TestMixin):
|
||||||
self.assertEquals(song_import.title, self.title, 'Song title should match')
|
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.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.authors, self.authors, 'Author(s) should match')
|
||||||
self.assertEquals(song_import.copyright, copyright, 'Copyright should match')
|
self.assertEquals(song_import.copyright, copyright_txt, 'Copyright should match')
|
||||||
self.assertEquals(song_import.verses, verses, 'Verses should match with test verses')
|
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)
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
|
|
||||||
from openlp.core.common import Registry
|
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
|
from tests.interfaces import MagicMock
|
||||||
|
|
||||||
|
|
||||||
|
@ -116,3 +116,46 @@ class TestBibleHTTP(TestCase):
|
||||||
|
|
||||||
# THEN: We should get back a valid service item
|
# 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'
|
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()
|
||||||
|
|
||||||
|
# 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):
|
||||||
|
"""
|
||||||
|
Test getting list of bibles from BibelGateway.com
|
||||||
|
"""
|
||||||
|
# GIVEN: A new Bible Gateway extraction class
|
||||||
|
handler = BGExtract()
|
||||||
|
|
||||||
|
# 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):
|
||||||
|
"""
|
||||||
|
Test getting list of bibles from Crosswalk.com
|
||||||
|
"""
|
||||||
|
# GIVEN: A new Crosswalk extraction class
|
||||||
|
handler = CWExtract()
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
|
Loading…
Reference in New Issue