diff --git a/openlp/core/common/languages.py b/openlp/core/common/languages.py
new file mode 100644
index 000000000..18235ac10
--- /dev/null
+++ b/openlp/core/common/languages.py
@@ -0,0 +1,201 @@
+# -*- coding: utf-8 -*-
+# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
+
+###############################################################################
+# OpenLP - Open Source Lyrics Projection #
+# --------------------------------------------------------------------------- #
+# Copyright (c) 2008-2016 OpenLP Developers #
+# --------------------------------------------------------------------------- #
+# This program is free software; you can redistribute it and/or modify it #
+# under the terms of the GNU General Public License as published by the Free #
+# Software Foundation; version 2 of the License. #
+# #
+# This program is distributed in the hope that it will be useful, but WITHOUT #
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
+# more details. #
+# #
+# You should have received a copy of the GNU General Public License along #
+# with this program; if not, write to the Free Software Foundation, Inc., 59 #
+# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
+###############################################################################
+"""
+The :mod:`languages` module provides a list of language names with utility functions.
+"""
+from collections import namedtuple
+
+from openlp.core.common import translate
+
+
+Language = namedtuple('Language', ['id', 'name', 'code'])
+languages = sorted([
+ Language(1, translate('common.languages', '(Afan) Oromo', 'Language code: om'), 'om'),
+ Language(2, translate('common.languages', 'Abkhazian', 'Language code: ab'), 'ab'),
+ Language(3, translate('common.languages', 'Afar', 'Language code: aa'), 'aa'),
+ Language(4, translate('common.languages', 'Afrikaans', 'Language code: af'), 'af'),
+ Language(5, translate('common.languages', 'Albanian', 'Language code: sq'), 'sq'),
+ Language(6, translate('common.languages', 'Amharic', 'Language code: am'), 'am'),
+ Language(140, translate('common.languages', 'Amuzgo', 'Language code: amu'), 'amu'),
+ Language(152, translate('common.languages', 'Ancient Greek', 'Language code: grc'), 'grc'),
+ Language(7, translate('common.languages', 'Arabic', 'Language code: ar'), 'ar'),
+ Language(8, translate('common.languages', 'Armenian', 'Language code: hy'), 'hy'),
+ Language(9, translate('common.languages', 'Assamese', 'Language code: as'), 'as'),
+ Language(10, translate('common.languages', 'Aymara', 'Language code: ay'), 'ay'),
+ Language(11, translate('common.languages', 'Azerbaijani', 'Language code: az'), 'az'),
+ Language(12, translate('common.languages', 'Bashkir', 'Language code: ba'), 'ba'),
+ Language(13, translate('common.languages', 'Basque', 'Language code: eu'), 'eu'),
+ Language(14, translate('common.languages', 'Bengali', 'Language code: bn'), 'bn'),
+ Language(15, translate('common.languages', 'Bhutani', 'Language code: dz'), 'dz'),
+ Language(16, translate('common.languages', 'Bihari', 'Language code: bh'), 'bh'),
+ Language(17, translate('common.languages', 'Bislama', 'Language code: bi'), 'bi'),
+ Language(18, translate('common.languages', 'Breton', 'Language code: br'), 'br'),
+ Language(19, translate('common.languages', 'Bulgarian', 'Language code: bg'), 'bg'),
+ Language(20, translate('common.languages', 'Burmese', 'Language code: my'), 'my'),
+ Language(21, translate('common.languages', 'Byelorussian', 'Language code: be'), 'be'),
+ Language(142, translate('common.languages', 'Cakchiquel', 'Language code: cak'), 'cak'),
+ Language(22, translate('common.languages', 'Cambodian', 'Language code: km'), 'km'),
+ Language(23, translate('common.languages', 'Catalan', 'Language code: ca'), 'ca'),
+ Language(24, translate('common.languages', 'Chinese', 'Language code: zh'), 'zh'),
+ Language(141, translate('common.languages', 'Comaltepec Chinantec', 'Language code: cco'), 'cco'),
+ Language(25, translate('common.languages', 'Corsican', 'Language code: co'), 'co'),
+ Language(26, translate('common.languages', 'Croatian', 'Language code: hr'), 'hr'),
+ Language(27, translate('common.languages', 'Czech', 'Language code: cs'), 'cs'),
+ Language(28, translate('common.languages', 'Danish', 'Language code: da'), 'da'),
+ Language(29, translate('common.languages', 'Dutch', 'Language code: nl'), 'nl'),
+ Language(30, translate('common.languages', 'English', 'Language code: en'), 'en'),
+ Language(31, translate('common.languages', 'Esperanto', 'Language code: eo'), 'eo'),
+ Language(32, translate('common.languages', 'Estonian', 'Language code: et'), 'et'),
+ Language(33, translate('common.languages', 'Faeroese', 'Language code: fo'), 'fo'),
+ Language(34, translate('common.languages', 'Fiji', 'Language code: fj'), 'fj'),
+ Language(35, translate('common.languages', 'Finnish', 'Language code: fi'), 'fi'),
+ Language(36, translate('common.languages', 'French', 'Language code: fr'), 'fr'),
+ Language(37, translate('common.languages', 'Frisian', 'Language code: fy'), 'fy'),
+ Language(38, translate('common.languages', 'Galician', 'Language code: gl'), 'gl'),
+ Language(39, translate('common.languages', 'Georgian', 'Language code: ka'), 'ka'),
+ Language(40, translate('common.languages', 'German', 'Language code: de'), 'de'),
+ Language(41, translate('common.languages', 'Greek', 'Language code: el'), 'el'),
+ Language(42, translate('common.languages', 'Greenlandic', 'Language code: kl'), 'kl'),
+ Language(43, translate('common.languages', 'Guarani', 'Language code: gn'), 'gn'),
+ Language(44, translate('common.languages', 'Gujarati', 'Language code: gu'), 'gu'),
+ Language(143, translate('common.languages', 'Haitian Creole', 'Language code: ht'), 'ht'),
+ Language(45, translate('common.languages', 'Hausa', 'Language code: ha'), 'ha'),
+ Language(46, translate('common.languages', 'Hebrew (former iw)', 'Language code: he'), 'he'),
+ Language(144, translate('common.languages', 'Hiligaynon', 'Language code: hil'), 'hil'),
+ Language(47, translate('common.languages', 'Hindi', 'Language code: hi'), 'hi'),
+ Language(48, translate('common.languages', 'Hungarian', 'Language code: hu'), 'hu'),
+ Language(49, translate('common.languages', 'Icelandic', 'Language code: is'), 'is'),
+ Language(50, translate('common.languages', 'Indonesian (former in)', 'Language code: id'), 'id'),
+ Language(51, translate('common.languages', 'Interlingua', 'Language code: ia'), 'ia'),
+ Language(52, translate('common.languages', 'Interlingue', 'Language code: ie'), 'ie'),
+ Language(54, translate('common.languages', 'Inuktitut (Eskimo)', 'Language code: iu'), 'iu'),
+ Language(53, translate('common.languages', 'Inupiak', 'Language code: ik'), 'ik'),
+ Language(55, translate('common.languages', 'Irish', 'Language code: ga'), 'ga'),
+ Language(56, translate('common.languages', 'Italian', 'Language code: it'), 'it'),
+ Language(145, translate('common.languages', 'Jakalteko', 'Language code: jac'), 'jac'),
+ Language(57, translate('common.languages', 'Japanese', 'Language code: ja'), 'ja'),
+ Language(58, translate('common.languages', 'Javanese', 'Language code: jw'), 'jw'),
+ Language(150, translate('common.languages', 'K\'iche\'', 'Language code: quc'), 'quc'),
+ Language(59, translate('common.languages', 'Kannada', 'Language code: kn'), 'kn'),
+ Language(60, translate('common.languages', 'Kashmiri', 'Language code: ks'), 'ks'),
+ Language(61, translate('common.languages', 'Kazakh', 'Language code: kk'), 'kk'),
+ Language(146, translate('common.languages', 'Kekchí ', 'Language code: kek'), 'kek'),
+ Language(62, translate('common.languages', 'Kinyarwanda', 'Language code: rw'), 'rw'),
+ Language(63, translate('common.languages', 'Kirghiz', 'Language code: ky'), 'ky'),
+ Language(64, translate('common.languages', 'Kirundi', 'Language code: rn'), 'rn'),
+ Language(65, translate('common.languages', 'Korean', 'Language code: ko'), 'ko'),
+ Language(66, translate('common.languages', 'Kurdish', 'Language code: ku'), 'ku'),
+ Language(67, translate('common.languages', 'Laothian', 'Language code: lo'), 'lo'),
+ Language(68, translate('common.languages', 'Latin', 'Language code: la'), 'la'),
+ Language(69, translate('common.languages', 'Latvian, Lettish', 'Language code: lv'), 'lv'),
+ Language(70, translate('common.languages', 'Lingala', 'Language code: ln'), 'ln'),
+ Language(71, translate('common.languages', 'Lithuanian', 'Language code: lt'), 'lt'),
+ Language(72, translate('common.languages', 'Macedonian', 'Language code: mk'), 'mk'),
+ Language(73, translate('common.languages', 'Malagasy', 'Language code: mg'), 'mg'),
+ Language(74, translate('common.languages', 'Malay', 'Language code: ms'), 'ms'),
+ Language(75, translate('common.languages', 'Malayalam', 'Language code: ml'), 'ml'),
+ Language(76, translate('common.languages', 'Maltese', 'Language code: mt'), 'mt'),
+ Language(148, translate('common.languages', 'Mam', 'Language code: mam'), 'mam'),
+ Language(77, translate('common.languages', 'Maori', 'Language code: mi'), 'mi'),
+ Language(147, translate('common.languages', 'Maori', 'Language code: mri'), 'mri'),
+ Language(78, translate('common.languages', 'Marathi', 'Language code: mr'), 'mr'),
+ Language(79, translate('common.languages', 'Moldavian', 'Language code: mo'), 'mo'),
+ Language(80, translate('common.languages', 'Mongolian', 'Language code: mn'), 'mn'),
+ Language(149, translate('common.languages', 'Nahuatl', 'Language code: nah'), 'nah'),
+ Language(81, translate('common.languages', 'Nauru', 'Language code: na'), 'na'),
+ Language(82, translate('common.languages', 'Nepali', 'Language code: ne'), 'ne'),
+ Language(83, translate('common.languages', 'Norwegian', 'Language code: no'), 'no'),
+ Language(84, translate('common.languages', 'Occitan', 'Language code: oc'), 'oc'),
+ Language(85, translate('common.languages', 'Oriya', 'Language code: or'), 'or'),
+ Language(86, translate('common.languages', 'Pashto, Pushto', 'Language code: ps'), 'ps'),
+ Language(87, translate('common.languages', 'Persian', 'Language code: fa'), 'fa'),
+ Language(151, translate('common.languages', 'Plautdietsch', 'Language code: pdt'), 'pdt'),
+ Language(88, translate('common.languages', 'Polish', 'Language code: pl'), 'pl'),
+ Language(89, translate('common.languages', 'Portuguese', 'Language code: pt'), 'pt'),
+ Language(90, translate('common.languages', 'Punjabi', 'Language code: pa'), 'pa'),
+ Language(91, translate('common.languages', 'Quechua', 'Language code: qu'), 'qu'),
+ Language(92, translate('common.languages', 'Rhaeto-Romance', 'Language code: rm'), 'rm'),
+ Language(93, translate('common.languages', 'Romanian', 'Language code: ro'), 'ro'),
+ Language(94, translate('common.languages', 'Russian', 'Language code: ru'), 'ru'),
+ Language(95, translate('common.languages', 'Samoan', 'Language code: sm'), 'sm'),
+ Language(96, translate('common.languages', 'Sangro', 'Language code: sg'), 'sg'),
+ Language(97, translate('common.languages', 'Sanskrit', 'Language code: sa'), 'sa'),
+ Language(98, translate('common.languages', 'Scots Gaelic', 'Language code: gd'), 'gd'),
+ Language(99, translate('common.languages', 'Serbian', 'Language code: sr'), 'sr'),
+ Language(100, translate('common.languages', 'Serbo-Croatian', 'Language code: sh'), 'sh'),
+ Language(101, translate('common.languages', 'Sesotho', 'Language code: st'), 'st'),
+ Language(102, translate('common.languages', 'Setswana', 'Language code: tn'), 'tn'),
+ Language(103, translate('common.languages', 'Shona', 'Language code: sn'), 'sn'),
+ Language(104, translate('common.languages', 'Sindhi', 'Language code: sd'), 'sd'),
+ Language(105, translate('common.languages', 'Singhalese', 'Language code: si'), 'si'),
+ Language(106, translate('common.languages', 'Siswati', 'Language code: ss'), 'ss'),
+ Language(107, translate('common.languages', 'Slovak', 'Language code: sk'), 'sk'),
+ Language(108, translate('common.languages', 'Slovenian', 'Language code: sl'), 'sl'),
+ Language(109, translate('common.languages', 'Somali', 'Language code: so'), 'so'),
+ Language(110, translate('common.languages', 'Spanish', 'Language code: es'), 'es'),
+ Language(111, translate('common.languages', 'Sudanese', 'Language code: su'), 'su'),
+ Language(112, translate('common.languages', 'Swahili', 'Language code: sw'), 'sw'),
+ Language(113, translate('common.languages', 'Swedish', 'Language code: sv'), 'sv'),
+ Language(114, translate('common.languages', 'Tagalog', 'Language code: tl'), 'tl'),
+ Language(115, translate('common.languages', 'Tajik', 'Language code: tg'), 'tg'),
+ Language(116, translate('common.languages', 'Tamil', 'Language code: ta'), 'ta'),
+ Language(117, translate('common.languages', 'Tatar', 'Language code: tt'), 'tt'),
+ Language(118, translate('common.languages', 'Tegulu', 'Language code: te'), 'te'),
+ Language(119, translate('common.languages', 'Thai', 'Language code: th'), 'th'),
+ Language(120, translate('common.languages', 'Tibetan', 'Language code: bo'), 'bo'),
+ Language(121, translate('common.languages', 'Tigrinya', 'Language code: ti'), 'ti'),
+ Language(122, translate('common.languages', 'Tonga', 'Language code: to'), 'to'),
+ Language(123, translate('common.languages', 'Tsonga', 'Language code: ts'), 'ts'),
+ Language(124, translate('common.languages', 'Turkish', 'Language code: tr'), 'tr'),
+ Language(125, translate('common.languages', 'Turkmen', 'Language code: tk'), 'tk'),
+ Language(126, translate('common.languages', 'Twi', 'Language code: tw'), 'tw'),
+ Language(127, translate('common.languages', 'Uigur', 'Language code: ug'), 'ug'),
+ Language(128, translate('common.languages', 'Ukrainian', 'Language code: uk'), 'uk'),
+ Language(129, translate('common.languages', 'Urdu', 'Language code: ur'), 'ur'),
+ Language(153, translate('common.languages', 'Uspanteco', 'Language code: usp'), 'usp'),
+ Language(130, translate('common.languages', 'Uzbek', 'Language code: uz'), 'uz'),
+ Language(131, translate('common.languages', 'Vietnamese', 'Language code: vi'), 'vi'),
+ Language(132, translate('common.languages', 'Volapuk', 'Language code: vo'), 'vo'),
+ Language(133, translate('common.languages', 'Welch', 'Language code: cy'), 'cy'),
+ Language(134, translate('common.languages', 'Wolof', 'Language code: wo'), 'wo'),
+ Language(135, translate('common.languages', 'Xhosa', 'Language code: xh'), 'xh'),
+ Language(136, translate('common.languages', 'Yiddish (former ji)', 'Language code: yi'), 'yi'),
+ Language(137, translate('common.languages', 'Yoruba', 'Language code: yo'), 'yo'),
+ Language(138, translate('common.languages', 'Zhuang', 'Language code: za'), 'za'),
+ Language(139, translate('common.languages', 'Zulu', 'Language code: zu'), 'zu')
+], key=lambda language: language.name)
+
+
+def get_language(name):
+ """
+ Find the language by its name or code.
+
+ :param name: The name or abbreviation of the language.
+ :return: The first match as a Language namedtuple or None
+ """
+ if name:
+ name_lower = name.lower()
+ name_title = name_lower[:1].upper() + name_lower[1:]
+ for language in languages:
+ if language.name == name_title or language.code == name_lower:
+ return language
+ return None
diff --git a/openlp/core/common/uistrings.py b/openlp/core/common/uistrings.py
index f19a35f6f..71799eb8a 100644
--- a/openlp/core/common/uistrings.py
+++ b/openlp/core/common/uistrings.py
@@ -23,6 +23,7 @@
The :mod:`uistrings` module provides standard strings for OpenLP.
"""
import logging
+import itertools
from openlp.core.common import translate
@@ -153,3 +154,30 @@ class UiStrings(object):
self.View = translate('OpenLP.Ui', 'View')
self.ViewMode = translate('OpenLP.Ui', 'View Mode')
self.Video = translate('OpenLP.Ui', 'Video')
+ self.BibleShortSearchTitle = translate('OpenLP.Ui', 'Search is Empty or too Short')
+ self.BibleShortSearch = translate('OpenLP.Ui', 'The search you have entered is empty or shorter '
+ 'than 3 characters long.
Please try again with '
+ 'a longer search.')
+ self.BibleNoBiblesTitle = translate('OpenLP.Ui', 'No Bibles Available')
+ self.BibleNoBibles = translate('OpenLP.Ui', 'There are no Bibles currently installed.
'
+ 'Please use the Import Wizard to install one or more Bibles.')
+ book_chapter = translate('OpenLP.Ui', 'Book Chapter')
+ chapter = translate('OpenLP.Ui', 'Chapter')
+ verse = translate('OpenLP.Ui', 'Verse')
+ gap = ' | '
+ psalm = translate('OpenLP.Ui', 'Psalm')
+ may_shorten = translate('OpenLP.Ui', 'Book names may be shortened from full names, for an example Ps 23 = '
+ 'Psalm 23')
+ bible_scripture_items = \
+ [book_chapter, gap, psalm, ' 23
',
+ book_chapter, '%(range)s', chapter, gap, psalm, ' 23%(range)s24
',
+ book_chapter, '%(verse)s', verse, '%(range)s', verse, gap, psalm, ' 23%(verse)s1%(range)s2
',
+ book_chapter, '%(verse)s', verse, '%(range)s', verse, '%(list)s', verse, '%(range)s', verse, gap, psalm,
+ ' 23%(verse)s1%(range)s2%(list)s5%(range)s6
',
+ book_chapter, '%(verse)s', verse, '%(range)s', verse, '%(list)s', chapter, '%(verse)s', verse, '%(range)s',
+ verse, gap, psalm, ' 23%(verse)s1%(range)s2%(list)s24%(verse)s1%(range)s3
',
+ book_chapter, '%(verse)s', verse, '%(range)s', chapter, '%(verse)s', verse, gap, psalm,
+ ' 23%(verse)s1%(range)s24%(verse)s1
', may_shorten]
+ itertools.chain.from_iterable(itertools.repeat(strings, 1) if isinstance(strings, str)
+ else strings for strings in bible_scripture_items)
+ self.BibleScriptureError = ''.join(str(joined) for joined in bible_scripture_items)
diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py
index 358c82543..c7c84f912 100644
--- a/openlp/core/lib/mediamanageritem.py
+++ b/openlp/core/lib/mediamanageritem.py
@@ -651,6 +651,20 @@ class MediaManagerItem(QtWidgets.QWidget, RegistryProperties):
item.setFont(font)
self.list_view.addItem(item)
+ def check_search_result_search_while_typing_short(self):
+ """
+ This is used in Bible "Search while typing" if the search is shorter than the min required len.
+ """
+ if self.list_view.count():
+ return
+ message = translate('OpenLP.MediaManagerItem', 'Search is too short to be used in: "Search while typing"')
+ item = QtWidgets.QListWidgetItem(message)
+ item.setFlags(QtCore.Qt.NoItemFlags)
+ font = QtGui.QFont()
+ font.setItalic(True)
+ item.setFont(font)
+ self.list_view.addItem(item)
+
def _get_id_of_item_to_generate(self, item, remote_item):
"""
Utility method to check items being submitted for slide generation.
diff --git a/openlp/core/lib/theme.py b/openlp/core/lib/theme.py
index fc20037e7..c27b08cd0 100644
--- a/openlp/core/lib/theme.py
+++ b/openlp/core/lib/theme.py
@@ -474,6 +474,7 @@ class ThemeXML(object):
if element.startswith('shadow') or element.startswith('outline'):
master = 'font_main'
# fix bold font
+ ret_value = None
if element == 'weight':
element = 'bold'
if value == 'Normal':
@@ -482,7 +483,7 @@ class ThemeXML(object):
ret_value = True
if element == 'proportion':
element = 'size'
- return False, master, element, ret_value
+ return False, master, element, ret_value if ret_value is not None else value
def _create_attr(self, master, element, value):
"""
diff --git a/openlp/plugins/bibles/bibleplugin.py b/openlp/plugins/bibles/bibleplugin.py
index ccc61ba56..3d27effe4 100644
--- a/openlp/plugins/bibles/bibleplugin.py
+++ b/openlp/plugins/bibles/bibleplugin.py
@@ -41,7 +41,8 @@ __default_settings__ = {
'bibles/db password': '',
'bibles/db hostname': '',
'bibles/db database': '',
- 'bibles/last search type': BibleSearch.Reference,
+ 'bibles/last search type': BibleSearch.Combined,
+ 'bibles/reset to combined quick search': True,
'bibles/verse layout style': LayoutStyle.VersePerSlide,
'bibles/book name language': LanguageSelection.Bible,
'bibles/display brackets': DisplayStyle.NoBrackets,
@@ -59,7 +60,9 @@ __default_settings__ = {
'bibles/range separator': '',
'bibles/list separator': '',
'bibles/end separator': '',
- 'bibles/last directory import': ''
+ 'bibles/last directory import': '',
+ 'bibles/hide combined quick error': False,
+ 'bibles/is search while typing enabled': True
}
diff --git a/openlp/plugins/bibles/forms/languageform.py b/openlp/plugins/bibles/forms/languageform.py
index 461f02c74..1ba71ad6e 100644
--- a/openlp/plugins/bibles/forms/languageform.py
+++ b/openlp/plugins/bibles/forms/languageform.py
@@ -29,9 +29,9 @@ from PyQt5.QtWidgets import QDialog
from PyQt5 import QtCore
from openlp.core.common import translate
+from openlp.core.common.languages import languages
from openlp.core.lib.ui import critical_error_message_box
from openlp.plugins.bibles.forms.languagedialog import Ui_LanguageDialog
-from openlp.plugins.bibles.lib.db import BiblesResourcesDB
log = logging.getLogger(__name__)
@@ -51,11 +51,11 @@ class LanguageForm(QDialog, Ui_LanguageDialog):
self.setupUi(self)
def exec(self, bible_name):
- self.language_combo_box.addItem('')
if bible_name:
- self.bible_label.setText(str(bible_name))
- items = BiblesResourcesDB.get_languages()
- self.language_combo_box.addItems([item['name'] for item in items])
+ self.bible_label.setText(bible_name)
+ self.language_combo_box.addItem('')
+ for language in languages:
+ self.language_combo_box.addItem(language.name, language.id)
return QDialog.exec(self)
def accept(self):
diff --git a/openlp/plugins/bibles/lib/biblestab.py b/openlp/plugins/bibles/lib/biblestab.py
index 5438d07f2..c087335ae 100644
--- a/openlp/plugins/bibles/lib/biblestab.py
+++ b/openlp/plugins/bibles/lib/biblestab.py
@@ -128,6 +128,20 @@ class BiblesTab(SettingsTab):
self.language_selection_layout.addWidget(self.language_selection_label)
self.language_selection_layout.addWidget(self.language_selection_combo_box)
self.right_layout.addWidget(self.language_selection_group_box)
+ self.bible_quick_settings_group_box = QtWidgets.QGroupBox(self.right_column)
+ self.bible_quick_settings_group_box.setObjectName('bible_quick_settings_group_box')
+ self.right_layout.addWidget(self.bible_quick_settings_group_box)
+ self.search_settings_layout = QtWidgets.QFormLayout(self.bible_quick_settings_group_box)
+ self.search_settings_layout.setObjectName('search_settings_layout')
+ self.reset_to_combined_quick_search_check_box = QtWidgets.QCheckBox(self.bible_quick_settings_group_box)
+ self.reset_to_combined_quick_search_check_box.setObjectName('reset_to_combined_quick_search_check_box')
+ self.search_settings_layout.addRow(self.reset_to_combined_quick_search_check_box)
+ self.hide_combined_quick_error_check_box = QtWidgets.QCheckBox(self.bible_quick_settings_group_box)
+ self.hide_combined_quick_error_check_box.setObjectName('hide_combined_quick_error_check_box')
+ self.search_settings_layout.addRow(self.hide_combined_quick_error_check_box)
+ self.bible_search_while_typing_check_box = QtWidgets.QCheckBox(self.bible_quick_settings_group_box)
+ self.bible_search_while_typing_check_box.setObjectName('bible_search_while_typing_check_box')
+ self.search_settings_layout.addRow(self.bible_search_while_typing_check_box)
self.left_layout.addStretch()
self.right_layout.addStretch()
# Signals and slots
@@ -151,6 +165,12 @@ class BiblesTab(SettingsTab):
self.end_separator_line_edit.editingFinished.connect(self.on_end_separator_line_edit_finished)
Registry().register_function('theme_update_list', self.update_theme_list)
self.language_selection_combo_box.activated.connect(self.on_language_selection_combo_box_changed)
+ self.reset_to_combined_quick_search_check_box.stateChanged.connect(
+ self.on_reset_to_combined_quick_search_check_box_changed)
+ self.hide_combined_quick_error_check_box.stateChanged.connect(
+ self.on_hide_combined_quick_error_check_box_changed)
+ self.bible_search_while_typing_check_box.stateChanged.connect(
+ self.on_bible_search_while_typing_check_box_changed)
def retranslateUi(self):
self.verse_display_group_box.setTitle(translate('BiblesPlugin.BiblesTab', 'Verse Display'))
@@ -194,6 +214,17 @@ class BiblesTab(SettingsTab):
LanguageSelection.Application, translate('BiblesPlugin.BiblesTab', 'Application Language'))
self.language_selection_combo_box.setItemText(
LanguageSelection.English, translate('BiblesPlugin.BiblesTab', 'English'))
+ self.bible_quick_settings_group_box.setTitle(translate('BiblesPlugin.BiblesTab', 'Quick Search Settings'))
+ self.reset_to_combined_quick_search_check_box.setText(translate('BiblesPlugin.BiblesTab',
+ 'Reset search type to "Text or Scripture'
+ ' Reference" on startup'))
+ self.hide_combined_quick_error_check_box.setText(translate('BiblesPlugin.BiblesTab',
+ 'Don\'t show error if nothing is found in "Text or '
+ 'Scripture Reference"'))
+ self.bible_search_while_typing_check_box.setText(translate('BiblesPlugin.BiblesTab',
+ 'Search automatically while typing (Text search must'
+ ' contain a\nminimum of {count} characters and a '
+ 'space for performance reasons)').format(count='8'))
def on_bible_theme_combo_box_changed(self):
self.bible_theme = self.bible_theme_combo_box.currentText()
@@ -302,6 +333,24 @@ class BiblesTab(SettingsTab):
self.end_separator_line_edit.setText(get_reference_separator('sep_e_default'))
self.end_separator_line_edit.setPalette(self.get_grey_text_palette(True))
+ def on_reset_to_combined_quick_search_check_box_changed(self, check_state):
+ """
+ Event handler for the 'hide_combined_quick_error' check box
+ """
+ self.reset_to_combined_quick_search = (check_state == QtCore.Qt.Checked)
+
+ def on_hide_combined_quick_error_check_box_changed(self, check_state):
+ """
+ Event handler for the 'hide_combined_quick_error' check box
+ """
+ self.hide_combined_quick_error = (check_state == QtCore.Qt.Checked)
+
+ def on_bible_search_while_typing_check_box_changed(self, check_state):
+ """
+ Event handler for the 'hide_combined_quick_error' check box
+ """
+ self.bible_search_while_typing = (check_state == QtCore.Qt.Checked)
+
def load(self):
settings = Settings()
settings.beginGroup(self.settings_section)
@@ -355,6 +404,12 @@ class BiblesTab(SettingsTab):
self.end_separator_check_box.setChecked(True)
self.language_selection = settings.value('book name language')
self.language_selection_combo_box.setCurrentIndex(self.language_selection)
+ self.reset_to_combined_quick_search = settings.value('reset to combined quick search')
+ self.reset_to_combined_quick_search_check_box.setChecked(self.reset_to_combined_quick_search)
+ self.hide_combined_quick_error = settings.value('hide combined quick error')
+ self.hide_combined_quick_error_check_box.setChecked(self.hide_combined_quick_error)
+ self.bible_search_while_typing = settings.value('is search while typing enabled')
+ self.bible_search_while_typing_check_box.setChecked(self.bible_search_while_typing)
settings.endGroup()
def save(self):
@@ -386,6 +441,9 @@ class BiblesTab(SettingsTab):
if self.language_selection != settings.value('book name language'):
settings.setValue('book name language', self.language_selection)
self.settings_form.register_post_process('bibles_load_list')
+ settings.setValue('reset to combined quick search', self.reset_to_combined_quick_search)
+ settings.setValue('hide combined quick error', self.hide_combined_quick_error)
+ settings.setValue('is search while typing enabled', self.bible_search_while_typing)
settings.endGroup()
if self.tab_visited:
self.settings_form.register_post_process('bibles_config_updated')
diff --git a/openlp/plugins/bibles/lib/db.py b/openlp/plugins/bibles/lib/db.py
index c23ade4a7..ff305dec6 100644
--- a/openlp/plugins/bibles/lib/db.py
+++ b/openlp/plugins/bibles/lib/db.py
@@ -128,6 +128,12 @@ class BibleDB(Manager, RegistryProperties):
The name of the database. This is also used as the file name for SQLite databases.
"""
log.info('BibleDB loaded')
+ self._setup(parent, **kwargs)
+
+ def _setup(self, parent, **kwargs):
+ """
+ Run some initial setup. This method is separate from __init__ in order to mock it out in tests.
+ """
self.bible_plugin = parent
self.session = None
if 'path' not in kwargs:
@@ -465,14 +471,13 @@ class BibleDB(Manager, RegistryProperties):
"""
log.debug('BibleDB.get_language()')
from openlp.plugins.bibles.forms import LanguageForm
- language = None
+ language_id = None
language_form = LanguageForm(self.wizard)
if language_form.exec(bible_name):
- language = str(language_form.language_combo_box.currentText())
- if not language:
+ combo_box = language_form.language_combo_box
+ language_id = combo_box.itemData(combo_box.currentIndex())
+ if not language_id:
return False
- language = BiblesResourcesDB.get_language(language)
- language_id = language['id']
self.save_meta('language_id', language_id)
return language_id
@@ -767,43 +772,6 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
return book[0]
return None
- @staticmethod
- def get_language(name):
- """
- Return a dict containing the language id, name and code by name or abbreviation.
-
- :param name: The name or abbreviation of the language.
- """
- log.debug('BiblesResourcesDB.get_language("{name}")'.format(name=name))
- 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()))
- if language:
- return {
- 'id': language[0][0],
- 'name': str(language[0][1]),
- 'code': str(language[0][2])
- }
- else:
- return None
-
- @staticmethod
- def get_languages():
- """
- Return a dict containing all languages with id, name and code.
- """
- log.debug('BiblesResourcesDB.get_languages()')
- languages = BiblesResourcesDB.run_sql('SELECT id, name, code FROM language ORDER by name')
- if languages:
- return [{
- 'id': language[0],
- 'name': str(language[1]),
- 'code': str(language[2])
- } for language in languages]
- else:
- return None
-
@staticmethod
def get_testament_reference():
"""
@@ -1014,7 +982,7 @@ class OldBibleDB(QtCore.QObject, Manager):
def get_verses(self, book_id):
"""
- Returns the verses of the Bible.
+ Returns the verses of the Bible.
"""
verses = self.run_sql(
'SELECT book_id, chapter, verse, text FROM verse WHERE book_id = ? ORDER BY id', (book_id, ))
diff --git a/openlp/plugins/bibles/lib/http.py b/openlp/plugins/bibles/lib/http.py
index c50745c2f..392cce05a 100644
--- a/openlp/plugins/bibles/lib/http.py
+++ b/openlp/plugins/bibles/lib/http.py
@@ -252,7 +252,7 @@ class BGExtract(RegistryProperties):
chapter=chapter,
version=version)
soup = get_soup_for_bible_ref(
- 'http://legacy.biblegateway.com/passage/?{url}'.format(url=url_params),
+ 'http://biblegateway.com/passage/?{url}'.format(url=url_params),
pre_parse_regex=r'', pre_parse_substitute='')
if not soup:
return None
@@ -281,7 +281,7 @@ class BGExtract(RegistryProperties):
"""
log.debug('BGExtract.get_books_from_http("{version}")'.format(version=version))
url_params = urllib.parse.urlencode({'action': 'getVersionInfo', 'vid': '{version}'.format(version=version)})
- reference_url = 'http://legacy.biblegateway.com/versions/?{url}#books'.format(url=url_params)
+ reference_url = 'http://biblegateway.com/versions/?{url}#books'.format(url=url_params)
page = get_web_page(reference_url)
if not page:
send_error_message('download')
@@ -312,7 +312,7 @@ class BGExtract(RegistryProperties):
for book in content:
book = book.find('td')
if book:
- books.append(book.contents[0])
+ books.append(book.contents[1])
return books
def get_bibles_from_http(self):
@@ -322,11 +322,11 @@ class BGExtract(RegistryProperties):
returns a list in the form [(biblename, biblekey, language_code)]
"""
log.debug('BGExtract.get_bibles_from_http')
- bible_url = 'https://legacy.biblegateway.com/versions/'
+ bible_url = 'https://biblegateway.com/versions/'
soup = get_soup_for_bible_ref(bible_url)
if not soup:
return None
- bible_select = soup.find('select', {'class': 'translation-dropdown'})
+ bible_select = soup.find('select', {'class': 'search-translation-select'})
if not bible_select:
log.debug('No select tags found - did site change?')
return None
@@ -532,28 +532,26 @@ class CWExtract(RegistryProperties):
returns a list in the form [(biblename, biblekey, language_code)]
"""
log.debug('CWExtract.get_bibles_from_http')
- bible_url = 'http://www.biblestudytools.com/'
+ bible_url = 'http://www.biblestudytools.com/bible-versions/'
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', {'class': 'log-translation'})
- if not option_tags:
- log.debug('No option tags found - did site change?')
+ h4_tags = soup.find_all('h4', {'class': 'small-header'})
+ if not h4_tags:
+ log.debug('No h4 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?')
+ for h4t in h4_tags:
+ short_name = None
+ if h4t.span:
+ short_name = h4t.span.get_text().strip().lower()
+ else:
+ log.error('No span tag found - did site change?')
return None
- if not tag_value:
+ if not short_name:
continue
+ h4t.span.extract()
+ tag_text = h4t.get_text().strip()
# 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]
@@ -561,12 +559,20 @@ class CWExtract(RegistryProperties):
language_code = CROSSWALK_LANGUAGES[language]
else:
language_code = ''
- # ... except for the latin vulgate
+ # ... except for those that don't...
elif 'latin' in tag_text.lower():
language_code = 'la'
+ elif 'la biblia' in tag_text.lower() or 'nueva' in tag_text.lower():
+ language_code = 'es'
+ elif 'chinese' in tag_text.lower():
+ language_code = 'zh'
+ elif 'greek' in tag_text.lower():
+ language_code = 'el'
+ elif 'nova' in tag_text.lower():
+ language_code = 'pt'
else:
language_code = 'en'
- bibles.append((tag_text, tag_value, language_code))
+ bibles.append((tag_text, short_name, language_code))
return bibles
diff --git a/openlp/plugins/bibles/lib/manager.py b/openlp/plugins/bibles/lib/manager.py
index 1c55222f2..9ea4c6612 100644
--- a/openlp/plugins/bibles/lib/manager.py
+++ b/openlp/plugins/bibles/lib/manager.py
@@ -23,8 +23,8 @@
import logging
import os
-from openlp.core.common import RegistryProperties, AppLocation, Settings, translate, delete_file
-from openlp.plugins.bibles.lib import parse_reference, get_reference_separator, LanguageSelection
+from openlp.core.common import RegistryProperties, AppLocation, Settings, translate, delete_file, UiStrings
+from openlp.plugins.bibles.lib import parse_reference, LanguageSelection
from openlp.plugins.bibles.lib.db import BibleDB, BibleMeta
from .csvbible import CSVBible
from .http import HTTPBible
@@ -267,42 +267,21 @@ class BibleManager(RegistryProperties):
For second bible this is necessary.
:param show_error:
"""
+ # If no bibles are installed, message is given.
log.debug('BibleManager.get_verses("{bible}", "{verse}")'.format(bible=bible, verse=verse_text))
if not bible:
if show_error:
self.main_window.information_message(
- translate('BiblesPlugin.BibleManager', 'No Bibles Available'),
- translate('BiblesPlugin.BibleManager', 'There are no Bibles currently installed. Please use the '
- 'Import Wizard to install one or more Bibles.')
- )
+ UiStrings().BibleNoBiblesTitle,
+ UiStrings().BibleNoBibles)
return None
+ # Get the language for books.
language_selection = self.get_language_selection(bible)
ref_list = parse_reference(verse_text, self.db_cache[bible], language_selection, book_ref_id)
if ref_list:
return self.db_cache[bible].get_verses(ref_list, show_error)
+ # If nothing is found. Message is given if this is not combined search. (defined in mediaitem.py)
else:
- if show_error:
- reference_separators = {
- 'verse': get_reference_separator('sep_v_display'),
- 'range': get_reference_separator('sep_r_display'),
- 'list': get_reference_separator('sep_l_display')}
- self.main_window.information_message(
- translate('BiblesPlugin.BibleManager', 'Scripture Reference Error'),
- translate('BiblesPlugin.BibleManager', 'Your scripture reference is either not supported by '
- 'OpenLP or is invalid. Please make sure your reference '
- 'conforms to one of the following patterns or consult the manual:\n\n'
- 'Book Chapter\n'
- 'Book Chapter%(range)sChapter\n'
- 'Book Chapter%(verse)sVerse%(range)sVerse\n'
- 'Book Chapter%(verse)sVerse%(range)sVerse%(list)sVerse'
- '%(range)sVerse\n'
- 'Book Chapter%(verse)sVerse%(range)sVerse%(list)sChapter'
- '%(verse)sVerse%(range)sVerse\n'
- 'Book Chapter%(verse)sVerse%(range)sChapter%(verse)sVerse',
- 'Please pay attention to the appended "s" of the wildcards '
- 'and refrain from translating the words inside the names in the brackets.')
- % reference_separators
- )
return None
def get_language_selection(self, bible):
@@ -334,13 +313,11 @@ class BibleManager(RegistryProperties):
:param text: The text to search for (unicode).
"""
log.debug('BibleManager.verse_search("{bible}", "{text}")'.format(bible=bible, text=text))
+ # If no bibles are installed, message is given.
if not bible:
self.main_window.information_message(
- translate('BiblesPlugin.BibleManager', 'No Bibles Available'),
- translate('BiblesPlugin.BibleManager',
- 'There are no Bibles currently installed. Please use the Import Wizard to install one or '
- 'more Bibles.')
- )
+ UiStrings().BibleNoBiblesTitle,
+ UiStrings().BibleNoBibles)
return None
# Check if the bible or second_bible is a web bible.
web_bible = self.db_cache[bible].get_object(BibleMeta, 'download_source')
@@ -348,20 +325,56 @@ class BibleManager(RegistryProperties):
if second_bible:
second_web_bible = self.db_cache[second_bible].get_object(BibleMeta, 'download_source')
if web_bible or second_web_bible:
+ # If either Bible is Web, cursor is reset to normal and message is given.
+ self.application.set_normal_cursor()
self.main_window.information_message(
- translate('BiblesPlugin.BibleManager', 'Web Bible cannot be used'),
- translate('BiblesPlugin.BibleManager', 'Text Search is not available with Web Bibles.')
+ translate('BiblesPlugin.BibleManager', 'Web Bible cannot be used in Text Search'),
+ translate('BiblesPlugin.BibleManager', 'Text Search is not available with Web Bibles.\n'
+ 'Please use the Scripture Reference Search instead.\n\n'
+ 'This means that the currently used Bible\nor Second Bible '
+ 'is installed as Web Bible.\n\n'
+ 'If you were trying to perform a Reference search\nin Combined '
+ 'Search, your reference is invalid.')
)
return None
- if text:
+ # Shorter than 3 char searches break OpenLP with very long search times, thus they are blocked.
+ if len(text) - text.count(' ') < 3:
+ return None
+ # Fetch the results from db. If no results are found, return None, no message is given for this.
+ elif text:
+ return self.db_cache[bible].verse_search(text)
+ else:
+ return None
+
+ def verse_search_while_typing(self, bible, second_bible, text):
+ """
+ Does a verse search for the given bible and text.
+ This is used during "Search while typing"
+ It's the same thing as the normal text search, but it does not show the web Bible error.
+ (It would result in the error popping every time a char is entered or removed)
+ It also does not have a minimum text len, this is set in mediaitem.py
+
+ :param bible: The bible to search in (unicode).
+ :param second_bible: The second bible (unicode). We do not search in this bible.
+ :param text: The text to search for (unicode).
+ """
+ # If no bibles are installed, message is given.
+ if not bible:
+ return None
+ # Check if the bible or second_bible is a web bible.
+ web_bible = self.db_cache[bible].get_object(BibleMeta, 'download_source')
+ second_web_bible = ''
+ if second_bible:
+ second_web_bible = self.db_cache[second_bible].get_object(BibleMeta, 'download_source')
+ if web_bible or second_web_bible:
+ # If either Bible is Web, cursor is reset to normal and search ends w/o any message.
+ self.check_search_result()
+ self.application.set_normal_cursor()
+ return None
+ # Fetch the results from db. If no results are found, return None, no message is given for this.
+ elif text:
return self.db_cache[bible].verse_search(text)
else:
- self.main_window.information_message(
- translate('BiblesPlugin.BibleManager', 'Scripture Reference Error'),
- translate('BiblesPlugin.BibleManager', 'You did not enter a search keyword.\nYou can separate '
- 'different keywords by a space to search for all of your keywords and you can separate '
- 'them by a comma to search for one of them.')
- )
return None
def save_meta_data(self, bible, version, copyright, permissions, book_name_language=None):
diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py
index d5304e867..debf786f7 100644
--- a/openlp/plugins/bibles/lib/mediaitem.py
+++ b/openlp/plugins/bibles/lib/mediaitem.py
@@ -35,6 +35,7 @@ from openlp.plugins.bibles.forms.editbibleform import EditBibleForm
from openlp.plugins.bibles.lib import LayoutStyle, DisplayStyle, VerseReferenceList, get_reference_separator, \
LanguageSelection, BibleStrings
from openlp.plugins.bibles.lib.db import BiblesResourcesDB
+import re
log = logging.getLogger(__name__)
@@ -45,6 +46,7 @@ class BibleSearch(object):
"""
Reference = 1
Text = 2
+ Combined = 3
class BibleMediaItem(MediaManagerItem):
@@ -56,6 +58,7 @@ class BibleMediaItem(MediaManagerItem):
log.info('Bible Media Item loaded')
def __init__(self, parent, plugin):
+ self.clear_icon = build_icon(':/bibles/bibles_search_clear.png')
self.lock_icon = build_icon(':/bibles/bibles_search_lock.png')
self.unlock_icon = build_icon(':/bibles/bibles_search_unlock.png')
MediaManagerItem.__init__(self, parent, plugin)
@@ -157,10 +160,15 @@ class BibleMediaItem(MediaManagerItem):
search_button_layout = QtWidgets.QHBoxLayout()
search_button_layout.setObjectName(prefix + 'search_button_layout')
search_button_layout.addStretch()
+ # Note: If we use QPushButton instead of the QToolButton, the icon will be larger than the Lock icon.
+ clear_button = QtWidgets.QToolButton(tab)
+ clear_button.setIcon(self.clear_icon)
+ clear_button.setObjectName(prefix + 'ClearButton')
lock_button = QtWidgets.QToolButton(tab)
lock_button.setIcon(self.unlock_icon)
lock_button.setCheckable(True)
lock_button.setObjectName(prefix + 'LockButton')
+ search_button_layout.addWidget(clear_button)
search_button_layout.addWidget(lock_button)
search_button = QtWidgets.QPushButton(tab)
search_button.setObjectName(prefix + 'SearchButton')
@@ -176,6 +184,7 @@ class BibleMediaItem(MediaManagerItem):
setattr(self, prefix + 'SecondComboBox', second_combo_box)
setattr(self, prefix + 'StyleLabel', style_label)
setattr(self, prefix + 'StyleComboBox', style_combo_box)
+ setattr(self, prefix + 'ClearButton', clear_button)
setattr(self, prefix + 'LockButton', lock_button)
setattr(self, prefix + 'SearchButtonLayout', search_button_layout)
setattr(self, prefix + 'SearchButton', search_button)
@@ -245,11 +254,14 @@ class BibleMediaItem(MediaManagerItem):
self.quickStyleComboBox.activated.connect(self.on_quick_style_combo_box_changed)
self.advancedStyleComboBox.activated.connect(self.on_advanced_style_combo_box_changed)
# Buttons
+ self.advancedClearButton.clicked.connect(self.on_clear_button)
+ self.quickClearButton.clicked.connect(self.on_clear_button)
self.advancedSearchButton.clicked.connect(self.on_advanced_search_button)
self.quickSearchButton.clicked.connect(self.on_quick_search_button)
# Other stuff
self.quick_search_edit.returnPressed.connect(self.on_quick_search_button)
self.search_tab_bar.currentChanged.connect(self.on_search_tab_bar_current_changed)
+ self.quick_search_edit.textChanged.connect(self.on_search_text_edit_changed)
def on_focus(self):
if self.quickTab.isVisible():
@@ -286,6 +298,7 @@ class BibleMediaItem(MediaManagerItem):
self.quickStyleComboBox.setItemText(LayoutStyle.VersePerSlide, UiStrings().VersePerSlide)
self.quickStyleComboBox.setItemText(LayoutStyle.VersePerLine, UiStrings().VersePerLine)
self.quickStyleComboBox.setItemText(LayoutStyle.Continuous, UiStrings().Continuous)
+ self.quickClearButton.setToolTip(translate('BiblesPlugin.MediaItem', 'Clear the search results.'))
self.quickLockButton.setToolTip(translate('BiblesPlugin.MediaItem',
'Toggle to keep or clear the previous results.'))
self.quickSearchButton.setText(UiStrings().Search)
@@ -300,6 +313,7 @@ class BibleMediaItem(MediaManagerItem):
self.advancedStyleComboBox.setItemText(LayoutStyle.VersePerSlide, UiStrings().VersePerSlide)
self.advancedStyleComboBox.setItemText(LayoutStyle.VersePerLine, UiStrings().VersePerLine)
self.advancedStyleComboBox.setItemText(LayoutStyle.Continuous, UiStrings().Continuous)
+ self.advancedClearButton.setToolTip(translate('BiblesPlugin.MediaItem', 'Clear the search results.'))
self.advancedLockButton.setToolTip(translate('BiblesPlugin.MediaItem',
'Toggle to keep or clear the previous results.'))
self.advancedSearchButton.setText(UiStrings().Search)
@@ -309,6 +323,9 @@ class BibleMediaItem(MediaManagerItem):
self.plugin.manager.media = self
self.load_bibles()
self.quick_search_edit.set_search_types([
+ (BibleSearch.Combined, ':/bibles/bibles_search_combined.png',
+ translate('BiblesPlugin.MediaItem', 'Text or Reference'),
+ translate('BiblesPlugin.MediaItem', 'Text or Reference...')),
(BibleSearch.Reference, ':/bibles/bibles_search_reference.png',
translate('BiblesPlugin.MediaItem', 'Scripture Reference'),
translate('BiblesPlugin.MediaItem', 'Search Scripture Reference...')),
@@ -424,18 +441,24 @@ class BibleMediaItem(MediaManagerItem):
def update_auto_completer(self):
"""
This updates the bible book completion list for the search field. The completion depends on the bible. It is
- only updated when we are doing a reference search, otherwise the auto completion list is removed.
+ only updated when we are doing reference or combined search, in text search the completion list is removed.
"""
log.debug('update_auto_completer')
- # Save the current search type to the configuration.
- Settings().setValue('{section}/last search type'.format(section=self.settings_section),
- self.quick_search_edit.current_search_type())
+ # Save the current search type to the configuration. If setting for automatically resetting the search type to
+ # Combined is enabled, use that otherwise use the currently selected search type.
+ # Note: This setting requires a restart to take effect.
+ if Settings().value(self.settings_section + '/reset to combined quick search'):
+ Settings().setValue('{section}/last search type'.format(section=self.settings_section),
+ BibleSearch.Combined)
+ else:
+ Settings().setValue('{section}/last search type'.format(section=self.settings_section),
+ self.quick_search_edit.current_search_type())
# Save the current bible to the configuration.
Settings().setValue('{section}/quick bible'.format(section=self.settings_section),
self.quickVersionComboBox.currentText())
books = []
- # We have to do a 'Reference Search'.
- if self.quick_search_edit.current_search_type() == BibleSearch.Reference:
+ # We have to do a 'Reference Search' (Or as part of Combined Search).
+ if self.quick_search_edit.current_search_type() is not BibleSearch.Text:
bibles = self.plugin.manager.get_bibles()
bible = self.quickVersionComboBox.currentText()
if bible:
@@ -525,7 +548,15 @@ class BibleMediaItem(MediaManagerItem):
self.advancedTab.setVisible(True)
self.advanced_book_combo_box.setFocus()
+ def on_clear_button(self):
+ # Clear the list, then set the "No search Results" message, then clear the text field and give it focus.
+ self.list_view.clear()
+ self.check_search_result()
+ self.quick_search_edit.clear()
+ self.quick_search_edit.setFocus()
+
def on_lock_button_toggled(self, checked):
+ self.quick_search_edit.setFocus()
if checked:
self.sender().setIcon(self.lock_icon)
else:
@@ -652,10 +683,120 @@ class BibleMediaItem(MediaManagerItem):
self.check_search_result()
self.application.set_normal_cursor()
+ def on_quick_reference_search(self):
+ """
+ We are doing a 'Reference Search'.
+ This search is called on def on_quick_search_button by Quick Reference and Combined Searches.
+ """
+ # Set Bibles to use the text input from Quick search field.
+ bible = self.quickVersionComboBox.currentText()
+ second_bible = self.quickSecondComboBox.currentText()
+ """
+ Get input from field and replace 'A-Z + . ' with ''
+ This will check if field has any '.' after A-Z and removes them. Eg. Gen. 1 = Ge 1 = Genesis 1
+ If Book name has '.' after number. eg. 1. Genesis, the search fails without the dot, and vice versa.
+ A better solution would be to make '.' optional in the search results. Current solution was easier to code.
+ """
+ text = self.quick_search_edit.text()
+ text = re.sub('\D[.]\s', ' ', text)
+ # This is triggered on reference search, use the search from manager.py
+ if self.quick_search_edit.current_search_type() != BibleSearch.Text:
+ self.search_results = self.plugin.manager.get_verses(bible, text)
+ if second_bible and self.search_results:
+ self.second_search_results = \
+ self.plugin.manager.get_verses(second_bible, text, self.search_results[0].book.book_reference_id)
+
+ def on_quick_text_search(self):
+ """
+ We are doing a 'Text Search'.
+ This search is called on def on_quick_search_button by Quick Text and Combined Searches.
+ """
+ # Set Bibles to use the text input from Quick search field.
+ bible = self.quickVersionComboBox.currentText()
+ second_bible = self.quickSecondComboBox.currentText()
+ text = self.quick_search_edit.text()
+ # If Text search ends with "," OpenLP will crash, prevent this from happening by removing all ","s.
+ text = re.sub('[,]', '', text)
+ self.application.set_busy_cursor()
+ # Get Bibles list
+ bibles = self.plugin.manager.get_bibles()
+ # Add results to "search_results"
+ self.search_results = self.plugin.manager.verse_search(bible, second_bible, text)
+ if second_bible and self.search_results:
+ # new_search_results is needed to make sure 2nd bible contains all verses. (And counting them on error)
+ text = []
+ new_search_results = []
+ count = 0
+ passage_not_found = False
+ # Search second bible for results of search_results to make sure everythigns there.
+ # Count all the unfound passages.
+ for verse in self.search_results:
+ db_book = bibles[second_bible].get_book_by_book_ref_id(verse.book.book_reference_id)
+ if not db_book:
+ log.debug('Passage "{name} {chapter:d}:{verse:d}" not found in '
+ 'Second Bible'.format(name=verse.book.name, chapter=verse.chapter, verse=verse.verse))
+ passage_not_found = True
+ count += 1
+ continue
+ new_search_results.append(verse)
+ text.append((verse.book.book_reference_id, verse.chapter, verse.verse, verse.verse))
+ if passage_not_found:
+ # This is for the 2nd Bible.
+ self.main_window.information_message(
+ translate('BiblesPlugin.MediaItem', 'Information'),
+ translate('BiblesPlugin.MediaItem', 'The second Bible does not contain all the verses '
+ 'that are in the main Bible.\nOnly verses found in both Bibles'
+ ' will be shown.\n\n{count:d} verses have not been included '
+ 'in the results.').format(count=count))
+ # Join the searches so only verses that are found on both Bibles are shown.
+ self.search_results = new_search_results
+ self.second_search_results = bibles[second_bible].get_verses(text)
+
+ def on_quick_text_search_while_typing(self):
+ """
+ We are doing a 'Text Search' while typing
+ Call the verse_search_while_typing from manager.py
+ It does not show web bible errors while typing.
+ (It would result in the error popping every time a char is entered or removed)
+ """
+ # Set Bibles to use the text input from Quick search field.
+ bible = self.quickVersionComboBox.currentText()
+ second_bible = self.quickSecondComboBox.currentText()
+ text = self.quick_search_edit.text()
+ # If Text search ends with "," OpenLP will crash, prevent this from happening by removing all ","s.
+ text = re.sub('[,]', '', text)
+ self.application.set_busy_cursor()
+ # Get Bibles list
+ bibles = self.plugin.manager.get_bibles()
+ # Add results to "search_results"
+ self.search_results = self.plugin.manager.verse_search_while_typing(bible, second_bible, text)
+ if second_bible and self.search_results:
+ # new_search_results is needed to make sure 2nd bible contains all verses. (And counting them on error)
+ text = []
+ new_search_results = []
+ count = 0
+ passage_not_found = False
+ # Search second bible for results of search_results to make sure everythigns there.
+ # Count all the unfound passages. Even thou no error is shown, this needs to be done or
+ # the code breaks later on.
+ for verse in self.search_results:
+ db_book = bibles[second_bible].get_book_by_book_ref_id(verse.book.book_reference_id)
+ if not db_book:
+ log.debug('Passage ("{versebookname}","{versechapter}","{verseverse}") not found in Second Bible'
+ .format(versebookname=verse.book.name, versechapter='verse.chapter',
+ verseverse=verse.verse))
+ count += 1
+ continue
+ new_search_results.append(verse)
+ text.append((verse.book.book_reference_id, verse.chapter, verse.verse, verse.verse))
+ # Join the searches so only verses that are found on both Bibles are shown.
+ self.search_results = new_search_results
+ self.second_search_results = bibles[second_bible].get_verses(text)
+
def on_quick_search_button(self):
"""
- Does a quick search and saves the search results. Quick search can either be "Reference Search" or
- "Text Search".
+ This triggers the proper Quick search based on which search type is used.
+ "Eg. "Reference Search", "Text Search" or "Combined search".
"""
log.debug('Quick Search Button clicked')
self.quickSearchButton.setEnabled(False)
@@ -664,41 +805,68 @@ class BibleMediaItem(MediaManagerItem):
second_bible = self.quickSecondComboBox.currentText()
text = self.quick_search_edit.text()
if self.quick_search_edit.current_search_type() == BibleSearch.Reference:
- # We are doing a 'Reference Search'.
- self.search_results = self.plugin.manager.get_verses(bible, text)
- if second_bible and self.search_results:
- self.second_search_results = \
- self.plugin.manager.get_verses(second_bible, text, self.search_results[0].book.book_reference_id)
- else:
- # We are doing a 'Text Search'.
- self.application.set_busy_cursor()
- bibles = self.plugin.manager.get_bibles()
- self.search_results = self.plugin.manager.verse_search(bible, second_bible, text)
- if second_bible and self.search_results:
- text = []
- new_search_results = []
- count = 0
- passage_not_found = False
- for verse in self.search_results:
- db_book = bibles[second_bible].get_book_by_book_ref_id(verse.book.book_reference_id)
- if not db_book:
- log.debug('Passage "{name} {chapter:d}:{verse:d}" not found in '
- 'Second Bible'.format(name=verse.book.name, chapter=verse.chapter, verse=verse.verse))
- passage_not_found = True
- count += 1
- continue
- new_search_results.append(verse)
- text.append((verse.book.book_reference_id, verse.chapter, verse.verse, verse.verse))
- if passage_not_found:
- QtWidgets.QMessageBox.information(
- self, translate('BiblesPlugin.MediaItem', 'Information'),
- translate('BiblesPlugin.MediaItem',
- 'The second Bible does not contain all the verses that are in the main Bible. '
- 'Only verses found in both Bibles will be shown. {count:d} verses have not been '
- 'included in the results.').format(count=count),
- QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Ok))
- self.search_results = new_search_results
- self.second_search_results = bibles[second_bible].get_verses(text)
+ # We are doing a 'Reference Search'. (Get script from def on_quick_reference_search)
+ self.on_quick_reference_search()
+ # Get reference separators from settings.
+ if not self.search_results:
+ reference_separators = {
+ 'verse': get_reference_separator('sep_v_display'),
+ 'range': get_reference_separator('sep_r_display'),
+ 'list': get_reference_separator('sep_l_display')}
+ self.main_window.information_message(
+ translate('BiblesPlugin.BibleManager', 'Scripture Reference Error'),
+ translate('BiblesPlugin.BibleManager', 'OpenLP couldn’t find anything '
+ 'with your search.
'
+ 'Please make sure that your reference follows '
+ 'one of these patterns:
%s'
+ % UiStrings().BibleScriptureError % reference_separators))
+ elif self.quick_search_edit.current_search_type() == BibleSearch.Text:
+ # We are doing a 'Text Search'. (Get script from def on_quick_text_search)
+ self.on_quick_text_search()
+ if not self.search_results and len(text) - text.count(' ') < 3 and bible:
+ self.main_window.information_message(
+ UiStrings().BibleShortSearchTitle,
+ UiStrings().BibleShortSearch)
+ elif self.quick_search_edit.current_search_type() == BibleSearch.Combined:
+ # We are doing a 'Combined search'. Starting with reference search.
+ # Perform only if text contains any numbers
+ if (char.isdigit() for char in text):
+ self.on_quick_reference_search()
+ """
+ If results are found, search will be finalized.
+ This check needs to be here in order to avoid duplicate errors.
+ If keyword is shorter than 3 (not including spaces), message is given. It's actually possible to find
+ verses with less than 3 chars (Eg. G1 = Genesis 1) thus this error is not shown if any results are found.
+ if no Bibles are installed, this message is not shown - "No bibles" message is shown instead.
+ """
+ if not self.search_results and len(text) - text.count(' ') < 3 and bible:
+ self.main_window.information_message(
+ UiStrings().BibleShortSearchTitle,
+ UiStrings().BibleShortSearch)
+ if not self.search_results and len(text) - text.count(' ') > 2 and bible:
+ # Text search starts here if no reference was found and keyword is longer than 2.
+ # > 2 check is required in order to avoid duplicate error messages for short keywords.
+ self.on_quick_text_search()
+ if not self.search_results and not \
+ Settings().value(self.settings_section + '/hide combined quick error'):
+ self.application.set_normal_cursor()
+ # Reference separators need to be defined both, in here and on reference search,
+ # error won't work if they are left out from one.
+ reference_separators = {
+ 'verse': get_reference_separator('sep_v_display'),
+ 'range': get_reference_separator('sep_r_display'),
+ 'list': get_reference_separator('sep_l_display')}
+ self.main_window.information_message(translate('BiblesPlugin.BibleManager', 'Nothing found'),
+ translate('BiblesPlugin.BibleManager',
+ 'OpenLP couldn’t find anything with your'
+ ' search.
If you tried to search'
+ ' with Scripture Reference, please make
sure'
+ ' that your reference follows one of these'
+ ' patterns:
%s'
+ % UiStrings().BibleScriptureError %
+ reference_separators))
+ # Finalizing the search
+ # List is cleared if not locked, results are listed, button is set available, cursor is set to normal.
if not self.quickLockButton.isChecked():
self.list_view.clear()
if self.list_view.count() != 0 and self.search_results:
@@ -709,6 +877,99 @@ class BibleMediaItem(MediaManagerItem):
self.check_search_result()
self.application.set_normal_cursor()
+ def on_quick_search_while_typing(self):
+ """
+ This function is called when "Search as you type" is enabled for Bibles.
+ It is basically the same thing as "on_quick_search_search" but all the error messages are removed.
+ This also has increased min len for text search for performance reasons.
+ For commented version, please visit def on_quick_search_button.
+ """
+ bible = self.quickVersionComboBox.currentText()
+ second_bible = self.quickSecondComboBox.currentText()
+ text = self.quick_search_edit.text()
+ if self.quick_search_edit.current_search_type() == BibleSearch.Combined:
+ # If text has no numbers, auto search limit is min 8 characters for performance reasons.
+ # If you change this value, also change it in biblestab.py (Count) in enabling search while typing.
+ if (char.isdigit() for char in text) and len(text) > 2:
+ self.on_quick_reference_search()
+ if not self.search_results and len(text) > 7:
+ self.on_quick_text_search_while_typing()
+ elif self.quick_search_edit.current_search_type() == BibleSearch.Reference:
+ self.on_quick_reference_search()
+ elif self.quick_search_edit.current_search_type() == BibleSearch.Text:
+ if len(text) > 7:
+ self.on_quick_text_search_while_typing()
+ if not self.quickLockButton.isChecked():
+ self.list_view.clear()
+ if self.list_view.count() != 0 and self.search_results:
+ self.__check_second_bible(bible, second_bible)
+ elif self.search_results:
+ self.display_results(bible, second_bible)
+ self.check_search_result()
+ self.application.set_normal_cursor()
+
+ def on_search_text_edit_changed(self):
+ """
+ If search automatically while typing is enabled, perform the search and list results when conditions are met.
+ """
+ if Settings().value('bibles/is search while typing enabled'):
+ text = self.quick_search_edit.text()
+ """
+ Use Regex for finding space + number in reference search and space + 2 characters in text search.
+ Also search for two characters (Searches require at least two sets of two characters)
+ These are used to prevent bad search queries from starting. (Long/crashing queries)
+ """
+ space_and_digit_reference = re.compile(' \d')
+ two_chars_text = re.compile('\S\S')
+ space_and_two_chars_text = re.compile(' \S\S')
+ # Turn this into a format that may be used in if statement.
+ count_space_digit_reference = space_and_digit_reference.findall(text)
+ count_two_chars_text = two_chars_text.findall(text)
+ count_spaces_two_chars_text = space_and_two_chars_text.findall(text)
+ """
+ The Limit is required for setting the proper "No items found" message.
+ "Limit" is also hard coded to on_quick_search_while_typing, it must be there to avoid bad search
+ performance. Limit 8 = Text search, 3 = Reference search.
+ """
+ limit = 8
+ if self.quick_search_edit.current_search_type() == BibleSearch.Combined:
+ if len(count_space_digit_reference) != 0:
+ limit = 3
+ elif self.quick_search_edit.current_search_type() == BibleSearch.Reference:
+ limit = 3
+ """
+ If text is empty, clear the list.
+ else: Start by checking if the search is suitable for "Search while typing"
+ """
+ if len(text) == 0:
+ if not self.quickLockButton.isChecked():
+ self.list_view.clear()
+ self.check_search_result()
+ else:
+ if limit == 3 and (len(text) < limit or len(count_space_digit_reference) == 0):
+ if not self.quickLockButton.isChecked():
+ self.list_view.clear()
+ self.check_search_result()
+ elif (limit == 8 and (len(text) < limit or len(count_spaces_two_chars_text) == 0 or
+ len(count_two_chars_text) < 2)):
+ if not self.quickLockButton.isChecked():
+ self.list_view.clear()
+ self.check_search_result_search_while_typing_short()
+ else:
+ """
+ Start search if no chars are entered or deleted for 0.2 s
+ If no Timer is set, Text search will break the search by sending repeative search Quaries on
+ all chars. Use the self.on_quick_search_while_typing, this does not contain any error messages.
+ """
+ self.search_timer = ()
+ if self.search_timer:
+ self.search_timer.stop()
+ self.search_timer.deleteLater()
+ self.search_timer = QtCore.QTimer()
+ self.search_timer.timeout.connect(self.on_quick_search_while_typing)
+ self.search_timer.setSingleShot(True)
+ self.search_timer.start(200)
+
def display_results(self, bible, second_bible=''):
"""
Displays the search results in the media manager. All data needed for further action is saved for/in each row.
diff --git a/openlp/plugins/bibles/lib/osis.py b/openlp/plugins/bibles/lib/osis.py
index 85aea70b3..4b217022e 100644
--- a/openlp/plugins/bibles/lib/osis.py
+++ b/openlp/plugins/bibles/lib/osis.py
@@ -23,7 +23,7 @@
import logging
from lxml import etree
-from openlp.core.common import translate, trace_error_handler
+from openlp.core.common import languages, translate, trace_error_handler
from openlp.plugins.bibles.lib.db import BibleDB, BiblesResourcesDB
from openlp.core.lib.ui import critical_error_message_box
@@ -62,9 +62,11 @@ class OSISBible(BibleDB):
namespace = {'ns': 'http://www.bibletechnologies.net/2003/OSIS/namespace'}
# Find bible language
language_id = None
- language = osis_bible_tree.xpath("//ns:osisText/@xml:lang", namespaces=namespace)
- if language:
- language_id = BiblesResourcesDB.get_language(language[0])
+ lang = osis_bible_tree.xpath("//ns:osisText/@xml:lang", namespaces=namespace)
+ if lang:
+ language = languages.get_language(lang[0])
+ if hasattr(language, 'id'):
+ language_id = language.id
# The language couldn't be detected, ask the user
if not language_id:
language_id = self.get_language(bible_name)
diff --git a/openlp/plugins/bibles/lib/sword.py b/openlp/plugins/bibles/lib/sword.py
index 77f08cb47..b37e9b583 100644
--- a/openlp/plugins/bibles/lib/sword.py
+++ b/openlp/plugins/bibles/lib/sword.py
@@ -23,7 +23,7 @@
import logging
from pysword import modules
-from openlp.core.common import translate
+from openlp.core.common import languages, translate
from openlp.core.lib.ui import critical_error_message_box
from openlp.plugins.bibles.lib.db import BibleDB, BiblesResourcesDB
@@ -57,9 +57,12 @@ class SwordBible(BibleDB):
pysword_modules = modules.SwordModules(self.sword_path)
pysword_module_json = pysword_modules.parse_modules()[self.sword_key]
bible = pysword_modules.get_bible_from_module(self.sword_key)
+ language_id = None
language = pysword_module_json['lang']
language = language[language.find('.') + 1:]
- language_id = BiblesResourcesDB.get_language(language)['id']
+ language = languages.get_language(language)
+ if hasattr(language, 'id'):
+ language_id = language.id
self.save_meta('language_id', language_id)
books = bible.get_structure().get_books()
# Count number of books
diff --git a/openlp/plugins/bibles/lib/zefania.py b/openlp/plugins/bibles/lib/zefania.py
index 482d98107..96ab69072 100644
--- a/openlp/plugins/bibles/lib/zefania.py
+++ b/openlp/plugins/bibles/lib/zefania.py
@@ -21,9 +21,9 @@
###############################################################################
import logging
-from lxml import etree, objectify
+from lxml import etree
-from openlp.core.common import translate
+from openlp.core.common import languages, translate
from openlp.core.lib.ui import critical_error_message_box
from openlp.plugins.bibles.lib.db import BibleDB, BiblesResourcesDB
@@ -62,7 +62,9 @@ class ZefaniaBible(BibleDB):
language_id = None
language = zefania_bible_tree.xpath("/XMLBIBLE/INFORMATION/language/text()")
if language:
- language_id = BiblesResourcesDB.get_language(language[0])
+ language = languages.get_language(language[0])
+ if hasattr(language, 'id'):
+ language_id = language.id
# The language couldn't be detected, ask the user
if not language_id:
language_id = self.get_language(bible_name)
diff --git a/openlp/plugins/bibles/resources/bibles_resources.sqlite b/openlp/plugins/bibles/resources/bibles_resources.sqlite
index 8f1777124..0db28194e 100644
Binary files a/openlp/plugins/bibles/resources/bibles_resources.sqlite and b/openlp/plugins/bibles/resources/bibles_resources.sqlite differ
diff --git a/openlp/plugins/songs/lib/importers/easyslides.py b/openlp/plugins/songs/lib/importers/easyslides.py
index 907a6c90f..2ae489208 100644
--- a/openlp/plugins/songs/lib/importers/easyslides.py
+++ b/openlp/plugins/songs/lib/importers/easyslides.py
@@ -46,7 +46,7 @@ class EasySlidesImport(SongImport):
def do_import(self):
log.info('Importing EasySlides XML file {source}'.format(source=self.import_source))
- parser = etree.XMLParser(remove_blank_text=True)
+ parser = etree.XMLParser(remove_blank_text=True, recover=True)
parsed_file = etree.parse(self.import_source, parser)
xml = etree.tostring(parsed_file).decode()
song_xml = objectify.fromstring(xml)
diff --git a/openlp/plugins/songs/lib/importers/videopsalm.py b/openlp/plugins/songs/lib/importers/videopsalm.py
index 25fd4d8eb..b536dd678 100644
--- a/openlp/plugins/songs/lib/importers/videopsalm.py
+++ b/openlp/plugins/songs/lib/importers/videopsalm.py
@@ -73,6 +73,14 @@ class VideoPsalmImport(SongImport):
processed_content += c
c = next(file_content_it)
processed_content += '"' + c
+ # Remove control characters
+ elif (c < chr(32)):
+ processed_content += ' '
+ # Handle escaped characters
+ elif c == '\\':
+ processed_content += c
+ c = next(file_content_it)
+ processed_content += c
else:
processed_content += c
songbook = json.loads(processed_content.strip())
diff --git a/openlp/plugins/songs/lib/openlyricsxml.py b/openlp/plugins/songs/lib/openlyricsxml.py
index 5adffb300..e2964661d 100644
--- a/openlp/plugins/songs/lib/openlyricsxml.py
+++ b/openlp/plugins/songs/lib/openlyricsxml.py
@@ -458,7 +458,7 @@ class OpenLyrics(object):
self._add_tag_to_formatting(tag, tags_element)
# Replace end tags.
for tag in end_tags:
- text = text.replace('{/{tag}}}'.format(tag=tag), '')
+ text = text.replace('{{{tag}}}'.format(tag=tag), '')
# Replace \n with
.
text = text.replace('\n', '
')
element = etree.XML('{text}'.format(text=text))
@@ -643,7 +643,7 @@ class OpenLyrics(object):
# Append text from tail and add formatting end tag.
# TODO: Verify format() with template variables
if element.tag == NSMAP % 'tag' and use_endtag:
- text += '{/{name}}}'.format(name=element.get('name'))
+ text += '{{{name}}}'.format(name=element.get('name'))
# Append text from tail.
if element.tail:
text += element.tail
diff --git a/resources/images/bibles_search_clear.png b/resources/images/bibles_search_clear.png
new file mode 100644
index 000000000..551ce7c96
Binary files /dev/null and b/resources/images/bibles_search_clear.png differ
diff --git a/resources/images/bibles_search_combined.png b/resources/images/bibles_search_combined.png
new file mode 100644
index 000000000..161b3fcd6
Binary files /dev/null and b/resources/images/bibles_search_combined.png differ
diff --git a/resources/images/openlp-2.qrc b/resources/images/openlp-2.qrc
index f2619b0c7..b45cc745d 100644
--- a/resources/images/openlp-2.qrc
+++ b/resources/images/openlp-2.qrc
@@ -30,9 +30,11 @@
image_new_group.png
+ bibles_search_combined.png
bibles_search_text.png
bibles_search_reference.png
bibles_upgrade_alert.png
+ bibles_search_clear.png
bibles_search_unlock.png
bibles_search_lock.png
diff --git a/scripts/jenkins_script.py b/scripts/jenkins_script.py
index 0a528d8a0..e119e144b 100755
--- a/scripts/jenkins_script.py
+++ b/scripts/jenkins_script.py
@@ -63,9 +63,10 @@ class OpenLPJobs(object):
Branch_Windows_Interface = 'Branch-04b-Windows_Interface_Tests'
Branch_PEP = 'Branch-05a-Code_Analysis'
Branch_Coverage = 'Branch-05b-Test_Coverage'
+ Branch_Pylint = 'Branch-05c-Code_Analysis2'
Jobs = [Branch_Pull, Branch_Functional, Branch_Interface, Branch_Windows_Functional, Branch_Windows_Interface,
- Branch_PEP, Branch_Coverage]
+ Branch_PEP, Branch_Coverage, Branch_Pylint]
class Colour(object):
diff --git a/tests/functional/openlp_core_common/test_languages.py b/tests/functional/openlp_core_common/test_languages.py
new file mode 100644
index 000000000..fa6756d08
--- /dev/null
+++ b/tests/functional/openlp_core_common/test_languages.py
@@ -0,0 +1,109 @@
+# -*- coding: utf-8 -*-
+# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
+
+###############################################################################
+# OpenLP - Open Source Lyrics Projection #
+# --------------------------------------------------------------------------- #
+# Copyright (c) 2008-2016 OpenLP Developers #
+# --------------------------------------------------------------------------- #
+# This program is free software; you can redistribute it and/or modify it #
+# under the terms of the GNU General Public License as published by the Free #
+# Software Foundation; version 2 of the License. #
+# #
+# This program is distributed in the hope that it will be useful, but WITHOUT #
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
+# more details. #
+# #
+# You should have received a copy of the GNU General Public License along #
+# with this program; if not, write to the Free Software Foundation, Inc., 59 #
+# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
+###############################################################################
+"""
+Package to test the openlp.core.lib.languages package.
+"""
+from unittest import TestCase
+
+from openlp.core.common import languages
+
+
+class TestLanguages(TestCase):
+
+ def languages_type_test(self):
+ """
+ Test the languages variable type
+ """
+
+ # GIVEN: The languages module
+ # WHEN: Accessing the languages variable
+ # THEN: It should be of type list
+ self.assertIsInstance(languages.languages, list, 'languages.languages should be of type list')
+
+ def language_selection_languages_type_test(self):
+ """
+ Test the selection of a language
+ """
+
+ # GIVEN: A list of languages from the languages module
+ # WHEN: Selecting the first item
+ language = languages.languages[0]
+
+ # THEN: It should be an instance of the Language namedtuple
+ self.assertIsInstance(language, languages.Language)
+ self.assertEqual(language.id, 1)
+ self.assertEqual(language.name, '(Afan) Oromo')
+ self.assertEqual(language.code, 'om')
+
+ def get_language_name_test(self):
+ """
+ Test get_language() when supplied with a language name.
+ """
+
+ # GIVEN: A language name, in capitals
+ # WHEN: Calling get_language with it
+ language = languages.get_language('YORUBA')
+
+ # THEN: The Language found using that name should be returned
+ self.assertIsInstance(language, languages.Language)
+ self.assertEqual(language.id, 137)
+ self.assertEqual(language.name, 'Yoruba')
+ self.assertEqual(language.code, 'yo')
+
+ def get_language_code_test(self):
+ """
+ Test get_language() when supplied with a language code.
+ """
+
+ # GIVEN: A language code in capitals
+ # WHEN: Calling get_language with it
+ language = languages.get_language('IA')
+
+ # THEN: The Language found using that code should be returned
+ self.assertIsInstance(language, languages.Language)
+ self.assertEqual(language.id, 51)
+ self.assertEqual(language.name, 'Interlingua')
+ self.assertEqual(language.code, 'ia')
+
+ def get_language_invalid_test(self):
+ """
+ Test get_language() when supplied with a string which is not a valid language name or code.
+ """
+
+ # GIVEN: A language code
+ # WHEN: Calling get_language with it
+ language = languages.get_language('qwerty')
+
+ # THEN: None should be returned
+ self.assertIsNone(language)
+
+ def get_language_invalid__none_test(self):
+ """
+ Test get_language() when supplied with a string which is not a valid language name or code.
+ """
+
+ # GIVEN: A language code
+ # WHEN: Calling get_language with it
+ language = languages.get_language(None)
+
+ # THEN: None should be returned
+ self.assertIsNone(language)
diff --git a/tests/functional/openlp_core_lib/test_theme.py b/tests/functional/openlp_core_lib/test_theme.py
index c006abb78..db6b78d02 100644
--- a/tests/functional/openlp_core_lib/test_theme.py
+++ b/tests/functional/openlp_core_lib/test_theme.py
@@ -22,43 +22,82 @@
"""
Package to test the openlp.core.lib.theme package.
"""
-from tests.functional import MagicMock, patch
from unittest import TestCase
+import os
from openlp.core.lib.theme import ThemeXML
-class TestTheme(TestCase):
+class TestThemeXML(TestCase):
"""
- Test the functions in the Theme module
+ Test the ThemeXML class
"""
- def setUp(self):
- """
- Create the UI
- """
- pass
-
- def tearDown(self):
- """
- Delete all the C++ objects at the end so that we don't have a segfault
- """
- pass
-
def test_new_theme(self):
"""
- Test the theme creation - basic test
+ Test the ThemeXML constructor
"""
- # GIVEN: A new theme
-
- # WHEN: A theme is created
+ # GIVEN: The ThemeXML class
+ # WHEN: A theme object is created
default_theme = ThemeXML()
- # THEN: We should get some default behaviours
- self.assertTrue(default_theme.background_border_color == '#000000', 'The theme should have a black border')
- self.assertTrue(default_theme.background_type == 'solid', 'The theme should have a solid backgrounds')
- self.assertTrue(default_theme.display_vertical_align == 0,
- 'The theme should have a display_vertical_align of 0')
- self.assertTrue(default_theme.font_footer_name == "Arial",
- 'The theme should have a font_footer_name of Arial')
- self.assertTrue(default_theme.font_main_bold is False, 'The theme should have a font_main_bold of false')
- self.assertTrue(len(default_theme.__dict__) == 47, 'The theme should have 47 variables')
+ # THEN: The default values should be correct
+ self.assertEqual('#000000', default_theme.background_border_color,
+ 'background_border_color should be "#000000"')
+ self.assertEqual('solid', default_theme.background_type, 'background_type should be "solid"')
+ self.assertEqual(0, default_theme.display_vertical_align, 'display_vertical_align should be 0')
+ self.assertEqual('Arial', default_theme.font_footer_name, 'font_footer_name should be "Arial"')
+ self.assertFalse(default_theme.font_main_bold, 'font_main_bold should be False')
+ self.assertEqual(47, len(default_theme.__dict__), 'The theme should have 47 attributes')
+
+ def test_expand_json(self):
+ """
+ Test the expand_json method
+ """
+ # GIVEN: A ThemeXML object and some JSON to "expand"
+ theme = ThemeXML()
+ theme_json = {
+ 'background': {
+ 'border_color': '#000000',
+ 'type': 'solid'
+ },
+ 'display': {
+ 'vertical_align': 0
+ },
+ 'font': {
+ 'footer': {
+ 'bold': False
+ },
+ 'main': {
+ 'name': 'Arial'
+ }
+ }
+ }
+
+ # WHEN: ThemeXML.expand_json() is run
+ theme.expand_json(theme_json)
+
+ # THEN: The attributes should be set on the object
+ self.assertEqual('#000000', theme.background_border_color, 'background_border_color should be "#000000"')
+ self.assertEqual('solid', theme.background_type, 'background_type should be "solid"')
+ self.assertEqual(0, theme.display_vertical_align, 'display_vertical_align should be 0')
+ self.assertFalse(theme.font_footer_bold, 'font_footer_bold should be False')
+ self.assertEqual('Arial', theme.font_main_name, 'font_main_name should be "Arial"')
+
+ def test_extend_image_filename(self):
+ """
+ Test the extend_image_filename method
+ """
+ # GIVEN: A theme object
+ theme = ThemeXML()
+ theme.theme_name = 'MyBeautifulTheme '
+ theme.background_filename = ' video.mp4'
+ theme.background_type = 'video'
+ path = os.path.expanduser('~')
+
+ # WHEN: ThemeXML.extend_image_filename is run
+ theme.extend_image_filename(path)
+
+ # THEN: The filename of the background should be correct
+ expected_filename = os.path.join(path, 'MyBeautifulTheme', 'video.mp4')
+ self.assertEqual(expected_filename, theme.background_filename)
+ self.assertEqual('MyBeautifulTheme', theme.theme_name)
diff --git a/tests/functional/openlp_plugins/bibles/test_db.py b/tests/functional/openlp_plugins/bibles/test_db.py
new file mode 100644
index 000000000..2807a8a3e
--- /dev/null
+++ b/tests/functional/openlp_plugins/bibles/test_db.py
@@ -0,0 +1,87 @@
+# -*- coding: utf-8 -*-
+# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
+
+###############################################################################
+# OpenLP - Open Source Lyrics Projection #
+# --------------------------------------------------------------------------- #
+# Copyright (c) 2008-2016 OpenLP Developers #
+# --------------------------------------------------------------------------- #
+# This program is free software; you can redistribute it and/or modify it #
+# under the terms of the GNU General Public License as published by the Free #
+# Software Foundation; version 2 of the License. #
+# #
+# This program is distributed in the hope that it will be useful, but WITHOUT #
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
+# more details. #
+# #
+# You should have received a copy of the GNU General Public License along #
+# with this program; if not, write to the Free Software Foundation, Inc., 59 #
+# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
+###############################################################################
+"""
+This module contains tests for the db submodule of the Bibles plugin.
+"""
+
+from unittest import TestCase
+
+from openlp.plugins.bibles.lib.db import BibleDB
+from tests.functional import MagicMock, patch
+
+
+class TestBibleDB(TestCase):
+ """
+ Test the functions in the BibleDB class.
+ """
+
+ def test_get_language_canceled(self):
+ """
+ Test the BibleDB.get_language method when the user rejects the dialog box
+ """
+ # GIVEN: A mocked LanguageForm with an exec method which returns QtDialog.Rejected and an instance of BibleDB
+ with patch('openlp.plugins.bibles.lib.db.BibleDB._setup'),\
+ patch('openlp.plugins.bibles.forms.LanguageForm') as mocked_language_form:
+
+ # The integer value of QtDialog.Rejected is 0. Using the enumeration causes a seg fault for some reason
+ mocked_language_form_instance = MagicMock(**{'exec.return_value': 0})
+ mocked_language_form.return_value = mocked_language_form_instance
+ mocked_parent = MagicMock()
+ instance = BibleDB(mocked_parent)
+ mocked_wizard = MagicMock()
+ instance.wizard = mocked_wizard
+
+ # WHEN: Calling get_language()
+ result = instance.get_language()
+
+ # THEN: get_language() should return False
+ mocked_language_form.assert_called_once_with(mocked_wizard)
+ mocked_language_form_instance.exec.assert_called_once_with(None)
+ self.assertFalse(result, 'get_language() should return False if the user rejects the dialog box')
+
+ def test_get_language_accepted(self):
+ """
+ Test the BibleDB.get_language method when the user accepts the dialog box
+ """
+ # GIVEN: A mocked LanguageForm with an exec method which returns QtDialog.Accepted an instance of BibleDB and
+ # a combobox with the selected item data as 10
+ with patch('openlp.plugins.bibles.lib.db.BibleDB._setup'), \
+ patch('openlp.plugins.bibles.lib.db.BibleDB.save_meta'), \
+ patch('openlp.plugins.bibles.forms.LanguageForm') as mocked_language_form:
+
+ # The integer value of QtDialog.Accepted is 1. Using the enumeration causes a seg fault for some reason
+ mocked_language_form_instance = MagicMock(**{'exec.return_value': 1,
+ 'language_combo_box.itemData.return_value': 10})
+ mocked_language_form.return_value = mocked_language_form_instance
+ mocked_parent = MagicMock()
+ instance = BibleDB(mocked_parent)
+ mocked_wizard = MagicMock()
+ instance.wizard = mocked_wizard
+
+ # WHEN: Calling get_language()
+ result = instance.get_language('Bible Name')
+
+ # THEN: get_language() should return the id of the selected language in the combo box
+ mocked_language_form.assert_called_once_with(mocked_wizard)
+ mocked_language_form_instance.exec.assert_called_once_with('Bible Name')
+ self.assertEqual(result, 10, 'get_language() should return the id of the language the user has chosen when '
+ 'they accept the dialog box')
diff --git a/tests/functional/openlp_plugins/bibles/test_mediaitem.py b/tests/functional/openlp_plugins/bibles/test_mediaitem.py
index f820c7a64..05418f177 100644
--- a/tests/functional/openlp_plugins/bibles/test_mediaitem.py
+++ b/tests/functional/openlp_plugins/bibles/test_mediaitem.py
@@ -23,6 +23,7 @@
This module contains tests for the lib submodule of the Presentations plugin.
"""
from unittest import TestCase
+from openlp.core.common import Registry
from openlp.plugins.bibles.lib.mediaitem import BibleMediaItem
from tests.functional import MagicMock, patch
from tests.helpers.testmixin import TestMixin
@@ -41,6 +42,9 @@ class TestMediaItem(TestCase, TestMixin):
patch('openlp.plugins.bibles.lib.mediaitem.BibleMediaItem.setup_item'):
self.media_item = BibleMediaItem(None, MagicMock())
self.setup_application()
+ self.mocked_main_window = MagicMock()
+ Registry.create()
+ Registry().register('main_window', self.mocked_main_window)
def test_display_results_no_results(self):
"""
@@ -109,3 +113,40 @@ class TestMediaItem(TestCase, TestMixin):
mocked_list_view.selectAll.assert_called_once_with()
self.assertEqual(self.media_item.search_results, {})
self.assertEqual(self.media_item.second_search_results, {})
+
+ def on_quick_search_button_general_test(self):
+ """
+ Test that general things, which should be called on all Quick searches are called.
+ """
+
+ # GIVEN: self.application as self.app, all the required functions
+ Registry.create()
+ Registry().register('application', self.app)
+ self.media_item.quickSearchButton = MagicMock()
+ self.app.process_events = MagicMock()
+ self.media_item.quickVersionComboBox = MagicMock()
+ self.media_item.quickVersionComboBox.currentText = MagicMock()
+ self.media_item.quickSecondComboBox = MagicMock()
+ self.media_item.quickSecondComboBox.currentText = MagicMock()
+ self.media_item.quick_search_edit = MagicMock()
+ self.media_item.quick_search_edit.text = MagicMock()
+ self.media_item.quickLockButton = MagicMock()
+ self.media_item.list_view = MagicMock()
+ self.media_item.search_results = MagicMock()
+ self.media_item.display_results = MagicMock()
+ self.media_item.check_search_result = MagicMock()
+ self.app.set_normal_cursor = MagicMock()
+
+ # WHEN: on_quick_search_button is called
+ self.media_item.on_quick_search_button()
+
+ # THEN: Search should had been started and finalized properly
+ self.assertEqual(1, self.app.process_events.call_count, 'Normal cursor should had been called once')
+ self.assertEqual(1, self.media_item.quickVersionComboBox.currentText.call_count, 'Should had been called once')
+ self.assertEqual(1, self.media_item.quickSecondComboBox.currentText.call_count, 'Should had been called once')
+ self.assertEqual(1, self.media_item.quick_search_edit.text.call_count, 'Text edit Should had been called once')
+ self.assertEqual(1, self.media_item.quickLockButton.isChecked.call_count, 'Lock Should had been called once')
+ self.assertEqual(1, self.media_item.display_results.call_count, 'Display results Should had been called once')
+ self.assertEqual(2, self.media_item.quickSearchButton.setEnabled.call_count, 'Disable and Enable the button')
+ self.assertEqual(1, self.media_item.check_search_result.call_count, 'Check results Should had been called once')
+ self.assertEqual(1, self.app.set_normal_cursor.call_count, 'Normal cursor should had been called once')
diff --git a/tests/functional/openlp_plugins/bibles/test_swordimport.py b/tests/functional/openlp_plugins/bibles/test_swordimport.py
index ca3c03691..25b6ab355 100644
--- a/tests/functional/openlp_plugins/bibles/test_swordimport.py
+++ b/tests/functional/openlp_plugins/bibles/test_swordimport.py
@@ -70,8 +70,8 @@ class TestSwordImport(TestCase):
@patch('openlp.plugins.bibles.lib.sword.SwordBible.application')
@patch('openlp.plugins.bibles.lib.sword.modules')
- @patch('openlp.plugins.bibles.lib.db.BiblesResourcesDB')
- def test_simple_import(self, mocked_bible_res_db, mocked_pysword_modules, mocked_application):
+ @patch('openlp.core.common.languages')
+ def test_simple_import(self, mocked_languages, mocked_pysword_modules, mocked_application):
"""
Test that a simple SWORD import works
"""
@@ -88,7 +88,7 @@ class TestSwordImport(TestCase):
importer.create_verse = MagicMock()
importer.create_book = MagicMock()
importer.session = MagicMock()
- mocked_bible_res_db.get_language.return_value = 'Danish'
+ mocked_languages.get_language.return_value = 'Danish'
mocked_bible = MagicMock()
mocked_genesis = MagicMock()
mocked_genesis.name = 'Genesis'
diff --git a/tests/functional/openlp_plugins/songs/test_videopsalm.py b/tests/functional/openlp_plugins/songs/test_videopsalm.py
index 1bf13241d..ff1a81db5 100644
--- a/tests/functional/openlp_plugins/songs/test_videopsalm.py
+++ b/tests/functional/openlp_plugins/songs/test_videopsalm.py
@@ -43,3 +43,5 @@ class TestVideoPsalmFileImport(SongImportTestHelper):
"""
self.file_import(os.path.join(TEST_PATH, 'videopsalm-as-safe-a-stronghold.json'),
self.load_external_result_data(os.path.join(TEST_PATH, 'as-safe-a-stronghold.json')))
+ self.file_import(os.path.join(TEST_PATH, 'videopsalm-as-safe-a-stronghold2.json'),
+ self.load_external_result_data(os.path.join(TEST_PATH, 'as-safe-a-stronghold2.json')))
diff --git a/tests/interfaces/openlp_plugins/bibles/test_lib_http.py b/tests/interfaces/openlp_plugins/bibles/test_lib_http.py
index 4a7fb4af3..de4d4bba8 100644
--- a/tests/interfaces/openlp_plugins/bibles/test_lib_http.py
+++ b/tests/interfaces/openlp_plugins/bibles/test_lib_http.py
@@ -50,7 +50,8 @@ class TestBibleHTTP(TestCase):
books = handler.get_books_from_http('NIV')
# THEN: We should get back a valid service item
- assert len(books) == 66, 'The bible should not have had any books added or removed'
+ self.assertEqual(len(books), 66, 'The bible should not have had any books added or removed')
+ self.assertEqual(books[0], 'Genesis', 'The first bible book should be Genesis')
def test_bible_gateway_extract_books_support_redirect(self):
"""
@@ -63,7 +64,7 @@ class TestBibleHTTP(TestCase):
books = handler.get_books_from_http('DN1933')
# THEN: We should get back a valid service item
- assert len(books) == 66, 'This bible should have 66 books'
+ self.assertEqual(len(books), 66, 'This bible should have 66 books')
def test_bible_gateway_extract_verse(self):
"""
@@ -76,7 +77,8 @@ class TestBibleHTTP(TestCase):
results = handler.get_bible_chapter('NIV', 'John', 3)
# 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'
+ self.assertEqual(len(results.verse_list), 36,
+ 'The book of John should not have had any verses added or removed')
def test_bible_gateway_extract_verse_nkjv(self):
"""
@@ -89,7 +91,8 @@ class TestBibleHTTP(TestCase):
results = handler.get_bible_chapter('NKJV', 'John', 3)
# 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'
+ self.assertEqual(len(results.verse_list), 36,
+ 'The book of John should not have had any verses added or removed')
def test_crosswalk_extract_books(self):
"""
@@ -102,7 +105,7 @@ class TestBibleHTTP(TestCase):
books = handler.get_books_from_http('niv')
# THEN: We should get back a valid service item
- assert len(books) == 66, 'The bible should not have had any books added or removed'
+ self.assertEqual(len(books), 66, 'The bible should not have had any books added or removed')
def test_crosswalk_extract_verse(self):
"""
@@ -115,7 +118,8 @@ class TestBibleHTTP(TestCase):
results = handler.get_bible_chapter('niv', 'john', 3)
# 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'
+ self.assertEqual(len(results.verse_list), 36,
+ 'The book of John should not have had any verses added or removed')
def test_bibleserver_get_bibles(self):
"""
@@ -144,9 +148,8 @@ class TestBibleHTTP(TestCase):
# 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)
+ self.assertIn(('Holman Christian Standard Bible (HCSB)', 'HCSB', 'en'), bibles)
- @skip("Waiting for Crosswalk to fix their server")
def test_crosswalk_get_bibles(self):
"""
Test getting list of bibles from Crosswalk.com
diff --git a/tests/resources/videopsalmsongs/as-safe-a-stronghold2.json b/tests/resources/videopsalmsongs/as-safe-a-stronghold2.json
new file mode 100644
index 000000000..f6becc92a
--- /dev/null
+++ b/tests/resources/videopsalmsongs/as-safe-a-stronghold2.json
@@ -0,0 +1,35 @@
+{
+ "authors": [
+ ["Martin Luther", "words"],
+ ["Unknown", "music"]
+ ],
+ "ccli_number": "12345",
+ "comments": "This is\nthe first comment\nThis is\nthe second comment\nThis is\nthe third comment\n",
+ "copyright": "Public Domain",
+ "song_book_name": "SongBook1",
+ "song_number": 0,
+ "title": "A Safe Stronghold Our God is Still",
+ "topics": [
+ "tema1",
+ "tema2"
+ ],
+ "verse_order_list": [],
+ "verses": [
+ [
+ "As safe a stronghold our God is still,\nA trusty shield and weapon;\nHe’ll help us clear from all the ill\nThat hath us now o’ertaken.\nThe ancient prince of hell\nHath risen with purpose fell;\nStrong mail of craft and power\nHe weareth in this hour;\nOn earth is not His fellow.",
+ "v"
+ ],
+ [
+ "With \"force\" of arms we nothing can,\nFull soon were we down-ridden;\nBut for us fights \\ the proper Man,\nWhom God Himself hath bidden.\nAsk ye: Who is this same?\nChrist Jesus is His name,\nThe Lord Sabaoth’s Son;\nHe, and no other one,\nShall conquer in the battle.",
+ "v"
+ ],
+ [
+ "And were this world all devils o’er,\nAnd watching to devour us,\nWe lay it not to heart so sore;\nNot they can overpower us.\nAnd let the prince of ill\nLook grim as e’er he will,\nHe harms us not a whit;\nFor why? his doom is writ;\nA word shall quickly slay him.",
+ "v"
+ ],
+ [
+ "God’s word, for all their craft and force,\nOne moment will not linger,\nBut, spite of hell, shall have its course;\n’Tis written by His finger.\nAnd though they take our life,\nGoods, honour, children, wife,\nYet is their profit small:\nThese things shall vanish all;\nThe city of God remaineth.",
+ "v"
+ ]
+ ]
+}
diff --git a/tests/resources/videopsalmsongs/videopsalm-as-safe-a-stronghold2.json b/tests/resources/videopsalmsongs/videopsalm-as-safe-a-stronghold2.json
new file mode 100644
index 000000000..11bc082e6
--- /dev/null
+++ b/tests/resources/videopsalmsongs/videopsalm-as-safe-a-stronghold2.json
@@ -0,0 +1,47 @@
+{Abbreviation:"SB1",Copyright:"Public domain",Songs:[{ID:3,Composer:"Unknown",Author:"Martin Luther",Copyright:"Public
+Domain",Theme:"tema1
+tema2",CCLI:"12345",Alias:"A safe stronghold",Memo1:"This is
+the first comment
+",Memo2:"This is
+the second comment
+",Memo3:"This is
+the third comment
+",Reference:"reference",Guid:"jtCkrJdPIUOmECjaQylg/g",Verses:[{
+Text:"As safe a stronghold our God is still,
+A trusty shield and weapon;
+He’ll help us clear from all the ill
+That hath us now o’ertaken.
+The ancient prince of hell
+Hath risen with purpose fell;
+Strong mail of craft and power
+He weareth in this hour;
+On earth is not His fellow."},{ID:2,
+Text:"With \"force\" of arms we nothing can,
+Full soon were we down-ridden;
+But for us fights \\ the proper Man,
+Whom God Himself hath bidden.
+Ask ye: Who is this same?
+Christ Jesus is His name,
+The Lord Sabaoth’s Son;
+He, and no other one,
+Shall conquer in the battle."},{ID:3,
+Text:"And were this world all devils o’er,
+And watching to devour us,
+We lay it not to heart so sore;
+Not they can overpower us.
+And let the prince of ill
+Look grim as e’er he will,
+He harms us not a whit;
+For why? his doom is writ;
+A word shall quickly slay him."},{ID:4,
+Text:"God’s word, for all their craft and force,
+One moment will not linger,
+But, spite of hell, shall have its course;
+’Tis written by His finger.
+And though they take our life,
+Goods, honour, children, wife,
+Yet is their profit small:
+These things shall vanish all;
+The city of God remaineth."}],AudioFile:"282.mp3",IsAudioFileEnabled:1,
+Text:"A Safe Stronghold Our God is Still"}],Guid:"khiHU2blX0Kb41dGdbDLhA",VersionDate:"20121012000000",
+Text:"SongBook1"}
diff --git a/tests/utils/test_pylint.py b/tests/utils/test_pylint.py
index dc6c83909..48c9e1393 100644
--- a/tests/utils/test_pylint.py
+++ b/tests/utils/test_pylint.py
@@ -23,8 +23,8 @@
Package to test for proper bzr tags.
"""
import os
-import logging
import platform
+import sys
from unittest import TestCase, SkipTest
try:
@@ -46,9 +46,18 @@ class TestPylint(TestCase):
"""
Test for pylint errors
"""
+ # Test if this file is specified in the arguments, if not skip the test.
+ in_argv = False
+ for arg in sys.argv:
+ if arg.endswith('test_pylint.py') or arg.endswith('test_pylint'):
+ in_argv = True
+ break
+ if not in_argv:
+ raise SkipTest('test_pylint.py not specified in arguments - skipping tests using pylint.')
+
# GIVEN: Some checks to disable and enable, and the pylint script
disabled_checks = 'import-error,no-member'
- enabled_checks = 'missing-format-argument-key,unused-format-string-argument'
+ enabled_checks = 'missing-format-argument-key,unused-format-string-argument,bad-format-string'
if is_win() or 'arch' in platform.dist()[0].lower():
pylint_script = 'pylint'
else:
@@ -84,6 +93,9 @@ class TestPylint(TestCase):
# Filter out PyQt related errors
elif ('no-name-in-module' in line or 'no-member' in line) and 'PyQt5' in line:
continue
+ # Filter out distutils related errors
+ elif 'distutils' in line:
+ continue
elif self._is_line_tolerated(line):
continue
else: