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 dccc3bdb4..e947b1362 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 @@ -155,3 +156,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 6a83f955d..2edea93cf 100644 --- a/openlp/core/lib/mediamanageritem.py +++ b/openlp/core/lib/mediamanageritem.py @@ -652,6 +652,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/screen.py b/openlp/core/lib/screen.py index e7b4c0b97..31ff3d725 100644 --- a/openlp/core/lib/screen.py +++ b/openlp/core/lib/screen.py @@ -167,7 +167,7 @@ class ScreenList(object): :param number: The screen number (int). """ - log.info('remove_screen {number:d}'.forma(number=number)) + log.info('remove_screen {number:d}'.format(number=number)) for screen in self.screen_list: if screen['number'] == number: self.screen_list.remove(screen) 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/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/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 61f74986a..0711d1257 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_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/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: