This commit is contained in:
Tim Bentley 2016-08-07 19:37:15 +01:00
commit cade36a332
32 changed files with 1158 additions and 212 deletions

View File

@ -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

View File

@ -23,6 +23,7 @@
The :mod:`uistrings` module provides standard strings for OpenLP. The :mod:`uistrings` module provides standard strings for OpenLP.
""" """
import logging import logging
import itertools
from openlp.core.common import translate from openlp.core.common import translate
@ -153,3 +154,30 @@ class UiStrings(object):
self.View = translate('OpenLP.Ui', 'View') self.View = translate('OpenLP.Ui', 'View')
self.ViewMode = translate('OpenLP.Ui', 'View Mode') self.ViewMode = translate('OpenLP.Ui', 'View Mode')
self.Video = translate('OpenLP.Ui', 'Video') self.Video = translate('OpenLP.Ui', 'Video')
self.BibleShortSearchTitle = translate('OpenLP.Ui', 'Search is Empty or too Short')
self.BibleShortSearch = translate('OpenLP.Ui', '<strong>The search you have entered is empty or shorter '
'than 3 characters long.</strong><br><br>Please try again with '
'a longer search.')
self.BibleNoBiblesTitle = translate('OpenLP.Ui', 'No Bibles Available')
self.BibleNoBibles = translate('OpenLP.Ui', '<strong>There are no Bibles currently installed.</strong><br><br>'
'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<br>',
book_chapter, '%(range)s', chapter, gap, psalm, ' 23%(range)s24<br>',
book_chapter, '%(verse)s', verse, '%(range)s', verse, gap, psalm, ' 23%(verse)s1%(range)s2<br>',
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<br>',
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<br>',
book_chapter, '%(verse)s', verse, '%(range)s', chapter, '%(verse)s', verse, gap, psalm,
' 23%(verse)s1%(range)s24%(verse)s1<br><br>', 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)

View File

@ -651,6 +651,20 @@ class MediaManagerItem(QtWidgets.QWidget, RegistryProperties):
item.setFont(font) item.setFont(font)
self.list_view.addItem(item) 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): def _get_id_of_item_to_generate(self, item, remote_item):
""" """
Utility method to check items being submitted for slide generation. Utility method to check items being submitted for slide generation.

View File

@ -474,6 +474,7 @@ class ThemeXML(object):
if element.startswith('shadow') or element.startswith('outline'): if element.startswith('shadow') or element.startswith('outline'):
master = 'font_main' master = 'font_main'
# fix bold font # fix bold font
ret_value = None
if element == 'weight': if element == 'weight':
element = 'bold' element = 'bold'
if value == 'Normal': if value == 'Normal':
@ -482,7 +483,7 @@ class ThemeXML(object):
ret_value = True ret_value = True
if element == 'proportion': if element == 'proportion':
element = 'size' 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): def _create_attr(self, master, element, value):
""" """

View File

@ -41,7 +41,8 @@ __default_settings__ = {
'bibles/db password': '', 'bibles/db password': '',
'bibles/db hostname': '', 'bibles/db hostname': '',
'bibles/db database': '', '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/verse layout style': LayoutStyle.VersePerSlide,
'bibles/book name language': LanguageSelection.Bible, 'bibles/book name language': LanguageSelection.Bible,
'bibles/display brackets': DisplayStyle.NoBrackets, 'bibles/display brackets': DisplayStyle.NoBrackets,
@ -59,7 +60,9 @@ __default_settings__ = {
'bibles/range separator': '', 'bibles/range separator': '',
'bibles/list separator': '', 'bibles/list separator': '',
'bibles/end 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
} }

View File

@ -29,9 +29,9 @@ from PyQt5.QtWidgets import QDialog
from PyQt5 import QtCore from PyQt5 import QtCore
from openlp.core.common import translate 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.core.lib.ui import critical_error_message_box
from openlp.plugins.bibles.forms.languagedialog import Ui_LanguageDialog from openlp.plugins.bibles.forms.languagedialog import Ui_LanguageDialog
from openlp.plugins.bibles.lib.db import BiblesResourcesDB
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -51,11 +51,11 @@ class LanguageForm(QDialog, Ui_LanguageDialog):
self.setupUi(self) self.setupUi(self)
def exec(self, bible_name): def exec(self, bible_name):
self.language_combo_box.addItem('')
if bible_name: if bible_name:
self.bible_label.setText(str(bible_name)) self.bible_label.setText(bible_name)
items = BiblesResourcesDB.get_languages() self.language_combo_box.addItem('')
self.language_combo_box.addItems([item['name'] for item in items]) for language in languages:
self.language_combo_box.addItem(language.name, language.id)
return QDialog.exec(self) return QDialog.exec(self)
def accept(self): def accept(self):

View File

@ -128,6 +128,20 @@ class BiblesTab(SettingsTab):
self.language_selection_layout.addWidget(self.language_selection_label) self.language_selection_layout.addWidget(self.language_selection_label)
self.language_selection_layout.addWidget(self.language_selection_combo_box) self.language_selection_layout.addWidget(self.language_selection_combo_box)
self.right_layout.addWidget(self.language_selection_group_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.left_layout.addStretch()
self.right_layout.addStretch() self.right_layout.addStretch()
# Signals and slots # Signals and slots
@ -151,6 +165,12 @@ class BiblesTab(SettingsTab):
self.end_separator_line_edit.editingFinished.connect(self.on_end_separator_line_edit_finished) self.end_separator_line_edit.editingFinished.connect(self.on_end_separator_line_edit_finished)
Registry().register_function('theme_update_list', self.update_theme_list) 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.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): def retranslateUi(self):
self.verse_display_group_box.setTitle(translate('BiblesPlugin.BiblesTab', 'Verse Display')) 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')) LanguageSelection.Application, translate('BiblesPlugin.BiblesTab', 'Application Language'))
self.language_selection_combo_box.setItemText( self.language_selection_combo_box.setItemText(
LanguageSelection.English, translate('BiblesPlugin.BiblesTab', 'English')) 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): def on_bible_theme_combo_box_changed(self):
self.bible_theme = self.bible_theme_combo_box.currentText() 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.setText(get_reference_separator('sep_e_default'))
self.end_separator_line_edit.setPalette(self.get_grey_text_palette(True)) 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): def load(self):
settings = Settings() settings = Settings()
settings.beginGroup(self.settings_section) settings.beginGroup(self.settings_section)
@ -355,6 +404,12 @@ class BiblesTab(SettingsTab):
self.end_separator_check_box.setChecked(True) self.end_separator_check_box.setChecked(True)
self.language_selection = settings.value('book name language') self.language_selection = settings.value('book name language')
self.language_selection_combo_box.setCurrentIndex(self.language_selection) 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() settings.endGroup()
def save(self): def save(self):
@ -386,6 +441,9 @@ class BiblesTab(SettingsTab):
if self.language_selection != settings.value('book name language'): if self.language_selection != settings.value('book name language'):
settings.setValue('book name language', self.language_selection) settings.setValue('book name language', self.language_selection)
self.settings_form.register_post_process('bibles_load_list') 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() settings.endGroup()
if self.tab_visited: if self.tab_visited:
self.settings_form.register_post_process('bibles_config_updated') self.settings_form.register_post_process('bibles_config_updated')

View File

@ -128,6 +128,12 @@ class BibleDB(Manager, RegistryProperties):
The name of the database. This is also used as the file name for SQLite databases. The name of the database. This is also used as the file name for SQLite databases.
""" """
log.info('BibleDB loaded') 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.bible_plugin = parent
self.session = None self.session = None
if 'path' not in kwargs: if 'path' not in kwargs:
@ -465,14 +471,13 @@ class BibleDB(Manager, RegistryProperties):
""" """
log.debug('BibleDB.get_language()') log.debug('BibleDB.get_language()')
from openlp.plugins.bibles.forms import LanguageForm from openlp.plugins.bibles.forms import LanguageForm
language = None language_id = None
language_form = LanguageForm(self.wizard) language_form = LanguageForm(self.wizard)
if language_form.exec(bible_name): if language_form.exec(bible_name):
language = str(language_form.language_combo_box.currentText()) combo_box = language_form.language_combo_box
if not language: language_id = combo_box.itemData(combo_box.currentIndex())
if not language_id:
return False return False
language = BiblesResourcesDB.get_language(language)
language_id = language['id']
self.save_meta('language_id', language_id) self.save_meta('language_id', language_id)
return language_id return language_id
@ -767,43 +772,6 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
return book[0] return book[0]
return None 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 @staticmethod
def get_testament_reference(): def get_testament_reference():
""" """
@ -1014,7 +982,7 @@ class OldBibleDB(QtCore.QObject, Manager):
def get_verses(self, book_id): def get_verses(self, book_id):
""" """
Returns the verses of the Bible. Returns the verses of the Bible.
""" """
verses = self.run_sql( verses = self.run_sql(
'SELECT book_id, chapter, verse, text FROM verse WHERE book_id = ? ORDER BY id', (book_id, )) 'SELECT book_id, chapter, verse, text FROM verse WHERE book_id = ? ORDER BY id', (book_id, ))

View File

@ -252,7 +252,7 @@ class BGExtract(RegistryProperties):
chapter=chapter, chapter=chapter,
version=version) version=version)
soup = get_soup_for_bible_ref( 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'<meta name.*?/>', pre_parse_substitute='') pre_parse_regex=r'<meta name.*?/>', pre_parse_substitute='')
if not soup: if not soup:
return None return None
@ -281,7 +281,7 @@ class BGExtract(RegistryProperties):
""" """
log.debug('BGExtract.get_books_from_http("{version}")'.format(version=version)) log.debug('BGExtract.get_books_from_http("{version}")'.format(version=version))
url_params = urllib.parse.urlencode({'action': 'getVersionInfo', 'vid': '{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) page = get_web_page(reference_url)
if not page: if not page:
send_error_message('download') send_error_message('download')
@ -312,7 +312,7 @@ class BGExtract(RegistryProperties):
for book in content: for book in content:
book = book.find('td') book = book.find('td')
if book: if book:
books.append(book.contents[0]) books.append(book.contents[1])
return books return books
def get_bibles_from_http(self): def get_bibles_from_http(self):
@ -322,11 +322,11 @@ class BGExtract(RegistryProperties):
returns a list in the form [(biblename, biblekey, language_code)] returns a list in the form [(biblename, biblekey, language_code)]
""" """
log.debug('BGExtract.get_bibles_from_http') 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) soup = get_soup_for_bible_ref(bible_url)
if not soup: if not soup:
return None return None
bible_select = soup.find('select', {'class': 'translation-dropdown'}) bible_select = soup.find('select', {'class': 'search-translation-select'})
if not bible_select: if not bible_select:
log.debug('No select tags found - did site change?') log.debug('No select tags found - did site change?')
return None return None
@ -532,28 +532,26 @@ class CWExtract(RegistryProperties):
returns a list in the form [(biblename, biblekey, language_code)] returns a list in the form [(biblename, biblekey, language_code)]
""" """
log.debug('CWExtract.get_bibles_from_http') 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) soup = get_soup_for_bible_ref(bible_url)
if not soup: if not soup:
return None return None
bible_select = soup.find('select') h4_tags = soup.find_all('h4', {'class': 'small-header'})
if not bible_select: if not h4_tags:
log.debug('No select tags found - did site change?') log.debug('No h4 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?')
return None return None
bibles = [] bibles = []
for ot in option_tags: for h4t in h4_tags:
tag_text = ot.get_text().strip() short_name = None
try: if h4t.span:
tag_value = ot['value'] short_name = h4t.span.get_text().strip().lower()
except KeyError: else:
log.exception('No value attribute found - did site change?') log.error('No span tag found - did site change?')
return None return None
if not tag_value: if not short_name:
continue continue
h4t.span.extract()
tag_text = h4t.get_text().strip()
# The names of non-english bibles has their language in parentheses at the end # The names of non-english bibles has their language in parentheses at the end
if tag_text.endswith(')'): if tag_text.endswith(')'):
language = tag_text[tag_text.rfind('(') + 1:-1] language = tag_text[tag_text.rfind('(') + 1:-1]
@ -561,12 +559,20 @@ class CWExtract(RegistryProperties):
language_code = CROSSWALK_LANGUAGES[language] language_code = CROSSWALK_LANGUAGES[language]
else: else:
language_code = '' language_code = ''
# ... except for the latin vulgate # ... except for those that don't...
elif 'latin' in tag_text.lower(): elif 'latin' in tag_text.lower():
language_code = 'la' 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: else:
language_code = 'en' language_code = 'en'
bibles.append((tag_text, tag_value, language_code)) bibles.append((tag_text, short_name, language_code))
return bibles return bibles

View File

@ -23,8 +23,8 @@
import logging import logging
import os import os
from openlp.core.common import RegistryProperties, AppLocation, Settings, translate, delete_file from openlp.core.common import RegistryProperties, AppLocation, Settings, translate, delete_file, UiStrings
from openlp.plugins.bibles.lib import parse_reference, get_reference_separator, LanguageSelection from openlp.plugins.bibles.lib import parse_reference, LanguageSelection
from openlp.plugins.bibles.lib.db import BibleDB, BibleMeta from openlp.plugins.bibles.lib.db import BibleDB, BibleMeta
from .csvbible import CSVBible from .csvbible import CSVBible
from .http import HTTPBible from .http import HTTPBible
@ -267,42 +267,21 @@ class BibleManager(RegistryProperties):
For second bible this is necessary. For second bible this is necessary.
:param show_error: :param show_error:
""" """
# If no bibles are installed, message is given.
log.debug('BibleManager.get_verses("{bible}", "{verse}")'.format(bible=bible, verse=verse_text)) log.debug('BibleManager.get_verses("{bible}", "{verse}")'.format(bible=bible, verse=verse_text))
if not bible: if not bible:
if show_error: if show_error:
self.main_window.information_message( self.main_window.information_message(
translate('BiblesPlugin.BibleManager', 'No Bibles Available'), UiStrings().BibleNoBiblesTitle,
translate('BiblesPlugin.BibleManager', 'There are no Bibles currently installed. Please use the ' UiStrings().BibleNoBibles)
'Import Wizard to install one or more Bibles.')
)
return None return None
# Get the language for books.
language_selection = self.get_language_selection(bible) language_selection = self.get_language_selection(bible)
ref_list = parse_reference(verse_text, self.db_cache[bible], language_selection, book_ref_id) ref_list = parse_reference(verse_text, self.db_cache[bible], language_selection, book_ref_id)
if ref_list: if ref_list:
return self.db_cache[bible].get_verses(ref_list, show_error) 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: 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 return None
def get_language_selection(self, bible): def get_language_selection(self, bible):
@ -334,13 +313,11 @@ class BibleManager(RegistryProperties):
:param text: The text to search for (unicode). :param text: The text to search for (unicode).
""" """
log.debug('BibleManager.verse_search("{bible}", "{text}")'.format(bible=bible, text=text)) log.debug('BibleManager.verse_search("{bible}", "{text}")'.format(bible=bible, text=text))
# If no bibles are installed, message is given.
if not bible: if not bible:
self.main_window.information_message( self.main_window.information_message(
translate('BiblesPlugin.BibleManager', 'No Bibles Available'), UiStrings().BibleNoBiblesTitle,
translate('BiblesPlugin.BibleManager', UiStrings().BibleNoBibles)
'There are no Bibles currently installed. Please use the Import Wizard to install one or '
'more Bibles.')
)
return None return None
# Check if the bible or second_bible is a web bible. # Check if the bible or second_bible is a web bible.
web_bible = self.db_cache[bible].get_object(BibleMeta, 'download_source') web_bible = self.db_cache[bible].get_object(BibleMeta, 'download_source')
@ -348,20 +325,56 @@ class BibleManager(RegistryProperties):
if second_bible: if second_bible:
second_web_bible = self.db_cache[second_bible].get_object(BibleMeta, 'download_source') second_web_bible = self.db_cache[second_bible].get_object(BibleMeta, 'download_source')
if web_bible or second_web_bible: 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( self.main_window.information_message(
translate('BiblesPlugin.BibleManager', 'Web Bible cannot be used'), translate('BiblesPlugin.BibleManager', 'Web Bible cannot be used in Text Search'),
translate('BiblesPlugin.BibleManager', 'Text Search is not available with Web Bibles.') 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 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) return self.db_cache[bible].verse_search(text)
else: 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 return None
def save_meta_data(self, bible, version, copyright, permissions, book_name_language=None): def save_meta_data(self, bible, version, copyright, permissions, book_name_language=None):

View File

@ -35,6 +35,7 @@ from openlp.plugins.bibles.forms.editbibleform import EditBibleForm
from openlp.plugins.bibles.lib import LayoutStyle, DisplayStyle, VerseReferenceList, get_reference_separator, \ from openlp.plugins.bibles.lib import LayoutStyle, DisplayStyle, VerseReferenceList, get_reference_separator, \
LanguageSelection, BibleStrings LanguageSelection, BibleStrings
from openlp.plugins.bibles.lib.db import BiblesResourcesDB from openlp.plugins.bibles.lib.db import BiblesResourcesDB
import re
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -45,6 +46,7 @@ class BibleSearch(object):
""" """
Reference = 1 Reference = 1
Text = 2 Text = 2
Combined = 3
class BibleMediaItem(MediaManagerItem): class BibleMediaItem(MediaManagerItem):
@ -56,6 +58,7 @@ class BibleMediaItem(MediaManagerItem):
log.info('Bible Media Item loaded') log.info('Bible Media Item loaded')
def __init__(self, parent, plugin): 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.lock_icon = build_icon(':/bibles/bibles_search_lock.png')
self.unlock_icon = build_icon(':/bibles/bibles_search_unlock.png') self.unlock_icon = build_icon(':/bibles/bibles_search_unlock.png')
MediaManagerItem.__init__(self, parent, plugin) MediaManagerItem.__init__(self, parent, plugin)
@ -157,10 +160,15 @@ class BibleMediaItem(MediaManagerItem):
search_button_layout = QtWidgets.QHBoxLayout() search_button_layout = QtWidgets.QHBoxLayout()
search_button_layout.setObjectName(prefix + 'search_button_layout') search_button_layout.setObjectName(prefix + 'search_button_layout')
search_button_layout.addStretch() 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 = QtWidgets.QToolButton(tab)
lock_button.setIcon(self.unlock_icon) lock_button.setIcon(self.unlock_icon)
lock_button.setCheckable(True) lock_button.setCheckable(True)
lock_button.setObjectName(prefix + 'LockButton') lock_button.setObjectName(prefix + 'LockButton')
search_button_layout.addWidget(clear_button)
search_button_layout.addWidget(lock_button) search_button_layout.addWidget(lock_button)
search_button = QtWidgets.QPushButton(tab) search_button = QtWidgets.QPushButton(tab)
search_button.setObjectName(prefix + 'SearchButton') search_button.setObjectName(prefix + 'SearchButton')
@ -176,6 +184,7 @@ class BibleMediaItem(MediaManagerItem):
setattr(self, prefix + 'SecondComboBox', second_combo_box) setattr(self, prefix + 'SecondComboBox', second_combo_box)
setattr(self, prefix + 'StyleLabel', style_label) setattr(self, prefix + 'StyleLabel', style_label)
setattr(self, prefix + 'StyleComboBox', style_combo_box) setattr(self, prefix + 'StyleComboBox', style_combo_box)
setattr(self, prefix + 'ClearButton', clear_button)
setattr(self, prefix + 'LockButton', lock_button) setattr(self, prefix + 'LockButton', lock_button)
setattr(self, prefix + 'SearchButtonLayout', search_button_layout) setattr(self, prefix + 'SearchButtonLayout', search_button_layout)
setattr(self, prefix + 'SearchButton', search_button) 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.quickStyleComboBox.activated.connect(self.on_quick_style_combo_box_changed)
self.advancedStyleComboBox.activated.connect(self.on_advanced_style_combo_box_changed) self.advancedStyleComboBox.activated.connect(self.on_advanced_style_combo_box_changed)
# Buttons # 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.advancedSearchButton.clicked.connect(self.on_advanced_search_button)
self.quickSearchButton.clicked.connect(self.on_quick_search_button) self.quickSearchButton.clicked.connect(self.on_quick_search_button)
# Other stuff # Other stuff
self.quick_search_edit.returnPressed.connect(self.on_quick_search_button) 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.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): def on_focus(self):
if self.quickTab.isVisible(): if self.quickTab.isVisible():
@ -286,6 +298,7 @@ class BibleMediaItem(MediaManagerItem):
self.quickStyleComboBox.setItemText(LayoutStyle.VersePerSlide, UiStrings().VersePerSlide) self.quickStyleComboBox.setItemText(LayoutStyle.VersePerSlide, UiStrings().VersePerSlide)
self.quickStyleComboBox.setItemText(LayoutStyle.VersePerLine, UiStrings().VersePerLine) self.quickStyleComboBox.setItemText(LayoutStyle.VersePerLine, UiStrings().VersePerLine)
self.quickStyleComboBox.setItemText(LayoutStyle.Continuous, UiStrings().Continuous) self.quickStyleComboBox.setItemText(LayoutStyle.Continuous, UiStrings().Continuous)
self.quickClearButton.setToolTip(translate('BiblesPlugin.MediaItem', 'Clear the search results.'))
self.quickLockButton.setToolTip(translate('BiblesPlugin.MediaItem', self.quickLockButton.setToolTip(translate('BiblesPlugin.MediaItem',
'Toggle to keep or clear the previous results.')) 'Toggle to keep or clear the previous results.'))
self.quickSearchButton.setText(UiStrings().Search) self.quickSearchButton.setText(UiStrings().Search)
@ -300,6 +313,7 @@ class BibleMediaItem(MediaManagerItem):
self.advancedStyleComboBox.setItemText(LayoutStyle.VersePerSlide, UiStrings().VersePerSlide) self.advancedStyleComboBox.setItemText(LayoutStyle.VersePerSlide, UiStrings().VersePerSlide)
self.advancedStyleComboBox.setItemText(LayoutStyle.VersePerLine, UiStrings().VersePerLine) self.advancedStyleComboBox.setItemText(LayoutStyle.VersePerLine, UiStrings().VersePerLine)
self.advancedStyleComboBox.setItemText(LayoutStyle.Continuous, UiStrings().Continuous) self.advancedStyleComboBox.setItemText(LayoutStyle.Continuous, UiStrings().Continuous)
self.advancedClearButton.setToolTip(translate('BiblesPlugin.MediaItem', 'Clear the search results.'))
self.advancedLockButton.setToolTip(translate('BiblesPlugin.MediaItem', self.advancedLockButton.setToolTip(translate('BiblesPlugin.MediaItem',
'Toggle to keep or clear the previous results.')) 'Toggle to keep or clear the previous results.'))
self.advancedSearchButton.setText(UiStrings().Search) self.advancedSearchButton.setText(UiStrings().Search)
@ -309,6 +323,9 @@ class BibleMediaItem(MediaManagerItem):
self.plugin.manager.media = self self.plugin.manager.media = self
self.load_bibles() self.load_bibles()
self.quick_search_edit.set_search_types([ 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', (BibleSearch.Reference, ':/bibles/bibles_search_reference.png',
translate('BiblesPlugin.MediaItem', 'Scripture Reference'), translate('BiblesPlugin.MediaItem', 'Scripture Reference'),
translate('BiblesPlugin.MediaItem', 'Search Scripture Reference...')), translate('BiblesPlugin.MediaItem', 'Search Scripture Reference...')),
@ -424,18 +441,24 @@ class BibleMediaItem(MediaManagerItem):
def update_auto_completer(self): def update_auto_completer(self):
""" """
This updates the bible book completion list for the search field. The completion depends on the bible. It is 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') log.debug('update_auto_completer')
# Save the current search type to the configuration. # Save the current search type to the configuration. If setting for automatically resetting the search type to
Settings().setValue('{section}/last search type'.format(section=self.settings_section), # Combined is enabled, use that otherwise use the currently selected search type.
self.quick_search_edit.current_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. # Save the current bible to the configuration.
Settings().setValue('{section}/quick bible'.format(section=self.settings_section), Settings().setValue('{section}/quick bible'.format(section=self.settings_section),
self.quickVersionComboBox.currentText()) self.quickVersionComboBox.currentText())
books = [] books = []
# We have to do a 'Reference Search'. # We have to do a 'Reference Search' (Or as part of Combined Search).
if self.quick_search_edit.current_search_type() == BibleSearch.Reference: if self.quick_search_edit.current_search_type() is not BibleSearch.Text:
bibles = self.plugin.manager.get_bibles() bibles = self.plugin.manager.get_bibles()
bible = self.quickVersionComboBox.currentText() bible = self.quickVersionComboBox.currentText()
if bible: if bible:
@ -525,7 +548,15 @@ class BibleMediaItem(MediaManagerItem):
self.advancedTab.setVisible(True) self.advancedTab.setVisible(True)
self.advanced_book_combo_box.setFocus() 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): def on_lock_button_toggled(self, checked):
self.quick_search_edit.setFocus()
if checked: if checked:
self.sender().setIcon(self.lock_icon) self.sender().setIcon(self.lock_icon)
else: else:
@ -652,10 +683,120 @@ class BibleMediaItem(MediaManagerItem):
self.check_search_result() self.check_search_result()
self.application.set_normal_cursor() 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): def on_quick_search_button(self):
""" """
Does a quick search and saves the search results. Quick search can either be "Reference Search" or This triggers the proper Quick search based on which search type is used.
"Text Search". "Eg. "Reference Search", "Text Search" or "Combined search".
""" """
log.debug('Quick Search Button clicked') log.debug('Quick Search Button clicked')
self.quickSearchButton.setEnabled(False) self.quickSearchButton.setEnabled(False)
@ -664,41 +805,68 @@ class BibleMediaItem(MediaManagerItem):
second_bible = self.quickSecondComboBox.currentText() second_bible = self.quickSecondComboBox.currentText()
text = self.quick_search_edit.text() text = self.quick_search_edit.text()
if self.quick_search_edit.current_search_type() == BibleSearch.Reference: if self.quick_search_edit.current_search_type() == BibleSearch.Reference:
# We are doing a 'Reference Search'. # We are doing a 'Reference Search'. (Get script from def on_quick_reference_search)
self.search_results = self.plugin.manager.get_verses(bible, text) self.on_quick_reference_search()
if second_bible and self.search_results: # Get reference separators from settings.
self.second_search_results = \ if not self.search_results:
self.plugin.manager.get_verses(second_bible, text, self.search_results[0].book.book_reference_id) reference_separators = {
else: 'verse': get_reference_separator('sep_v_display'),
# We are doing a 'Text Search'. 'range': get_reference_separator('sep_r_display'),
self.application.set_busy_cursor() 'list': get_reference_separator('sep_l_display')}
bibles = self.plugin.manager.get_bibles() self.main_window.information_message(
self.search_results = self.plugin.manager.verse_search(bible, second_bible, text) translate('BiblesPlugin.BibleManager', 'Scripture Reference Error'),
if second_bible and self.search_results: translate('BiblesPlugin.BibleManager', '<strong>OpenLP couldnt find anything '
text = [] 'with your search.<br><br>'
new_search_results = [] 'Please make sure that your reference follows '
count = 0 'one of these patterns:</strong><br><br>%s'
passage_not_found = False % UiStrings().BibleScriptureError % reference_separators))
for verse in self.search_results: elif self.quick_search_edit.current_search_type() == BibleSearch.Text:
db_book = bibles[second_bible].get_book_by_book_ref_id(verse.book.book_reference_id) # We are doing a 'Text Search'. (Get script from def on_quick_text_search)
if not db_book: self.on_quick_text_search()
log.debug('Passage "{name} {chapter:d}:{verse:d}" not found in ' if not self.search_results and len(text) - text.count(' ') < 3 and bible:
'Second Bible'.format(name=verse.book.name, chapter=verse.chapter, verse=verse.verse)) self.main_window.information_message(
passage_not_found = True UiStrings().BibleShortSearchTitle,
count += 1 UiStrings().BibleShortSearch)
continue elif self.quick_search_edit.current_search_type() == BibleSearch.Combined:
new_search_results.append(verse) # We are doing a 'Combined search'. Starting with reference search.
text.append((verse.book.book_reference_id, verse.chapter, verse.verse, verse.verse)) # Perform only if text contains any numbers
if passage_not_found: if (char.isdigit() for char in text):
QtWidgets.QMessageBox.information( self.on_quick_reference_search()
self, translate('BiblesPlugin.MediaItem', 'Information'), """
translate('BiblesPlugin.MediaItem', If results are found, search will be finalized.
'The second Bible does not contain all the verses that are in the main Bible. ' This check needs to be here in order to avoid duplicate errors.
'Only verses found in both Bibles will be shown. {count:d} verses have not been ' If keyword is shorter than 3 (not including spaces), message is given. It's actually possible to find
'included in the results.').format(count=count), verses with less than 3 chars (Eg. G1 = Genesis 1) thus this error is not shown if any results are found.
QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Ok)) if no Bibles are installed, this message is not shown - "No bibles" message is shown instead.
self.search_results = new_search_results """
self.second_search_results = bibles[second_bible].get_verses(text) 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',
'<strong>OpenLP couldnt find anything with your'
' search.</strong><br><br>If you tried to search'
' with Scripture Reference, please make<br> sure'
' that your reference follows one of these'
' patterns: <br><br>%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(): if not self.quickLockButton.isChecked():
self.list_view.clear() self.list_view.clear()
if self.list_view.count() != 0 and self.search_results: if self.list_view.count() != 0 and self.search_results:
@ -709,6 +877,99 @@ class BibleMediaItem(MediaManagerItem):
self.check_search_result() self.check_search_result()
self.application.set_normal_cursor() 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=''): 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. Displays the search results in the media manager. All data needed for further action is saved for/in each row.

View File

@ -23,7 +23,7 @@
import logging import logging
from lxml import etree 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.plugins.bibles.lib.db import BibleDB, BiblesResourcesDB
from openlp.core.lib.ui import critical_error_message_box 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'} namespace = {'ns': 'http://www.bibletechnologies.net/2003/OSIS/namespace'}
# Find bible language # Find bible language
language_id = None language_id = None
language = osis_bible_tree.xpath("//ns:osisText/@xml:lang", namespaces=namespace) lang = osis_bible_tree.xpath("//ns:osisText/@xml:lang", namespaces=namespace)
if language: if lang:
language_id = BiblesResourcesDB.get_language(language[0]) language = languages.get_language(lang[0])
if hasattr(language, 'id'):
language_id = language.id
# The language couldn't be detected, ask the user # The language couldn't be detected, ask the user
if not language_id: if not language_id:
language_id = self.get_language(bible_name) language_id = self.get_language(bible_name)

View File

@ -23,7 +23,7 @@
import logging import logging
from pysword import modules 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.core.lib.ui import critical_error_message_box
from openlp.plugins.bibles.lib.db import BibleDB, BiblesResourcesDB from openlp.plugins.bibles.lib.db import BibleDB, BiblesResourcesDB
@ -57,9 +57,12 @@ class SwordBible(BibleDB):
pysword_modules = modules.SwordModules(self.sword_path) pysword_modules = modules.SwordModules(self.sword_path)
pysword_module_json = pysword_modules.parse_modules()[self.sword_key] pysword_module_json = pysword_modules.parse_modules()[self.sword_key]
bible = pysword_modules.get_bible_from_module(self.sword_key) bible = pysword_modules.get_bible_from_module(self.sword_key)
language_id = None
language = pysword_module_json['lang'] language = pysword_module_json['lang']
language = language[language.find('.') + 1:] 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) self.save_meta('language_id', language_id)
books = bible.get_structure().get_books() books = bible.get_structure().get_books()
# Count number of books # Count number of books

View File

@ -21,9 +21,9 @@
############################################################################### ###############################################################################
import logging 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.core.lib.ui import critical_error_message_box
from openlp.plugins.bibles.lib.db import BibleDB, BiblesResourcesDB from openlp.plugins.bibles.lib.db import BibleDB, BiblesResourcesDB
@ -62,7 +62,9 @@ class ZefaniaBible(BibleDB):
language_id = None language_id = None
language = zefania_bible_tree.xpath("/XMLBIBLE/INFORMATION/language/text()") language = zefania_bible_tree.xpath("/XMLBIBLE/INFORMATION/language/text()")
if language: 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 # The language couldn't be detected, ask the user
if not language_id: if not language_id:
language_id = self.get_language(bible_name) language_id = self.get_language(bible_name)

View File

@ -46,7 +46,7 @@ class EasySlidesImport(SongImport):
def do_import(self): def do_import(self):
log.info('Importing EasySlides XML file {source}'.format(source=self.import_source)) 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) parsed_file = etree.parse(self.import_source, parser)
xml = etree.tostring(parsed_file).decode() xml = etree.tostring(parsed_file).decode()
song_xml = objectify.fromstring(xml) song_xml = objectify.fromstring(xml)

View File

@ -73,6 +73,14 @@ class VideoPsalmImport(SongImport):
processed_content += c processed_content += c
c = next(file_content_it) c = next(file_content_it)
processed_content += '"' + c 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: else:
processed_content += c processed_content += c
songbook = json.loads(processed_content.strip()) songbook = json.loads(processed_content.strip())

View File

@ -458,7 +458,7 @@ class OpenLyrics(object):
self._add_tag_to_formatting(tag, tags_element) self._add_tag_to_formatting(tag, tags_element)
# Replace end tags. # Replace end tags.
for tag in end_tags: for tag in end_tags:
text = text.replace('{/{tag}}}'.format(tag=tag), '</tag>') text = text.replace('{{{tag}}}'.format(tag=tag), '</tag>')
# Replace \n with <br/>. # Replace \n with <br/>.
text = text.replace('\n', '<br/>') text = text.replace('\n', '<br/>')
element = etree.XML('<lines>{text}</lines>'.format(text=text)) element = etree.XML('<lines>{text}</lines>'.format(text=text))
@ -643,7 +643,7 @@ class OpenLyrics(object):
# Append text from tail and add formatting end tag. # Append text from tail and add formatting end tag.
# TODO: Verify format() with template variables # TODO: Verify format() with template variables
if element.tag == NSMAP % 'tag' and use_endtag: 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. # Append text from tail.
if element.tail: if element.tail:
text += element.tail text += element.tail

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -30,9 +30,11 @@
<file>image_new_group.png</file> <file>image_new_group.png</file>
</qresource> </qresource>
<qresource prefix="bibles"> <qresource prefix="bibles">
<file>bibles_search_combined.png</file>
<file>bibles_search_text.png</file> <file>bibles_search_text.png</file>
<file>bibles_search_reference.png</file> <file>bibles_search_reference.png</file>
<file>bibles_upgrade_alert.png</file> <file>bibles_upgrade_alert.png</file>
<file>bibles_search_clear.png</file>
<file>bibles_search_unlock.png</file> <file>bibles_search_unlock.png</file>
<file>bibles_search_lock.png</file> <file>bibles_search_lock.png</file>
</qresource> </qresource>

View File

@ -63,9 +63,10 @@ class OpenLPJobs(object):
Branch_Windows_Interface = 'Branch-04b-Windows_Interface_Tests' Branch_Windows_Interface = 'Branch-04b-Windows_Interface_Tests'
Branch_PEP = 'Branch-05a-Code_Analysis' Branch_PEP = 'Branch-05a-Code_Analysis'
Branch_Coverage = 'Branch-05b-Test_Coverage' 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, 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): class Colour(object):

View File

@ -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)

View File

@ -22,43 +22,82 @@
""" """
Package to test the openlp.core.lib.theme package. Package to test the openlp.core.lib.theme package.
""" """
from tests.functional import MagicMock, patch
from unittest import TestCase from unittest import TestCase
import os
from openlp.core.lib.theme import ThemeXML 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): def test_new_theme(self):
""" """
Test the theme creation - basic test Test the ThemeXML constructor
""" """
# GIVEN: A new theme # GIVEN: The ThemeXML class
# WHEN: A theme object is created
# WHEN: A theme is created
default_theme = ThemeXML() default_theme = ThemeXML()
# THEN: We should get some default behaviours # THEN: The default values should be correct
self.assertTrue(default_theme.background_border_color == '#000000', 'The theme should have a black border') self.assertEqual('#000000', default_theme.background_border_color,
self.assertTrue(default_theme.background_type == 'solid', 'The theme should have a solid backgrounds') 'background_border_color should be "#000000"')
self.assertTrue(default_theme.display_vertical_align == 0, self.assertEqual('solid', default_theme.background_type, 'background_type should be "solid"')
'The theme should have a display_vertical_align of 0') self.assertEqual(0, default_theme.display_vertical_align, 'display_vertical_align should be 0')
self.assertTrue(default_theme.font_footer_name == "Arial", self.assertEqual('Arial', default_theme.font_footer_name, 'font_footer_name should be "Arial"')
'The theme should have a font_footer_name of Arial') self.assertFalse(default_theme.font_main_bold, 'font_main_bold should be False')
self.assertTrue(default_theme.font_main_bold is False, 'The theme should have a font_main_bold of false') self.assertEqual(47, len(default_theme.__dict__), 'The theme should have 47 attributes')
self.assertTrue(len(default_theme.__dict__) == 47, 'The theme should have 47 variables')
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)

View File

@ -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')

View File

@ -23,6 +23,7 @@
This module contains tests for the lib submodule of the Presentations plugin. This module contains tests for the lib submodule of the Presentations plugin.
""" """
from unittest import TestCase from unittest import TestCase
from openlp.core.common import Registry
from openlp.plugins.bibles.lib.mediaitem import BibleMediaItem from openlp.plugins.bibles.lib.mediaitem import BibleMediaItem
from tests.functional import MagicMock, patch from tests.functional import MagicMock, patch
from tests.helpers.testmixin import TestMixin from tests.helpers.testmixin import TestMixin
@ -41,6 +42,9 @@ class TestMediaItem(TestCase, TestMixin):
patch('openlp.plugins.bibles.lib.mediaitem.BibleMediaItem.setup_item'): patch('openlp.plugins.bibles.lib.mediaitem.BibleMediaItem.setup_item'):
self.media_item = BibleMediaItem(None, MagicMock()) self.media_item = BibleMediaItem(None, MagicMock())
self.setup_application() 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): def test_display_results_no_results(self):
""" """
@ -109,3 +113,40 @@ class TestMediaItem(TestCase, TestMixin):
mocked_list_view.selectAll.assert_called_once_with() mocked_list_view.selectAll.assert_called_once_with()
self.assertEqual(self.media_item.search_results, {}) self.assertEqual(self.media_item.search_results, {})
self.assertEqual(self.media_item.second_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')

View File

@ -70,8 +70,8 @@ class TestSwordImport(TestCase):
@patch('openlp.plugins.bibles.lib.sword.SwordBible.application') @patch('openlp.plugins.bibles.lib.sword.SwordBible.application')
@patch('openlp.plugins.bibles.lib.sword.modules') @patch('openlp.plugins.bibles.lib.sword.modules')
@patch('openlp.plugins.bibles.lib.db.BiblesResourcesDB') @patch('openlp.core.common.languages')
def test_simple_import(self, mocked_bible_res_db, mocked_pysword_modules, mocked_application): def test_simple_import(self, mocked_languages, mocked_pysword_modules, mocked_application):
""" """
Test that a simple SWORD import works Test that a simple SWORD import works
""" """
@ -88,7 +88,7 @@ class TestSwordImport(TestCase):
importer.create_verse = MagicMock() importer.create_verse = MagicMock()
importer.create_book = MagicMock() importer.create_book = MagicMock()
importer.session = MagicMock() importer.session = MagicMock()
mocked_bible_res_db.get_language.return_value = 'Danish' mocked_languages.get_language.return_value = 'Danish'
mocked_bible = MagicMock() mocked_bible = MagicMock()
mocked_genesis = MagicMock() mocked_genesis = MagicMock()
mocked_genesis.name = 'Genesis' mocked_genesis.name = 'Genesis'

View File

@ -43,3 +43,5 @@ class TestVideoPsalmFileImport(SongImportTestHelper):
""" """
self.file_import(os.path.join(TEST_PATH, 'videopsalm-as-safe-a-stronghold.json'), 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.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')))

View File

@ -50,7 +50,8 @@ class TestBibleHTTP(TestCase):
books = handler.get_books_from_http('NIV') books = handler.get_books_from_http('NIV')
# THEN: We should get back a valid service item # 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): def test_bible_gateway_extract_books_support_redirect(self):
""" """
@ -63,7 +64,7 @@ class TestBibleHTTP(TestCase):
books = handler.get_books_from_http('DN1933') books = handler.get_books_from_http('DN1933')
# THEN: We should get back a valid service item # 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): def test_bible_gateway_extract_verse(self):
""" """
@ -76,7 +77,8 @@ class TestBibleHTTP(TestCase):
results = handler.get_bible_chapter('NIV', 'John', 3) results = handler.get_bible_chapter('NIV', 'John', 3)
# THEN: We should get back a valid service item # THEN: We should get back a valid service item
assert len(results.verse_list) == 36, 'The book of John should not have had any verses added or removed' 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): def test_bible_gateway_extract_verse_nkjv(self):
""" """
@ -89,7 +91,8 @@ class TestBibleHTTP(TestCase):
results = handler.get_bible_chapter('NKJV', 'John', 3) results = handler.get_bible_chapter('NKJV', 'John', 3)
# THEN: We should get back a valid service item # THEN: We should get back a valid service item
assert len(results.verse_list) == 36, 'The book of John should not have had any verses added or removed' 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): def test_crosswalk_extract_books(self):
""" """
@ -102,7 +105,7 @@ class TestBibleHTTP(TestCase):
books = handler.get_books_from_http('niv') books = handler.get_books_from_http('niv')
# THEN: We should get back a valid service item # 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): def test_crosswalk_extract_verse(self):
""" """
@ -115,7 +118,8 @@ class TestBibleHTTP(TestCase):
results = handler.get_bible_chapter('niv', 'john', 3) results = handler.get_bible_chapter('niv', 'john', 3)
# THEN: We should get back a valid service item # THEN: We should get back a valid service item
assert len(results.verse_list) == 36, 'The book of John should not have had any verses added or removed' 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): 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 # THEN: The list should not be None, and some known bibles should be there
self.assertIsNotNone(bibles) 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): def test_crosswalk_get_bibles(self):
""" """
Test getting list of bibles from Crosswalk.com Test getting list of bibles from Crosswalk.com

View File

@ -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;\nHell help us clear from all the ill\nThat hath us now oertaken.\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 Sabaoths Son;\nHe, and no other one,\nShall conquer in the battle.",
"v"
],
[
"And were this world all devils oer,\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 eer he will,\nHe harms us not a whit;\nFor why? his doom is writ;\nA word shall quickly slay him.",
"v"
],
[
"Gods word, for all their craft and force,\nOne moment will not linger,\nBut, spite of hell, shall have its course;\nTis 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"
]
]
}

View File

@ -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;
Hell help us clear from all the ill
That hath us now oertaken.
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 Sabaoths Son;
He, and no other one,
Shall conquer in the battle."},{ID:3,
Text:"And were this world all devils oer,
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 eer he will,
He harms us not a whit;
For why? his doom is writ;
A word shall quickly slay him."},{ID:4,
Text:"Gods 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"}

View File

@ -23,8 +23,8 @@
Package to test for proper bzr tags. Package to test for proper bzr tags.
""" """
import os import os
import logging
import platform import platform
import sys
from unittest import TestCase, SkipTest from unittest import TestCase, SkipTest
try: try:
@ -46,9 +46,18 @@ class TestPylint(TestCase):
""" """
Test for pylint errors 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 # GIVEN: Some checks to disable and enable, and the pylint script
disabled_checks = 'import-error,no-member' 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(): if is_win() or 'arch' in platform.dist()[0].lower():
pylint_script = 'pylint' pylint_script = 'pylint'
else: else:
@ -84,6 +93,9 @@ class TestPylint(TestCase):
# Filter out PyQt related errors # Filter out PyQt related errors
elif ('no-name-in-module' in line or 'no-member' in line) and 'PyQt5' in line: elif ('no-name-in-module' in line or 'no-member' in line) and 'PyQt5' in line:
continue continue
# Filter out distutils related errors
elif 'distutils' in line:
continue
elif self._is_line_tolerated(line): elif self._is_line_tolerated(line):
continue continue
else: else: