- Merged trunk on 5.6.16

- Removed some additional error messages from "Search while typing"
  (Count verses not found in both bibles, no Bibles installed)
This commit is contained in:
suutari-olli 2016-06-05 15:15:51 +03:00
commit 9990472586
205 changed files with 1908 additions and 1454 deletions

17
nose2.cfg Normal file
View File

@ -0,0 +1,17 @@
[unittest]
verbose = True
[log-capture]
always-on = True
clear-handlers = True
filter = -nose
log-level = ERROR
[test-result]
always-on = True
descriptions = True
[coverage]
always-on = False
coverage = openlp
coverage-report = html

View File

@ -55,6 +55,7 @@ class Registry(object):
registry = cls() registry = cls()
registry.service_list = {} registry.service_list = {}
registry.functions_list = {} registry.functions_list = {}
registry.working_flags = {}
# Allow the tests to remove Registry entries but not the live system # Allow the tests to remove Registry entries but not the live system
registry.running_under_test = 'nose' in sys.argv[0] registry.running_under_test = 'nose' in sys.argv[0]
registry.initialising = True registry.initialising = True
@ -90,8 +91,7 @@ class Registry(object):
def remove(self, key): def remove(self, key):
""" """
Removes the registry value from the list based on the key passed in (Only valid and active for testing Removes the registry value from the list based on the key passed in.
framework).
:param key: The service to be deleted. :param key: The service to be deleted.
""" """
@ -145,3 +145,34 @@ class Registry(object):
trace_error_handler(log) trace_error_handler(log)
log.error("Event {event} called but not registered".format(event=event)) log.error("Event {event} called but not registered".format(event=event))
return results return results
def get_flag(self, key):
"""
Extracts the working_flag value from the list based on the key passed in
:param key: The flag to be retrieved.
"""
if key in self.working_flags:
return self.working_flags[key]
else:
trace_error_handler(log)
log.error('Working Flag {key} not found in list'.format(key=key))
raise KeyError('Working Flag {key} not found in list'.format(key=key))
def set_flag(self, key, reference):
"""
Sets a working_flag based on the key passed in.
:param key: The working_flag to be created this is usually a major class like "renderer" or "main_window" .
:param reference: The data to be saved.
"""
self.working_flags[key] = reference
def remove_flag(self, key):
"""
Removes the working flags value from the list based on the key passed.
:param key: The working_flag to be deleted.
"""
if key in self.working_flags:
del self.working_flags[key]

View File

@ -24,13 +24,12 @@ The :mod:`lib` module contains most of the components and libraries that make
OpenLP work. OpenLP work.
""" """
from distutils.version import LooseVersion
import logging import logging
import os import os
from distutils.version import LooseVersion
from PyQt5 import QtCore, QtGui, Qt, QtWidgets from PyQt5 import QtCore, QtGui, Qt, QtWidgets
from openlp.core.common import translate from openlp.core.common import translate
log = logging.getLogger(__name__ + '.__init__') log = logging.getLogger(__name__ + '.__init__')
@ -342,7 +341,6 @@ from .exceptions import ValidationError
from .filedialog import FileDialog from .filedialog import FileDialog
from .screen import ScreenList from .screen import ScreenList
from .formattingtags import FormattingTags from .formattingtags import FormattingTags
from .spelltextedit import SpellTextEdit
from .plugin import PluginStatus, StringContent, Plugin from .plugin import PluginStatus, StringContent, Plugin
from .pluginmanager import PluginManager from .pluginmanager import PluginManager
from .settingstab import SettingsTab from .settingstab import SettingsTab

View File

@ -58,7 +58,7 @@ SocketSTate = QAbstractSocket.SocketState
PJLINK_PREFIX = '%' PJLINK_PREFIX = '%'
PJLINK_CLASS = '1' PJLINK_CLASS = '1'
PJLINK_HEADER = '%s%s' % (PJLINK_PREFIX, PJLINK_CLASS) PJLINK_HEADER = '{prefix}{linkclass}'.format(prefix=PJLINK_PREFIX, linkclass=PJLINK_CLASS)
PJLINK_SUFFIX = CR PJLINK_SUFFIX = CR
@ -160,8 +160,10 @@ class PJLink1(QTcpSocket):
self.source = None self.source = None
self.other_info = None self.other_info = None
if hasattr(self, 'timer'): if hasattr(self, 'timer'):
log.debug('({ip}): Calling timer.stop()'.format(ip=self.ip))
self.timer.stop() self.timer.stop()
if hasattr(self, 'socket_timer'): if hasattr(self, 'socket_timer'):
log.debug('({ip}): Calling socket_timer.stop()'.format(ip=self.ip))
self.socket_timer.stop() self.socket_timer.stop()
self.send_queue = [] self.send_queue = []
self.send_busy = False self.send_busy = False

View File

@ -28,6 +28,7 @@ from .dockwidget import OpenLPDockWidget
from .wizard import OpenLPWizard, WizardStrings from .wizard import OpenLPWizard, WizardStrings
from .mediadockmanager import MediaDockManager from .mediadockmanager import MediaDockManager
from .listpreviewwidget import ListPreviewWidget from .listpreviewwidget import ListPreviewWidget
from .spelltextedit import SpellTextEdit
__all__ = ['ColorButton', 'ListPreviewWidget', 'ListWidgetWithDnD', 'OpenLPToolbar', 'OpenLPDockWidget', __all__ = ['ColorButton', 'ListPreviewWidget', 'ListWidgetWithDnD', 'OpenLPToolbar', 'OpenLPDockWidget',
'OpenLPWizard', 'WizardStrings', 'MediaDockManager', 'ListPreviewWidget'] 'OpenLPWizard', 'WizardStrings', 'MediaDockManager', 'ListPreviewWidget', 'SpellTextEdit']

View File

@ -142,6 +142,7 @@ class SpellTextEdit(QtWidgets.QPlainTextEdit):
""" """
Replaces the selected text with word. Replaces the selected text with word.
""" """
tag = tag.replace('&', '')
for html in FormattingTags.get_html_tags(): for html in FormattingTags.get_html_tags():
if tag == html['desc']: if tag == html['desc']:
cursor = self.textCursor() cursor = self.textCursor()

View File

@ -134,6 +134,7 @@ def format_milliseconds(milliseconds):
:param milliseconds: Milliseconds to format :param milliseconds: Milliseconds to format
:return: Time string in format: hh.mm.ss,ttt :return: Time string in format: hh.mm.ss,ttt
""" """
milliseconds = int(milliseconds)
seconds, millis = divmod(milliseconds, 1000) seconds, millis = divmod(milliseconds, 1000)
minutes, seconds = divmod(seconds, 60) minutes, seconds = divmod(seconds, 60)
hours, minutes = divmod(minutes, 60) hours, minutes = divmod(minutes, 60)

View File

@ -25,7 +25,8 @@ The UI widgets of the print service dialog.
from PyQt5 import QtCore, QtWidgets, QtPrintSupport from PyQt5 import QtCore, QtWidgets, QtPrintSupport
from openlp.core.common import UiStrings, translate from openlp.core.common import UiStrings, translate
from openlp.core.lib import SpellTextEdit, build_icon from openlp.core.lib import build_icon
from openlp.core.ui.lib import SpellTextEdit
class ZoomSize(object): class ZoomSize(object):

View File

@ -25,7 +25,7 @@ The :mod:`~openlp.core.ui.servicenoteform` module contains the `ServiceNoteForm`
from PyQt5 import QtCore, QtWidgets from PyQt5 import QtCore, QtWidgets
from openlp.core.common import Registry, RegistryProperties, translate from openlp.core.common import Registry, RegistryProperties, translate
from openlp.core.lib import SpellTextEdit from openlp.core.ui.lib import SpellTextEdit
from openlp.core.lib.ui import create_button_box from openlp.core.lib.ui import create_button_box

View File

@ -88,6 +88,7 @@ JAVASCRIPT = """
} }
} }
""" """
# TODO: Verify format() with variable templates
CSS = """ CSS = """
#alert { #alert {
position: absolute; position: absolute;
@ -244,6 +245,9 @@ class AlertsPlugin(Plugin):
:param frame: The Web frame holding the page. :param frame: The Web frame holding the page.
""" """
align = VerticalType.Names[self.settings_tab.location] align = VerticalType.Names[self.settings_tab.location]
frame.evaluateJavaScript('update_css("%s", "%s", "%s", "%s", "%s")' % frame.evaluateJavaScript('update_css("{align}", "{face}", "{size}", "{color}", '
(align, self.settings_tab.font_face, self.settings_tab.font_size, '"{background}")'.format(align=align,
self.settings_tab.font_color, self.settings_tab.background_color)) face=self.settings_tab.font_face,
size=self.settings_tab.font_size,
color=self.settings_tab.font_color,
background=self.settings_tab.background_color))

View File

@ -62,7 +62,7 @@ class AlertsManager(OpenLPMixin, RegistryMixin, QtCore.QObject, RegistryProperti
:param text: The text to display :param text: The text to display
""" """
self.log_debug('display alert called %s' % text) self.log_debug('display alert called {text}'.format(text=text))
if text: if text:
self.alert_list.append(text) self.alert_list.append(text)
if self.timer_id != 0: if self.timer_id != 0:

View File

@ -197,5 +197,6 @@ class AlertsTab(SettingsTab):
font.setBold(True) font.setBold(True)
font.setPointSize(self.font_size) font.setPointSize(self.font_size)
self.font_preview.setFont(font) self.font_preview.setFont(font)
self.font_preview.setStyleSheet('background-color: %s; color: %s' % (self.background_color, self.font_color)) self.font_preview.setStyleSheet('background-color: {back}; color: {front}'.format(back=self.background_color,
front=self.font_color))
self.changed = True self.changed = True

View File

@ -593,22 +593,27 @@ class BibleImportForm(OpenLPWizard):
""" """
Show the file open dialog for the books CSV file. Show the file open dialog for the books CSV file.
""" """
# TODO: Verify format() with varible template
self.get_file_name( self.get_file_name(
WizardStrings.OpenTypeFile % WizardStrings.CSV, self.csv_books_edit, 'last directory import', '%s (*.csv)' % WizardStrings.OpenTypeFile % WizardStrings.CSV,
translate('BiblesPlugin.ImportWizardForm', 'CSV File')) self.csv_books_edit,
'last directory import',
'{name} (*.csv)'.format(name=translate('BiblesPlugin.ImportWizardForm', 'CSV File')))
def on_csv_verses_browse_button_clicked(self): def on_csv_verses_browse_button_clicked(self):
""" """
Show the file open dialog for the verses CSV file. Show the file open dialog for the verses CSV file.
""" """
# TODO: Verify format() with variable template
self.get_file_name(WizardStrings.OpenTypeFile % WizardStrings.CSV, self.csv_verses_edit, self.get_file_name(WizardStrings.OpenTypeFile % WizardStrings.CSV, self.csv_verses_edit,
'last directory import', '%s (*.csv)' % 'last directory import',
translate('BiblesPlugin.ImportWizardForm', 'CSV File')) '{name} (*.csv)'.format(name=translate('BiblesPlugin.ImportWizardForm', 'CSV File')))
def on_open_song_browse_button_clicked(self): def on_open_song_browse_button_clicked(self):
""" """
Show the file open dialog for the OpenSong file. Show the file open dialog for the OpenSong file.
""" """
# TODO: Verify format() with variable template
self.get_file_name(WizardStrings.OpenTypeFile % WizardStrings.OS, self.open_song_file_edit, self.get_file_name(WizardStrings.OpenTypeFile % WizardStrings.OS, self.open_song_file_edit,
'last directory import') 'last directory import')
@ -616,6 +621,7 @@ class BibleImportForm(OpenLPWizard):
""" """
Show the file open dialog for the Zefania file. Show the file open dialog for the Zefania file.
""" """
# TODO: Verify format() with variable template
self.get_file_name(WizardStrings.OpenTypeFile % WizardStrings.ZEF, self.zefania_file_edit, self.get_file_name(WizardStrings.OpenTypeFile % WizardStrings.ZEF, self.zefania_file_edit,
'last directory import') 'last directory import')
@ -631,6 +637,7 @@ class BibleImportForm(OpenLPWizard):
self.web_progress_bar.setVisible(True) self.web_progress_bar.setVisible(True)
self.web_progress_bar.setValue(0) self.web_progress_bar.setValue(0)
proxy_server = self.field('proxy_server') proxy_server = self.field('proxy_server')
# TODO: Where does critical_error_message_box get %s string from?
for (download_type, extractor) in ((WebDownload.Crosswalk, CWExtract(proxy_server)), for (download_type, extractor) in ((WebDownload.Crosswalk, CWExtract(proxy_server)),
(WebDownload.BibleGateway, BGExtract(proxy_server)), (WebDownload.BibleGateway, BGExtract(proxy_server)),
(WebDownload.Bibleserver, BSExtract(proxy_server))): (WebDownload.Bibleserver, BSExtract(proxy_server))):

View File

@ -209,7 +209,7 @@ class BibleUpgradeForm(OpenLPWizard):
for number, filename in enumerate(self.files): for number, filename in enumerate(self.files):
bible = OldBibleDB(self.media_item, path=self.path, file=filename[0]) bible = OldBibleDB(self.media_item, path=self.path, file=filename[0])
self.checkBox[number] = QtWidgets.QCheckBox(self.scrollAreaContents) self.checkBox[number] = QtWidgets.QCheckBox(self.scrollAreaContents)
self.checkBox[number].setObjectName('checkBox[%d]' % number) self.checkBox[number].setObjectName('checkBox[{count:d}]'.format(count=number))
self.checkBox[number].setText(bible.get_name()) self.checkBox[number].setText(bible.get_name())
self.checkBox[number].setCheckState(QtCore.Qt.Checked) self.checkBox[number].setCheckState(QtCore.Qt.Checked)
self.formLayout.addWidget(self.checkBox[number]) self.formLayout.addWidget(self.checkBox[number])
@ -364,7 +364,10 @@ class BibleUpgradeForm(OpenLPWizard):
name = filename[1] name = filename[1]
self.progress_label.setText( self.progress_label.setText(
translate('BiblesPlugin.UpgradeWizardForm', translate('BiblesPlugin.UpgradeWizardForm',
'Upgrading Bible %s of %s: "%s"\nUpgrading ...') % (number + 1, max_bibles, name)) 'Upgrading Bible {count} of {total}: "{name}"\n'
'Upgrading ...').format(count=number + 1,
total=max_bibles,
name=name))
self.new_bibles[number] = BibleDB(self.media_item, path=self.path, name=name, file=filename[0]) self.new_bibles[number] = BibleDB(self.media_item, path=self.path, name=name, file=filename[0])
self.new_bibles[number].register(self.plugin.upgrade_wizard) self.new_bibles[number].register(self.plugin.upgrade_wizard)
metadata = old_bible.get_metadata() metadata = old_bible.get_metadata()
@ -394,17 +397,19 @@ class BibleUpgradeForm(OpenLPWizard):
handler = BSExtract(proxy_server) handler = BSExtract(proxy_server)
books = handler.get_books_from_http(meta_data['download_name']) books = handler.get_books_from_http(meta_data['download_name'])
if not books: if not books:
log.error('Upgrading books from %s - download name: "%s" failed' % ( log.error('Upgrading books from {uri} - '
meta_data['download_source'], meta_data['download_name'])) 'download name: "{name}" failed'.format(uri=meta_data['download_source'],
name=meta_data['download_name']))
self.new_bibles[number].session.close() self.new_bibles[number].session.close()
del self.new_bibles[number] del self.new_bibles[number]
critical_error_message_box( critical_error_message_box(
translate('BiblesPlugin.UpgradeWizardForm', 'Download Error'), translate('BiblesPlugin.UpgradeWizardForm', 'Download Error'),
translate('BiblesPlugin.UpgradeWizardForm', translate('BiblesPlugin.UpgradeWizardForm',
'To upgrade your Web Bibles an Internet connection is required.')) 'To upgrade your Web Bibles an Internet connection is required.'))
self.increment_progress_bar(translate( text = translate('BiblesPlugin.UpgradeWizardForm',
'BiblesPlugin.UpgradeWizardForm', 'Upgrading Bible %s of %s: "%s"\nFailed') % 'Upgrading Bible {count} of {total}: "{name}"\n'
(number + 1, max_bibles, name), self.progress_bar.maximum() - self.progress_bar.value()) 'Failed').format(count=number + 1, total=max_bibles, name=name)
self.increment_progress_bar(text, self.progress_bar.maximum() - self.progress_bar.value())
self.success[number] = False self.success[number] = False
continue continue
bible = BiblesResourcesDB.get_webbible( bible = BiblesResourcesDB.get_webbible(
@ -416,12 +421,13 @@ class BibleUpgradeForm(OpenLPWizard):
else: else:
language_id = self.new_bibles[number].get_language(name) language_id = self.new_bibles[number].get_language(name)
if not language_id: if not language_id:
log.warning('Upgrading from "%s" failed' % filename[0]) log.warning('Upgrading from "{name}" failed'.format(name=filename[0]))
self.new_bibles[number].session.close() self.new_bibles[number].session.close()
del self.new_bibles[number] del self.new_bibles[number]
self.increment_progress_bar( self.increment_progress_bar(
translate('BiblesPlugin.UpgradeWizardForm', translate('BiblesPlugin.UpgradeWizardForm',
'Upgrading Bible %s of %s: "%s"\nFailed') % (number + 1, max_bibles, name), 'Upgrading Bible {count} of {total}: "{name}"\n'
'Failed').format(count=number + 1, total=max_bibles, name=name),
self.progress_bar.maximum() - self.progress_bar.value()) self.progress_bar.maximum() - self.progress_bar.value())
self.success[number] = False self.success[number] = False
continue continue
@ -432,13 +438,15 @@ class BibleUpgradeForm(OpenLPWizard):
break break
self.increment_progress_bar( self.increment_progress_bar(
translate('BiblesPlugin.UpgradeWizardForm', translate('BiblesPlugin.UpgradeWizardForm',
'Upgrading Bible %s of %s: "%s"\nUpgrading %s ...') % 'Upgrading Bible {count} of {total}: "{name}"\n'
(number + 1, max_bibles, name, book)) 'Upgrading {book} ...').format(count=number + 1, total=max_bibles,
name=name, book=book))
book_ref_id = self.new_bibles[number].\ book_ref_id = self.new_bibles[number].\
get_book_ref_id_by_name(book, len(books), language_id) get_book_ref_id_by_name(book, len(books), language_id)
if not book_ref_id: if not book_ref_id:
log.warning('Upgrading books from %s - download name: "%s" aborted by user' % ( log.warning('Upgrading books from {source} - download name: "{name}" '
meta_data['download_source'], meta_data['download_name'])) 'aborted by user'.format(source=meta_data['download_source'],
name=meta_data['download_name']))
self.new_bibles[number].session.close() self.new_bibles[number].session.close()
del self.new_bibles[number] del self.new_bibles[number]
self.success[number] = False self.success[number] = False
@ -450,7 +458,7 @@ class BibleUpgradeForm(OpenLPWizard):
if oldbook: if oldbook:
verses = old_bible.get_verses(oldbook['id']) verses = old_bible.get_verses(oldbook['id'])
if not verses: if not verses:
log.warning('No verses found to import for book "%s"', book) log.warning('No verses found to import for book "{book}"'.format(book=book))
continue continue
for verse in verses: for verse in verses:
if self.stop_import_flag: if self.stop_import_flag:
@ -465,12 +473,13 @@ class BibleUpgradeForm(OpenLPWizard):
if not language_id: if not language_id:
language_id = self.new_bibles[number].get_language(name) language_id = self.new_bibles[number].get_language(name)
if not language_id: if not language_id:
log.warning('Upgrading books from "%s" failed' % name) log.warning('Upgrading books from "{name}" failed'.format(name=name))
self.new_bibles[number].session.close() self.new_bibles[number].session.close()
del self.new_bibles[number] del self.new_bibles[number]
self.increment_progress_bar( self.increment_progress_bar(
translate('BiblesPlugin.UpgradeWizardForm', translate('BiblesPlugin.UpgradeWizardForm',
'Upgrading Bible %s of %s: "%s"\nFailed') % (number + 1, max_bibles, name), 'Upgrading Bible {count} of {total}: "{name}"\n'
'Failed').format(count=number + 1, total=max_bibles, name=name),
self.progress_bar.maximum() - self.progress_bar.value()) self.progress_bar.maximum() - self.progress_bar.value())
self.success[number] = False self.success[number] = False
continue continue
@ -482,11 +491,12 @@ class BibleUpgradeForm(OpenLPWizard):
break break
self.increment_progress_bar( self.increment_progress_bar(
translate('BiblesPlugin.UpgradeWizardForm', translate('BiblesPlugin.UpgradeWizardForm',
'Upgrading Bible %s of %s: "%s"\nUpgrading %s ...') % 'Upgrading Bible {count} of {total}: "{name}"\n'
(number + 1, max_bibles, name, book['name'])) 'Upgrading {book} ...').format(count=number + 1, total=max_bibles,
name=name, book=book['name']))
book_ref_id = self.new_bibles[number].get_book_ref_id_by_name(book['name'], len(books), language_id) book_ref_id = self.new_bibles[number].get_book_ref_id_by_name(book['name'], len(books), language_id)
if not book_ref_id: if not book_ref_id:
log.warning('Upgrading books from %s " failed - aborted by user' % name) log.warning('Upgrading books from {name} " failed - aborted by user'.format(name=name))
self.new_bibles[number].session.close() self.new_bibles[number].session.close()
del self.new_bibles[number] del self.new_bibles[number]
self.success[number] = False self.success[number] = False
@ -496,7 +506,7 @@ class BibleUpgradeForm(OpenLPWizard):
book_details['testament_id']) book_details['testament_id'])
verses = old_bible.get_verses(book['id']) verses = old_bible.get_verses(book['id'])
if not verses: if not verses:
log.warning('No verses found to import for book "%s"', book['name']) log.warning('No verses found to import for book "{book}"'.format(book=book['name']))
self.new_bibles[number].delete_book(db_book) self.new_bibles[number].delete_book(db_book)
continue continue
for verse in verses: for verse in verses:
@ -510,14 +520,16 @@ class BibleUpgradeForm(OpenLPWizard):
if not self.success.get(number, True): if not self.success.get(number, True):
self.increment_progress_bar( self.increment_progress_bar(
translate('BiblesPlugin.UpgradeWizardForm', translate('BiblesPlugin.UpgradeWizardForm',
'Upgrading Bible %s of %s: "%s"\nFailed') % (number + 1, max_bibles, name), 'Upgrading Bible {count} of {total}: "{name}"\n'
'Failed').format(count=number + 1, total=max_bibles, name=name),
self.progress_bar.maximum() - self.progress_bar.value()) self.progress_bar.maximum() - self.progress_bar.value())
else: else:
self.success[number] = True self.success[number] = True
self.new_bibles[number].save_meta('name', name) self.new_bibles[number].save_meta('name', name)
self.increment_progress_bar( self.increment_progress_bar(
translate('BiblesPlugin.UpgradeWizardForm', translate('BiblesPlugin.UpgradeWizardForm',
'Upgrading Bible %s of %s: "%s"\nComplete') % (number + 1, max_bibles, name)) 'Upgrading Bible {count} of {total}: "{name}"\n'
'Complete').format(count=number + 1, total=max_bibles, name=name))
if number in self.new_bibles: if number in self.new_bibles:
self.new_bibles[number].session.close() self.new_bibles[number].session.close()
# Close the last bible's connection if possible. # Close the last bible's connection if possible.
@ -540,20 +552,22 @@ class BibleUpgradeForm(OpenLPWizard):
# Copy not upgraded bible back. # Copy not upgraded bible back.
shutil.move(os.path.join(self.temp_dir, filename[0]), self.path) shutil.move(os.path.join(self.temp_dir, filename[0]), self.path)
if failed_import > 0: if failed_import > 0:
failed_import_text = translate('BiblesPlugin.UpgradeWizardForm', ', %s failed') % failed_import failed_import_text = translate('BiblesPlugin.UpgradeWizardForm',
', {name} failed').format(name=failed_import)
else: else:
failed_import_text = '' failed_import_text = ''
if successful_import > 0: if successful_import > 0:
if self.includeWebBible: if self.includeWebBible:
self.progress_label.setText( self.progress_label.setText(
translate('BiblesPlugin.UpgradeWizardForm', translate('BiblesPlugin.UpgradeWizardForm',
'Upgrading Bible(s): %(success)d successful%(failed_text)s\nPlease note that verses ' 'Upgrading Bible(s): {count:d} successful{failed}\nPlease note that verses '
'from Web Bibles will be downloaded on demand and so an Internet connection is required.') 'from Web Bibles will be downloaded on demand and so an Internet connection is required.'
% {'success': successful_import, 'failed_text': failed_import_text}) ).format(count=successful_import, failed=failed_import_text))
else: else:
self.progress_label.setText( self.progress_label.setText(
translate('BiblesPlugin.UpgradeWizardForm', 'Upgrading Bible(s): %s successful%s') % ( translate('BiblesPlugin.UpgradeWizardForm',
successful_import, failed_import_text)) 'Upgrading Bible(s): {count:d} successful{failed}').format(count=successful_import,
failed=failed_import_text))
else: else:
self.progress_label.setText(translate('BiblesPlugin.UpgradeWizardForm', 'Upgrade failed.')) self.progress_label.setText(translate('BiblesPlugin.UpgradeWizardForm', 'Upgrade failed.'))
# Remove temp directory. # Remove temp directory.

View File

@ -103,9 +103,11 @@ class Ui_EditBibleDialog(object):
self.book_name_edit = {} self.book_name_edit = {}
for book in BiblesResourcesDB.get_books(): for book in BiblesResourcesDB.get_books():
self.book_name_label[book['abbreviation']] = QtWidgets.QLabel(self.book_name_widget) self.book_name_label[book['abbreviation']] = QtWidgets.QLabel(self.book_name_widget)
self.book_name_label[book['abbreviation']].setObjectName('book_name_label[%s]' % book['abbreviation']) self.book_name_label[book['abbreviation']].setObjectName(
'book_name_label[{name}]'.format(book=book['abbreviation']))
self.book_name_edit[book['abbreviation']] = QtWidgets.QLineEdit(self.book_name_widget) self.book_name_edit[book['abbreviation']] = QtWidgets.QLineEdit(self.book_name_widget)
self.book_name_edit[book['abbreviation']].setObjectName('book_name_edit[%s]' % book['abbreviation']) self.book_name_edit[book['abbreviation']].setObjectName(
'book_name_edit[{name}]'.format(name=book['abbreviation']))
self.book_name_widget_layout.addRow( self.book_name_widget_layout.addRow(
self.book_name_label[book['abbreviation']], self.book_name_label[book['abbreviation']],
self.book_name_edit[book['abbreviation']]) self.book_name_edit[book['abbreviation']])
@ -148,4 +150,5 @@ class Ui_EditBibleDialog(object):
self.bible_tab_widget.indexOf(self.book_name_tab), self.bible_tab_widget.indexOf(self.book_name_tab),
translate('SongsPlugin.EditBibleForm', 'Custom Book Names')) translate('SongsPlugin.EditBibleForm', 'Custom Book Names'))
for book in BiblesResourcesDB.get_books(): for book in BiblesResourcesDB.get_books():
self.book_name_label[book['abbreviation']].setText('%s:' % str(self.book_names[book['abbreviation']])) self.book_name_label[book['abbreviation']].setText(
'{text}:'.format(text=self.book_names[book['abbreviation']]))

View File

@ -39,7 +39,7 @@ class EditBibleForm(QtWidgets.QDialog, Ui_EditBibleDialog, RegistryProperties):
""" """
Class to manage the editing of a bible Class to manage the editing of a bible
""" """
log.info('%s EditBibleForm loaded', __name__) log.info('{name} EditBibleForm loaded'.format(name=__name__))
def __init__(self, media_item, parent, manager): def __init__(self, media_item, parent, manager):
""" """
@ -168,16 +168,17 @@ class EditBibleForm(QtWidgets.QDialog, Ui_EditBibleDialog, RegistryProperties):
self.book_name_edit[abbreviation].setFocus() self.book_name_edit[abbreviation].setFocus()
critical_error_message_box( critical_error_message_box(
UiStrings().EmptyField, UiStrings().EmptyField,
translate('BiblesPlugin.BibleEditForm', 'You need to specify a book name for "%s".') % translate('BiblesPlugin.BibleEditForm',
self.book_names[abbreviation]) 'You need to specify a book name for "{text}".').format(text=self.book_names[abbreviation]))
return False return False
elif not book_regex.match(new_book_name): elif not book_regex.match(new_book_name):
self.book_name_edit[abbreviation].setFocus() self.book_name_edit[abbreviation].setFocus()
critical_error_message_box( critical_error_message_box(
UiStrings().EmptyField, UiStrings().EmptyField,
translate('BiblesPlugin.BibleEditForm', translate('BiblesPlugin.BibleEditForm',
'The book name "%s" is not correct.\nNumbers can only be used at the beginning and must\nbe ' 'The book name "{name}" is not correct.\n'
'followed by one or more non-numeric characters.') % new_book_name) 'Numbers can only be used at the beginning and must\nbe '
'followed by one or more non-numeric characters.').format(name=new_book_name))
return False return False
for abbr, book in self.books.items(): for abbr, book in self.books.items():
if book: if book:
@ -187,7 +188,7 @@ class EditBibleForm(QtWidgets.QDialog, Ui_EditBibleDialog, RegistryProperties):
self.book_name_edit[abbreviation].setFocus() self.book_name_edit[abbreviation].setFocus()
critical_error_message_box( critical_error_message_box(
translate('BiblesPlugin.BibleEditForm', 'Duplicate Book Name'), translate('BiblesPlugin.BibleEditForm', 'Duplicate Book Name'),
translate('BiblesPlugin.BibleEditForm', 'The Book Name "%s" has been entered more than once.') translate('BiblesPlugin.BibleEditForm',
% new_book_name) 'The Book Name "{name}" has been entered more than once.').format(name=new_book_name))
return False return False
return True return True

View File

@ -86,7 +86,7 @@ class CSVBible(BibleDB):
success = True success = True
language_id = self.get_language(bible_name) language_id = self.get_language(bible_name)
if not language_id: if not language_id:
log.error('Importing books from "%s" failed' % self.filename) log.error('Importing books from "{name}" failed'.format(name=self.filename))
return False return False
books_file = None books_file = None
book_list = {} book_list = {}
@ -98,11 +98,11 @@ class CSVBible(BibleDB):
for line in books_reader: for line in books_reader:
if self.stop_import_flag: if self.stop_import_flag:
break break
self.wizard.increment_progress_bar(translate('BiblesPlugin.CSVBible', 'Importing books... %s') self.wizard.increment_progress_bar(translate('BiblesPlugin.CSVBible',
% line[2]) 'Importing books... {text}').format(text=line[2]))
book_ref_id = self.get_book_ref_id_by_name(line[2], 67, language_id) book_ref_id = self.get_book_ref_id_by_name(line[2], 67, language_id)
if not book_ref_id: if not book_ref_id:
log.error('Importing books from "%s" failed' % self.books_file) log.error('Importing books from "{name}" failed'.format(name=self.books_file))
return False return False
book_details = BiblesResourcesDB.get_book_by_id(book_ref_id) book_details = BiblesResourcesDB.get_book_by_id(book_ref_id)
self.create_book(line[2], book_ref_id, book_details['testament_id']) self.create_book(line[2], book_ref_id, book_details['testament_id'])
@ -134,9 +134,11 @@ class CSVBible(BibleDB):
if book_ptr != line_book: if book_ptr != line_book:
book = self.get_book(line_book) book = self.get_book(line_book)
book_ptr = book.name book_ptr = book.name
# TODO: Check out this conversion in translations
self.wizard.increment_progress_bar( self.wizard.increment_progress_bar(
translate('BiblesPlugin.CSVBible', translate('BiblesPlugin.CSVBible',
'Importing verses from %s...' % book.name, 'Importing verses from <book name>...')) 'Importing verses from {name}...'.format(name=book.name),
'Importing verses from <book name>...'))
self.session.commit() self.session.commit()
verse_text = line[3] verse_text = line[3]
self.create_verse(book.id, line[1], line[2], verse_text) self.create_verse(book.id, line[1], line[2], verse_text)

View File

@ -185,7 +185,7 @@ class BibleDB(Manager, RegistryProperties):
:param testament: *Defaults to 1.* The testament_reference_id from :param testament: *Defaults to 1.* The testament_reference_id from
bibles_resources.sqlite of the testament this book belongs to. bibles_resources.sqlite of the testament this book belongs to.
""" """
log.debug('BibleDB.create_book("%s", "%s")' % (name, bk_ref_id)) log.debug('BibleDB.create_book("{name}", "{number}")'.format(name=name, number=bk_ref_id))
book = Book.populate(name=name, book_reference_id=bk_ref_id, testament_reference_id=testament) book = Book.populate(name=name, book_reference_id=bk_ref_id, testament_reference_id=testament)
self.save_object(book) self.save_object(book)
return book return book
@ -196,7 +196,7 @@ class BibleDB(Manager, RegistryProperties):
:param book: The book object :param book: The book object
""" """
log.debug('BibleDB.update_book("%s")' % book.name) log.debug('BibleDB.update_book("{name}")'.format(name=book.name))
return self.save_object(book) return self.save_object(book)
def delete_book(self, db_book): def delete_book(self, db_book):
@ -205,7 +205,7 @@ class BibleDB(Manager, RegistryProperties):
:param db_book: The book object. :param db_book: The book object.
""" """
log.debug('BibleDB.delete_book("%s")' % db_book.name) log.debug('BibleDB.delete_book("{name}")'.format(name=db_book.name))
if self.delete_object(Book, db_book.id): if self.delete_object(Book, db_book.id):
return True return True
return False return False
@ -219,7 +219,7 @@ class BibleDB(Manager, RegistryProperties):
:param text_list: A dict of the verses to be inserted. The key is the verse number, and the value is the :param text_list: A dict of the verses to be inserted. The key is the verse number, and the value is the
verse text. verse text.
""" """
log.debug('BibleDBcreate_chapter("%s", "%s")' % (book_id, chapter)) log.debug('BibleDBcreate_chapter("{number}", "{chapter}")'.format(number=book_id, chapter=chapter))
# Text list has book and chapter as first two elements of the array. # Text list has book and chapter as first two elements of the array.
for verse_number, verse_text in text_list.items(): for verse_number, verse_text in text_list.items():
verse = Verse.populate( verse = Verse.populate(
@ -266,7 +266,7 @@ class BibleDB(Manager, RegistryProperties):
""" """
if not isinstance(value, str): if not isinstance(value, str):
value = str(value) value = str(value)
log.debug('BibleDB.save_meta("%s/%s")' % (key, value)) log.debug('BibleDB.save_meta("{key}/{val}")'.format(key=key, val=value))
meta = self.get_object(BibleMeta, key) meta = self.get_object(BibleMeta, key)
if meta: if meta:
meta.value = value meta.value = value
@ -280,7 +280,7 @@ class BibleDB(Manager, RegistryProperties):
:param book: The name of the book to return. :param book: The name of the book to return.
""" """
log.debug('BibleDB.get_book("%s")' % book) log.debug('BibleDB.get_book("{book}")'.format(book=book))
return self.get_object_filtered(Book, Book.name.like(book + '%')) return self.get_object_filtered(Book, Book.name.like(book + '%'))
def get_books(self): def get_books(self):
@ -297,11 +297,11 @@ class BibleDB(Manager, RegistryProperties):
:param ref_id: The reference id of the book to return. :param ref_id: The reference id of the book to return.
""" """
log.debug('BibleDB.get_book_by_book_ref_id("%s")' % ref_id) log.debug('BibleDB.get_book_by_book_ref_id("{ref}")'.format(ref=ref_id))
return self.get_object_filtered(Book, Book.book_reference_id.like(ref_id)) return self.get_object_filtered(Book, Book.book_reference_id.like(ref_id))
def get_book_ref_id_by_name(self, book, maxbooks, language_id=None): def get_book_ref_id_by_name(self, book, maxbooks, language_id=None):
log.debug('BibleDB.get_book_ref_id_by_name:("%s", "%s")' % (book, language_id)) log.debug('BibleDB.get_book_ref_id_by_name:("{book}", "{lang}")'.format(book=book, lang=language_id))
book_id = None book_id = None
if BiblesResourcesDB.get_book(book, True): if BiblesResourcesDB.get_book(book, True):
book_temp = BiblesResourcesDB.get_book(book, True) book_temp = BiblesResourcesDB.get_book(book, True)
@ -327,13 +327,14 @@ class BibleDB(Manager, RegistryProperties):
:param book: The name of the book, according to the selected language. :param book: The name of the book, according to the selected language.
:param language_selection: The language selection the user has chosen in the settings section of the Bible. :param language_selection: The language selection the user has chosen in the settings section of the Bible.
""" """
log.debug('get_book_ref_id_by_localised_name("%s", "%s")' % (book, language_selection)) log.debug('get_book_ref_id_by_localised_name("{book}", "{lang}")'.format(book=book, lang=language_selection))
from openlp.plugins.bibles.lib import LanguageSelection, BibleStrings from openlp.plugins.bibles.lib import LanguageSelection, BibleStrings
book_names = BibleStrings().BookNames book_names = BibleStrings().BookNames
# escape reserved characters # escape reserved characters
book_escaped = book book_escaped = book
for character in RESERVED_CHARACTERS: for character in RESERVED_CHARACTERS:
book_escaped = book_escaped.replace(character, '\\' + character) book_escaped = book_escaped.replace(character, '\\' + character)
# TODO: Verify regex patters before using format()
regex_book = re.compile('\s*%s\s*' % '\s*'.join( regex_book = re.compile('\s*%s\s*' % '\s*'.join(
book_escaped.split()), re.UNICODE | re.IGNORECASE) book_escaped.split()), re.UNICODE | re.IGNORECASE)
if language_selection == LanguageSelection.Bible: if language_selection == LanguageSelection.Bible:
@ -374,14 +375,14 @@ class BibleDB(Manager, RegistryProperties):
[('35', 1, 1, 1), ('35', 2, 2, 3)] [('35', 1, 1, 1), ('35', 2, 2, 3)]
:param show_error: :param show_error:
""" """
log.debug('BibleDB.get_verses("%s")' % reference_list) log.debug('BibleDB.get_verses("{ref}")'.format(ref=reference_list))
verse_list = [] verse_list = []
book_error = False book_error = False
for book_id, chapter, start_verse, end_verse in reference_list: for book_id, chapter, start_verse, end_verse in reference_list:
db_book = self.get_book_by_book_ref_id(book_id) db_book = self.get_book_by_book_ref_id(book_id)
if db_book: if db_book:
book_id = db_book.book_reference_id book_id = db_book.book_reference_id
log.debug('Book name corrected to "%s"' % db_book.name) log.debug('Book name corrected to "{book}"'.format(book=db_book.name))
if end_verse == -1: if end_verse == -1:
end_verse = self.get_verse_count(book_id, chapter) end_verse = self.get_verse_count(book_id, chapter)
verses = self.session.query(Verse) \ verses = self.session.query(Verse) \
@ -393,7 +394,7 @@ class BibleDB(Manager, RegistryProperties):
.all() .all()
verse_list.extend(verses) verse_list.extend(verses)
else: else:
log.debug('OpenLP failed to find book with id "%s"' % book_id) log.debug('OpenLP failed to find book with id "{book}"'.format(book=book_id))
book_error = True book_error = True
if book_error and show_error: if book_error and show_error:
critical_error_message_box( critical_error_message_box(
@ -412,8 +413,9 @@ class BibleDB(Manager, RegistryProperties):
contains spaces, it will split apart and AND'd on the list of contains spaces, it will split apart and AND'd on the list of
values. values.
""" """
log.debug('BibleDB.verse_search("%s")' % text) log.debug('BibleDB.verse_search("{text}")'.format(text=text))
verses = self.session.query(Verse) verses = self.session.query(Verse)
# TODO: Find out what this is doing before converting to format()
if text.find(',') > -1: if text.find(',') > -1:
keywords = ['%%%s%%' % keyword.strip() for keyword in text.split(',')] keywords = ['%%%s%%' % keyword.strip() for keyword in text.split(',')]
or_clause = [Verse.text.like(keyword) for keyword in keywords] or_clause = [Verse.text.like(keyword) for keyword in keywords]
@ -431,7 +433,7 @@ class BibleDB(Manager, RegistryProperties):
:param book: The book object to get the chapter count for. :param book: The book object to get the chapter count for.
""" """
log.debug('BibleDB.get_chapter_count("%s")' % book.name) log.debug('BibleDB.get_chapter_count("{book}")'.format(book=book.name))
count = self.session.query(func.max(Verse.chapter)).join(Book).filter( count = self.session.query(func.max(Verse.chapter)).join(Book).filter(
Book.book_reference_id == book.book_reference_id).scalar() Book.book_reference_id == book.book_reference_id).scalar()
if not count: if not count:
@ -445,7 +447,7 @@ class BibleDB(Manager, RegistryProperties):
:param book_ref_id: The book reference id. :param book_ref_id: The book reference id.
:param chapter: The chapter to get the verse count for. :param chapter: The chapter to get the verse count for.
""" """
log.debug('BibleDB.get_verse_count("%s", "%s")' % (book_ref_id, chapter)) log.debug('BibleDB.get_verse_count("{ref}", "{chapter}")'.format(ref=book_ref_id, chapter=chapter))
count = self.session.query(func.max(Verse.verse)).join(Book) \ count = self.session.query(func.max(Verse.verse)).join(Book) \
.filter(Book.book_reference_id == book_ref_id) \ .filter(Book.book_reference_id == book_ref_id) \
.filter(Verse.chapter == chapter) \ .filter(Verse.chapter == chapter) \
@ -551,7 +553,7 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
:param name: The name or abbreviation of the book. :param name: The name or abbreviation of the book.
:param lower: True if the comparison should be only lowercase :param lower: True if the comparison should be only lowercase
""" """
log.debug('BiblesResourcesDB.get_book("%s")' % name) log.debug('BiblesResourcesDB.get_book("{name}")'.format(name=name))
if not isinstance(name, str): if not isinstance(name, str):
name = str(name) name = str(name)
if lower: if lower:
@ -580,7 +582,7 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
:param string: The string to search for in the book names or abbreviations. :param string: The string to search for in the book names or abbreviations.
""" """
log.debug('BiblesResourcesDB.get_book_like("%s")' % string) log.debug('BiblesResourcesDB.get_book_like("{text}")'.format(text=string))
if not isinstance(string, str): if not isinstance(string, str):
name = str(string) name = str(string)
books = BiblesResourcesDB.run_sql( books = BiblesResourcesDB.run_sql(
@ -605,7 +607,7 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
:param book_id: The id of the book. :param book_id: The id of the book.
""" """
log.debug('BiblesResourcesDB.get_book_by_id("%s")' % book_id) log.debug('BiblesResourcesDB.get_book_by_id("{book}")'.format(book=book_id))
if not isinstance(book_id, int): if not isinstance(book_id, int):
book_id = int(book_id) book_id = int(book_id)
books = BiblesResourcesDB.run_sql( books = BiblesResourcesDB.run_sql(
@ -629,7 +631,7 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
:param book_ref_id: The id of a book. :param book_ref_id: The id of a book.
:param chapter: The chapter number. :param chapter: The chapter number.
""" """
log.debug('BiblesResourcesDB.get_chapter("%s", "%s")' % (book_ref_id, chapter)) log.debug('BiblesResourcesDB.get_chapter("{book}", "{ref}")'.format(book=book_ref_id, ref=chapter))
if not isinstance(chapter, int): if not isinstance(chapter, int):
chapter = int(chapter) chapter = int(chapter)
chapters = BiblesResourcesDB.run_sql( chapters = BiblesResourcesDB.run_sql(
@ -652,7 +654,7 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
:param book_ref_id: The id of the book. :param book_ref_id: The id of the book.
""" """
log.debug('BiblesResourcesDB.get_chapter_count("%s")' % book_ref_id) log.debug('BiblesResourcesDB.get_chapter_count("{ref}")'.format(ref=book_ref_id))
details = BiblesResourcesDB.get_book_by_id(book_ref_id) details = BiblesResourcesDB.get_book_by_id(book_ref_id)
if details: if details:
return details['chapters'] return details['chapters']
@ -666,7 +668,7 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
:param book_ref_id: The id of the book. :param book_ref_id: The id of the book.
:param chapter: The number of the chapter. :param chapter: The number of the chapter.
""" """
log.debug('BiblesResourcesDB.get_verse_count("%s", "%s")' % (book_ref_id, chapter)) log.debug('BiblesResourcesDB.get_verse_count("{ref}", "{chapter}")'.format(ref=book_ref_id, chapter=chapter))
details = BiblesResourcesDB.get_chapter(book_ref_id, chapter) details = BiblesResourcesDB.get_chapter(book_ref_id, chapter)
if details: if details:
return details['verse_count'] return details['verse_count']
@ -679,7 +681,7 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
:param source: The name or abbreviation of the book. :param source: The name or abbreviation of the book.
""" """
log.debug('BiblesResourcesDB.get_download_source("%s")' % source) log.debug('BiblesResourcesDB.get_download_source("{source}")'.format(source=source))
if not isinstance(source, str): if not isinstance(source, str):
source = str(source) source = str(source)
source = source.title() source = source.title()
@ -700,7 +702,7 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
:param source: The source of the web_bible. :param source: The source of the web_bible.
""" """
log.debug('BiblesResourcesDB.get_webbibles("%s")' % source) log.debug('BiblesResourcesDB.get_webbibles("{source}")'.format(source=source))
if not isinstance(source, str): if not isinstance(source, str):
source = str(source) source = str(source)
source = BiblesResourcesDB.get_download_source(source) source = BiblesResourcesDB.get_download_source(source)
@ -725,7 +727,7 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
:param abbreviation: The abbreviation of the web_bible. :param abbreviation: The abbreviation of the web_bible.
:param source: The source of the web_bible. :param source: The source of the web_bible.
""" """
log.debug('BiblesResourcesDB.get_webbibles("%s", "%s")' % (abbreviation, source)) log.debug('BiblesResourcesDB.get_webbibles("{text}", "{source}")'.format(text=abbreviation, source=source))
if not isinstance(abbreviation, str): if not isinstance(abbreviation, str):
abbreviation = str(abbreviation) abbreviation = str(abbreviation)
if not isinstance(source, str): if not isinstance(source, str):
@ -753,7 +755,7 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
:param name: The name to search the id. :param name: The name to search the id.
:param language_id: The language_id for which language should be searched :param language_id: The language_id for which language should be searched
""" """
log.debug('BiblesResourcesDB.get_alternative_book_name("%s", "%s")' % (name, language_id)) log.debug('BiblesResourcesDB.get_alternative_book_name("{name}", "{lang}")'.format(name=name, lang=language_id))
if language_id: if language_id:
books = BiblesResourcesDB.run_sql( books = BiblesResourcesDB.run_sql(
'SELECT book_reference_id, name FROM alternative_book_names WHERE language_id = ? ORDER BY id', 'SELECT book_reference_id, name FROM alternative_book_names WHERE language_id = ? ORDER BY id',
@ -772,7 +774,7 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
:param name: The name or abbreviation of the language. :param name: The name or abbreviation of the language.
""" """
log.debug('BiblesResourcesDB.get_language("%s")' % name) log.debug('BiblesResourcesDB.get_language("{name}")'.format(name=name))
if not isinstance(name, str): if not isinstance(name, str):
name = str(name) name = str(name)
language = BiblesResourcesDB.run_sql( language = BiblesResourcesDB.run_sql(
@ -868,7 +870,7 @@ class AlternativeBookNamesDB(QtCore.QObject, Manager):
:param name: The name to search the id. :param name: The name to search the id.
:param language_id: The language_id for which language should be searched :param language_id: The language_id for which language should be searched
""" """
log.debug('AlternativeBookNamesDB.get_book_reference_id("%s", "%s")' % (name, language_id)) log.debug('AlternativeBookNamesDB.get_book_reference_id("{name}", "{ref}")'.format(name=name, ref=language_id))
if language_id: if language_id:
books = AlternativeBookNamesDB.run_sql( books = AlternativeBookNamesDB.run_sql(
'SELECT book_reference_id, name FROM alternative_book_names WHERE language_id = ?', (language_id, )) 'SELECT book_reference_id, name FROM alternative_book_names WHERE language_id = ?', (language_id, ))
@ -889,8 +891,8 @@ class AlternativeBookNamesDB(QtCore.QObject, Manager):
:param book_reference_id: The book_reference_id of the book. :param book_reference_id: The book_reference_id of the book.
:param language_id: The language to which the alternative book name belong. :param language_id: The language to which the alternative book name belong.
""" """
log.debug('AlternativeBookNamesDB.create_alternative_book_name("%s", "%s", "%s")' % log.debug('AlternativeBookNamesDB.create_alternative_book_name("{name}", '
(name, book_reference_id, language_id)) '"{ref}", "{lang}")'.format(name=name, ref=book_reference_id, lang=language_id))
return AlternativeBookNamesDB.run_sql( return AlternativeBookNamesDB.run_sql(
'INSERT INTO alternative_book_names(book_reference_id, language_id, name) ' 'INSERT INTO alternative_book_names(book_reference_id, language_id, name) '
'VALUES (?, ?, ?)', (book_reference_id, language_id, name), True) 'VALUES (?, ?, ?)', (book_reference_id, language_id, name), True)

View File

@ -90,7 +90,7 @@ class BGExtract(RegistryProperties):
Extract verses from BibleGateway Extract verses from BibleGateway
""" """
def __init__(self, proxy_url=None): def __init__(self, proxy_url=None):
log.debug('BGExtract.init("%s")', proxy_url) log.debug('BGExtract.init("{url}")'.format(url=proxy_url))
self.proxy_url = proxy_url self.proxy_url = proxy_url
socket.setdefaulttimeout(30) socket.setdefaulttimeout(30)
@ -188,7 +188,7 @@ class BGExtract(RegistryProperties):
if len(verse_parts) > 1: if len(verse_parts) > 1:
verse = int(verse_parts[0]) verse = int(verse_parts[0])
except TypeError: except TypeError:
log.warning('Illegal verse number: %s', str(verse)) log.warning('Illegal verse number: {verse:d}'.format(verse=verse))
verses.append((verse, text)) verses.append((verse, text))
verse_list = {} verse_list = {}
for verse, text in verses[::-1]: for verse, text in verses[::-1]:
@ -221,7 +221,7 @@ class BGExtract(RegistryProperties):
if len(verse_parts) > 1: if len(verse_parts) > 1:
clean_verse_num = int(verse_parts[0]) clean_verse_num = int(verse_parts[0])
except TypeError: except TypeError:
log.warning('Illegal verse number: %s', str(raw_verse_num)) log.warning('Illegal verse number: {verse:d}'.format(verse=raw_verse_num))
if clean_verse_num: if clean_verse_num:
verse_text = raw_verse_num.next_element verse_text = raw_verse_num.next_element
part = raw_verse_num.next_element.next_element part = raw_verse_num.next_element.next_element
@ -244,11 +244,15 @@ class BGExtract(RegistryProperties):
:param book_name: Name of the Book. :param book_name: Name of the Book.
:param chapter: Chapter number. :param chapter: Chapter number.
""" """
log.debug('BGExtract.get_bible_chapter("%s", "%s", "%s")', version, book_name, chapter) log.debug('BGExtract.get_bible_chapter("{version}", "{name}", "{chapter}")'.format(version=version,
name=book_name,
chapter=chapter))
url_book_name = urllib.parse.quote(book_name.encode("utf-8")) url_book_name = urllib.parse.quote(book_name.encode("utf-8"))
url_params = 'search=%s+%s&version=%s' % (url_book_name, chapter, version) url_params = 'search={name}+{chapter}&version={version}'.format(name=url_book_name,
chapter=chapter,
version=version)
soup = get_soup_for_bible_ref( soup = get_soup_for_bible_ref(
'http://legacy.biblegateway.com/passage/?%s' % url_params, 'http://legacy.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
@ -257,7 +261,7 @@ class BGExtract(RegistryProperties):
return None return None
self._clean_soup(div) self._clean_soup(div)
span_list = div.find_all('span', 'text') span_list = div.find_all('span', 'text')
log.debug('Span list: %s', span_list) log.debug('Span list: {span}'.format(span=span_list))
if not span_list: if not span_list:
# If we don't get any spans then we must have the old HTML format # If we don't get any spans then we must have the old HTML format
verse_list = self._extract_verses_old(div) verse_list = self._extract_verses_old(div)
@ -275,9 +279,9 @@ class BGExtract(RegistryProperties):
:param version: The version of the Bible like NIV for New International Version :param version: The version of the Bible like NIV for New International Version
""" """
log.debug('BGExtract.get_books_from_http("%s")', version) log.debug('BGExtract.get_books_from_http("{version}")'.format(version=version))
url_params = urllib.parse.urlencode({'action': 'getVersionInfo', 'vid': '%s' % version}) url_params = urllib.parse.urlencode({'action': 'getVersionInfo', 'vid': '{version}'.format(version=version)})
reference_url = 'http://legacy.biblegateway.com/versions/?%s#books' % url_params reference_url = 'http://legacy.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')
@ -353,7 +357,7 @@ class BSExtract(RegistryProperties):
Extract verses from Bibleserver.com Extract verses from Bibleserver.com
""" """
def __init__(self, proxy_url=None): def __init__(self, proxy_url=None):
log.debug('BSExtract.init("%s")', proxy_url) log.debug('BSExtract.init("{url}")'.format(url=proxy_url))
self.proxy_url = proxy_url self.proxy_url = proxy_url
socket.setdefaulttimeout(30) socket.setdefaulttimeout(30)
@ -365,10 +369,14 @@ class BSExtract(RegistryProperties):
:param book_name: Text name of bible book e.g. Genesis, 1. John, 1John or Offenbarung :param book_name: Text name of bible book e.g. Genesis, 1. John, 1John or Offenbarung
:param chapter: Chapter number :param chapter: Chapter number
""" """
log.debug('BSExtract.get_bible_chapter("%s", "%s", "%s")', version, book_name, chapter) log.debug('BSExtract.get_bible_chapter("{version}", "{book}", "{chapter}")'.format(version=version,
book=book_name,
chapter=chapter))
url_version = urllib.parse.quote(version.encode("utf-8")) url_version = urllib.parse.quote(version.encode("utf-8"))
url_book_name = urllib.parse.quote(book_name.encode("utf-8")) url_book_name = urllib.parse.quote(book_name.encode("utf-8"))
chapter_url = 'http://m.bibleserver.com/text/%s/%s%d' % (url_version, url_book_name, chapter) chapter_url = 'http://m.bibleserver.com/text/{version}/{name}{chapter:d}'.format(version=url_version,
name=url_book_name,
chapter=chapter)
header = ('Accept-Language', 'en') header = ('Accept-Language', 'en')
soup = get_soup_for_bible_ref(chapter_url, header) soup = get_soup_for_bible_ref(chapter_url, header)
if not soup: if not soup:
@ -393,9 +401,9 @@ class BSExtract(RegistryProperties):
:param version: The version of the Bible like NIV for New International Version :param version: The version of the Bible like NIV for New International Version
""" """
log.debug('BSExtract.get_books_from_http("%s")', version) log.debug('BSExtract.get_books_from_http("{version}")'.format(version=version))
url_version = urllib.parse.quote(version.encode("utf-8")) url_version = urllib.parse.quote(version.encode("utf-8"))
chapter_url = 'http://m.bibleserver.com/overlay/selectBook?translation=%s' % url_version chapter_url = 'http://m.bibleserver.com/overlay/selectBook?translation={version}'.format(version=url_version)
soup = get_soup_for_bible_ref(chapter_url) soup = get_soup_for_bible_ref(chapter_url)
if not soup: if not soup:
return None return None
@ -450,7 +458,7 @@ class CWExtract(RegistryProperties):
Extract verses from CrossWalk/BibleStudyTools Extract verses from CrossWalk/BibleStudyTools
""" """
def __init__(self, proxy_url=None): def __init__(self, proxy_url=None):
log.debug('CWExtract.init("%s")', proxy_url) log.debug('CWExtract.init("{url}")'.format(url=proxy_url))
self.proxy_url = proxy_url self.proxy_url = proxy_url
socket.setdefaulttimeout(30) socket.setdefaulttimeout(30)
@ -462,11 +470,15 @@ class CWExtract(RegistryProperties):
:param book_name: Text name of in english e.g. 'gen' for Genesis :param book_name: Text name of in english e.g. 'gen' for Genesis
:param chapter: Chapter number :param chapter: Chapter number
""" """
log.debug('CWExtract.get_bible_chapter("%s", "%s", "%s")', version, book_name, chapter) log.debug('CWExtract.get_bible_chapter("{version}", "{book}", "{chapter}")'.format(version=version,
book=book_name,
chapter=chapter))
url_book_name = book_name.replace(' ', '-') url_book_name = book_name.replace(' ', '-')
url_book_name = url_book_name.lower() url_book_name = url_book_name.lower()
url_book_name = urllib.parse.quote(url_book_name.encode("utf-8")) url_book_name = urllib.parse.quote(url_book_name.encode("utf-8"))
chapter_url = 'http://www.biblestudytools.com/%s/%s/%s.html' % (version, url_book_name, chapter) chapter_url = 'http://www.biblestudytools.com/{version}/{book}/{chapter}.html'.format(version=version,
book=url_book_name,
chapter=chapter)
soup = get_soup_for_bible_ref(chapter_url) soup = get_soup_for_bible_ref(chapter_url)
if not soup: if not soup:
return None return None
@ -499,8 +511,8 @@ class CWExtract(RegistryProperties):
:param version: The version of the bible like NIV for New International Version :param version: The version of the bible like NIV for New International Version
""" """
log.debug('CWExtract.get_books_from_http("%s")', version) log.debug('CWExtract.get_books_from_http("{version}")'.format(version=version))
chapter_url = 'http://www.biblestudytools.com/%s/' % version chapter_url = 'http://www.biblestudytools.com/{version}/'.format(version=version)
soup = get_soup_for_bible_ref(chapter_url) soup = get_soup_for_bible_ref(chapter_url)
if not soup: if not soup:
return None return None
@ -559,7 +571,7 @@ class CWExtract(RegistryProperties):
class HTTPBible(BibleDB, RegistryProperties): class HTTPBible(BibleDB, RegistryProperties):
log.info('%s HTTPBible loaded', __name__) log.info('{name} HTTPBible loaded'.format(name=__name__))
def __init__(self, parent, **kwargs): def __init__(self, parent, **kwargs):
""" """
@ -615,8 +627,8 @@ class HTTPBible(BibleDB, RegistryProperties):
handler = BSExtract(self.proxy_server) handler = BSExtract(self.proxy_server)
books = handler.get_books_from_http(self.download_name) books = handler.get_books_from_http(self.download_name)
if not books: if not books:
log.error('Importing books from %s - download name: "%s" failed' % log.error('Importing books from {source} - download name: "{name}" '
(self.download_source, self.download_name)) 'failed'.format(source=self.download_source, name=self.download_name))
return False return False
self.wizard.progress_bar.setMaximum(len(books) + 2) self.wizard.progress_bar.setMaximum(len(books) + 2)
self.wizard.increment_progress_bar(translate('BiblesPlugin.HTTPBible', 'Registering Language...')) self.wizard.increment_progress_bar(translate('BiblesPlugin.HTTPBible', 'Registering Language...'))
@ -625,21 +637,24 @@ class HTTPBible(BibleDB, RegistryProperties):
else: else:
self.language_id = self.get_language(bible_name) self.language_id = self.get_language(bible_name)
if not self.language_id: if not self.language_id:
log.error('Importing books from %s failed' % self.filename) log.error('Importing books from {name} failed'.format(name=self.filename))
return False return False
for book in books: for book in books:
if self.stop_import_flag: if self.stop_import_flag:
break break
self.wizard.increment_progress_bar(translate( self.wizard.increment_progress_bar(translate('BiblesPlugin.HTTPBible',
'BiblesPlugin.HTTPBible', 'Importing %s...', 'Importing <book name>...') % book) 'Importing {book}...',
'Importing <book name>...').format(book=book))
book_ref_id = self.get_book_ref_id_by_name(book, len(books), self.language_id) book_ref_id = self.get_book_ref_id_by_name(book, len(books), self.language_id)
if not book_ref_id: if not book_ref_id:
log.error('Importing books from %s - download name: "%s" failed' % log.error('Importing books from {source} - download name: "{name}" '
(self.download_source, self.download_name)) 'failed'.format(source=self.download_source, name=self.download_name))
return False return False
book_details = BiblesResourcesDB.get_book_by_id(book_ref_id) book_details = BiblesResourcesDB.get_book_by_id(book_ref_id)
log.debug('Book details: Name:%s; id:%s; testament_id:%s', log.debug('Book details: Name:{book}; id:{ref}; '
book, book_ref_id, book_details['testament_id']) 'testament_id:{detail}'.format(book=book,
ref=book_ref_id,
detail=book_details['testament_id']))
self.create_book(book, book_ref_id, book_details['testament_id']) self.create_book(book, book_ref_id, book_details['testament_id'])
if self.stop_import_flag: if self.stop_import_flag:
return False return False
@ -664,7 +679,7 @@ class HTTPBible(BibleDB, RegistryProperties):
[('35', 1, 1, 1), ('35', 2, 2, 3)] [('35', 1, 1, 1), ('35', 2, 2, 3)]
""" """
log.debug('HTTPBible.get_verses("%s")', reference_list) log.debug('HTTPBible.get_verses("{ref}")'.format(ref=reference_list))
for reference in reference_list: for reference in reference_list:
book_id = reference[0] book_id = reference[0]
db_book = self.get_book_by_book_ref_id(book_id) db_book = self.get_book_by_book_ref_id(book_id)
@ -698,8 +713,8 @@ class HTTPBible(BibleDB, RegistryProperties):
""" """
Receive the request and call the relevant handler methods. Receive the request and call the relevant handler methods.
""" """
log.debug('HTTPBible.get_chapter("%s", "%s")', book, chapter) log.debug('HTTPBible.get_chapter("{book}", "{chapter}")'.format(book=book, chapter=chapter))
log.debug('source = %s', self.download_source) log.debug('source = {source}'.format(source=self.download_source))
if self.download_source.lower() == 'crosswalk': if self.download_source.lower() == 'crosswalk':
handler = CWExtract(self.proxy_server) handler = CWExtract(self.proxy_server)
elif self.download_source.lower() == 'biblegateway': elif self.download_source.lower() == 'biblegateway':
@ -712,7 +727,7 @@ class HTTPBible(BibleDB, RegistryProperties):
""" """
Return the list of books. Return the list of books.
""" """
log.debug('HTTPBible.get_books("%s")', Book.name) log.debug('HTTPBible.get_books("{name}")'.format(name=Book.name))
return self.get_all_objects(Book, order_by_ref=Book.id) return self.get_all_objects(Book, order_by_ref=Book.id)
def get_chapter_count(self, book): def get_chapter_count(self, book):
@ -721,7 +736,7 @@ class HTTPBible(BibleDB, RegistryProperties):
:param book: The book object to get the chapter count for. :param book: The book object to get the chapter count for.
""" """
log.debug('HTTPBible.get_chapter_count("%s")', book.name) log.debug('HTTPBible.get_chapter_count("{name}")'.format(name=book.name))
return BiblesResourcesDB.get_chapter_count(book.book_reference_id) return BiblesResourcesDB.get_chapter_count(book.book_reference_id)
def get_verse_count(self, book_id, chapter): def get_verse_count(self, book_id, chapter):
@ -731,7 +746,7 @@ class HTTPBible(BibleDB, RegistryProperties):
:param book_id: The name of the book. :param book_id: The name of the book.
:param chapter: The chapter whose verses are being counted. :param chapter: The chapter whose verses are being counted.
""" """
log.debug('HTTPBible.get_verse_count("%s", %s)', book_id, chapter) log.debug('HTTPBible.get_verse_count("{ref}", {chapter})'.format(ref=book_id, chapter=chapter))
return BiblesResourcesDB.get_verse_count(book_id, chapter) return BiblesResourcesDB.get_verse_count(book_id, chapter)

View File

@ -122,7 +122,7 @@ class BibleManager(RegistryProperties):
files = AppLocation.get_files(self.settings_section, self.suffix) files = AppLocation.get_files(self.settings_section, self.suffix)
if 'alternative_book_names.sqlite' in files: if 'alternative_book_names.sqlite' in files:
files.remove('alternative_book_names.sqlite') files.remove('alternative_book_names.sqlite')
log.debug('Bible Files %s', files) log.debug('Bible Files {text}'.format(text=files))
self.db_cache = {} self.db_cache = {}
self.old_bible_databases = [] self.old_bible_databases = []
for filename in files: for filename in files:
@ -135,7 +135,7 @@ class BibleManager(RegistryProperties):
bible.session.close() bible.session.close()
delete_file(os.path.join(self.path, filename)) delete_file(os.path.join(self.path, filename))
continue continue
log.debug('Bible Name: "%s"', name) log.debug('Bible Name: "{name}"'.format(name=name))
self.db_cache[name] = bible self.db_cache[name] = bible
# Look to see if lazy load bible exists and get create getter. # Look to see if lazy load bible exists and get create getter.
source = self.db_cache[name].get_object(BibleMeta, 'download_source') source = self.db_cache[name].get_object(BibleMeta, 'download_source')
@ -177,7 +177,7 @@ class BibleManager(RegistryProperties):
:param name: The name of the bible. :param name: The name of the bible.
""" """
log.debug('BibleManager.delete_bible("%s")', name) log.debug('BibleManager.delete_bible("{name}")'.format(name=name))
bible = self.db_cache[name] bible = self.db_cache[name]
bible.session.close() bible.session.close()
bible.session = None bible.session = None
@ -196,7 +196,7 @@ class BibleManager(RegistryProperties):
:param bible: Unicode. The Bible to get the list of books from. :param bible: Unicode. The Bible to get the list of books from.
""" """
log.debug('BibleManager.get_books("%s")', bible) log.debug('BibleManager.get_books("{bible}")'.format(bible=bible))
return [ return [
{ {
'name': book.name, 'name': book.name,
@ -213,7 +213,7 @@ class BibleManager(RegistryProperties):
:param bible: Unicode. The Bible to get the list of books from. :param bible: Unicode. The Bible to get the list of books from.
:param id: Unicode. The book_reference_id to get the book for. :param id: Unicode. The book_reference_id to get the book for.
""" """
log.debug('BibleManager.get_book_by_id("%s", "%s")', bible, id) log.debug('BibleManager.get_book_by_id("{bible}", "{ref}")'.format(bible=bible, ref=id))
return self.db_cache[bible].get_book_by_book_ref_id(id) return self.db_cache[bible].get_book_by_book_ref_id(id)
def get_chapter_count(self, bible, book): def get_chapter_count(self, bible, book):
@ -223,14 +223,16 @@ class BibleManager(RegistryProperties):
:param bible: Unicode. The Bible to get the list of books from. :param bible: Unicode. The Bible to get the list of books from.
:param book: The book object to get the chapter count for. :param book: The book object to get the chapter count for.
""" """
log.debug('BibleManager.get_book_chapter_count ("%s", "%s")', bible, book.name) log.debug('BibleManager.get_book_chapter_count ("{bible}", "{name}")'.format(bible=bible, name=book.name))
return self.db_cache[bible].get_chapter_count(book) return self.db_cache[bible].get_chapter_count(book)
def get_verse_count(self, bible, book, chapter): def get_verse_count(self, bible, book, chapter):
""" """
Returns all the number of verses for a given book and chapterMaxBibleBookVerses. Returns all the number of verses for a given book and chapterMaxBibleBookVerses.
""" """
log.debug('BibleManager.get_verse_count("%s", "%s", %s)', bible, book, chapter) log.debug('BibleManager.get_verse_count("{bible}", "{book}", {chapter})'.format(bible=bible,
book=book,
chapter=chapter))
language_selection = self.get_language_selection(bible) language_selection = self.get_language_selection(bible)
book_ref_id = self.db_cache[bible].get_book_ref_id_by_localised_name(book, language_selection) book_ref_id = self.db_cache[bible].get_book_ref_id_by_localised_name(book, language_selection)
return self.db_cache[bible].get_verse_count(book_ref_id, chapter) return self.db_cache[bible].get_verse_count(book_ref_id, chapter)
@ -240,7 +242,8 @@ class BibleManager(RegistryProperties):
Returns all the number of verses for a given Returns all the number of verses for a given
book_ref_id and chapterMaxBibleBookVerses. book_ref_id and chapterMaxBibleBookVerses.
""" """
log.debug('BibleManager.get_verse_count_by_book_ref_id("%s", "%s", "%s")', bible, book_ref_id, chapter) log.debug('BibleManager.get_verse_count_by_book_ref_id("{bible}", '
'"{book}", "{chapter}")'.format(bible=bible, book=book_ref_id, chapter=chapter))
return self.db_cache[bible].get_verse_count(book_ref_id, chapter) return self.db_cache[bible].get_verse_count(book_ref_id, chapter)
def get_verses(self, bible, verse_text, book_ref_id=False, show_error=True): def get_verses(self, bible, verse_text, book_ref_id=False, show_error=True):
@ -264,8 +267,8 @@ class BibleManager(RegistryProperties):
For second bible this is necessary. For second bible this is necessary.
:param show_error: :param show_error:
""" """
log.debug('BibleManager.get_verses("%s", "%s")', bible, verse_text)
# If no bibles are installed, message is given. # If no bibles are installed, message is given.
log.debug('BibleManager.get_verses("{bible}", "{verse}")'.format(bible=bible, verse=verse_text))
if not bible: if not bible:
if show_error: if show_error:
self.main_window.information_message( self.main_window.information_message(
@ -287,7 +290,7 @@ class BibleManager(RegistryProperties):
:param bible: Unicode. The Bible to get the language selection from. :param bible: Unicode. The Bible to get the language selection from.
""" """
log.debug('BibleManager.get_language_selection("%s")', bible) log.debug('BibleManager.get_language_selection("{bible}")'.format(bible=bible))
language_selection = self.get_meta_data(bible, 'book_name_language') language_selection = self.get_meta_data(bible, 'book_name_language')
if not language_selection or language_selection.value == "None" or language_selection.value == "-1": if not language_selection or language_selection.value == "None" or language_selection.value == "-1":
# If None is returned, it's not the singleton object but a # If None is returned, it's not the singleton object but a
@ -309,7 +312,7 @@ class BibleManager(RegistryProperties):
:param second_bible: The second bible (unicode). We do not search in this bible. :param second_bible: The second bible (unicode). We do not search in this bible.
:param text: The text to search for (unicode). :param text: The text to search for (unicode).
""" """
log.debug('BibleManager.verse_search("%s", "%s")', bible, text) log.debug('BibleManager.verse_search("{bible}", "{text}")'.format(bible=bible, text=text))
# If no bibles are installed, message is given. # If no bibles are installed, message is given.
if not bible: if not bible:
self.main_window.information_message( self.main_window.information_message(
@ -355,12 +358,8 @@ class BibleManager(RegistryProperties):
:param second_bible: The second bible (unicode). We do not search in this bible. :param second_bible: The second bible (unicode). We do not search in this bible.
:param text: The text to search for (unicode). :param text: The text to search for (unicode).
""" """
log.debug('BibleManager.verse_search("%s", "%s")', bible, text)
# If no bibles are installed, message is given. # If no bibles are installed, message is given.
if not bible: if not bible:
self.main_window.information_message(
UiStrings().BibleNoBiblesTitle,
UiStrings().BibleNoBibles)
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')
@ -381,7 +380,10 @@ class BibleManager(RegistryProperties):
""" """
Saves the bibles meta data. Saves the bibles meta data.
""" """
log.debug('save_meta data %s, %s, %s, %s', bible, version, copyright, permissions) log.debug('save_meta data {bible}, {version}, {copyright}, {perms}'.format(bible=bible,
version=version,
cr=copyright,
perms=permissions))
self.db_cache[bible].save_meta('name', version) self.db_cache[bible].save_meta('name', version)
self.db_cache[bible].save_meta('copyright', copyright) self.db_cache[bible].save_meta('copyright', copyright)
self.db_cache[bible].save_meta('permissions', permissions) self.db_cache[bible].save_meta('permissions', permissions)
@ -391,14 +393,14 @@ class BibleManager(RegistryProperties):
""" """
Returns the meta data for a given key. Returns the meta data for a given key.
""" """
log.debug('get_meta %s,%s', bible, key) log.debug('get_meta {bible},{key}'.format(bible=bible, key=key))
return self.db_cache[bible].get_object(BibleMeta, key) return self.db_cache[bible].get_object(BibleMeta, key)
def update_book(self, bible, book): def update_book(self, bible, book):
""" """
Update a book of the bible. Update a book of the bible.
""" """
log.debug('BibleManager.update_book("%s", "%s")', bible, book.name) log.debug('BibleManager.update_book("{bible}", "{name}")'.format(bible=bible, name=book.name))
self.db_cache[bible].update_book(book) self.db_cache[bible].update_book(book)
def exists(self, name): def exists(self, name):
@ -408,7 +410,7 @@ class BibleManager(RegistryProperties):
if not isinstance(name, str): if not isinstance(name, str):
name = str(name) name = str(name)
for bible in list(self.db_cache.keys()): for bible in list(self.db_cache.keys()):
log.debug('Bible from cache in is_new_bible %s', bible) log.debug('Bible from cache in is_new_bible {bible}'.format(bible=bible))
if not isinstance(bible, str): if not isinstance(bible, str):
bible = str(bible) bible = str(bible)
if bible == name: if bible == name:

View File

@ -283,7 +283,7 @@ class BibleMediaItem(MediaManagerItem):
def retranslateUi(self): def retranslateUi(self):
log.debug('retranslateUi') log.debug('retranslateUi')
self.quick_search_label.setText(translate('BiblesPlugin.MediaItem', 'Find:')) self.quick_search_label.setText(translate('BiblesPlugin.MediaItem', 'Find:'))
self.quickVersionLabel.setText('%s:' % UiStrings().Version) self.quickVersionLabel.setText('{version}:'.format(version=UiStrings().Version))
self.quickSecondLabel.setText(translate('BiblesPlugin.MediaItem', 'Second:')) self.quickSecondLabel.setText(translate('BiblesPlugin.MediaItem', 'Second:'))
self.quickStyleLabel.setText(UiStrings().LayoutStyle) self.quickStyleLabel.setText(UiStrings().LayoutStyle)
self.quickStyleComboBox.setItemText(LayoutStyle.VersePerSlide, UiStrings().VersePerSlide) self.quickStyleComboBox.setItemText(LayoutStyle.VersePerSlide, UiStrings().VersePerSlide)
@ -297,7 +297,7 @@ class BibleMediaItem(MediaManagerItem):
self.advanced_verse_label.setText(translate('BiblesPlugin.MediaItem', 'Verse:')) self.advanced_verse_label.setText(translate('BiblesPlugin.MediaItem', 'Verse:'))
self.advanced_from_label.setText(translate('BiblesPlugin.MediaItem', 'From:')) self.advanced_from_label.setText(translate('BiblesPlugin.MediaItem', 'From:'))
self.advanced_to_label.setText(translate('BiblesPlugin.MediaItem', 'To:')) self.advanced_to_label.setText(translate('BiblesPlugin.MediaItem', 'To:'))
self.advancedVersionLabel.setText('%s:' % UiStrings().Version) self.advancedVersionLabel.setText('{version}:'.format(version=UiStrings().Version))
self.advancedSecondLabel.setText(translate('BiblesPlugin.MediaItem', 'Second:')) self.advancedSecondLabel.setText(translate('BiblesPlugin.MediaItem', 'Second:'))
self.advancedStyleLabel.setText(UiStrings().LayoutStyle) self.advancedStyleLabel.setText(UiStrings().LayoutStyle)
self.advancedStyleComboBox.setItemText(LayoutStyle.VersePerSlide, UiStrings().VersePerSlide) self.advancedStyleComboBox.setItemText(LayoutStyle.VersePerSlide, UiStrings().VersePerSlide)
@ -322,7 +322,8 @@ class BibleMediaItem(MediaManagerItem):
translate('BiblesPlugin.MediaItem', 'Text Search'), translate('BiblesPlugin.MediaItem', 'Text Search'),
translate('BiblesPlugin.MediaItem', 'Search Text...')) translate('BiblesPlugin.MediaItem', 'Search Text...'))
]) ])
self.quick_search_edit.set_current_search_type(Settings().value('%s/last search type' % self.settings_section)) text = self.settings_section
self.quick_search_edit.set_current_search_type(Settings().value('{text}/last search type'.format(text=text)))
self.config_update() self.config_update()
log.debug('bible manager initialise complete') log.debug('bible manager initialise complete')
@ -370,7 +371,7 @@ class BibleMediaItem(MediaManagerItem):
:param bible: The bible to initialise (unicode). :param bible: The bible to initialise (unicode).
:param last_book_id: The "book reference id" of the book which is chosen at the moment. (int) :param last_book_id: The "book reference id" of the book which is chosen at the moment. (int)
""" """
log.debug('initialise_advanced_bible %s, %s', bible, last_book_id) log.debug('initialise_advanced_bible {bible}, {ref}'.format(bible=bible, ref=last_book_id))
book_data = self.plugin.manager.get_books(bible) book_data = self.plugin.manager.get_books(bible)
second_bible = self.advancedSecondComboBox.currentText() second_bible = self.advancedSecondComboBox.currentText()
if second_bible != '': if second_bible != '':
@ -412,7 +413,7 @@ class BibleMediaItem(MediaManagerItem):
self.initialise_chapter_verse(bible, first_book['name'], first_book['book_reference_id']) self.initialise_chapter_verse(bible, first_book['name'], first_book['book_reference_id'])
def initialise_chapter_verse(self, bible, book, book_ref_id): def initialise_chapter_verse(self, bible, book, book_ref_id):
log.debug('initialise_chapter_verse %s, %s, %s', bible, book, book_ref_id) log.debug('initialise_chapter_verse {bible}, {book}, {ref}'.format(bible=bible, book=book, ref=book_ref_id))
book = self.plugin.manager.get_book_by_id(bible, book_ref_id) book = self.plugin.manager.get_book_by_id(bible, book_ref_id)
self.chapter_count = self.plugin.manager.get_chapter_count(bible, book) self.chapter_count = self.plugin.manager.get_chapter_count(bible, book)
verse_count = self.plugin.manager.get_verse_count_by_book_ref_id(bible, book_ref_id, 1) verse_count = self.plugin.manager.get_verse_count_by_book_ref_id(bible, book_ref_id, 1)
@ -434,13 +435,16 @@ class BibleMediaItem(MediaManagerItem):
log.debug('update_auto_completer') log.debug('update_auto_completer')
# Save the current search type to the configuration. If setting for automatically resetting the search type to # Save the current search type to the configuration. If setting for automatically resetting the search type to
# Combined is enabled, use that otherwise use the currently selected search type. # Combined is enabled, use that otherwise use the currently selected search type.
# Note: This setting requires a restart to take effect.
if Settings().value(self.settings_section + '/reset to combined quick search'): if Settings().value(self.settings_section + '/reset to combined quick search'):
Settings().setValue('%s/last search type' % self.settings_section, BibleSearch.Combined) Settings().setValue('{section}/last search type'.format(section=self.settings_section),
BibleSearch.Combined)
else: else:
Settings().setValue('%s/last search type' % self.settings_section, Settings().setValue('{section}/last search type'.format(section=self.settings_section),
self.quick_search_edit.current_search_type()) self.quick_search_edit.current_search_type())
# Save the current bible to the configuration. # Save the current bible to the configuration.
Settings().setValue(self.settings_section + '/quick bible', self.quickVersionComboBox.currentText()) Settings().setValue('{section}/quick bible'.format(section=self.settings_section),
self.quickVersionComboBox.currentText())
books = [] books = []
# We have to do a 'Reference Search' (Or as part of Combined Search). # We have to do a 'Reference Search' (Or as part of Combined Search).
if self.quick_search_edit.current_search_type() is not BibleSearch.Text: if self.quick_search_edit.current_search_type() is not BibleSearch.Text:
@ -513,9 +517,10 @@ class BibleMediaItem(MediaManagerItem):
if bible: if bible:
if QtWidgets.QMessageBox.question( if QtWidgets.QMessageBox.question(
self, UiStrings().ConfirmDelete, self, UiStrings().ConfirmDelete,
translate('BiblesPlugin.MediaItem', 'Are you sure you want to completely delete "%s" Bible from ' translate('BiblesPlugin.MediaItem',
'OpenLP?\n\nYou will need to re-import this Bible to use it ' 'Are you sure you want to completely delete "{bible}" Bible '
'again.') % bible, 'from OpenLP?\n\nYou will need to re-import this Bible to use it '
'again.').format(bible=bible),
QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No), QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No),
QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.No: QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.No:
return return
@ -617,7 +622,7 @@ class BibleMediaItem(MediaManagerItem):
:param combo: The combo box itself (QComboBox). :param combo: The combo box itself (QComboBox).
:param restore: If True, then the combo's currentText will be restored after adjusting (if possible). :param restore: If True, then the combo's currentText will be restored after adjusting (if possible).
""" """
log.debug('adjust_combo_box %s, %s, %s', combo, range_from, range_to) log.debug('adjust_combo_box {box}, {start}, {end}'.format(box=combo, start=range_from, end=range_to))
if restore: if restore:
old_text = combo.currentText() old_text = combo.currentText()
combo.clear() combo.clear()
@ -644,7 +649,7 @@ class BibleMediaItem(MediaManagerItem):
range_separator = get_reference_separator('sep_r_display') range_separator = get_reference_separator('sep_r_display')
verse_range = chapter_from + verse_separator + verse_from + range_separator + chapter_to + \ verse_range = chapter_from + verse_separator + verse_from + range_separator + chapter_to + \
verse_separator + verse_to verse_separator + verse_to
verse_text = '%s %s' % (book, verse_range) verse_text = '{book} {verse}'.format(book=book, verse=verse_range)
self.application.set_busy_cursor() self.application.set_busy_cursor()
self.search_results = self.plugin.manager.get_verses(bible, verse_text, book_ref_id) self.search_results = self.plugin.manager.get_verses(bible, verse_text, book_ref_id)
if second_bible: if second_bible:
@ -707,9 +712,8 @@ class BibleMediaItem(MediaManagerItem):
for verse in self.search_results: for verse in self.search_results:
db_book = bibles[second_bible].get_book_by_book_ref_id(verse.book.book_reference_id) db_book = bibles[second_bible].get_book_by_book_ref_id(verse.book.book_reference_id)
if not db_book: if not db_book:
log.debug('Passage ("{versebookname}","{versechapter}","{verseverse}") not found in Second Bible' log.debug('Passage "{name} {chapter:d}:{verse:d}" not found in '
.format(versebookname=verse.book.name, versechapter='verse.chapter', 'Second Bible'.format(name=verse.book.name, chapter=verse.chapter, verse=verse.verse))
verseverse=verse.verse))
passage_not_found = True passage_not_found = True
count += 1 count += 1
continue continue
@ -721,7 +725,7 @@ class BibleMediaItem(MediaManagerItem):
translate('BiblesPlugin.MediaItem', 'Information'), translate('BiblesPlugin.MediaItem', 'Information'),
translate('BiblesPlugin.MediaItem', 'The second Bible does not contain all the verses ' translate('BiblesPlugin.MediaItem', 'The second Bible does not contain all the verses '
'that are in the main Bible.\nOnly verses found in both Bibles' 'that are in the main Bible.\nOnly verses found in both Bibles'
' will be shown.\n\n {count} verses have not been included ' ' will be shown.\n\n{count:d} verses have not been included '
'in the results.').format(count=count)) 'in the results.').format(count=count))
# Join the searches so only verses that are found on both Bibles are shown. # Join the searches so only verses that are found on both Bibles are shown.
self.search_results = new_search_results self.search_results = new_search_results
@ -750,26 +754,18 @@ class BibleMediaItem(MediaManagerItem):
count = 0 count = 0
passage_not_found = False passage_not_found = False
# Search second bible for results of search_results to make sure everythigns there. # Search second bible for results of search_results to make sure everythigns there.
# Count all the unfound passages. # 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: for verse in self.search_results:
db_book = bibles[second_bible].get_book_by_book_ref_id(verse.book.book_reference_id) db_book = bibles[second_bible].get_book_by_book_ref_id(verse.book.book_reference_id)
if not db_book: if not db_book:
log.debug('Passage ("{versebookname}","{versechapter}","{verseverse}") not found in Second Bible' log.debug('Passage ("{versebookname}","{versechapter}","{verseverse}") not found in Second Bible'
.format(versebookname=verse.book.name, versechapter='verse.chapter', .format(versebookname=verse.book.name, versechapter='verse.chapter',
verseverse=verse.verse)) verseverse=verse.verse))
passage_not_found = True
count += 1 count += 1
continue continue
new_search_results.append(verse) new_search_results.append(verse)
text.append((verse.book.book_reference_id, verse.chapter, verse.verse, verse.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} 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. # Join the searches so only verses that are found on both Bibles are shown.
self.search_results = new_search_results self.search_results = new_search_results
self.second_search_results = bibles[second_bible].get_verses(text) self.second_search_results = bibles[second_bible].get_verses(text)
@ -878,7 +874,7 @@ class BibleMediaItem(MediaManagerItem):
self.on_quick_reference_search() self.on_quick_reference_search()
elif self.quick_search_edit.current_search_type() == BibleSearch.Text: elif self.quick_search_edit.current_search_type() == BibleSearch.Text:
if len(text) > 7: if len(text) > 7:
self.on_quick_text_search() self.on_quick_text_search_while_typing()
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:
@ -986,10 +982,19 @@ class BibleMediaItem(MediaManagerItem):
except TypeError: except TypeError:
log.exception('The second_search_results does not have this book.') log.exception('The second_search_results does not have this book.')
break break
bible_text = '%s %d%s%d (%s, %s)' % (book, verse.chapter, verse_separator, verse.verse, version, bible_text = ('{book} {chapter:d}{sep}{verse:d} '
second_version) '({version1}, {version2})').format(book=book,
chapter=verse.chapter,
sep=verse_separator,
verse=verse.verse,
version1=version,
version2=second_version)
else: else:
bible_text = '%s %d%s%d (%s)' % (book, verse.chapter, verse_separator, verse.verse, version) bible_text = '{book} {chapter:d}{sep}{verse:d} ({version})'.format(book=book,
chapter=verse.chapter,
sep=verse_separator,
verse=verse.verse,
version=version)
bible_verse = QtWidgets.QListWidgetItem(bible_text) bible_verse = QtWidgets.QListWidgetItem(bible_text)
bible_verse.setData(QtCore.Qt.UserRole, data) bible_verse.setData(QtCore.Qt.UserRole, data)
items.append(bible_verse) items.append(bible_verse)
@ -1036,20 +1041,22 @@ class BibleMediaItem(MediaManagerItem):
verses.add(book, chapter, verse, version, copyright, permissions) verses.add(book, chapter, verse, version, copyright, permissions)
verse_text = self.format_verse(old_chapter, chapter, verse) verse_text = self.format_verse(old_chapter, chapter, verse)
if second_bible: if second_bible:
bible_text = '%s%s\n\n%s&nbsp;%s' % (verse_text, text, verse_text, second_text) bible_text = '{verse}{text1}\n\n{verse}&nbsp;{text2}'.format(verse=verse_text,
text1=text,
text2=second_text)
raw_slides.append(bible_text.rstrip()) raw_slides.append(bible_text.rstrip())
bible_text = '' bible_text = ''
# If we are 'Verse Per Slide' then create a new slide. # If we are 'Verse Per Slide' then create a new slide.
elif self.settings.layout_style == LayoutStyle.VersePerSlide: elif self.settings.layout_style == LayoutStyle.VersePerSlide:
bible_text = '%s%s' % (verse_text, text) bible_text = '{verse}{text}'.format(verse=verse_text, text=text)
raw_slides.append(bible_text.rstrip()) raw_slides.append(bible_text.rstrip())
bible_text = '' bible_text = ''
# If we are 'Verse Per Line' then force a new line. # If we are 'Verse Per Line' then force a new line.
elif self.settings.layout_style == LayoutStyle.VersePerLine: elif self.settings.layout_style == LayoutStyle.VersePerLine:
bible_text = '%s%s%s\n' % (bible_text, verse_text, text) bible_text = '{bible}{verse}{text}\n'.format(bible=bible_text, verse=verse_text, text=text)
# We have to be 'Continuous'. # We have to be 'Continuous'.
else: else:
bible_text = '%s %s%s\n' % (bible_text, verse_text, text) bible_text = '{bible} {verse}{text}\n'.format(bible=bible_text, verse=verse_text, text=text)
bible_text = bible_text.strip(' ') bible_text = bible_text.strip(' ')
if not old_item: if not old_item:
start_item = bitem start_item = bitem
@ -1076,7 +1083,7 @@ class BibleMediaItem(MediaManagerItem):
service_item.add_capability(ItemCapabilities.CanWordSplit) service_item.add_capability(ItemCapabilities.CanWordSplit)
service_item.add_capability(ItemCapabilities.CanEditTitle) service_item.add_capability(ItemCapabilities.CanEditTitle)
# Service Item: Title # Service Item: Title
service_item.title = '%s %s' % (verses.format_verses(), verses.format_versions()) service_item.title = '{verse} {version}'.format(verse=verses.format_verses(), version=verses.format_versions())
# Service Item: Theme # Service Item: Theme
if not self.settings.bible_theme: if not self.settings.bible_theme:
service_item.theme = None service_item.theme = None
@ -1104,7 +1111,7 @@ class BibleMediaItem(MediaManagerItem):
start_bible = self._decode_qt_object(start_bitem, 'bible') start_bible = self._decode_qt_object(start_bitem, 'bible')
start_second_bible = self._decode_qt_object(start_bitem, 'second_bible') start_second_bible = self._decode_qt_object(start_bitem, 'second_bible')
if start_second_bible: if start_second_bible:
bibles = '%s, %s' % (start_bible, start_second_bible) bibles = '{bible1}, {bible2}'.format(bible1=start_bible, bible2=start_second_bible)
else: else:
bibles = start_bible bibles = start_bible
if start_chapter == old_chapter: if start_chapter == old_chapter:
@ -1115,7 +1122,7 @@ class BibleMediaItem(MediaManagerItem):
else: else:
verse_range = start_chapter + verse_separator + start_verse + \ verse_range = start_chapter + verse_separator + start_verse + \
range_separator + old_chapter + verse_separator + old_verse range_separator + old_chapter + verse_separator + old_verse
return '%s %s (%s)' % (start_book, verse_range, bibles) return '{book} {verse} ({bible})'.format(book=start_book, verse=verse_range, bible=bibles)
def check_title(self, bitem, old_bitem): def check_title(self, bitem, old_bitem):
""" """
@ -1168,12 +1175,12 @@ class BibleMediaItem(MediaManagerItem):
else: else:
verse_text = str(verse) verse_text = str(verse)
if self.settings.display_style == DisplayStyle.Round: if self.settings.display_style == DisplayStyle.Round:
return '{su}(%s){/su}&nbsp;' % verse_text return '{{su}}({verse}){{/su}}&nbsp;'.format(verse=verse_text)
if self.settings.display_style == DisplayStyle.Curly: if self.settings.display_style == DisplayStyle.Curly:
return '{su}{%s}{/su}&nbsp;' % verse_text return '{{su}}{{{verse}}}{{/su}}&nbsp;'.format(verse=verse_text)
if self.settings.display_style == DisplayStyle.Square: if self.settings.display_style == DisplayStyle.Square:
return '{su}[%s]{/su}&nbsp;' % verse_text return '{{su}}[{verse}]{{/su}}&nbsp;'.format(verse=verse_text)
return '{su}%s{/su}&nbsp;' % verse_text return '{{su}}{verse}{{/su}}&nbsp;'.format(verse=verse_text)
def search(self, string, showError): def search(self, string, showError):
""" """

View File

@ -63,7 +63,7 @@ class OpenSongBible(BibleDB):
""" """
Loads a Bible from file. Loads a Bible from file.
""" """
log.debug('Starting OpenSong import from "%s"' % self.filename) log.debug('Starting OpenSong import from "{name}"'.format(name=self.filename))
if not isinstance(self.filename, str): if not isinstance(self.filename, str):
self.filename = str(self.filename, 'utf8') self.filename = str(self.filename, 'utf8')
import_file = None import_file = None
@ -84,14 +84,14 @@ class OpenSongBible(BibleDB):
# No language info in the opensong format, so ask the user # No language info in the opensong format, so ask the user
language_id = self.get_language(bible_name) language_id = self.get_language(bible_name)
if not language_id: if not language_id:
log.error('Importing books from "%s" failed' % self.filename) log.error('Importing books from "{name}" failed'.format(name=self.filename))
return False return False
for book in bible.b: for book in bible.b:
if self.stop_import_flag: if self.stop_import_flag:
break break
book_ref_id = self.get_book_ref_id_by_name(str(book.attrib['n']), len(bible.b), language_id) book_ref_id = self.get_book_ref_id_by_name(str(book.attrib['n']), len(bible.b), language_id)
if not book_ref_id: if not book_ref_id:
log.error('Importing books from "%s" failed' % self.filename) log.error('Importing books from "{name}" failed'.format(name=self.filename))
return False return False
book_details = BiblesResourcesDB.get_book_by_id(book_ref_id) book_details = BiblesResourcesDB.get_book_by_id(book_ref_id)
db_book = self.create_book(book.attrib['n'], book_ref_id, book_details['testament_id']) db_book = self.create_book(book.attrib['n'], book_ref_id, book_details['testament_id'])
@ -117,14 +117,14 @@ class OpenSongBible(BibleDB):
if len(verse_parts) > 1: if len(verse_parts) > 1:
number = int(verse_parts[0]) number = int(verse_parts[0])
except TypeError: except TypeError:
log.warning('Illegal verse number: %s', str(verse.attrib['n'])) log.warning('Illegal verse number: {verse:d}'.format(verse.attrib['n']))
verse_number = number verse_number = number
else: else:
verse_number += 1 verse_number += 1
self.create_verse(db_book.id, chapter_number, verse_number, self.get_text(verse)) self.create_verse(db_book.id, chapter_number, verse_number, self.get_text(verse))
self.wizard.increment_progress_bar( self.wizard.increment_progress_bar(translate('BiblesPlugin.Opensong',
translate('BiblesPlugin.Opensong', 'Importing %(bookname)s %(chapter)s...') % 'Importing {name} {chapter}...'
{'bookname': db_book.name, 'chapter': chapter_number}) ).format(name=db_book.name, chapter=chapter_number))
self.session.commit() self.session.commit()
self.application.process_events() self.application.process_events()
except etree.XMLSyntaxError as inst: except etree.XMLSyntaxError as inst:

View File

@ -49,7 +49,7 @@ class OSISBible(BibleDB):
""" """
Loads a Bible from file. Loads a Bible from file.
""" """
log.debug('Starting OSIS import from "%s"' % self.filename) log.debug('Starting OSIS import from "{name}"'.format(name=self.filename))
if not isinstance(self.filename, str): if not isinstance(self.filename, str):
self.filename = str(self.filename, 'utf8') self.filename = str(self.filename, 'utf8')
import_file = None import_file = None
@ -69,7 +69,7 @@ class OSISBible(BibleDB):
if not language_id: if not language_id:
language_id = self.get_language(bible_name) language_id = self.get_language(bible_name)
if not language_id: if not language_id:
log.error('Importing books from "%s" failed' % self.filename) log.error('Importing books from "{name}" failed'.format(name=self.filename))
return False return False
self.save_meta('language_id', language_id) self.save_meta('language_id', language_id)
num_books = int(osis_bible_tree.xpath("count(//ns:div[@type='book'])", namespaces=namespace)) num_books = int(osis_bible_tree.xpath("count(//ns:div[@type='book'])", namespaces=namespace))
@ -129,7 +129,7 @@ class OSISBible(BibleDB):
if not book_ref_id: if not book_ref_id:
book_ref_id = self.get_book_ref_id_by_localised_name(book.get('osisID')) book_ref_id = self.get_book_ref_id_by_localised_name(book.get('osisID'))
if not book_ref_id: if not book_ref_id:
log.error('Importing books from "%s" failed' % self.filename) log.error('Importing books from "{name}" failed'.format(name=self.filename))
return False return False
book_details = BiblesResourcesDB.get_book_by_id(book_ref_id) book_details = BiblesResourcesDB.get_book_by_id(book_ref_id)
db_book = self.create_book(book_details['name'], book_ref_id, book_details['testament_id']) db_book = self.create_book(book_details['name'], book_ref_id, book_details['testament_id'])
@ -187,7 +187,8 @@ class OSISBible(BibleDB):
trace_error_handler(log) trace_error_handler(log)
success = False success = False
critical_error_message_box(message=translate('BiblesPlugin.OsisImport', critical_error_message_box(message=translate('BiblesPlugin.OsisImport',
'The file is not a valid OSIS-XML file: \n%s' % e.msg)) 'The file is not a valid OSIS-XML file:'
'\n{text}').format(text=e.msg))
finally: finally:
if import_file: if import_file:
import_file.close() import_file.close()

View File

@ -51,7 +51,7 @@ class SwordBible(BibleDB):
""" """
Loads a Bible from SWORD module. Loads a Bible from SWORD module.
""" """
log.debug('Starting SWORD import from "%s"' % self.sword_key) log.debug('Starting SWORD import from "{key}"'.format(key=self.sword_key))
success = True success = True
try: try:
pysword_modules = modules.SwordModules(self.sword_path) pysword_modules = modules.SwordModules(self.sword_path)
@ -84,14 +84,14 @@ class SwordBible(BibleDB):
verse_number += 1 verse_number += 1
self.create_verse(db_book.id, chapter_number, verse_number, verse) self.create_verse(db_book.id, chapter_number, verse_number, verse)
self.wizard.increment_progress_bar( self.wizard.increment_progress_bar(
translate('BiblesPlugin.Sword', 'Importing %s...') % db_book.name) translate('BiblesPlugin.Sword', 'Importing {name}...').format(name=db_book.name))
self.session.commit() self.session.commit()
self.application.process_events() self.application.process_events()
except Exception as e: except Exception as e:
critical_error_message_box( critical_error_message_box(
message=translate('BiblesPlugin.SwordImport', 'An unexpected error happened while importing the SWORD ' message=translate('BiblesPlugin.SwordImport', 'An unexpected error happened while importing the SWORD '
'bible, please report this to the OpenLP developers.\n' 'bible, please report this to the OpenLP developers.\n'
'%s' % e)) '{error}').format(error=e))
log.exception(str(e)) log.exception(str(e))
success = False success = False
if self.stop_import_flag: if self.stop_import_flag:

View File

@ -101,7 +101,7 @@ def upgrade_1(session, metadata):
metadata_table.c.key == 'download source' metadata_table.c.key == 'download source'
) )
).scalar() ).scalar()
log.debug('download source: %s', value_count) log.debug('download source: {count}'.format(count=value_count))
if value_count > 0: if value_count > 0:
session.execute(insert(metadata_table).values( session.execute(insert(metadata_table).values(
key='download_source', key='download_source',
@ -121,7 +121,7 @@ def upgrade_1(session, metadata):
metadata_table.c.key == 'download name' metadata_table.c.key == 'download name'
) )
).scalar() ).scalar()
log.debug('download name: %s', value_count) log.debug('download name: {count}'.format(count=value_count))
if value_count > 0: if value_count > 0:
session.execute(insert(metadata_table).values( session.execute(insert(metadata_table).values(
key='download_name', key='download_name',
@ -141,7 +141,7 @@ def upgrade_1(session, metadata):
metadata_table.c.key == 'proxy server' metadata_table.c.key == 'proxy server'
) )
).scalar() ).scalar()
log.debug('proxy server: %s', value_count) log.debug('proxy server: {count}'.format(count=value_count))
if value_count > 0: if value_count > 0:
session.execute(insert(metadata_table).values( session.execute(insert(metadata_table).values(
key='proxy_server', key='proxy_server',
@ -161,7 +161,7 @@ def upgrade_1(session, metadata):
metadata_table.c.key == 'proxy username' metadata_table.c.key == 'proxy username'
) )
).scalar() ).scalar()
log.debug('proxy username: %s', value_count) log.debug('proxy username: {count}'.format(count=value_count))
if value_count > 0: if value_count > 0:
session.execute(insert(metadata_table).values( session.execute(insert(metadata_table).values(
key='proxy_username', key='proxy_username',
@ -181,7 +181,7 @@ def upgrade_1(session, metadata):
metadata_table.c.key == 'proxy password' metadata_table.c.key == 'proxy password'
) )
).scalar() ).scalar()
log.debug('proxy password: %s', value_count) log.debug('proxy password: {count}'.format(count=value_count))
if value_count > 0: if value_count > 0:
session.execute(insert(metadata_table).values( session.execute(insert(metadata_table).values(
key='proxy_password', key='proxy_password',

View File

@ -61,23 +61,29 @@ class VerseReferenceList(object):
result = '' result = ''
for index, verse in enumerate(self.verse_list): for index, verse in enumerate(self.verse_list):
if index == 0: if index == 0:
result = '%s %s%s%s' % (verse['book'], verse['chapter'], verse_sep, verse['start']) result = '{book} {chapter}{sep}{verse}'.format(book=verse['book'],
chapter=verse['chapter'],
sep=verse_sep,
verse=verse['start'])
if verse['start'] != verse['end']: if verse['start'] != verse['end']:
result = '%s%s%s' % (result, range_sep, verse['end']) result = '{result}{sep}{end}'.format(result=result, sep=range_sep, end=verse['end'])
continue continue
prev = index - 1 prev = index - 1
if self.verse_list[prev]['version'] != verse['version']: if self.verse_list[prev]['version'] != verse['version']:
result = '%s (%s)' % (result, self.verse_list[prev]['version']) result = '{result} ({version})'.format(result=result, version=self.verse_list[prev]['version'])
result += '%s ' % list_sep result += '{sep} '.format(sep=list_sep)
if self.verse_list[prev]['book'] != verse['book']: if self.verse_list[prev]['book'] != verse['book']:
result = '%s%s %s%s' % (result, verse['book'], verse['chapter'], verse_sep) result = '{result}{book} {chapter}{sep}'.format(result=result,
book=verse['book'],
chapter=verse['chapter'],
sep=verse_sep)
elif self.verse_list[prev]['chapter'] != verse['chapter']: elif self.verse_list[prev]['chapter'] != verse['chapter']:
result = '%s%s%s' % (result, verse['chapter'], verse_sep) result = '{result}{chapter}{sep}'.format(result=result, chapter=verse['chapter'], sep=verse_sep)
result += str(verse['start']) result += str(verse['start'])
if verse['start'] != verse['end']: if verse['start'] != verse['end']:
result = '%s%s%s' % (result, range_sep, verse['end']) result = '{result}{sep}{end}'.format(result=result, sep=range_sep, end=verse['end'])
if len(self.version_list) > 1: if len(self.version_list) > 1:
result = '%s (%s)' % (result, verse['version']) result = '{result} ({version})'.format(result=result, version=verse['version'])
return result return result
def format_versions(self, copyright=True, permission=True): def format_versions(self, copyright=True, permission=True):

View File

@ -48,7 +48,7 @@ class ZefaniaBible(BibleDB):
""" """
Loads a Bible from file. Loads a Bible from file.
""" """
log.debug('Starting Zefania import from "%s"' % self.filename) log.debug('Starting Zefania import from "{name}"'.format(name=self.filename))
if not isinstance(self.filename, str): if not isinstance(self.filename, str):
self.filename = str(self.filename, 'utf8') self.filename = str(self.filename, 'utf8')
import_file = None import_file = None
@ -67,7 +67,7 @@ class ZefaniaBible(BibleDB):
if not language_id: if not language_id:
language_id = self.get_language(bible_name) language_id = self.get_language(bible_name)
if not language_id: if not language_id:
log.error('Importing books from "%s" failed' % self.filename) log.error('Importing books from "{name}" failed'.format(name=self.filename))
return False return False
self.save_meta('language_id', language_id) self.save_meta('language_id', language_id)
num_books = int(zefania_bible_tree.xpath('count(//BIBLEBOOK)')) num_books = int(zefania_bible_tree.xpath('count(//BIBLEBOOK)'))
@ -92,7 +92,7 @@ class ZefaniaBible(BibleDB):
log.debug('Could not find a name, will use number, basically a guess.') log.debug('Could not find a name, will use number, basically a guess.')
book_ref_id = int(bnumber) book_ref_id = int(bnumber)
if not book_ref_id: if not book_ref_id:
log.error('Importing books from "%s" failed' % self.filename) log.error('Importing books from "{name}" failed'.format(name=self.filename))
return False return False
book_details = BiblesResourcesDB.get_book_by_id(book_ref_id) book_details = BiblesResourcesDB.get_book_by_id(book_ref_id)
db_book = self.create_book(book_details['name'], book_ref_id, book_details['testament_id']) db_book = self.create_book(book_details['name'], book_ref_id, book_details['testament_id'])
@ -104,8 +104,9 @@ class ZefaniaBible(BibleDB):
verse_number = VERS.get("vnumber") verse_number = VERS.get("vnumber")
self.create_verse(db_book.id, chapter_number, verse_number, VERS.text.replace('<BR/>', '\n')) self.create_verse(db_book.id, chapter_number, verse_number, VERS.text.replace('<BR/>', '\n'))
self.wizard.increment_progress_bar( self.wizard.increment_progress_bar(
translate('BiblesPlugin.Zefnia', 'Importing %(bookname)s %(chapter)s...') % translate('BiblesPlugin.Zefnia',
{'bookname': db_book.name, 'chapter': chapter_number}) 'Importing {book} {chapter}...').format(book=db_book.name,
chapter=chapter_number))
self.session.commit() self.session.commit()
self.application.process_events() self.application.process_events()
except Exception as e: except Exception as e:

View File

@ -23,8 +23,9 @@
from PyQt5 import QtWidgets from PyQt5 import QtWidgets
from openlp.core.common import UiStrings, translate from openlp.core.common import UiStrings, translate
from openlp.core.lib import SpellTextEdit, build_icon from openlp.core.lib import build_icon
from openlp.core.lib.ui import create_button, create_button_box from openlp.core.lib.ui import create_button, create_button_box
from openlp.core.ui.lib import SpellTextEdit
class Ui_CustomSlideEditDialog(object): class Ui_CustomSlideEditDialog(object):

View File

@ -128,7 +128,7 @@ class CustomXMLParser(object):
try: try:
self.custom_xml = objectify.fromstring(xml) self.custom_xml = objectify.fromstring(xml)
except etree.XMLSyntaxError: except etree.XMLSyntaxError:
log.exception('Invalid xml %s', xml) log.exception('Invalid xml {xml}'.format(xml=xml))
def get_verses(self): def get_verses(self):
""" """

View File

@ -94,7 +94,7 @@ class CustomMediaItem(MediaManagerItem):
""" """
""" """
self.search_text_label.setText('%s:' % UiStrings().Search) self.search_text_label.setText('{text}:'.format(text=UiStrings().Search))
self.search_text_button.setText(UiStrings().Search) self.search_text_button.setText(UiStrings().Search)
def initialise(self): def initialise(self):
@ -105,7 +105,8 @@ class CustomMediaItem(MediaManagerItem):
[(CustomSearch.Titles, ':/songs/song_search_title.png', translate('SongsPlugin.MediaItem', 'Titles'), [(CustomSearch.Titles, ':/songs/song_search_title.png', translate('SongsPlugin.MediaItem', 'Titles'),
translate('SongsPlugin.MediaItem', 'Search Titles...')), translate('SongsPlugin.MediaItem', 'Search Titles...')),
(CustomSearch.Themes, ':/slides/slide_theme.png', UiStrings().Themes, UiStrings().SearchThemes)]) (CustomSearch.Themes, ':/slides/slide_theme.png', UiStrings().Themes, UiStrings().SearchThemes)])
self.search_text_edit.set_current_search_type(Settings().value('%s/last search type' % self.settings_section)) text = '{section}/last search type'.format(section=self.settings_section)
self.search_text_edit.set_current_search_type(Settings().value(text))
self.load_list(self.plugin.db_manager.get_all_objects(CustomSlide, order_by_ref=CustomSlide.title)) self.load_list(self.plugin.db_manager.get_all_objects(CustomSlide, order_by_ref=CustomSlide.title))
self.config_update() self.config_update()
@ -190,7 +191,8 @@ class CustomMediaItem(MediaManagerItem):
if QtWidgets.QMessageBox.question( if QtWidgets.QMessageBox.question(
self, UiStrings().ConfirmDelete, self, UiStrings().ConfirmDelete,
translate('CustomPlugin.MediaItem', translate('CustomPlugin.MediaItem',
'Are you sure you want to delete the "%d" selected custom slide(s)?') % len(items), 'Are you sure you want to delete the "{items:d}" '
'selected custom slide(s)?').format(items=len(items)),
QtWidgets.QMessageBox.StandardButtons( QtWidgets.QMessageBox.StandardButtons(
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No), QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No),
QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.No: QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.No:
@ -249,10 +251,11 @@ class CustomMediaItem(MediaManagerItem):
Search the plugin database Search the plugin database
""" """
# Save the current search type to the configuration. # Save the current search type to the configuration.
Settings().setValue('%s/last search type' % self.settings_section, self.search_text_edit.current_search_type()) Settings().setValue('{section}/last search type'.format(section=self.settings_section),
self.search_text_edit.current_search_type())
# Reload the list considering the new search type. # Reload the list considering the new search type.
search_type = self.search_text_edit.current_search_type() search_type = self.search_text_edit.current_search_type()
search_keywords = '%' + self.whitespace.sub(' ', self.search_text_edit.displayText()) + '%' search_keywords = '%{search}%'.format(search=self.whitespace.sub(' ', self.search_text_edit.displayText()))
if search_type == CustomSearch.Titles: if search_type == CustomSearch.Titles:
log.debug('Titles Search') log.debug('Titles Search')
search_results = self.plugin.db_manager.get_all_objects(CustomSlide, search_results = self.plugin.db_manager.get_all_objects(CustomSlide,
@ -347,7 +350,7 @@ class CustomMediaItem(MediaManagerItem):
:param string: The search string :param string: The search string
:param show_error: The error string to be show. :param show_error: The error string to be show.
""" """
search = '%' + string.lower() + '%' search = '%{search}%'.forma(search=string.lower())
search_results = self.plugin.db_manager.get_all_objects(CustomSlide, search_results = self.plugin.db_manager.get_all_objects(CustomSlide,
or_(func.lower(CustomSlide.title).like(search), or_(func.lower(CustomSlide.title).like(search),
func.lower(CustomSlide.text).like(search)), func.lower(CustomSlide.text).like(search)),

View File

@ -74,7 +74,7 @@ class ImageMediaItem(MediaManagerItem):
def retranslateUi(self): def retranslateUi(self):
self.on_new_prompt = translate('ImagePlugin.MediaItem', 'Select Image(s)') self.on_new_prompt = translate('ImagePlugin.MediaItem', 'Select Image(s)')
file_formats = get_images_filter() file_formats = get_images_filter()
self.on_new_file_masks = '%s;;%s (*)' % (file_formats, UiStrings().AllFiles) self.on_new_file_masks = '{formats};;{files} (*)'.format(formats=file_formats, files=UiStrings().AllFiles)
self.add_group_action.setText(UiStrings().AddGroup) self.add_group_action.setText(UiStrings().AddGroup)
self.add_group_action.setToolTip(UiStrings().AddGroup) self.add_group_action.setToolTip(UiStrings().AddGroup)
self.replace_action.setText(UiStrings().ReplaceBG) self.replace_action.setText(UiStrings().ReplaceBG)
@ -113,7 +113,7 @@ class ImageMediaItem(MediaManagerItem):
self.list_view = TreeWidgetWithDnD(self, self.plugin.name) self.list_view = TreeWidgetWithDnD(self, self.plugin.name)
self.list_view.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) self.list_view.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
self.list_view.setAlternatingRowColors(True) self.list_view.setAlternatingRowColors(True)
self.list_view.setObjectName('%sTreeView' % self.plugin.name) self.list_view.setObjectName('{name}TreeView'.format(name=self.plugin.name))
# Add to pageLayout # Add to pageLayout
self.page_layout.addWidget(self.list_view) self.page_layout.addWidget(self.list_view)
# define and add the context menu # define and add the context menu
@ -127,21 +127,21 @@ class ImageMediaItem(MediaManagerItem):
create_widget_action(self.list_view, separator=True) create_widget_action(self.list_view, separator=True)
create_widget_action( create_widget_action(
self.list_view, self.list_view,
'listView%s%sItem' % (self.plugin.name.title(), StringContent.Preview.title()), 'listView{name}{preview}Item'.format(name=self.plugin.name.title(), preview=StringContent.Preview.title()),
text=self.plugin.get_string(StringContent.Preview)['title'], text=self.plugin.get_string(StringContent.Preview)['title'],
icon=':/general/general_preview.png', icon=':/general/general_preview.png',
can_shortcuts=True, can_shortcuts=True,
triggers=self.on_preview_click) triggers=self.on_preview_click)
create_widget_action( create_widget_action(
self.list_view, self.list_view,
'listView%s%sItem' % (self.plugin.name.title(), StringContent.Live.title()), 'listView{name}{live}Item'.format(name=self.plugin.name.title(), live=StringContent.Live.title()),
text=self.plugin.get_string(StringContent.Live)['title'], text=self.plugin.get_string(StringContent.Live)['title'],
icon=':/general/general_live.png', icon=':/general/general_live.png',
can_shortcuts=True, can_shortcuts=True,
triggers=self.on_live_click) triggers=self.on_live_click)
create_widget_action( create_widget_action(
self.list_view, self.list_view,
'listView%s%sItem' % (self.plugin.name.title(), StringContent.Service.title()), 'listView{name}{service}Item'.format(name=self.plugin.name.title(), service=StringContent.Service.title()),
can_shortcuts=True, can_shortcuts=True,
text=self.plugin.get_string(StringContent.Service)['title'], text=self.plugin.get_string(StringContent.Service)['title'],
icon=':/general/general_add.png', icon=':/general/general_add.png',
@ -157,7 +157,7 @@ class ImageMediaItem(MediaManagerItem):
if self.has_delete_icon: if self.has_delete_icon:
create_widget_action( create_widget_action(
self.list_view, self.list_view,
'listView%s%sItem' % (self.plugin.name.title(), StringContent.Delete.title()), 'listView{name}{delete}Item'.format(name=self.plugin.name.title(), delete=StringContent.Delete.title()),
text=self.plugin.get_string(StringContent.Delete)['title'], text=self.plugin.get_string(StringContent.Delete)['title'],
icon=':/general/general_delete.png', icon=':/general/general_delete.png',
can_shortcuts=True, triggers=self.on_delete_click) can_shortcuts=True, triggers=self.on_delete_click)
@ -245,8 +245,8 @@ class ImageMediaItem(MediaManagerItem):
self.list_view.parent(), self.list_view.parent(),
translate('ImagePlugin.MediaItem', 'Remove group'), translate('ImagePlugin.MediaItem', 'Remove group'),
translate('ImagePlugin.MediaItem', translate('ImagePlugin.MediaItem',
'Are you sure you want to remove "%s" and everything in it?') % 'Are you sure you want to remove "{name}" and everything in it?'
item_data.group_name, ).format(name=item_data.group_name),
QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Yes |
QtWidgets.QMessageBox.No) QtWidgets.QMessageBox.No)
) == QtWidgets.QMessageBox.Yes: ) == QtWidgets.QMessageBox.Yes:
@ -355,7 +355,7 @@ class ImageMediaItem(MediaManagerItem):
# characters. # characters.
images.sort(key=lambda image_object: get_locale_key(os.path.split(str(image_object.filename))[1])) images.sort(key=lambda image_object: get_locale_key(os.path.split(str(image_object.filename))[1]))
for image_file in images: for image_file in images:
log.debug('Loading image: %s', image_file.filename) log.debug('Loading image: {name}'.format(name=image_file.filename))
filename = os.path.split(image_file.filename)[1] filename = os.path.split(image_file.filename)[1]
thumb = self.generate_thumbnail_path(image_file) thumb = self.generate_thumbnail_path(image_file)
if not os.path.exists(image_file.filename): if not os.path.exists(image_file.filename):
@ -481,7 +481,7 @@ class ImageMediaItem(MediaManagerItem):
for filename in images_list: for filename in images_list:
if not isinstance(filename, str): if not isinstance(filename, str):
continue continue
log.debug('Adding new image: %s', filename) log.debug('Adding new image: {name}'.format(name=filename))
image_file = ImageFilenames() image_file = ImageFilenames()
image_file.group_id = group_id image_file.group_id = group_id
image_file.filename = str(filename) image_file.filename = str(filename)
@ -589,14 +589,15 @@ class ImageMediaItem(MediaManagerItem):
if not remote: if not remote:
critical_error_message_box( critical_error_message_box(
translate('ImagePlugin.MediaItem', 'Missing Image(s)'), translate('ImagePlugin.MediaItem', 'Missing Image(s)'),
translate('ImagePlugin.MediaItem', 'The following image(s) no longer exist: %s') translate('ImagePlugin.MediaItem', 'The following image(s) no longer exist: {names}'
% '\n'.join(missing_items_file_names)) ).format(names='\n'.join(missing_items_file_names)))
return False return False
# We have missing as well as existing images. We ask what to do. # We have missing as well as existing images. We ask what to do.
elif missing_items_file_names and QtWidgets.QMessageBox.question( elif missing_items_file_names and QtWidgets.QMessageBox.question(
self, translate('ImagePlugin.MediaItem', 'Missing Image(s)'), self, translate('ImagePlugin.MediaItem', 'Missing Image(s)'),
translate('ImagePlugin.MediaItem', 'The following image(s) no longer exist: %s\n' translate('ImagePlugin.MediaItem', 'The following image(s) no longer exist: {names}\n'
'Do you want to add the other images anyway?') % '\n'.join(missing_items_file_names), 'Do you want to add the other images anyway?'
).format(names='\n'.join(missing_items_file_names)),
QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.No | QtWidgets.QMessageBox.Yes)) == \ QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.No | QtWidgets.QMessageBox.Yes)) == \
QtWidgets.QMessageBox.No: QtWidgets.QMessageBox.No:
return False return False
@ -688,7 +689,7 @@ class ImageMediaItem(MediaManagerItem):
critical_error_message_box( critical_error_message_box(
UiStrings().LiveBGError, UiStrings().LiveBGError,
translate('ImagePlugin.MediaItem', 'There was a problem replacing your background, ' translate('ImagePlugin.MediaItem', 'There was a problem replacing your background, '
'the image file "%s" no longer exists.') % filename) 'the image file "{name}" no longer exists.').format(name=filename))
def search(self, string, show_error=True): def search(self, string, show_error=True):
""" """

View File

@ -46,7 +46,7 @@ class MediaClipSelectorForm(QtWidgets.QDialog, Ui_MediaClipSelector, RegistryPro
""" """
Class to manage the clip selection Class to manage the clip selection
""" """
log.info('%s MediaClipSelectorForm loaded', __name__) log.info('{name} MediaClipSelectorForm loaded'.format(name=__name__))
def __init__(self, media_item, parent, manager): def __init__(self, media_item, parent, manager):
""" """
@ -265,7 +265,8 @@ class MediaClipSelectorForm(QtWidgets.QDialog, Ui_MediaClipSelector, RegistryPro
# Enable audio track combobox if anything is in it # Enable audio track combobox if anything is in it
if len(titles) > 0: if len(titles) > 0:
self.titles_combo_box.setDisabled(False) self.titles_combo_box.setDisabled(False)
log.debug('load_disc_button end - vlc_media_player state: %s' % self.vlc_media_player.get_state()) log.debug('load_disc_button end - '
'vlc_media_player state: {state}'.format(state=self.vlc_media_player.get_state()))
@QtCore.pyqtSlot(bool) @QtCore.pyqtSlot(bool)
def on_play_button_clicked(self, clicked): def on_play_button_clicked(self, clicked):
@ -374,7 +375,7 @@ class MediaClipSelectorForm(QtWidgets.QDialog, Ui_MediaClipSelector, RegistryPro
:param index: The index of the newly chosen title track. :param index: The index of the newly chosen title track.
""" """
log.debug('in on_titles_combo_box_changed, index: %d', index) log.debug('in on_titles_combo_box_changed, index: {index:d}'.format(index=index))
vlc = get_vlc() vlc = get_vlc()
if not self.vlc_media_player: if not self.vlc_media_player:
log.error('vlc_media_player was None') log.error('vlc_media_player was None')
@ -407,7 +408,7 @@ class MediaClipSelectorForm(QtWidgets.QDialog, Ui_MediaClipSelector, RegistryPro
self.vlc_media_player.audio_set_mute(True) self.vlc_media_player.audio_set_mute(True)
# Get audio tracks # Get audio tracks
audio_tracks = self.vlc_media_player.audio_get_track_description() audio_tracks = self.vlc_media_player.audio_get_track_description()
log.debug('number of audio tracks: %d' % len(audio_tracks)) log.debug('number of audio tracks: {tracks:d}'.format(tracks=len(audio_tracks)))
# Clear the audio track combobox, insert new tracks # Clear the audio track combobox, insert new tracks
self.audio_tracks_combobox.clear() self.audio_tracks_combobox.clear()
for audio_track in audio_tracks: for audio_track in audio_tracks:
@ -433,14 +434,14 @@ class MediaClipSelectorForm(QtWidgets.QDialog, Ui_MediaClipSelector, RegistryPro
self.toggle_disable_player(False) self.toggle_disable_player(False)
# Set media length info # Set media length info
self.playback_length = self.vlc_media_player.get_length() self.playback_length = self.vlc_media_player.get_length()
log.debug('playback_length: %d ms' % self.playback_length) log.debug('playback_length: {length:d} ms'.format(length=self.playback_length))
# if length is 0, wait a bit, maybe vlc will change its mind... # if length is 0, wait a bit, maybe vlc will change its mind...
loop_count = 0 loop_count = 0
while self.playback_length == 0 and loop_count < 20: while self.playback_length == 0 and loop_count < 20:
sleep(0.1) sleep(0.1)
self.playback_length = self.vlc_media_player.get_length() self.playback_length = self.vlc_media_player.get_length()
loop_count += 1 loop_count += 1
log.debug('in loop, playback_length: %d ms' % self.playback_length) log.debug('in loop, playback_length: {length:d} ms'.format(length=self.playback_length))
self.position_slider.setMaximum(self.playback_length) self.position_slider.setMaximum(self.playback_length)
# setup start and end time # setup start and end time
rounded_vlc_ms_length = int(round(self.playback_length / 100.0) * 100.0) rounded_vlc_ms_length = int(round(self.playback_length / 100.0) * 100.0)
@ -455,7 +456,8 @@ class MediaClipSelectorForm(QtWidgets.QDialog, Ui_MediaClipSelector, RegistryPro
sleep(0.1) sleep(0.1)
self.vlc_media_player.set_pause(1) self.vlc_media_player.set_pause(1)
loop_count += 1 loop_count += 1
log.debug('titles_combo_box end - vlc_media_player state: %s' % self.vlc_media_player.get_state()) log.debug('titles_combo_box end - '
'vlc_media_player state: {state}'.format(state=self.vlc_media_player.get_state()))
self.application.set_normal_cursor() self.application.set_normal_cursor()
@QtCore.pyqtSlot(int) @QtCore.pyqtSlot(int)
@ -468,7 +470,8 @@ class MediaClipSelectorForm(QtWidgets.QDialog, Ui_MediaClipSelector, RegistryPro
if not self.vlc_media_player: if not self.vlc_media_player:
return return
audio_track = self.audio_tracks_combobox.itemData(index) audio_track = self.audio_tracks_combobox.itemData(index)
log.debug('in on_audio_tracks_combobox_currentIndexChanged, index: %d audio_track: %s' % (index, audio_track)) log.debug('in on_audio_tracks_combobox_currentIndexChanged, '
'index: {index:d} audio_track: {tracks}'.format(index=index, tracks=audio_track))
if audio_track and int(audio_track) > 0: if audio_track and int(audio_track) > 0:
self.vlc_media_player.audio_set_track(int(audio_track)) self.vlc_media_player.audio_set_track(int(audio_track))
@ -566,7 +569,9 @@ class MediaClipSelectorForm(QtWidgets.QDialog, Ui_MediaClipSelector, RegistryPro
translate('MediaPlugin.MediaClipSelectorForm', translate('MediaPlugin.MediaClipSelectorForm',
'The CD was not loaded correctly, please re-load and try again.')) 'The CD was not loaded correctly, please re-load and try again.'))
return return
optical = 'optical:%d:-1:-1:%d:%d:' % (title, start_time_ms, end_time_ms) optical = 'optical:{title:d}:-1:-1:{start:d}:{end:d}:'.format(title=title,
start=start_time_ms,
end=end_time_ms)
else: else:
audio_track = self.audio_tracks_combobox.itemData(self.audio_tracks_combobox.currentIndex()) audio_track = self.audio_tracks_combobox.itemData(self.audio_tracks_combobox.currentIndex())
subtitle_track = self.subtitle_tracks_combobox.itemData(self.subtitle_tracks_combobox.currentIndex()) subtitle_track = self.subtitle_tracks_combobox.itemData(self.subtitle_tracks_combobox.currentIndex())
@ -577,7 +582,11 @@ class MediaClipSelectorForm(QtWidgets.QDialog, Ui_MediaClipSelector, RegistryPro
translate('MediaPlugin.MediaClipSelectorForm', translate('MediaPlugin.MediaClipSelectorForm',
'The DVD was not loaded correctly, please re-load and try again.')) 'The DVD was not loaded correctly, please re-load and try again.'))
return return
optical = 'optical:%d:%d:%d:%d:%d:' % (title, audio_track, subtitle_track, start_time_ms, end_time_ms) optical = 'optical:{title:d}:{audio:d}:{sub:d}:{start:d}:{end:d}:'.format(title=title,
audio=audio_track,
sub=subtitle_track,
start=start_time_ms,
end=end_time_ms)
# Ask for an alternative name for the mediaclip # Ask for an alternative name for the mediaclip
while True: while True:
new_optical_name, ok = QtWidgets.QInputDialog.getText(self, translate('MediaPlugin.MediaClipSelectorForm', new_optical_name, ok = QtWidgets.QInputDialog.getText(self, translate('MediaPlugin.MediaClipSelectorForm',
@ -634,10 +643,10 @@ class MediaClipSelectorForm(QtWidgets.QDialog, Ui_MediaClipSelector, RegistryPro
# use win api to find optical drives # use win api to find optical drives
fso = Dispatch('scripting.filesystemobject') fso = Dispatch('scripting.filesystemobject')
for drive in fso.Drives: for drive in fso.Drives:
log.debug('Drive %s has type %d' % (drive.DriveLetter, drive.DriveType)) log.debug('Drive {drive} has type {types:d}'.format(drive=drive.DriveLetter, types=drive.DriveType))
# if type is 4, it is a cd-rom drive # if type is 4, it is a cd-rom drive
if drive.DriveType == 4: if drive.DriveType == 4:
self.media_path_combobox.addItem('%s:\\' % drive.DriveLetter) self.media_path_combobox.addItem('{drive}:\\'.format(drive=drive.DriveLetter))
elif is_linux(): elif is_linux():
# Get disc devices from dbus and find the ones that are optical # Get disc devices from dbus and find the ones that are optical
bus = dbus.SystemBus() bus = dbus.SystemBus()

View File

@ -51,7 +51,7 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
""" """
media_go_live = QtCore.pyqtSignal(list) media_go_live = QtCore.pyqtSignal(list)
media_add_to_service = QtCore.pyqtSignal(list) media_add_to_service = QtCore.pyqtSignal(list)
log.info('%s MediaMediaItem loaded', __name__) log.info('{name} MediaMediaItem loaded'.format(name=__name__))
def __init__(self, parent, plugin): def __init__(self, parent, plugin):
self.setup() self.setup()
@ -232,7 +232,7 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
critical_error_message_box(UiStrings().LiveBGError, critical_error_message_box(UiStrings().LiveBGError,
translate('MediaPlugin.MediaItem', translate('MediaPlugin.MediaItem',
'There was a problem replacing your background, ' 'There was a problem replacing your background, '
'the media file "%s" no longer exists.') % filename) 'the media file "{name}" no longer exists.').format(name=filename))
def generate_slide_data(self, service_item, item=None, xml_version=False, remote=False, def generate_slide_data(self, service_item, item=None, xml_version=False, remote=False,
context=ServiceItemContext.Service): context=ServiceItemContext.Service):
@ -258,7 +258,8 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
# Optical disc is no longer present # Optical disc is no longer present
critical_error_message_box( critical_error_message_box(
translate('MediaPlugin.MediaItem', 'Missing Media File'), translate('MediaPlugin.MediaItem', 'Missing Media File'),
translate('MediaPlugin.MediaItem', 'The optical disc %s is no longer available.') % name) translate('MediaPlugin.MediaItem',
'The optical disc {name} is no longer available.').format(name=name))
return False return False
service_item.processor = self.display_type_combo_box.currentText() service_item.processor = self.display_type_combo_box.currentText()
service_item.add_from_command(filename, name, CLAPPERBOARD) service_item.add_from_command(filename, name, CLAPPERBOARD)
@ -275,7 +276,7 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
# File is no longer present # File is no longer present
critical_error_message_box( critical_error_message_box(
translate('MediaPlugin.MediaItem', 'Missing Media File'), translate('MediaPlugin.MediaItem', 'Missing Media File'),
translate('MediaPlugin.MediaItem', 'The file %s no longer exists.') % filename) translate('MediaPlugin.MediaItem', 'The file {name} no longer exists.').format(name=filename))
return False return False
(path, name) = os.path.split(filename) (path, name) = os.path.split(filename)
service_item.title = name service_item.title = name
@ -308,9 +309,11 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
Rebuild the tab in the media manager when changes are made in the settings. Rebuild the tab in the media manager when changes are made in the settings.
""" """
self.populate_display_types() self.populate_display_types()
self.on_new_file_masks = translate('MediaPlugin.MediaItem', 'Videos (%s);;Audio (%s);;%s (*)') % ( self.on_new_file_masks = translate('MediaPlugin.MediaItem',
' '.join(self.media_controller.video_extensions_list), 'Videos ({video});;Audio ({audio});;{files} '
' '.join(self.media_controller.audio_extensions_list), UiStrings().AllFiles) '(*)').format(video=' '.join(self.media_controller.video_extensions_list),
audio=' '.join(self.media_controller.audio_extensions_list),
files=UiStrings().AllFiles)
def populate_display_types(self): def populate_display_types(self):
""" """
@ -365,7 +368,9 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
item_name = QtWidgets.QListWidgetItem(clip_name) item_name = QtWidgets.QListWidgetItem(clip_name)
item_name.setIcon(self.optical_icon) item_name.setIcon(self.optical_icon)
item_name.setData(QtCore.Qt.UserRole, track) item_name.setData(QtCore.Qt.UserRole, track)
item_name.setToolTip('%s@%s-%s' % (file_name, format_milliseconds(start), format_milliseconds(end))) item_name.setToolTip('{name}@{start}-{end}'.format(name=file_name,
start=format_milliseconds(start),
end=format_milliseconds(end)))
elif not os.path.exists(track): elif not os.path.exists(track):
# File doesn't exist, mark as error. # File doesn't exist, mark as error.
file_name = os.path.split(str(track))[1] file_name = os.path.split(str(track))[1]
@ -377,7 +382,8 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
# Normal media file handling. # Normal media file handling.
file_name = os.path.split(str(track))[1] file_name = os.path.split(str(track))[1]
item_name = QtWidgets.QListWidgetItem(file_name) item_name = QtWidgets.QListWidgetItem(file_name)
if '*.%s' % (file_name.split('.')[-1].lower()) in self.media_controller.audio_extensions_list: search = file_name.split('.')[-1].lower()
if '*.{text}'.format(text=search) in self.media_controller.audio_extensions_list:
item_name.setIcon(self.audio_icon) item_name.setIcon(self.audio_icon)
else: else:
item_name.setIcon(self.video_icon) item_name.setIcon(self.video_icon)

View File

@ -49,7 +49,7 @@ class MediaPlugin(Plugin):
""" """
The media plugin adds the ability to playback audio and video content. The media plugin adds the ability to playback audio and video content.
""" """
log.info('%s MediaPlugin loaded', __name__) log.info('{name} MediaPlugin loaded'.format(name=__name__))
def __init__(self): def __init__(self):
super(MediaPlugin, self).__init__('media', __default_settings__, MediaMediaItem) super(MediaPlugin, self).__init__('media', __default_settings__, MediaMediaItem)

View File

@ -236,7 +236,7 @@ class ImpressDocument(PresentationDocument):
try: try:
self.document = desktop.loadComponentFromURL(url, '_blank', 0, properties) self.document = desktop.loadComponentFromURL(url, '_blank', 0, properties)
except: except:
log.warning('Failed to load presentation %s' % url) log.warning('Failed to load presentation {url}'.format(url=url))
return False return False
self.presentation = self.document.getPresentation() self.presentation = self.document.getPresentation()
self.presentation.Display = ScreenList().current['number'] + 1 self.presentation.Display = ScreenList().current['number'] + 1
@ -269,16 +269,16 @@ class ImpressDocument(PresentationDocument):
for index in range(pages.getCount()): for index in range(pages.getCount()):
page = pages.getByIndex(index) page = pages.getByIndex(index)
doc.getCurrentController().setCurrentPage(page) doc.getCurrentController().setCurrentPage(page)
url_path = '%s/%s.png' % (thumb_dir_url, str(index + 1)) url_path = '{path}/{name}.png'.format(path=thumb_dir_url, name=str(index + 1))
path = os.path.join(self.get_temp_folder(), str(index + 1) + '.png') path = os.path.join(self.get_temp_folder(), str(index + 1) + '.png')
try: try:
doc.storeToURL(url_path, properties) doc.storeToURL(url_path, properties)
self.convert_thumbnail(path, index + 1) self.convert_thumbnail(path, index + 1)
delete_file(path) delete_file(path)
except ErrorCodeIOException as exception: except ErrorCodeIOException as exception:
log.exception('ERROR! ErrorCodeIOException %d' % exception.ErrCode) log.exception('ERROR! ErrorCodeIOException {error:d}'.format(error=exception.ErrCode))
except: except:
log.exception('%s - Unable to store openoffice preview' % path) log.exception('{path} - Unable to store openoffice preview'.format(path=path))
def create_property(self, name, value): def create_property(self, name, value):
""" """

View File

@ -88,9 +88,10 @@ class PresentationMediaItem(MediaManagerItem):
file_types = self.controllers[controller].supports + self.controllers[controller].also_supports file_types = self.controllers[controller].supports + self.controllers[controller].also_supports
for file_type in file_types: for file_type in file_types:
if file_type not in file_type_string: if file_type not in file_type_string:
file_type_string += '*.%s ' % file_type file_type_string += '*.{text} '.format(text=file_type)
self.service_manager.supported_suffixes(file_type) self.service_manager.supported_suffixes(file_type)
self.on_new_file_masks = translate('PresentationPlugin.MediaItem', 'Presentations (%s)') % file_type_string self.on_new_file_masks = translate('PresentationPlugin.MediaItem',
'Presentations ({text})').format(text=file_type_string)
def required_icons(self): def required_icons(self):
""" """
@ -306,13 +307,13 @@ class PresentationMediaItem(MediaManagerItem):
os.path.join(doc.get_temp_folder(), 'mainslide001.png')): os.path.join(doc.get_temp_folder(), 'mainslide001.png')):
doc.load_presentation() doc.load_presentation()
i = 1 i = 1
image = os.path.join(doc.get_temp_folder(), 'mainslide%03d.png' % i) image = os.path.join(doc.get_temp_folder(), 'mainslide{number:0>3d}.png'.format(number=i))
thumbnail = os.path.join(doc.get_thumbnail_folder(), 'slide%d.png' % i) thumbnail = os.path.join(doc.get_thumbnail_folder(), 'slide%d.png' % i)
while os.path.isfile(image): while os.path.isfile(image):
service_item.add_from_image(image, name, thumbnail=thumbnail) service_item.add_from_image(image, name, thumbnail=thumbnail)
i += 1 i += 1
image = os.path.join(doc.get_temp_folder(), 'mainslide%03d.png' % i) image = os.path.join(doc.get_temp_folder(), 'mainslide{number:0>3d}.png'.format(number=i))
thumbnail = os.path.join(doc.get_thumbnail_folder(), 'slide%d.png' % i) thumbnail = os.path.join(doc.get_thumbnail_folder(), 'slide{number:d}.png'.format(number=i))
service_item.add_capability(ItemCapabilities.HasThumbnails) service_item.add_capability(ItemCapabilities.HasThumbnails)
doc.close_presentation() doc.close_presentation()
return True return True
@ -321,7 +322,8 @@ class PresentationMediaItem(MediaManagerItem):
if not remote: if not remote:
critical_error_message_box(translate('PresentationPlugin.MediaItem', 'Missing Presentation'), critical_error_message_box(translate('PresentationPlugin.MediaItem', 'Missing Presentation'),
translate('PresentationPlugin.MediaItem', translate('PresentationPlugin.MediaItem',
'The presentation %s no longer exists.') % filename) 'The presentation {name} no longer exists.'
).format(name=filename))
return False return False
else: else:
service_item.processor = self.display_type_combo_box.currentText() service_item.processor = self.display_type_combo_box.currentText()
@ -367,15 +369,16 @@ class PresentationMediaItem(MediaManagerItem):
critical_error_message_box(translate('PresentationPlugin.MediaItem', critical_error_message_box(translate('PresentationPlugin.MediaItem',
'Missing Presentation'), 'Missing Presentation'),
translate('PresentationPlugin.MediaItem', translate('PresentationPlugin.MediaItem',
'The presentation %s is incomplete, please reload.') 'The presentation {name} is incomplete, '
% filename) 'please reload.').format(name=filename))
return False return False
else: else:
# File is no longer present # File is no longer present
if not remote: if not remote:
critical_error_message_box(translate('PresentationPlugin.MediaItem', 'Missing Presentation'), critical_error_message_box(translate('PresentationPlugin.MediaItem', 'Missing Presentation'),
translate('PresentationPlugin.MediaItem', translate('PresentationPlugin.MediaItem',
'The presentation %s no longer exists.') % filename) 'The presentation {name} no longer exists.'
).format(name=filename))
return False return False
def find_controller_by_type(self, filename): def find_controller_by_type(self, filename):

View File

@ -48,14 +48,14 @@ class Controller(object):
self.is_live = live self.is_live = live
self.doc = None self.doc = None
self.hide_mode = None self.hide_mode = None
log.info('%s controller loaded' % live) log.info('{name} controller loaded'.format(name=live))
def add_handler(self, controller, file, hide_mode, slide_no): def add_handler(self, controller, file, hide_mode, slide_no):
""" """
Add a handler, which is an instance of a presentation and slidecontroller combination. If the slidecontroller Add a handler, which is an instance of a presentation and slidecontroller combination. If the slidecontroller
has a display then load the presentation. has a display then load the presentation.
""" """
log.debug('Live = %s, add_handler %s' % (self.is_live, file)) log.debug('Live = {live}, add_handler {handler}'.format(live=self.is_live, handler=file))
self.controller = controller self.controller = controller
if self.doc is not None: if self.doc is not None:
self.shutdown() self.shutdown()
@ -67,7 +67,7 @@ class Controller(object):
return return
self.doc.slidenumber = slide_no self.doc.slidenumber = slide_no
self.hide_mode = hide_mode self.hide_mode = hide_mode
log.debug('add_handler, slide_number: %d' % slide_no) log.debug('add_handler, slide_number: {slide:d}'.format(slide=slide_no))
if self.is_live: if self.is_live:
if hide_mode == HideMode.Screen: if hide_mode == HideMode.Screen:
Registry().execute('live_display_hide', HideMode.Screen) Registry().execute('live_display_hide', HideMode.Screen)
@ -87,14 +87,14 @@ class Controller(object):
""" """
Active the presentation, and show it on the screen. Use the last slide number. Active the presentation, and show it on the screen. Use the last slide number.
""" """
log.debug('Live = %s, activate' % self.is_live) log.debug('Live = {live}, activate'.format(live=self.is_live))
if not self.doc: if not self.doc:
return False return False
if self.doc.is_active(): if self.doc.is_active():
return True return True
if not self.doc.is_loaded(): if not self.doc.is_loaded():
if not self.doc.load_presentation(): if not self.doc.load_presentation():
log.warning('Failed to activate %s' % self.doc.file_path) log.warning('Failed to activate {path}'.format(path=self.doc.file_path))
return False return False
if self.is_live: if self.is_live:
self.doc.start_presentation() self.doc.start_presentation()
@ -105,14 +105,14 @@ class Controller(object):
if self.doc.is_active(): if self.doc.is_active():
return True return True
else: else:
log.warning('Failed to activate %s' % self.doc.file_path) log.warning('Failed to activate {path}'.format(path=self.doc.file_path))
return False return False
def slide(self, slide): def slide(self, slide):
""" """
Go to a specific slide Go to a specific slide
""" """
log.debug('Live = %s, slide' % self.is_live) log.debug('Live = {live}, slide'.format(live=self.is_live))
if not self.doc: if not self.doc:
return return
if not self.is_live: if not self.is_live:
@ -130,7 +130,7 @@ class Controller(object):
""" """
Based on the handler passed at startup triggers the first slide. Based on the handler passed at startup triggers the first slide.
""" """
log.debug('Live = %s, first' % self.is_live) log.debug('Live = {live}, first'.format(live=self.is_live))
if not self.doc: if not self.doc:
return return
if not self.is_live: if not self.is_live:
@ -148,7 +148,7 @@ class Controller(object):
""" """
Based on the handler passed at startup triggers the last slide. Based on the handler passed at startup triggers the last slide.
""" """
log.debug('Live = %s, last' % self.is_live) log.debug('Live = {live}, last'.format(live=self.is_live))
if not self.doc: if not self.doc:
return return
if not self.is_live: if not self.is_live:
@ -166,7 +166,7 @@ class Controller(object):
""" """
Based on the handler passed at startup triggers the next slide event. Based on the handler passed at startup triggers the next slide event.
""" """
log.debug('Live = %s, next' % self.is_live) log.debug('Live = {live}, next'.format(live=self.is_live))
if not self.doc: if not self.doc:
return return
if not self.is_live: if not self.is_live:
@ -191,7 +191,7 @@ class Controller(object):
""" """
Based on the handler passed at startup triggers the previous slide event. Based on the handler passed at startup triggers the previous slide event.
""" """
log.debug('Live = %s, previous' % self.is_live) log.debug('Live = {live}, previous'.formta(live=self.is_live))
if not self.doc: if not self.doc:
return return
if not self.is_live: if not self.is_live:
@ -212,7 +212,7 @@ class Controller(object):
""" """
Based on the handler passed at startup triggers slide show to shut down. Based on the handler passed at startup triggers slide show to shut down.
""" """
log.debug('Live = %s, shutdown' % self.is_live) log.debug('Live = {live}, shutdown'.format(live=self.is_live))
if not self.doc: if not self.doc:
return return
self.doc.close_presentation() self.doc.close_presentation()
@ -222,7 +222,7 @@ class Controller(object):
""" """
Instruct the controller to blank the presentation. Instruct the controller to blank the presentation.
""" """
log.debug('Live = %s, blank' % self.is_live) log.debug('Live = {live}, blank'.format(live=self.is_live))
self.hide_mode = hide_mode self.hide_mode = hide_mode
if not self.doc: if not self.doc:
return return
@ -243,7 +243,7 @@ class Controller(object):
""" """
Instruct the controller to stop and hide the presentation. Instruct the controller to stop and hide the presentation.
""" """
log.debug('Live = %s, stop' % self.is_live) log.debug('Live = {live}, stop'.format(live=self.is_live))
# The document has not been loaded yet, so don't do anything. This can happen when going live with a # The document has not been loaded yet, so don't do anything. This can happen when going live with a
# presentation while blanked to desktop. # presentation while blanked to desktop.
if not self.doc: if not self.doc:
@ -266,7 +266,7 @@ class Controller(object):
""" """
Instruct the controller to unblank the presentation. Instruct the controller to unblank the presentation.
""" """
log.debug('Live = %s, unblank' % self.is_live) log.debug('Live = {live}, unblank'.format(live=self.is_live))
self.hide_mode = None self.hide_mode = None
if not self.doc: if not self.doc:
return return
@ -321,7 +321,7 @@ class MessageListener(object):
""" """
Start of new presentation. Save the handler as any new presentations start here Start of new presentation. Save the handler as any new presentations start here
""" """
log.debug('Startup called with message %s' % message) log.debug('Startup called with message {text}'.format(text=message))
is_live = message[1] is_live = message[1]
item = message[0] item = message[0]
hide_mode = message[2] hide_mode = message[2]
@ -332,7 +332,7 @@ class MessageListener(object):
# the conversion has already been done at this point. # the conversion has already been done at this point.
file_type = os.path.splitext(file.lower())[1][1:] file_type = os.path.splitext(file.lower())[1][1:]
if file_type in PDF_CONTROLLER_FILETYPES: if file_type in PDF_CONTROLLER_FILETYPES:
log.debug('Converting from pdf/xps/oxps to images for serviceitem with file %s', file) log.debug('Converting from pdf/xps/oxps to images for serviceitem with file {name}'.format(name=file))
# Create a copy of the original item, and then clear the original item so it can be filled with images # Create a copy of the original item, and then clear the original item so it can be filled with images
item_cpy = copy.copy(item) item_cpy = copy.copy(item)
item.__init__(None) item.__init__(None)

View File

@ -87,7 +87,7 @@ class PdfController(PresentationController):
if found_gs: if found_gs:
program_type = 'gs' program_type = 'gs'
break break
log.debug('in check_binary, found: %s', program_type) log.debug('in check_binary, found: {text}'.format(text=program_type))
return program_type return program_type
def check_available(self): def check_available(self):
@ -255,11 +255,13 @@ class PdfDocument(PresentationDocument):
os.makedirs(self.get_temp_folder()) os.makedirs(self.get_temp_folder())
if self.controller.mudrawbin: if self.controller.mudrawbin:
log.debug('loading presentation using mudraw') log.debug('loading presentation using mudraw')
# TODO: Find out where the string conversion actually happens
runlog = check_output([self.controller.mudrawbin, '-w', str(size.width()), '-h', str(size.height()), runlog = check_output([self.controller.mudrawbin, '-w', str(size.width()), '-h', str(size.height()),
'-o', os.path.join(self.get_temp_folder(), 'mainslide%03d.png'), self.file_path], '-o', os.path.join(self.get_temp_folder(), 'mainslide%03d.png'), self.file_path],
startupinfo=self.startupinfo) startupinfo=self.startupinfo)
elif self.controller.mutoolbin: elif self.controller.mutoolbin:
log.debug('loading presentation using mutool') log.debug('loading presentation using mutool')
# TODO: Find out where the string convertsion actually happens
runlog = check_output([self.controller.mutoolbin, 'draw', '-w', str(size.width()), '-h', runlog = check_output([self.controller.mutoolbin, 'draw', '-w', str(size.width()), '-h',
str(size.height()), str(size.height()),
'-o', os.path.join(self.get_temp_folder(), 'mainslide%03d.png'), self.file_path], '-o', os.path.join(self.get_temp_folder(), 'mainslide%03d.png'), self.file_path],
@ -267,6 +269,7 @@ class PdfDocument(PresentationDocument):
elif self.controller.gsbin: elif self.controller.gsbin:
log.debug('loading presentation using gs') log.debug('loading presentation using gs')
resolution = self.gs_get_resolution(size) resolution = self.gs_get_resolution(size)
# TODO: Find out where the string conversion actually happens
runlog = check_output([self.controller.gsbin, '-dSAFER', '-dNOPAUSE', '-dBATCH', '-sDEVICE=png16m', runlog = check_output([self.controller.gsbin, '-dSAFER', '-dNOPAUSE', '-dBATCH', '-sDEVICE=png16m',
'-r' + str(resolution), '-dTextAlphaBits=4', '-dGraphicsAlphaBits=4', '-r' + str(resolution), '-dTextAlphaBits=4', '-dGraphicsAlphaBits=4',
'-sOutputFile=' + os.path.join(self.get_temp_folder(), 'mainslide%03d.png'), '-sOutputFile=' + os.path.join(self.get_temp_folder(), 'mainslide%03d.png'),

View File

@ -179,7 +179,7 @@ class PowerpointDocument(PresentationDocument):
if not self.presentation.Slides(num + 1).SlideShowTransition.Hidden: if not self.presentation.Slides(num + 1).SlideShowTransition.Hidden:
self.index_map[key] = num + 1 self.index_map[key] = num + 1
self.presentation.Slides(num + 1).Export( self.presentation.Slides(num + 1).Export(
os.path.join(self.get_thumbnail_folder(), 'slide%d.png' % (key)), 'png', 320, 240) os.path.join(self.get_thumbnail_folder(), 'slide{key:d}.png'.format(key=key)), 'png', 320, 240)
key += 1 key += 1
self.slide_count = key - 1 self.slide_count = key - 1
@ -345,8 +345,9 @@ class PowerpointDocument(PresentationDocument):
# Find the presentation window and save the handle for later # Find the presentation window and save the handle for later
self.presentation_hwnd = None self.presentation_hwnd = None
if ppt_window: if ppt_window:
log.debug('main display size: y=%d, height=%d, x=%d, width=%d' log.debug('main display size: y={y:d}, height={height:d}, '
% (size.y(), size.height(), size.x(), size.width())) 'x={x:d}, width={width:d}'.format(y=size.y(), height=size.height(),
x=size.x(), width=size.width()))
win32gui.EnumWindows(self._window_enum_callback, size) win32gui.EnumWindows(self._window_enum_callback, size)
# Make sure powerpoint doesn't steal focus, unless we're on a single screen setup # Make sure powerpoint doesn't steal focus, unless we're on a single screen setup
if len(ScreenList().screen_list) > 1: if len(ScreenList().screen_list) > 1:
@ -361,10 +362,18 @@ class PowerpointDocument(PresentationDocument):
# it is the powerpoint presentation window. # it is the powerpoint presentation window.
(left, top, right, bottom) = win32gui.GetWindowRect(hwnd) (left, top, right, bottom) = win32gui.GetWindowRect(hwnd)
window_title = win32gui.GetWindowText(hwnd) window_title = win32gui.GetWindowText(hwnd)
log.debug('window size: left=%d, top=%d, right=%d, width=%d' % (left, top, right, bottom)) log.debug('window size: left=left:d}, top={top:d}, '
log.debug('compare size: %d and %d, %d and %d, %d and %d, %d and %d' 'right={right:d}, bottom={bottom:d}'.format(left=left, top=top, right=right, bottom=bottom))
% (size.y(), top, size.height(), (bottom - top), size.x(), left, size.width(), (right - left))) log.debug('compare size: {y:d} and {top:d}, {height:d} and {vertical:d}, '
log.debug('window title: %s' % window_title) '{x:d} and {left}, {width:d} and {horizontal:d}'.format(y=size.y(),
top=top,
height=size.height(),
vertical=(bottom - top),
x=size.x(),
left=left,
width=size.width(),
horizontal=(right - left)))
log.debug('window title: {title}'.format(title=window_title))
filename_root, filename_ext = os.path.splitext(os.path.basename(self.file_path)) filename_root, filename_ext = os.path.splitext(os.path.basename(self.file_path))
if size.y() == top and size.height() == (bottom - top) and size.x() == left and \ if size.y() == top and size.height() == (bottom - top) and size.x() == left and \
size.width() == (right - left) and filename_root in window_title: size.width() == (right - left) and filename_root in window_title:
@ -416,8 +425,8 @@ class PowerpointDocument(PresentationDocument):
and self.get_slide_number() == slide_no: and self.get_slide_number() == slide_no:
click_index = self.presentation.SlideShowWindow.View.GetClickIndex() click_index = self.presentation.SlideShowWindow.View.GetClickIndex()
click_count = self.presentation.SlideShowWindow.View.GetClickCount() click_count = self.presentation.SlideShowWindow.View.GetClickCount()
log.debug('We are already on this slide - go to next effect if any left, idx: %d, count: %d' log.debug('We are already on this slide - go to next effect if any left, idx: '
% (click_index, click_count)) '{index:d}, count: {count:d}'.format(index=click_index, count=click_count))
if click_index < click_count: if click_index < click_count:
self.next_step() self.next_step()
else: else:

View File

@ -148,7 +148,7 @@ class PptviewDocument(PresentationDocument):
return return
log.debug('create_thumbnails proceeding') log.debug('create_thumbnails proceeding')
for idx in range(self.get_slide_count()): for idx in range(self.get_slide_count()):
path = '%s\\slide%s.bmp' % (self.get_temp_folder(), str(idx + 1)) path = '{folder}\\slide{index}.bmp'.format(folder=self.get_temp_folder(), index=str(idx + 1))
self.convert_thumbnail(path, idx + 1) self.convert_thumbnail(path, idx + 1)
def create_titles_and_notes(self): def create_titles_and_notes(self):

View File

@ -278,7 +278,7 @@ class PresentationDocument(object):
prefix = 'live' prefix = 'live'
else: else:
prefix = 'preview' prefix = 'preview'
Registry().execute('slidecontroller_%s_change' % prefix, self.slide_number - 1) Registry().execute('slidecontroller_{prefix}_change'.format(prefix=prefix), self.slide_number - 1)
def get_slide_text(self, slide_no): def get_slide_text(self, slide_no):
""" """
@ -312,7 +312,7 @@ class PresentationDocument(object):
log.exception('Failed to open/read existing titles file') log.exception('Failed to open/read existing titles file')
titles = [] titles = []
for slide_no, title in enumerate(titles, 1): for slide_no, title in enumerate(titles, 1):
notes_file = os.path.join(self.get_thumbnail_folder(), 'slideNotes%d.txt' % slide_no) notes_file = os.path.join(self.get_thumbnail_folder(), 'slideNotes{number:d}.txt'.format(number=slide_no))
note = '' note = ''
if os.path.exists(notes_file): if os.path.exists(notes_file):
try: try:
@ -335,7 +335,8 @@ class PresentationDocument(object):
fo.writelines(titles) fo.writelines(titles)
if notes: if notes:
for slide_no, note in enumerate(notes, 1): for slide_no, note in enumerate(notes, 1):
notes_file = os.path.join(self.get_thumbnail_folder(), 'slideNotes%d.txt' % slide_no) notes_file = os.path.join(self.get_thumbnail_folder(),
'slideNotes{number:d}.txt'.format(number=slide_no))
with open(notes_file, mode='wt', encoding='utf-8') as fn: with open(notes_file, mode='wt', encoding='utf-8') as fn:
fn.write(note) fn.write(note)

View File

@ -137,7 +137,8 @@ class PresentationTab(SettingsTab):
if checkbox.isEnabled(): if checkbox.isEnabled():
checkbox.setText(controller.name) checkbox.setText(controller.name)
else: else:
checkbox.setText(translate('PresentationPlugin.PresentationTab', '%s (unavailable)') % controller.name) checkbox.setText(translate('PresentationPlugin.PresentationTab',
'{name} (unavailable)').format(name=controller.name))
def load(self): def load(self):
""" """

View File

@ -128,11 +128,11 @@ class PresentationPlugin(Plugin):
path = os.path.join(controller_dir, filename) path = os.path.join(controller_dir, filename)
if os.path.isfile(path): if os.path.isfile(path):
module_name = 'openlp.plugins.presentations.lib.' + os.path.splitext(filename)[0] module_name = 'openlp.plugins.presentations.lib.' + os.path.splitext(filename)[0]
log.debug('Importing controller %s', module_name) log.debug('Importing controller {name}'.format(name=module_name))
try: try:
__import__(module_name, globals(), locals(), []) __import__(module_name, globals(), locals(), [])
except ImportError: except ImportError:
log.warning('Failed to import %s on path %s', module_name, path) log.warning('Failed to import {name} on path {path}'.format(name=module_name, path=path))
controller_classes = PresentationController.__subclasses__() controller_classes = PresentationController.__subclasses__()
for controller_class in controller_classes: for controller_class in controller_classes:
controller = controller_class(self) controller = controller_class(self)

View File

@ -141,7 +141,8 @@ class HttpRouter(RegistryProperties):
""" """
Initialise the router stack and any other variables. Initialise the router stack and any other variables.
""" """
auth_code = "%s:%s" % (Settings().value('remotes/user id'), Settings().value('remotes/password')) auth_code = "{user}:{password}".format(user=Settings().value('remotes/user id'),
password=Settings().value('remotes/password'))
try: try:
self.auth = base64.b64encode(auth_code) self.auth = base64.b64encode(auth_code)
except TypeError: except TypeError:
@ -189,7 +190,7 @@ class HttpRouter(RegistryProperties):
if self.headers['Authorization'] is None: if self.headers['Authorization'] is None:
self.do_authorisation() self.do_authorisation()
self.wfile.write(bytes('no auth header received', 'UTF-8')) self.wfile.write(bytes('no auth header received', 'UTF-8'))
elif self.headers['Authorization'] == 'Basic %s' % self.auth: elif self.headers['Authorization'] == 'Basic {auth}'.format(auth=self.auth):
self.do_http_success() self.do_http_success()
self.call_function(function, *args) self.call_function(function, *args)
else: else:
@ -231,7 +232,7 @@ class HttpRouter(RegistryProperties):
for route, func in self.routes: for route, func in self.routes:
match = re.match(route, url_path_split.path) match = re.match(route, url_path_split.path)
if match: if match:
log.debug('Route "%s" matched "%s"', route, url_path) log.debug('Route "{route}" matched "{path}"'.format(route=route, path=url_path))
args = [] args = []
for param in match.groups(): for param in match.groups():
args.append(param) args.append(param)
@ -319,9 +320,9 @@ class HttpRouter(RegistryProperties):
stage = translate('RemotePlugin.Mobile', 'Stage View') stage = translate('RemotePlugin.Mobile', 'Stage View')
live = translate('RemotePlugin.Mobile', 'Live View') live = translate('RemotePlugin.Mobile', 'Live View')
self.template_vars = { self.template_vars = {
'app_title': "%s %s" % (UiStrings().OLPV2x, remote), 'app_title': "{main} {remote}".format(main=UiStrings().OLPV2x, remote=remote),
'stage_title': "%s %s" % (UiStrings().OLPV2x, stage), 'stage_title': "{main} {stage}".format(main=UiStrings().OLPV2x, stage=stage),
'live_title': "%s %s" % (UiStrings().OLPV2x, live), 'live_title': "{main} {live}".format(main=UiStrings().OLPV2x, live=live),
'service_manager': translate('RemotePlugin.Mobile', 'Service Manager'), 'service_manager': translate('RemotePlugin.Mobile', 'Service Manager'),
'slide_controller': translate('RemotePlugin.Mobile', 'Slide Controller'), 'slide_controller': translate('RemotePlugin.Mobile', 'Slide Controller'),
'alerts': translate('RemotePlugin.Mobile', 'Alerts'), 'alerts': translate('RemotePlugin.Mobile', 'Alerts'),
@ -354,7 +355,7 @@ class HttpRouter(RegistryProperties):
:param file_name: file name with path :param file_name: file name with path
:return: :return:
""" """
log.debug('serve file request %s' % file_name) log.debug('serve file request {name}'.format(name=file_name))
parts = file_name.split('/') parts = file_name.split('/')
if len(parts) == 1: if len(parts) == 1:
file_name = os.path.join(parts[0], 'stage.html') file_name = os.path.join(parts[0], 'stage.html')
@ -381,10 +382,10 @@ class HttpRouter(RegistryProperties):
content = Template(filename=path, input_encoding='utf-8', output_encoding='utf-8').render(**variables) content = Template(filename=path, input_encoding='utf-8', output_encoding='utf-8').render(**variables)
else: else:
file_handle = open(path, 'rb') file_handle = open(path, 'rb')
log.debug('Opened %s' % path) log.debug('Opened {path}'.format(path=path))
content = file_handle.read() content = file_handle.read()
except IOError: except IOError:
log.exception('Failed to open %s' % path) log.exception('Failed to open {path}'.format(path=path))
return self.do_not_found() return self.do_not_found()
finally: finally:
if file_handle: if file_handle:
@ -402,7 +403,7 @@ class HttpRouter(RegistryProperties):
Ultimately for i18n, this could first look for xx/file.html before falling back to file.html. Ultimately for i18n, this could first look for xx/file.html before falling back to file.html.
where xx is the language, e.g. 'en' where xx is the language, e.g. 'en'
""" """
log.debug('serve file request %s' % file_name) log.debug('serve file request {name}'.format(name=file_name))
if not file_name: if not file_name:
file_name = 'index.html' file_name = 'index.html'
if '.' not in file_name: if '.' not in file_name:
@ -433,7 +434,9 @@ class HttpRouter(RegistryProperties):
:param dimensions: image size :param dimensions: image size
:param controller_name: controller to be called :param controller_name: controller to be called
""" """
log.debug('serve thumbnail %s/thumbnails%s/%s' % (controller_name, dimensions, file_name)) log.debug('serve thumbnail {cname}/thumbnails{dim}/{fname}'.format(cname=controller_name,
dim=dimensions,
fname=file_name))
supported_controllers = ['presentations', 'images'] supported_controllers = ['presentations', 'images']
# -1 means use the default dimension in ImageManager # -1 means use the default dimension in ImageManager
width = -1 width = -1
@ -539,7 +542,7 @@ class HttpRouter(RegistryProperties):
:param var: variable - not used :param var: variable - not used
""" """
log.debug("controller_text var = %s" % var) log.debug("controller_text var = {var}".format(var=var))
current_item = self.live_controller.service_item current_item = self.live_controller.service_item
data = [] data = []
if current_item: if current_item:
@ -594,7 +597,8 @@ class HttpRouter(RegistryProperties):
:param display_type: This is the type of slide controller, either ``preview`` or ``live``. :param display_type: This is the type of slide controller, either ``preview`` or ``live``.
:param action: The action to perform. :param action: The action to perform.
""" """
event = getattr(self.live_controller, 'slidecontroller_%s_%s' % (display_type, action)) event = getattr(self.live_controller, 'slidecontroller_{display}_{action}'.format(display=display_type,
action=action))
if self.request_data: if self.request_data:
try: try:
data = json.loads(self.request_data)['request']['id'] data = json.loads(self.request_data)['request']['id']
@ -623,7 +627,7 @@ class HttpRouter(RegistryProperties):
:param action: The action to perform. :param action: The action to perform.
""" """
event = getattr(self.service_manager, 'servicemanager_%s_item' % action) event = getattr(self.service_manager, 'servicemanager_{action}_item'.format(action=action))
if self.request_data: if self.request_data:
try: try:
data = int(json.loads(self.request_data)['request']['id']) data = int(json.loads(self.request_data)['request']['id'])
@ -680,7 +684,7 @@ class HttpRouter(RegistryProperties):
return self.do_http_error() return self.do_http_error()
plugin = self.plugin_manager.get_plugin_by_name(plugin_name) plugin = self.plugin_manager.get_plugin_by_name(plugin_name)
if plugin.status == PluginStatus.Active and plugin.media_item: if plugin.status == PluginStatus.Active and plugin.media_item:
getattr(plugin.media_item, '%s_go_live' % plugin_name).emit([request_id, True]) getattr(plugin.media_item, '{name}_go_live'.format(name=plugin_name)).emit([request_id, True])
return self.do_http_success() return self.do_http_success()
def add_to_service(self, plugin_name): def add_to_service(self, plugin_name):
@ -696,5 +700,5 @@ class HttpRouter(RegistryProperties):
plugin = self.plugin_manager.get_plugin_by_name(plugin_name) plugin = self.plugin_manager.get_plugin_by_name(plugin_name)
if plugin.status == PluginStatus.Active and plugin.media_item: if plugin.status == PluginStatus.Active and plugin.media_item:
item_id = plugin.media_item.create_item_from_id(request_id) item_id = plugin.media_item.create_item_from_id(request_id)
getattr(plugin.media_item, '%s_add_to_service' % plugin_name).emit([item_id, True]) getattr(plugin.media_item, '{name}_add_to_service'.format(name=plugin_name)).emit([item_id, True])
self.do_http_success() self.do_http_success()

View File

@ -136,11 +136,13 @@ class OpenLPServer(RegistryProperties):
while loop < 4: while loop < 4:
try: try:
self.httpd = server_class((address, port), CustomHandler) self.httpd = server_class((address, port), CustomHandler)
log.debug("Server started for class %s %s %d" % (server_class, address, port)) log.debug("Server started for class {name} {address} {port:d}".format(name=server_class,
address=address,
port=port))
break break
except OSError: except OSError:
log.debug("failed to start http server thread state %d %s" % log.debug("failed to start http server thread state "
(loop, self.http_thread.isRunning())) "{loop:d} {running}".format(loop=loop, running=self.http_thread.isRunning()))
loop += 1 loop += 1
time.sleep(0.1) time.sleep(0.1)
except: except:

View File

@ -192,14 +192,14 @@ class RemoteTab(SettingsTab):
'Show thumbnails of non-text slides in remote and stage view.')) 'Show thumbnails of non-text slides in remote and stage view.'))
self.android_app_group_box.setTitle(translate('RemotePlugin.RemoteTab', 'Android App')) self.android_app_group_box.setTitle(translate('RemotePlugin.RemoteTab', 'Android App'))
self.android_qr_description_label.setText( self.android_qr_description_label.setText(
translate('RemotePlugin.RemoteTab', 'Scan the QR code or click <a href="%s">download</a> to install the ' translate('RemotePlugin.RemoteTab',
'Android app from Google Play.') % 'Scan the QR code or click <a href="{qr}">download</a> to install the Android app from Google '
'https://play.google.com/store/apps/details?id=org.openlp.android2') 'Play.').format(qr='https://play.google.com/store/apps/details?id=org.openlp.android2'))
self.ios_app_group_box.setTitle(translate('RemotePlugin.RemoteTab', 'iOS App')) self.ios_app_group_box.setTitle(translate('RemotePlugin.RemoteTab', 'iOS App'))
self.ios_qr_description_label.setText( self.ios_qr_description_label.setText(
translate('RemotePlugin.RemoteTab', 'Scan the QR code or click <a href="%s">download</a> to install the ' translate('RemotePlugin.RemoteTab',
'iOS app from the App Store.') % 'Scan the QR code or click <a href="{qr}">download</a> to install the iOS app from the App '
'https://itunes.apple.com/app/id1096218725') 'Store.').format(qr='https://itunes.apple.com/app/id1096218725'))
self.https_settings_group_box.setTitle(translate('RemotePlugin.RemoteTab', 'HTTPS Server')) self.https_settings_group_box.setTitle(translate('RemotePlugin.RemoteTab', 'HTTPS Server'))
self.https_error_label.setText( self.https_error_label.setText(
translate('RemotePlugin.RemoteTab', 'Could not find an SSL certificate. The HTTPS server will not be ' translate('RemotePlugin.RemoteTab', 'Could not find an SSL certificate. The HTTPS server will not be '
@ -217,18 +217,18 @@ class RemoteTab(SettingsTab):
Update the display based on the data input on the screen Update the display based on the data input on the screen
""" """
ip_address = self.get_ip_address(self.address_edit.text()) ip_address = self.get_ip_address(self.address_edit.text())
http_url = 'http://%s:%s/' % (ip_address, self.port_spin_box.value()) http_url = 'http://{url}:{text}/'.format(url=ip_address, text=self.port_spin_box.value())
https_url = 'https://%s:%s/' % (ip_address, self.https_port_spin_box.value()) https_url = 'https://{url}:{text}/'.format(url=ip_address, text=self.https_port_spin_box.value())
self.remote_url.setText('<a href="%s">%s</a>' % (http_url, http_url)) self.remote_url.setText('<a href="{url}">{url}</a>'.format(url=http_url))
self.remote_https_url.setText('<a href="%s">%s</a>' % (https_url, https_url)) self.remote_https_url.setText('<a href="{url}">{url}</a>'.format(url=https_url))
http_url_temp = http_url + 'stage' http_url_temp = http_url + 'stage'
https_url_temp = https_url + 'stage' https_url_temp = https_url + 'stage'
self.stage_url.setText('<a href="%s">%s</a>' % (http_url_temp, http_url_temp)) self.stage_url.setText('<a href="{url}">{url}</a>'.format(url=http_url_temp))
self.stage_https_url.setText('<a href="%s">%s</a>' % (https_url_temp, https_url_temp)) self.stage_https_url.setText('<a href="{url}">{url}</a>'.format(url=https_url_temp))
http_url_temp = http_url + 'main' http_url_temp = http_url + 'main'
https_url_temp = https_url + 'main' https_url_temp = https_url + 'main'
self.live_url.setText('<a href="%s">%s</a>' % (http_url_temp, http_url_temp)) self.live_url.setText('<a href="{url}">{url}</a>'.format(url=http_url_temp))
self.live_https_url.setText('<a href="%s">%s</a>' % (https_url_temp, https_url_temp)) self.live_https_url.setText('<a href="{url}">{url}</a>'.format(url=https_url_temp))
def get_ip_address(self, ip_address): def get_ip_address(self, ip_address):
""" """

View File

@ -130,6 +130,7 @@ class DuplicateSongRemovalForm(OpenLPWizard, RegistryProperties):
Song wizard localisation. Song wizard localisation.
""" """
self.setWindowTitle(translate('Wizard', 'Wizard')) self.setWindowTitle(translate('Wizard', 'Wizard'))
# TODO: Check format() using template strings
self.title_label.setText(WizardStrings.HeaderStyle % translate('OpenLP.Ui', self.title_label.setText(WizardStrings.HeaderStyle % translate('OpenLP.Ui',
'Welcome to the Duplicate Song Removal Wizard')) 'Welcome to the Duplicate Song Removal Wizard'))
self.information_label.setText( self.information_label.setText(
@ -148,8 +149,8 @@ class DuplicateSongRemovalForm(OpenLPWizard, RegistryProperties):
Set the wizard review page header text. Set the wizard review page header text.
""" """
self.review_page.setTitle( self.review_page.setTitle(
translate('Wizard', 'Review duplicate songs (%s/%s)') % translate('Wizard', 'Review duplicate songs ({current}/{total})').format(current=self.review_current_count,
(self.review_current_count, self.review_total_count)) total=self.review_total_count))
def custom_page_changed(self, page_id): def custom_page_changed(self, page_id):
""" """

View File

@ -50,7 +50,7 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
""" """
Class to manage the editing of a song Class to manage the editing of a song
""" """
log.info('%s EditSongForm loaded', __name__) log.info('{name} EditSongForm loaded'.format(name=__name__))
def __init__(self, media_item, parent, manager): def __init__(self, media_item, parent, manager):
""" """
@ -185,20 +185,23 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
verse = verse.data(QtCore.Qt.UserRole) verse = verse.data(QtCore.Qt.UserRole)
if verse not in verse_names: if verse not in verse_names:
verses.append(verse) verses.append(verse)
verse_names.append('%s%s' % (VerseType.translated_tag(verse[0]), verse[1:])) verse_names.append('{verse1}{verse2}'.format(verse1=VerseType.translated_tag(verse[0]),
verse2=verse[1:]))
for count, item in enumerate(order): for count, item in enumerate(order):
if item not in verses: if item not in verses:
invalid_verses.append(order_names[count]) invalid_verses.append(order_names[count])
if invalid_verses: if invalid_verses:
valid = create_separated_list(verse_names) valid = create_separated_list(verse_names)
if len(invalid_verses) > 1: if len(invalid_verses) > 1:
msg = translate('SongsPlugin.EditSongForm', 'There are no verses corresponding to "%(invalid)s". ' msg = translate('SongsPlugin.EditSongForm',
'Valid entries are %(valid)s.\nPlease enter the verses separated by spaces.') % \ 'There are no verses corresponding to "{invalid}". Valid entries are {valid}.\n'
{'invalid': ', '.join(invalid_verses), 'valid': valid} 'Please enter the verses separated by spaces.'
).format(invalid=', '.join(invalid_verses), valid=valid)
else: else:
msg = translate('SongsPlugin.EditSongForm', 'There is no verse corresponding to "%(invalid)s".' msg = translate('SongsPlugin.EditSongForm',
'Valid entries are %(valid)s.\nPlease enter the verses separated by spaces.') % \ 'There is no verse corresponding to "{invalid}". Valid entries are {valid}.\n'
{'invalid': invalid_verses[0], 'valid': valid} 'Please enter the verses separated by spaces.').format(invalid=invalid_verses[0],
valid=valid)
critical_error_message_box(title=translate('SongsPlugin.EditSongForm', 'Invalid Verse Order'), critical_error_message_box(title=translate('SongsPlugin.EditSongForm', 'Invalid Verse Order'),
message=msg) message=msg)
return len(invalid_verses) == 0 return len(invalid_verses) == 0
@ -242,23 +245,24 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
field = item.data(QtCore.Qt.UserRole) field = item.data(QtCore.Qt.UserRole)
verse_tags.append(field) verse_tags.append(field)
if not self._validate_tags(tags): if not self._validate_tags(tags):
misplaced_tags.append('%s %s' % (VerseType.translated_name(field[0]), field[1:])) misplaced_tags.append('{field1} {field2}'.format(field1=VerseType.translated_name(field[0]),
field2=field[1:]))
if misplaced_tags: if misplaced_tags:
critical_error_message_box( critical_error_message_box(
message=translate('SongsPlugin.EditSongForm', message=translate('SongsPlugin.EditSongForm',
'There are misplaced formatting tags in the following verses:\n\n%s\n\n' 'There are misplaced formatting tags in the following verses:\n\n{tag}\n\n'
'Please correct these tags before continuing.' % ', '.join(misplaced_tags))) 'Please correct these tags before continuing.').format(tag=', '.join(misplaced_tags)))
return False return False
for tag in verse_tags: for tag in verse_tags:
if verse_tags.count(tag) > 26: if verse_tags.count(tag) > 26:
# lp#1310523: OpenLyrics allows only a-z variants of one verse: # lp#1310523: OpenLyrics allows only a-z variants of one verse:
# http://openlyrics.info/dataformat.html#verse-name # http://openlyrics.info/dataformat.html#verse-name
critical_error_message_box(message=translate( critical_error_message_box(message=translate(
'SongsPlugin.EditSongForm', 'You have %(count)s verses named %(name)s %(number)s. ' 'SongsPlugin.EditSongForm',
'You can have at most 26 verses with the same name' % 'You have {count} verses named {name} {number}. You can have at most '
{'count': verse_tags.count(tag), '26 verses with the same name').format(count=verse_tags.count(tag),
'name': VerseType.translated_name(tag[0]), name=VerseType.translated_name(tag[0]),
'number': tag[1:]})) number=tag[1:]))
return False return False
return True return True
@ -313,7 +317,7 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
self.song.verse_order = re.sub('([' + verse.upper() + verse.lower() + '])(\W|$)', self.song.verse_order = re.sub('([' + verse.upper() + verse.lower() + '])(\W|$)',
r'\g<1>1\2', self.song.verse_order) r'\g<1>1\2', self.song.verse_order)
except: except:
log.exception('Problem processing song Lyrics \n%s', sxml.dump_xml()) log.exception('Problem processing song Lyrics \n{xml}'.forma(xml=sxml.dump_xml()))
raise raise
def keyPressEvent(self, event): def keyPressEvent(self, event):
@ -492,7 +496,7 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
verse[0]['type'] = VerseType.tags[index] verse[0]['type'] = VerseType.tags[index]
if verse[0]['label'] == '': if verse[0]['label'] == '':
verse[0]['label'] = '1' verse[0]['label'] = '1'
verse_def = '%s%s' % (verse[0]['type'], verse[0]['label']) verse_def = '{verse}{label}'.format(verse=verse[0]['type'], label=verse[0]['label'])
item = QtWidgets.QTableWidgetItem(verse[1]) item = QtWidgets.QTableWidgetItem(verse[1])
item.setData(QtCore.Qt.UserRole, verse_def) item.setData(QtCore.Qt.UserRole, verse_def)
self.verse_list_widget.setItem(count, 0, item) self.verse_list_widget.setItem(count, 0, item)
@ -501,7 +505,7 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
for count, verse in enumerate(verses): for count, verse in enumerate(verses):
self.verse_list_widget.setRowCount(self.verse_list_widget.rowCount() + 1) self.verse_list_widget.setRowCount(self.verse_list_widget.rowCount() + 1)
item = QtWidgets.QTableWidgetItem(verse) item = QtWidgets.QTableWidgetItem(verse)
verse_def = '%s%s' % (VerseType.tags[VerseType.Verse], str(count + 1)) verse_def = '{verse}{count:d}'.format(verse=VerseType.tags[VerseType.Verse], count=(count + 1))
item.setData(QtCore.Qt.UserRole, verse_def) item.setData(QtCore.Qt.UserRole, verse_def)
self.verse_list_widget.setItem(count, 0, item) self.verse_list_widget.setItem(count, 0, item)
if self.song.verse_order: if self.song.verse_order:
@ -514,7 +518,7 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
if verse_index is None: if verse_index is None:
verse_index = VerseType.from_tag(verse_def[0]) verse_index = VerseType.from_tag(verse_def[0])
verse_tag = VerseType.translated_tags[verse_index].upper() verse_tag = VerseType.translated_tags[verse_index].upper()
translated.append('%s%s' % (verse_tag, verse_def[1:])) translated.append('{tag}{verse}'.format(tag=verse_tag, verse=verse_def[1:]))
self.verse_order_edit.setText(' '.join(translated)) self.verse_order_edit.setText(' '.join(translated))
else: else:
self.verse_order_edit.setText('') self.verse_order_edit.setText('')
@ -554,7 +558,7 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
item = self.verse_list_widget.item(row, 0) item = self.verse_list_widget.item(row, 0)
verse_def = item.data(QtCore.Qt.UserRole) verse_def = item.data(QtCore.Qt.UserRole)
verse_tag = VerseType.translated_tag(verse_def[0]) verse_tag = VerseType.translated_tag(verse_def[0])
row_def = '%s%s' % (verse_tag, verse_def[1:]) row_def = '{tag}{verse}'.format(tag=verse_tag, verse=verse_def[1:])
row_label.append(row_def) row_label.append(row_def)
self.verse_list_widget.setVerticalHeaderLabels(row_label) self.verse_list_widget.setVerticalHeaderLabels(row_label)
self.verse_list_widget.resizeRowsToContents() self.verse_list_widget.resizeRowsToContents()
@ -742,7 +746,7 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
self.verse_form.set_verse('', True) self.verse_form.set_verse('', True)
if self.verse_form.exec(): if self.verse_form.exec():
after_text, verse_tag, verse_num = self.verse_form.get_verse() after_text, verse_tag, verse_num = self.verse_form.get_verse()
verse_def = '%s%s' % (verse_tag, verse_num) verse_def = '{tag}{number}'.format(tag=verse_tag, number=verse_num)
item = QtWidgets.QTableWidgetItem(after_text) item = QtWidgets.QTableWidgetItem(after_text)
item.setData(QtCore.Qt.UserRole, verse_def) item.setData(QtCore.Qt.UserRole, verse_def)
item.setText(after_text) item.setText(after_text)
@ -760,7 +764,7 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
self.verse_form.set_verse(temp_text, True, verse_id) self.verse_form.set_verse(temp_text, True, verse_id)
if self.verse_form.exec(): if self.verse_form.exec():
after_text, verse_tag, verse_num = self.verse_form.get_verse() after_text, verse_tag, verse_num = self.verse_form.get_verse()
verse_def = '%s%s' % (verse_tag, verse_num) verse_def = '{tag}{number}'.format(tag=verse_tag, number=verse_num)
item.setData(QtCore.Qt.UserRole, verse_def) item.setData(QtCore.Qt.UserRole, verse_def)
item.setText(after_text) item.setText(after_text)
# number of lines has changed, repaint the list moving the data # number of lines has changed, repaint the list moving the data
@ -793,7 +797,7 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
field = item.data(QtCore.Qt.UserRole) field = item.data(QtCore.Qt.UserRole)
verse_tag = VerseType.translated_name(field[0]) verse_tag = VerseType.translated_name(field[0])
verse_num = field[1:] verse_num = field[1:]
verse_list += '---[%s:%s]---\n' % (verse_tag, verse_num) verse_list += '---[{tag}:{number}]---\n'.format(tag=verse_tag, number=verse_num)
verse_list += item.text() verse_list += item.text()
verse_list += '\n' verse_list += '\n'
self.verse_form.set_verse(verse_list) self.verse_form.set_verse(verse_list)
@ -828,7 +832,7 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
verse_num = match.group(1) verse_num = match.group(1)
else: else:
verse_num = '1' verse_num = '1'
verse_def = '%s%s' % (verse_tag, verse_num) verse_def = '{tag}{number}'.format(tag=verse_tag, number=verse_num)
else: else:
if parts.endswith('\n'): if parts.endswith('\n'):
parts = parts.rstrip('\n') parts = parts.rstrip('\n')
@ -919,7 +923,7 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
""" """
Loads file(s) from the filesystem. Loads file(s) from the filesystem.
""" """
filters = '%s (*)' % UiStrings().AllFiles filters = '{text} (*)'.format(text=UiStrings().AllFiles)
file_names = FileDialog.getOpenFileNames(self, translate('SongsPlugin.EditSongForm', 'Open File(s)'), '', file_names = FileDialog.getOpenFileNames(self, translate('SongsPlugin.EditSongForm', 'Open File(s)'), '',
filters) filters)
for filename in file_names: for filename in file_names:
@ -1027,7 +1031,7 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
for item in order_text.split(): for item in order_text.split():
verse_tag = VerseType.tags[VerseType.from_translated_tag(item[0])] verse_tag = VerseType.tags[VerseType.from_translated_tag(item[0])]
verse_num = item[1:].lower() verse_num = item[1:].lower()
order.append('%s%s' % (verse_tag, verse_num)) order.append('{tag}{number}'.format(tag=verse_tag, number=verse_num))
self.song.verse_order = ' '.join(order) self.song.verse_order = ' '.join(order)
self.song.ccli_number = self.ccli_number_edit.text() self.song.ccli_number = self.ccli_number_edit.text()
theme_name = self.theme_combo_box.currentText() theme_name = self.theme_combo_box.currentText()
@ -1082,12 +1086,12 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
try: try:
os.remove(audio) os.remove(audio)
except: except:
log.exception('Could not remove file: %s', audio) log.exception('Could not remove file: {audio}'.format(audio=audio))
if not files: if not files:
try: try:
os.rmdir(save_path) os.rmdir(save_path)
except OSError: except OSError:
log.exception('Could not remove directory: %s', save_path) log.exception('Could not remove directory: {path}'.format(path=save_path))
clean_song(self.manager, self.song) clean_song(self.manager, self.song)
self.manager.save_object(self.song) self.manager.save_object(self.song)
self.media_item.auto_select_id = self.song.id self.media_item.auto_select_id = self.song.id

View File

@ -22,7 +22,8 @@
from PyQt5 import QtWidgets from PyQt5 import QtWidgets
from openlp.core.lib import SpellTextEdit, build_icon, translate from openlp.core.ui.lib import SpellTextEdit
from openlp.core.lib import build_icon, translate
from openlp.core.lib.ui import UiStrings, create_button_box from openlp.core.lib.ui import UiStrings, create_button_box
from openlp.plugins.songs.lib import VerseType from openlp.plugins.songs.lib import VerseType

View File

@ -59,7 +59,7 @@ class EditVerseForm(QtWidgets.QDialog, Ui_EditVerseDialog):
if self.verse_text_edit.textCursor().columnNumber() != 0: if self.verse_text_edit.textCursor().columnNumber() != 0:
self.verse_text_edit.insertPlainText('\n') self.verse_text_edit.insertPlainText('\n')
verse_tag = VerseType.translated_name(verse_tag) verse_tag = VerseType.translated_name(verse_tag)
self.verse_text_edit.insertPlainText('---[%s:%s]---\n' % (verse_tag, verse_num)) self.verse_text_edit.insertPlainText('---[{tag}:{number}]---\n'.format(tag=verse_tag, number=verse_num))
self.verse_text_edit.setFocus() self.verse_text_edit.setFocus()
def on_split_button_clicked(self): def on_split_button_clicked(self):
@ -107,7 +107,7 @@ class EditVerseForm(QtWidgets.QDialog, Ui_EditVerseDialog):
self.verse_type_combo_box.currentIndex()] self.verse_type_combo_box.currentIndex()]
if not text: if not text:
return return
position = text.rfind('---[%s' % verse_name, 0, position) position = text.rfind('---[{verse}'.format(verse=verse_name), 0, position)
if position == -1: if position == -1:
self.verse_number_box.setValue(1) self.verse_number_box.setValue(1)
return return
@ -124,7 +124,7 @@ class EditVerseForm(QtWidgets.QDialog, Ui_EditVerseDialog):
verse_num = 1 verse_num = 1
self.verse_number_box.setValue(verse_num) self.verse_number_box.setValue(verse_num)
def set_verse(self, text, single=False, tag='%s1' % VerseType.tags[VerseType.Verse]): def set_verse(self, text, single=False, tag='{verse}1'.format(verse=VerseType.tags[VerseType.Verse])):
""" """
Save the verse Save the verse
@ -142,7 +142,7 @@ class EditVerseForm(QtWidgets.QDialog, Ui_EditVerseDialog):
self.insert_button.setVisible(False) self.insert_button.setVisible(False)
else: else:
if not text: if not text:
text = '---[%s:1]---\n' % VerseType.translated_names[VerseType.Verse] text = '---[{tag}:1]---\n'.format(tag=VerseType.translated_names[VerseType.Verse])
self.verse_type_combo_box.setCurrentIndex(0) self.verse_type_combo_box.setCurrentIndex(0)
self.verse_number_box.setValue(1) self.verse_number_box.setValue(1)
self.insert_button.setVisible(True) self.insert_button.setVisible(True)
@ -167,5 +167,5 @@ class EditVerseForm(QtWidgets.QDialog, Ui_EditVerseDialog):
""" """
text = self.verse_text_edit.toPlainText() text = self.verse_text_edit.toPlainText()
if not text.startswith('---['): if not text.startswith('---['):
text = '---[%s:1]---\n%s' % (VerseType.translated_names[VerseType.Verse], text) text = '---[{tag}:1]---\n{text}'.format(tag=VerseType.translated_names[VerseType.Verse], text=text)
return text return text

View File

@ -34,7 +34,7 @@ class MediaFilesForm(QtWidgets.QDialog, Ui_MediaFilesDialog):
""" """
Class to show a list of files from the Class to show a list of files from the
""" """
log.info('%s MediaFilesForm loaded', __name__) log.info('{name} MediaFilesForm loaded'.format(name=__name__))
def __init__(self, parent): def __init__(self, parent):
super(MediaFilesForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint) super(MediaFilesForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint)

View File

@ -143,6 +143,7 @@ class SongExportForm(OpenLPWizard):
Song wizard localisation. Song wizard localisation.
""" """
self.setWindowTitle(translate('SongsPlugin.ExportWizardForm', 'Song Export Wizard')) self.setWindowTitle(translate('SongsPlugin.ExportWizardForm', 'Song Export Wizard'))
# TODO: Verify format() with template variables
self.title_label.setText(WizardStrings.HeaderStyle % self.title_label.setText(WizardStrings.HeaderStyle %
translate('OpenLP.Ui', 'Welcome to the Song Export Wizard')) translate('OpenLP.Ui', 'Welcome to the Song Export Wizard'))
self.information_label.setText( self.information_label.setText(
@ -151,7 +152,7 @@ class SongExportForm(OpenLPWizard):
self.available_songs_page.setTitle(translate('SongsPlugin.ExportWizardForm', 'Select Songs')) self.available_songs_page.setTitle(translate('SongsPlugin.ExportWizardForm', 'Select Songs'))
self.available_songs_page.setSubTitle(translate('SongsPlugin.ExportWizardForm', self.available_songs_page.setSubTitle(translate('SongsPlugin.ExportWizardForm',
'Check the songs you want to export.')) 'Check the songs you want to export.'))
self.search_label.setText('%s:' % UiStrings().Search) self.search_label.setText('{text}:'.format(text=UiStrings().Search))
self.uncheck_button.setText(translate('SongsPlugin.ExportWizardForm', 'Uncheck All')) self.uncheck_button.setText(translate('SongsPlugin.ExportWizardForm', 'Uncheck All'))
self.check_button.setText(translate('SongsPlugin.ExportWizardForm', 'Check All')) self.check_button.setText(translate('SongsPlugin.ExportWizardForm', 'Check All'))
self.export_song_page.setTitle(translate('SongsPlugin.ExportWizardForm', 'Select Directory')) self.export_song_page.setTitle(translate('SongsPlugin.ExportWizardForm', 'Select Directory'))
@ -223,7 +224,7 @@ class SongExportForm(OpenLPWizard):
if song.temporary: if song.temporary:
continue continue
authors = create_separated_list([author.display_name for author in song.authors]) authors = create_separated_list([author.display_name for author in song.authors])
title = '%s (%s)' % (str(song.title), authors) title = '{title} ({author})'.format(title=song.title, author=authors)
item = QtWidgets.QListWidgetItem(title) item = QtWidgets.QListWidgetItem(title)
item.setData(QtCore.Qt.UserRole, song) item.setData(QtCore.Qt.UserRole, song)
item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsEnabled) item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsEnabled)
@ -257,7 +258,7 @@ class SongExportForm(OpenLPWizard):
self.progress_label.setText(translate('SongsPlugin.SongExportForm', 'Your song export failed.')) self.progress_label.setText(translate('SongsPlugin.SongExportForm', 'Your song export failed.'))
except OSError as ose: except OSError as ose:
self.progress_label.setText(translate('SongsPlugin.SongExportForm', 'Your song export failed because this ' self.progress_label.setText(translate('SongsPlugin.SongExportForm', 'Your song export failed because this '
'error occurred: %s') % ose.strerror) 'error occurred: {error}').format(error=ose.strerror))
def on_search_line_edit_changed(self, text): def on_search_line_edit_changed(self, text):
""" """

View File

@ -132,6 +132,7 @@ class SongImportForm(OpenLPWizard, RegistryProperties):
Song wizard localisation. Song wizard localisation.
""" """
self.setWindowTitle(translate('SongsPlugin.ImportWizardForm', 'Song Import Wizard')) self.setWindowTitle(translate('SongsPlugin.ImportWizardForm', 'Song Import Wizard'))
# TODO: Verify format() with template variables
self.title_label.setText(WizardStrings.HeaderStyle % translate('OpenLP.Ui', self.title_label.setText(WizardStrings.HeaderStyle % translate('OpenLP.Ui',
'Welcome to the Song Import Wizard')) 'Welcome to the Song Import Wizard'))
self.information_label.setText( self.information_label.setText(
@ -236,7 +237,7 @@ class SongImportForm(OpenLPWizard, RegistryProperties):
""" """
if filters: if filters:
filters += ';;' filters += ';;'
filters += '%s (*)' % UiStrings().AllFiles filters += '{text} (*)'.format(text=UiStrings().AllFiles)
file_names = FileDialog.getOpenFileNames( file_names = FileDialog.getOpenFileNames(
self, title, self, title,
Settings().value(self.plugin.settings_section + '/last directory import'), filters) Settings().value(self.plugin.settings_section + '/last directory import'), filters)
@ -271,9 +272,11 @@ class SongImportForm(OpenLPWizard, RegistryProperties):
select_mode, format_name, ext_filter = SongFormat.get(this_format, 'selectMode', 'name', 'filter') select_mode, format_name, ext_filter = SongFormat.get(this_format, 'selectMode', 'name', 'filter')
file_path_edit = self.format_widgets[this_format]['file_path_edit'] file_path_edit = self.format_widgets[this_format]['file_path_edit']
if select_mode == SongFormatSelect.SingleFile: if select_mode == SongFormatSelect.SingleFile:
# TODO: Verify format() with template variables
self.get_file_name( self.get_file_name(
WizardStrings.OpenTypeFile % format_name, file_path_edit, 'last directory import', ext_filter) WizardStrings.OpenTypeFile % format_name, file_path_edit, 'last directory import', ext_filter)
elif select_mode == SongFormatSelect.SingleFolder: elif select_mode == SongFormatSelect.SingleFolder:
# TODO: Verify format() with template variables
self.get_folder(WizardStrings.OpenTypeFolder % format_name, file_path_edit, 'last directory import') self.get_folder(WizardStrings.OpenTypeFolder % format_name, file_path_edit, 'last directory import')
def on_add_button_clicked(self): def on_add_button_clicked(self):
@ -283,6 +286,7 @@ class SongImportForm(OpenLPWizard, RegistryProperties):
this_format = self.current_format this_format = self.current_format
select_mode, format_name, ext_filter, custom_title = \ select_mode, format_name, ext_filter, custom_title = \
SongFormat.get(this_format, 'selectMode', 'name', 'filter', 'getFilesTitle') SongFormat.get(this_format, 'selectMode', 'name', 'filter', 'getFilesTitle')
# TODO: Verify format() with template variables
title = custom_title if custom_title else WizardStrings.OpenTypeFile % format_name title = custom_title if custom_title else WizardStrings.OpenTypeFile % format_name
if select_mode == SongFormatSelect.MultipleFiles: if select_mode == SongFormatSelect.MultipleFiles:
self.get_files(title, self.format_widgets[this_format]['file_list_widget'], ext_filter) self.get_files(title, self.format_widgets[this_format]['file_list_widget'], ext_filter)

View File

@ -164,7 +164,8 @@ class SongMaintenanceForm(QtWidgets.QDialog, Ui_SongMaintenanceDialog, RegistryP
books = self.manager.get_all_objects(Book) books = self.manager.get_all_objects(Book)
books.sort(key=get_book_key) books.sort(key=get_book_key)
for book in books: for book in books:
book_name = QtWidgets.QListWidgetItem('%s (%s)' % (book.name, book.publisher)) book_name = QtWidgets.QListWidgetItem('{name} ({publisher})'.format(name=book.name,
publisher=book.publisher))
book_name.setData(QtCore.Qt.UserRole, book.id) book_name.setData(QtCore.Qt.UserRole, book.id)
self.song_books_list_widget.addItem(book_name) self.song_books_list_widget.addItem(book_name)
@ -310,11 +311,12 @@ class SongMaintenanceForm(QtWidgets.QDialog, Ui_SongMaintenanceDialog, RegistryP
else: else:
critical_error_message_box( critical_error_message_box(
message=translate('SongsPlugin.SongMaintenanceForm', 'Could not save your changes.')) message=translate('SongsPlugin.SongMaintenanceForm', 'Could not save your changes.'))
elif critical_error_message_box(message=translate( elif critical_error_message_box(
'SongsPlugin.SongMaintenanceForm', 'The author %s already exists. Would you like to make songs with ' message=translate(
'author %s use the existing author %s?') % 'SongsPlugin.SongMaintenanceForm',
(author.display_name, temp_display_name, author.display_name), parent=self, question=True) == \ 'The author {original} already exists. Would you like to make songs with author {new} use the '
QtWidgets.QMessageBox.Yes: 'existing author {original}?').format(original=author.display_name, new=temp_display_name),
parent=self, question=True) == QtWidgets.QMessageBox.Yes:
self._merge_objects(author, self.merge_authors, self.reset_authors) self._merge_objects(author, self.merge_authors, self.reset_authors)
else: else:
# We restore the author's old first and last name as well as # We restore the author's old first and last name as well as
@ -346,9 +348,10 @@ class SongMaintenanceForm(QtWidgets.QDialog, Ui_SongMaintenanceDialog, RegistryP
critical_error_message_box( critical_error_message_box(
message=translate('SongsPlugin.SongMaintenanceForm', 'Could not save your changes.')) message=translate('SongsPlugin.SongMaintenanceForm', 'Could not save your changes.'))
elif critical_error_message_box( elif critical_error_message_box(
message=translate('SongsPlugin.SongMaintenanceForm', message=translate('SongsPlugin.SongMaintenanceForm',
'The topic %s already exists. Would you like to make songs with topic %s use the ' 'The topic {original} already exists. Would you like to make songs with '
'existing topic %s?') % (topic.name, temp_name, topic.name), 'topic {new} use the existing topic {original}?').format(original=topic.name,
new=temp_name),
parent=self, question=True) == QtWidgets.QMessageBox.Yes: parent=self, question=True) == QtWidgets.QMessageBox.Yes:
self._merge_objects(topic, self.merge_topics, self.reset_topics) self._merge_objects(topic, self.merge_topics, self.reset_topics)
else: else:
@ -384,9 +387,10 @@ class SongMaintenanceForm(QtWidgets.QDialog, Ui_SongMaintenanceDialog, RegistryP
critical_error_message_box( critical_error_message_box(
message=translate('SongsPlugin.SongMaintenanceForm', 'Could not save your changes.')) message=translate('SongsPlugin.SongMaintenanceForm', 'Could not save your changes.'))
elif critical_error_message_box( elif critical_error_message_box(
message=translate('SongsPlugin.SongMaintenanceForm', message=translate('SongsPlugin.SongMaintenanceForm',
'The book %s already exists. Would you like to make ' 'The book {original} already exists. Would you like to make songs with '
'songs with book %s use the existing book %s?') % (book.name, temp_name, book.name), 'book {new} use the existing book {original}?').format(original=book.name,
new=temp_name),
parent=self, question=True) == QtWidgets.QMessageBox.Yes: parent=self, question=True) == QtWidgets.QMessageBox.Yes:
self._merge_objects(book, self.merge_song_books, self.reset_song_books) self._merge_objects(book, self.merge_song_books, self.reset_song_books)
else: else:

View File

@ -242,7 +242,8 @@ class Ui_SongSelectDialog(object):
self.search_label.setText(translate('SongsPlugin.SongSelectForm', 'Search Text:')) self.search_label.setText(translate('SongsPlugin.SongSelectForm', 'Search Text:'))
self.search_button.setText(translate('SongsPlugin.SongSelectForm', 'Search')) self.search_button.setText(translate('SongsPlugin.SongSelectForm', 'Search'))
self.stop_button.setText(translate('SongsPlugin.SongSelectForm', 'Stop')) self.stop_button.setText(translate('SongsPlugin.SongSelectForm', 'Stop'))
self.result_count_label.setText(translate('SongsPlugin.SongSelectForm', 'Found %s song(s)') % 0) self.result_count_label.setText(translate('SongsPlugin.SongSelectForm',
'Found {count:d} song(s)').format(count=0))
self.logout_button.setText(translate('SongsPlugin.SongSelectForm', 'Logout')) self.logout_button.setText(translate('SongsPlugin.SongSelectForm', 'Logout'))
self.view_button.setText(translate('SongsPlugin.SongSelectForm', 'View')) self.view_button.setText(translate('SongsPlugin.SongSelectForm', 'View'))
self.title_label.setText(translate('SongsPlugin.SongSelectForm', 'Title:')) self.title_label.setText(translate('SongsPlugin.SongSelectForm', 'Title:'))

View File

@ -305,7 +305,8 @@ class SongSelectForm(QtWidgets.QDialog, Ui_SongSelectDialog):
self.search_progress_bar.setValue(0) self.search_progress_bar.setValue(0)
self.set_progress_visible(True) self.set_progress_visible(True)
self.search_results_widget.clear() self.search_results_widget.clear()
self.result_count_label.setText(translate('SongsPlugin.SongSelectForm', 'Found %s song(s)') % self.song_count) self.result_count_label.setText(translate('SongsPlugin.SongSelectForm',
'Found {count:d} song(s)').format(count=self.song_count))
self.application.process_events() self.application.process_events()
self.song_count = 0 self.song_count = 0
search_history = self.search_combobox.getItems() search_history = self.search_combobox.getItems()
@ -343,7 +344,8 @@ class SongSelectForm(QtWidgets.QDialog, Ui_SongSelectDialog):
:param song: :param song:
""" """
self.song_count += 1 self.song_count += 1
self.result_count_label.setText(translate('SongsPlugin.SongSelectForm', 'Found %s song(s)') % self.song_count) self.result_count_label.setText(translate('SongsPlugin.SongSelectForm',
'Found {count:d} song(s)').format(count=self.song_count))
item_title = song['title'] + ' (' + ', '.join(song['authors']) + ')' item_title = song['title'] + ' (' + ', '.join(song['authors']) + ')'
song_item = QtWidgets.QListWidgetItem(item_title, self.search_results_widget) song_item = QtWidgets.QListWidgetItem(item_title, self.search_results_widget)
song_item.setData(QtCore.Qt.UserRole, song) song_item.setData(QtCore.Qt.UserRole, song)

View File

@ -534,11 +534,11 @@ def delete_song(song_id, song_plugin):
try: try:
os.remove(media_file.file_name) os.remove(media_file.file_name)
except OSError: except OSError:
log.exception('Could not remove file: %s', media_file.file_name) log.exception('Could not remove file: {name}'.format(name=media_file.file_name))
try: try:
save_path = os.path.join(AppLocation.get_section_data_path(song_plugin.name), 'audio', str(song_id)) save_path = os.path.join(AppLocation.get_section_data_path(song_plugin.name), 'audio', str(song_id))
if os.path.exists(save_path): if os.path.exists(save_path):
os.rmdir(save_path) os.rmdir(save_path)
except OSError: except OSError:
log.exception('Could not remove directory: %s', save_path) log.exception('Could not remove directory: {path}'.format(path=save_path))
song_plugin.manager.delete_object(Song, song_id) song_plugin.manager.delete_object(Song, song_id)

View File

@ -39,7 +39,7 @@ class Author(BaseModel):
""" """
def get_display_name(self, author_type=None): def get_display_name(self, author_type=None):
if author_type: if author_type:
return "%s (%s)" % (self.display_name, AuthorType.Types[author_type]) return "{name} ({author})".format(name=self.display_name, author=AuthorType.Types[author_type])
return self.display_name return self.display_name
@ -105,7 +105,9 @@ class Book(BaseModel):
Book model Book model
""" """
def __repr__(self): def __repr__(self):
return '<Book id="%s" name="%s" publisher="%s" />' % (str(self.id), self.name, self.publisher) return '<Book id="{myid:d}" name="{name}" publisher="{publisher}" />'.format(myid=self.id,
name=self.name,
publisher=self.publisher)
class MediaFile(BaseModel): class MediaFile(BaseModel):
@ -187,7 +189,7 @@ class SongBookEntry(BaseModel):
@staticmethod @staticmethod
def get_display_name(songbook_name, entry): def get_display_name(songbook_name, entry):
if entry: if entry:
return "%s #%s" % (songbook_name, entry) return "{name} #{entry}".format(name=songbook_name, entry=entry)
return songbook_name return songbook_name

View File

@ -56,13 +56,13 @@ try:
from .importers.songsoffellowship import SongsOfFellowshipImport from .importers.songsoffellowship import SongsOfFellowshipImport
HAS_SOF = True HAS_SOF = True
except ImportError: except ImportError:
log.exception('Error importing %s', 'SongsOfFellowshipImport') log.exception('Error importing {text}'.format(text='SongsOfFellowshipImport'))
HAS_SOF = False HAS_SOF = False
try: try:
from .importers.openoffice import OpenOfficeImport from .importers.openoffice import OpenOfficeImport
HAS_OOO = True HAS_OOO = True
except ImportError: except ImportError:
log.exception('Error importing %s', 'OooImport') log.exception('Error importing {text}'.format(text='OooImport'))
HAS_OOO = False HAS_OOO = False
HAS_MEDIASHOUT = False HAS_MEDIASHOUT = False
if is_win(): if is_win():
@ -70,21 +70,21 @@ if is_win():
from .importers.mediashout import MediaShoutImport from .importers.mediashout import MediaShoutImport
HAS_MEDIASHOUT = True HAS_MEDIASHOUT = True
except ImportError: except ImportError:
log.exception('Error importing %s', 'MediaShoutImport') log.exception('Error importing {text}'.format(text='MediaShoutImport'))
HAS_WORSHIPCENTERPRO = False HAS_WORSHIPCENTERPRO = False
if is_win(): if is_win():
try: try:
from .importers.worshipcenterpro import WorshipCenterProImport from .importers.worshipcenterpro import WorshipCenterProImport
HAS_WORSHIPCENTERPRO = True HAS_WORSHIPCENTERPRO = True
except ImportError: except ImportError:
log.exception('Error importing %s', 'WorshipCenterProImport') log.exception('Error importing {text}'.format(text='WorshipCenterProImport'))
HAS_OPSPRO = False HAS_OPSPRO = False
if is_win(): if is_win():
try: try:
from .importers.opspro import OPSProImport from .importers.opspro import OPSProImport
HAS_OPSPRO = True HAS_OPSPRO = True
except ImportError: except ImportError:
log.exception('Error importing %s', 'OPSProImport') log.exception('Error importing {text}'.format(text='OPSProImport'))
class SongFormatSelect(object): class SongFormatSelect(object):
@ -198,7 +198,7 @@ class SongFormat(object):
'class': OpenLyricsImport, 'class': OpenLyricsImport,
'name': 'OpenLyrics', 'name': 'OpenLyrics',
'prefix': 'openLyrics', 'prefix': 'openLyrics',
'filter': '%s (*.xml)' % translate('SongsPlugin.ImportWizardForm', 'OpenLyrics Files'), 'filter': '{text} (*.xml)'.format(text=translate('SongsPlugin.ImportWizardForm', 'OpenLyrics Files')),
'comboBoxText': translate('SongsPlugin.ImportWizardForm', 'OpenLyrics or OpenLP 2 Exported Song') 'comboBoxText': translate('SongsPlugin.ImportWizardForm', 'OpenLyrics or OpenLP 2 Exported Song')
}, },
OpenLP2: { OpenLP2: {
@ -206,7 +206,7 @@ class SongFormat(object):
'name': UiStrings().OLPV2, 'name': UiStrings().OLPV2,
'prefix': 'openLP2', 'prefix': 'openLP2',
'selectMode': SongFormatSelect.SingleFile, 'selectMode': SongFormatSelect.SingleFile,
'filter': '%s (*.sqlite)' % (translate('SongsPlugin.ImportWizardForm', 'OpenLP 2 Databases')) 'filter': '{text} (*.sqlite)'.format(text=translate('SongsPlugin.ImportWizardForm', 'OpenLP 2 Databases'))
}, },
Generic: { Generic: {
'name': translate('SongsPlugin.ImportWizardForm', 'Generic Document/Presentation'), 'name': translate('SongsPlugin.ImportWizardForm', 'Generic Document/Presentation'),
@ -221,46 +221,50 @@ class SongFormat(object):
'class': CCLIFileImport, 'class': CCLIFileImport,
'name': 'CCLI/SongSelect', 'name': 'CCLI/SongSelect',
'prefix': 'ccli', 'prefix': 'ccli',
'filter': '%s (*.usr *.txt *.bin)' % translate('SongsPlugin.ImportWizardForm', 'CCLI SongSelect Files') 'filter': '{text} (*.usr *.txt *.bin)'.format(text=translate('SongsPlugin.ImportWizardForm',
'CCLI SongSelect Files'))
}, },
DreamBeam: { DreamBeam: {
'class': DreamBeamImport, 'class': DreamBeamImport,
'name': 'DreamBeam', 'name': 'DreamBeam',
'prefix': 'dreamBeam', 'prefix': 'dreamBeam',
'filter': '%s (*.xml)' % translate('SongsPlugin.ImportWizardForm', 'DreamBeam Song Files') 'filter': '{text} (*.xml)'.format(text=translate('SongsPlugin.ImportWizardForm', 'DreamBeam Song Files'))
}, },
EasySlides: { EasySlides: {
'class': EasySlidesImport, 'class': EasySlidesImport,
'name': 'EasySlides', 'name': 'EasySlides',
'prefix': 'easySlides', 'prefix': 'easySlides',
'selectMode': SongFormatSelect.SingleFile, 'selectMode': SongFormatSelect.SingleFile,
'filter': '%s (*.xml)' % translate('SongsPlugin.ImportWizardForm', 'EasySlides XML File') 'filter': '{text} (*.xml)'.format(text=translate('SongsPlugin.ImportWizardForm', 'EasySlides XML File'))
}, },
EasyWorshipDB: { EasyWorshipDB: {
'class': EasyWorshipSongImport, 'class': EasyWorshipSongImport,
'name': 'EasyWorship Song Database', 'name': 'EasyWorship Song Database',
'prefix': 'ew', 'prefix': 'ew',
'selectMode': SongFormatSelect.SingleFile, 'selectMode': SongFormatSelect.SingleFile,
'filter': '%s (*.db)' % translate('SongsPlugin.ImportWizardForm', 'EasyWorship Song Database') 'filter': '{text} (*.db)'.format(text=translate('SongsPlugin.ImportWizardForm',
'EasyWorship Song Database'))
}, },
EasyWorshipService: { EasyWorshipService: {
'class': EasyWorshipSongImport, 'class': EasyWorshipSongImport,
'name': 'EasyWorship Service File', 'name': 'EasyWorship Service File',
'prefix': 'ew', 'prefix': 'ew',
'selectMode': SongFormatSelect.SingleFile, 'selectMode': SongFormatSelect.SingleFile,
'filter': '%s (*.ews)' % translate('SongsPlugin.ImportWizardForm', 'EasyWorship Service File') 'filter': '{text} (*.ews)'.format(text=translate('SongsPlugin.ImportWizardForm',
'EasyWorship Service File'))
}, },
FoilPresenter: { FoilPresenter: {
'class': FoilPresenterImport, 'class': FoilPresenterImport,
'name': 'Foilpresenter', 'name': 'Foilpresenter',
'prefix': 'foilPresenter', 'prefix': 'foilPresenter',
'filter': '%s (*.foil)' % translate('SongsPlugin.ImportWizardForm', 'Foilpresenter Song Files') 'filter': '{text} (*.foil)'.format(text=translate('SongsPlugin.ImportWizardForm',
'Foilpresenter Song Files'))
}, },
Lyrix: { Lyrix: {
'class': LyrixImport, 'class': LyrixImport,
'name': 'LyriX', 'name': 'LyriX',
'prefix': 'lyrix', 'prefix': 'lyrix',
'filter': '%s (*.txt)' % translate('SongsPlugin.ImportWizardForm', 'LyriX Files'), 'filter': '{text} (*.txt)'.format(text=translate('SongsPlugin.ImportWizardForm', 'LyriX Files')),
'comboBoxText': translate('SongsPlugin.ImportWizardForm', 'LyriX (Exported TXT-files)') 'comboBoxText': translate('SongsPlugin.ImportWizardForm', 'LyriX (Exported TXT-files)')
}, },
MediaShout: { MediaShout: {
@ -268,7 +272,7 @@ class SongFormat(object):
'prefix': 'mediaShout', 'prefix': 'mediaShout',
'canDisable': True, 'canDisable': True,
'selectMode': SongFormatSelect.SingleFile, 'selectMode': SongFormatSelect.SingleFile,
'filter': '%s (*.mdb)' % translate('SongsPlugin.ImportWizardForm', 'MediaShout Database'), 'filter': '{text} (*.mdb)'.format(text=translate('SongsPlugin.ImportWizardForm', 'MediaShout Database')),
'disabledLabelText': translate('SongsPlugin.ImportWizardForm', 'disabledLabelText': translate('SongsPlugin.ImportWizardForm',
'The MediaShout importer is only supported on Windows. It has ' 'The MediaShout importer is only supported on Windows. It has '
'been disabled due to a missing Python module. If you want to ' 'been disabled due to a missing Python module. If you want to '
@ -285,7 +289,7 @@ class SongFormat(object):
'prefix': 'OPSPro', 'prefix': 'OPSPro',
'canDisable': True, 'canDisable': True,
'selectMode': SongFormatSelect.SingleFile, 'selectMode': SongFormatSelect.SingleFile,
'filter': '%s (*.mdb)' % translate('SongsPlugin.ImportWizardForm', 'OPS Pro database'), 'filter': '{text} (*.mdb)'.format(text=translate('SongsPlugin.ImportWizardForm', 'OPS Pro database')),
'disabledLabelText': translate('SongsPlugin.ImportWizardForm', 'disabledLabelText': translate('SongsPlugin.ImportWizardForm',
'The OPS Pro importer is only supported on Windows. It has been ' 'The OPS Pro importer is only supported on Windows. It has been '
'disabled due to a missing Python module. If you want to use this ' 'disabled due to a missing Python module. If you want to use this '
@ -295,7 +299,7 @@ class SongFormat(object):
'class': PowerPraiseImport, 'class': PowerPraiseImport,
'name': 'PowerPraise', 'name': 'PowerPraise',
'prefix': 'powerPraise', 'prefix': 'powerPraise',
'filter': '%s (*.ppl)' % translate('SongsPlugin.ImportWizardForm', 'PowerPraise Song Files') 'filter': '{text} (*.ppl)'.format(text=translate('SongsPlugin.ImportWizardForm', 'PowerPraise Song Files'))
}, },
PowerSong: { PowerSong: {
'class': PowerSongImport, 'class': PowerSongImport,
@ -309,26 +313,29 @@ class SongFormat(object):
'class': PresentationManagerImport, 'class': PresentationManagerImport,
'name': 'PresentationManager', 'name': 'PresentationManager',
'prefix': 'presentationManager', 'prefix': 'presentationManager',
'filter': '%s (*.sng)' % translate('SongsPlugin.ImportWizardForm', 'PresentationManager Song Files') 'filter': '{text} (*.sng)'.format(text=translate('SongsPlugin.ImportWizardForm',
'PresentationManager Song Files'))
}, },
ProPresenter: { ProPresenter: {
'class': ProPresenterImport, 'class': ProPresenterImport,
'name': 'ProPresenter 4, 5 and 6', 'name': 'ProPresenter 4, 5 and 6',
'prefix': 'proPresenter', 'prefix': 'proPresenter',
'filter': '%s (*.pro4 *.pro5 *.pro6)' % translate('SongsPlugin.ImportWizardForm', 'ProPresenter Song Files') 'filter': '{text} (*.pro4 *.pro5 *.pro6)'.format(text=translate('SongsPlugin.ImportWizardForm',
'ProPresenter Song Files'))
}, },
SongBeamer: { SongBeamer: {
'class': SongBeamerImport, 'class': SongBeamerImport,
'name': 'SongBeamer', 'name': 'SongBeamer',
'prefix': 'songBeamer', 'prefix': 'songBeamer',
'filter': '%s (*.sng)' % translate('SongsPlugin.ImportWizardForm', 'SongBeamer Files') 'filter': '{text} (*.sng)'.format(text=translate('SongsPlugin.ImportWizardForm',
'SongBeamer Files'))
}, },
SongPro: { SongPro: {
'class': SongProImport, 'class': SongProImport,
'name': 'SongPro', 'name': 'SongPro',
'prefix': 'songPro', 'prefix': 'songPro',
'selectMode': SongFormatSelect.SingleFile, 'selectMode': SongFormatSelect.SingleFile,
'filter': '%s (*.txt)' % translate('SongsPlugin.ImportWizardForm', 'SongPro Text Files'), 'filter': '{text} (*.txt)'.format(text=translate('SongsPlugin.ImportWizardForm', 'SongPro Text Files')),
'comboBoxText': translate('SongsPlugin.ImportWizardForm', 'SongPro (Export File)'), 'comboBoxText': translate('SongsPlugin.ImportWizardForm', 'SongPro (Export File)'),
'descriptionText': translate('SongsPlugin.ImportWizardForm', 'descriptionText': translate('SongsPlugin.ImportWizardForm',
'In SongPro, export your songs using the File -> Export menu') 'In SongPro, export your songs using the File -> Export menu')
@ -337,13 +344,15 @@ class SongFormat(object):
'class': SongShowPlusImport, 'class': SongShowPlusImport,
'name': 'SongShow Plus', 'name': 'SongShow Plus',
'prefix': 'songShowPlus', 'prefix': 'songShowPlus',
'filter': '%s (*.sbsong)' % translate('SongsPlugin.ImportWizardForm', 'SongShow Plus Song Files') 'filter': '{text} (*.sbsong)'.format(text=translate('SongsPlugin.ImportWizardForm',
'SongShow Plus Song Files'))
}, },
SongsOfFellowship: { SongsOfFellowship: {
'name': 'Songs of Fellowship', 'name': 'Songs of Fellowship',
'prefix': 'songsOfFellowship', 'prefix': 'songsOfFellowship',
'canDisable': True, 'canDisable': True,
'filter': '%s (*.rtf)' % translate('SongsPlugin.ImportWizardForm', 'Songs Of Fellowship Song Files'), 'filter': '{text} (*.rtf)'.format(text=translate('SongsPlugin.ImportWizardForm',
'Songs Of Fellowship Song Files')),
'disabledLabelText': translate('SongsPlugin.ImportWizardForm', 'disabledLabelText': translate('SongsPlugin.ImportWizardForm',
'The Songs of Fellowship importer has been disabled because ' 'The Songs of Fellowship importer has been disabled because '
'OpenLP cannot access OpenOffice or LibreOffice.') 'OpenLP cannot access OpenOffice or LibreOffice.')
@ -352,30 +361,33 @@ class SongFormat(object):
'class': SundayPlusImport, 'class': SundayPlusImport,
'name': 'SundayPlus', 'name': 'SundayPlus',
'prefix': 'sundayPlus', 'prefix': 'sundayPlus',
'filter': '%s (*.ptf)' % translate('SongsPlugin.ImportWizardForm', 'SundayPlus Song Files') 'filter': '{text} (*.ptf)'.format(text=translate('SongsPlugin.ImportWizardForm', 'SundayPlus Song Files'))
}, },
VideoPsalm: { VideoPsalm: {
'class': VideoPsalmImport, 'class': VideoPsalmImport,
'name': 'VideoPsalm', 'name': 'VideoPsalm',
'prefix': 'videopsalm', 'prefix': 'videopsalm',
'selectMode': SongFormatSelect.SingleFile, 'selectMode': SongFormatSelect.SingleFile,
'filter': '%s (*.json)' % translate('SongsPlugin.ImportWizardForm', 'VideoPsalm Files'), 'filter': '{text} (*.json)'.format(text=translate('SongsPlugin.ImportWizardForm', 'VideoPsalm Files')),
'comboBoxText': translate('SongsPlugin.ImportWizardForm', 'VideoPsalm'), 'comboBoxText': translate('SongsPlugin.ImportWizardForm', 'VideoPsalm'),
'descriptionText': translate('SongsPlugin.ImportWizardForm', 'The VideoPsalm songbooks are normally located' 'descriptionText': translate('SongsPlugin.ImportWizardForm', 'The VideoPsalm songbooks are normally located'
' in %s') % 'C:\\Users\\Public\\Documents\\VideoPsalm\\SongBooks\\' ' in {path}').format(path='C:\\Users\\Public\\Documents\\VideoPsalm'
'\\SongBooks\\')
}, },
WordsOfWorship: { WordsOfWorship: {
'class': WordsOfWorshipImport, 'class': WordsOfWorshipImport,
'name': 'Words of Worship', 'name': 'Words of Worship',
'prefix': 'wordsOfWorship', 'prefix': 'wordsOfWorship',
'filter': '%s (*.wsg *.wow-song)' % translate('SongsPlugin.ImportWizardForm', 'Words Of Worship Song Files') 'filter': '{text} (*.wsg *.wow-song)'.format(text=translate('SongsPlugin.ImportWizardForm',
'Words Of Worship Song Files'))
}, },
WorshipAssistant: { WorshipAssistant: {
'class': WorshipAssistantImport, 'class': WorshipAssistantImport,
'name': 'Worship Assistant 0', 'name': 'Worship Assistant 0',
'prefix': 'worshipAssistant', 'prefix': 'worshipAssistant',
'selectMode': SongFormatSelect.SingleFile, 'selectMode': SongFormatSelect.SingleFile,
'filter': '%s (*.csv)' % translate('SongsPlugin.ImportWizardForm', 'Worship Assistant Files'), 'filter': '{text} (*.csv)'.format(text=translate('SongsPlugin.ImportWizardForm',
'Worship Assistant Files')),
'comboBoxText': translate('SongsPlugin.ImportWizardForm', 'Worship Assistant (CSV)'), 'comboBoxText': translate('SongsPlugin.ImportWizardForm', 'Worship Assistant (CSV)'),
'descriptionText': translate('SongsPlugin.ImportWizardForm', 'descriptionText': translate('SongsPlugin.ImportWizardForm',
'In Worship Assistant, export your Database to a CSV file.') 'In Worship Assistant, export your Database to a CSV file.')
@ -385,7 +397,8 @@ class SongFormat(object):
'prefix': 'worshipCenterPro', 'prefix': 'worshipCenterPro',
'canDisable': True, 'canDisable': True,
'selectMode': SongFormatSelect.SingleFile, 'selectMode': SongFormatSelect.SingleFile,
'filter': '%s (*.mdb)' % translate('SongsPlugin.ImportWizardForm', 'WorshipCenter Pro Song Files'), 'filter': '{text} (*.mdb)'.format(text=translate('SongsPlugin.ImportWizardForm',
'WorshipCenter Pro Song Files')),
'disabledLabelText': translate('SongsPlugin.ImportWizardForm', 'disabledLabelText': translate('SongsPlugin.ImportWizardForm',
'The WorshipCenter Pro importer is only supported on Windows. It has been ' 'The WorshipCenter Pro importer is only supported on Windows. It has been '
'disabled due to a missing Python module. If you want to use this ' 'disabled due to a missing Python module. If you want to use this '

View File

@ -58,7 +58,7 @@ class CCLIFileImport(SongImport):
self.import_wizard.progress_bar.setMaximum(len(self.import_source)) self.import_wizard.progress_bar.setMaximum(len(self.import_source))
for filename in self.import_source: for filename in self.import_source:
filename = str(filename) filename = str(filename)
log.debug('Importing CCLI File: %s', filename) log.debug('Importing CCLI File: {name}'.format(name=filename))
if os.path.isfile(filename): if os.path.isfile(filename):
detect_file = open(filename, 'rb') detect_file = open(filename, 'rb')
detect_content = detect_file.read(2048) detect_content = detect_file.read(2048)
@ -76,17 +76,17 @@ class CCLIFileImport(SongImport):
infile.close() infile.close()
ext = os.path.splitext(filename)[1] ext = os.path.splitext(filename)[1]
if ext.lower() == '.usr' or ext.lower() == '.bin': if ext.lower() == '.usr' or ext.lower() == '.bin':
log.info('SongSelect USR format file found: %s', filename) log.info('SongSelect USR format file found: {name}'.format(name=filename))
if not self.do_import_usr_file(lines): if not self.do_import_usr_file(lines):
self.log_error(filename) self.log_error(filename)
elif ext.lower() == '.txt': elif ext.lower() == '.txt':
log.info('SongSelect TEXT format file found: %s', filename) log.info('SongSelect TEXT format file found: {name}'.format(name=filename))
if not self.do_import_txt_file(lines): if not self.do_import_txt_file(lines):
self.log_error(filename) self.log_error(filename)
else: else:
self.log_error(filename, translate('SongsPlugin.CCLIFileImport', 'The file does not have a valid ' self.log_error(filename, translate('SongsPlugin.CCLIFileImport', 'The file does not have a valid '
'extension.')) 'extension.'))
log.info('Extension %s is not valid', filename) log.info('Extension {name} is not valid'.format(name=filename))
if self.stop_import_flag: if self.stop_import_flag:
return return
@ -146,7 +146,7 @@ class CCLIFileImport(SongImport):
:param text_list: An array of strings containing the usr file content. :param text_list: An array of strings containing the usr file content.
""" """
log.debug('USR file text: %s', text_list) log.debug('USR file text: {text}'.format(text=text_list))
song_author = '' song_author = ''
song_topics = '' song_topics = ''
for line in text_list: for line in text_list:
@ -193,7 +193,7 @@ class CCLIFileImport(SongImport):
if check_first_verse_line: if check_first_verse_line:
if verse_lines[0].startswith('(PRE-CHORUS'): if verse_lines[0].startswith('(PRE-CHORUS'):
verse_type = VerseType.tags[VerseType.PreChorus] verse_type = VerseType.tags[VerseType.PreChorus]
log.debug('USR verse PRE-CHORUS: %s', verse_lines[0]) log.debug('USR verse PRE-CHORUS: {lines}'.format(lines=verse_lines[0]))
verse_text = verse_lines[1] verse_text = verse_lines[1]
elif verse_lines[0].startswith('(BRIDGE'): elif verse_lines[0].startswith('(BRIDGE'):
verse_type = VerseType.tags[VerseType.Bridge] verse_type = VerseType.tags[VerseType.Bridge]
@ -248,7 +248,7 @@ class CCLIFileImport(SongImport):
# e.g. CCLI-Liedlizenznummer: 14 / CCLI License No. 14 # e.g. CCLI-Liedlizenznummer: 14 / CCLI License No. 14
""" """
log.debug('TXT file text: %s', text_list) log.debug('TXT file text: {text}'.format(text=text_list))
line_number = 0 line_number = 0
check_first_verse_line = False check_first_verse_line = False
verse_text = '' verse_text = ''

View File

@ -90,7 +90,7 @@ class DreamBeamImport(SongImport):
try: try:
parsed_file = etree.parse(open(file, 'r'), parser) parsed_file = etree.parse(open(file, 'r'), parser)
except etree.XMLSyntaxError: except etree.XMLSyntaxError:
log.exception('XML syntax error in file %s' % file) log.exception('XML syntax error in file {name}'.format(name=file))
self.log_error(file, SongStrings.XMLSyntaxError) self.log_error(file, SongStrings.XMLSyntaxError)
continue continue
xml = etree.tostring(parsed_file).decode() xml = etree.tostring(parsed_file).decode()
@ -115,15 +115,17 @@ class DreamBeamImport(SongImport):
verse_type = lyrics_item.get('Type') verse_type = lyrics_item.get('Type')
verse_number = lyrics_item.get('Number') verse_number = lyrics_item.get('Number')
verse_text = str(lyrics_item.text) verse_text = str(lyrics_item.text)
self.add_verse(verse_text, ('%s%s' % (verse_type[:1], verse_number))) self.add_verse(verse_text,
'{verse}{number}'.format(verse=verse_type[:1], number=verse_number))
if hasattr(song_xml, 'Collection'): if hasattr(song_xml, 'Collection'):
self.song_book_name = str(song_xml.Collection.text) self.song_book_name = str(song_xml.Collection.text)
if hasattr(song_xml, 'Number'): if hasattr(song_xml, 'Number'):
self.song_number = str(song_xml.Number.text) self.song_number = str(song_xml.Number.text)
if hasattr(song_xml, 'Sequence'): if hasattr(song_xml, 'Sequence'):
for lyrics_sequence_item in (song_xml.Sequence.iterchildren()): for lyrics_sequence_item in (song_xml.Sequence.iterchildren()):
self.verse_order_list.append("%s%s" % (lyrics_sequence_item.get('Type')[:1], item = lyrics_sequence_item.get('Type')[:1]
lyrics_sequence_item.get('Number'))) self.verse_order_list.append("{item}{number}".format(item=item),
lyrics_sequence_item.get('Number'))
if hasattr(song_xml, 'Notes'): if hasattr(song_xml, 'Notes'):
self.comments = str(song_xml.Notes.text) self.comments = str(song_xml.Notes.text)
else: else:

View File

@ -45,7 +45,7 @@ class EasySlidesImport(SongImport):
super(EasySlidesImport, self).__init__(manager, **kwargs) super(EasySlidesImport, self).__init__(manager, **kwargs)
def do_import(self): def do_import(self):
log.info('Importing EasySlides XML file %s', 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)
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()
@ -96,10 +96,10 @@ class EasySlidesImport(SongImport):
try: try:
setattr(self, self_attribute, str(import_attribute).strip()) setattr(self, self_attribute, str(import_attribute).strip())
except UnicodeDecodeError: except UnicodeDecodeError:
log.exception('UnicodeDecodeError decoding %s' % import_attribute) log.exception('UnicodeDecodeError decoding {attribute}'.format(attribute=import_attribute))
self._success = False self._success = False
except AttributeError: except AttributeError:
log.exception('No attribute %s' % import_attribute) log.exception('No attribute {attribute}'.format(attribute=import_attribute))
if mandatory: if mandatory:
self._success = False self._success = False
@ -119,7 +119,7 @@ class EasySlidesImport(SongImport):
try: try:
self.add_copyright(str(element).strip()) self.add_copyright(str(element).strip())
except UnicodeDecodeError: except UnicodeDecodeError:
log.exception('Unicode error on decoding copyright: %s' % element) log.exception('Unicode error on decoding copyright: {element}'.format(element=element))
self._success = False self._success = False
except AttributeError: except AttributeError:
pass pass
@ -157,9 +157,10 @@ class EasySlidesImport(SongImport):
separators = (separator_lines > 0) separators = (separator_lines > 0)
# the number of different regions in song - 1 # the number of different regions in song - 1
if len(region_lines) > 1: if len(region_lines) > 1:
log.info('EasySlidesImport: the file contained a song named "%s"' log.info('EasySlidesImport: the file contained a song named "{title}"'
'with more than two regions, but only two regions are tested, encountered regions were: %s', 'with more than two regions, but only two regions are tested, '
self.title, ','.join(list(region_lines.keys()))) 'encountered regions were: {keys}'.format(title=self.title,
keys=','.join(list(region_lines.keys()))))
# if the song has regions # if the song has regions
regions = (len(region_lines) > 0) regions = (len(region_lines) > 0)
# if the regions are inside verses # if the regions are inside verses
@ -232,7 +233,7 @@ class EasySlidesImport(SongImport):
for [reg, vt, vn, inst] in our_verse_order: for [reg, vt, vn, inst] in our_verse_order:
if self._list_has(verses, [reg, vt, vn, inst]): if self._list_has(verses, [reg, vt, vn, inst]):
# this is false, but needs user input # this is false, but needs user input
versetag = '%s%s' % (vt, vn) versetag = '{tag}{number}'.format(tag=vt, number=vn)
versetags.append(versetag) versetags.append(versetag)
lines = '\n'.join(verses[reg][vt][vn][inst]) lines = '\n'.join(verses[reg][vt][vn][inst])
self.add_verse(lines, versetag) self.add_verse(lines, versetag)
@ -259,7 +260,8 @@ class EasySlidesImport(SongImport):
if tag in versetags: if tag in versetags:
self.verse_order_list.append(tag) self.verse_order_list.append(tag)
else: else:
log.info('Got order item %s, which is not in versetags, dropping item from presentation order', tag) log.info('Got order item {tag}, which is not in versetags, dropping item from presentation '
'order'.format(tag=tag))
except UnicodeDecodeError: except UnicodeDecodeError:
log.exception('Unicode decode error while decoding Sequence') log.exception('Unicode decode error while decoding Sequence')
self._success = False self._success = False

View File

@ -171,15 +171,16 @@ class EasyWorshipSongImport(SongImport):
if copyright: if copyright:
self.copyright += ', ' self.copyright += ', '
self.copyright += translate('SongsPlugin.EasyWorshipSongImport', self.copyright += translate('SongsPlugin.EasyWorshipSongImport',
'Administered by %s') % admin 'Administered by {admin}').format(admin=admin)
# Set the SongImport object members. # Set the SongImport object members.
self.set_song_import_object(authors, inflated_content) self.set_song_import_object(authors, inflated_content)
if self.stop_import_flag: if self.stop_import_flag:
break break
if self.entry_error_log: if self.entry_error_log:
self.log_error(self.import_source, self.log_error(self.import_source,
translate('SongsPlugin.EasyWorshipSongImport', '"%s" could not be imported. %s') translate('SongsPlugin.EasyWorshipSongImport',
% (self.title, self.entry_error_log)) '"{title}" could not be imported. {entry}').format(title=self.title,
entry=self.entry_error_log))
self.entry_error_log = '' self.entry_error_log = ''
elif not self.finish(): elif not self.finish():
self.log_error(self.import_source) self.log_error(self.import_source)
@ -306,7 +307,7 @@ class EasyWorshipSongImport(SongImport):
if copy: if copy:
self.copyright += ', ' self.copyright += ', '
self.copyright += translate('SongsPlugin.EasyWorshipSongImport', self.copyright += translate('SongsPlugin.EasyWorshipSongImport',
'Administered by %s') % admin.decode(self.encoding) 'Administered by {admin}').format(admin=admin.decode(self.encoding))
if ccli: if ccli:
self.ccli_number = ccli.decode(self.encoding) self.ccli_number = ccli.decode(self.encoding)
if authors: if authors:
@ -319,15 +320,17 @@ class EasyWorshipSongImport(SongImport):
break break
if self.entry_error_log: if self.entry_error_log:
self.log_error(self.import_source, self.log_error(self.import_source,
translate('SongsPlugin.EasyWorshipSongImport', '"%s" could not be imported. %s') translate('SongsPlugin.EasyWorshipSongImport',
% (self.title, self.entry_error_log)) '"{title}" could not be imported. '
'{entry}').format(title=self.title, entry=self.entry_error_log))
self.entry_error_log = '' self.entry_error_log = ''
elif not self.finish(): elif not self.finish():
self.log_error(self.import_source) self.log_error(self.import_source)
except Exception as e: except Exception as e:
self.log_error(self.import_source, self.log_error(self.import_source,
translate('SongsPlugin.EasyWorshipSongImport', '"%s" could not be imported. %s') translate('SongsPlugin.EasyWorshipSongImport',
% (self.title, e)) '"{title}" could not be imported. {error}').format(title=self.title,
error=e))
db_file.close() db_file.close()
self.memo_file.close() self.memo_file.close()
@ -421,7 +424,7 @@ class EasyWorshipSongImport(SongImport):
fsl = ['>'] fsl = ['>']
for field_desc in field_descriptions: for field_desc in field_descriptions:
if field_desc.field_type == FieldType.String: if field_desc.field_type == FieldType.String:
fsl.append('%ds' % field_desc.size) fsl.append('{size:d}s'.format(size=field_desc.size))
elif field_desc.field_type == FieldType.Int16: elif field_desc.field_type == FieldType.Int16:
fsl.append('H') fsl.append('H')
elif field_desc.field_type == FieldType.Int32: elif field_desc.field_type == FieldType.Int32:
@ -429,13 +432,13 @@ class EasyWorshipSongImport(SongImport):
elif field_desc.field_type == FieldType.Logical: elif field_desc.field_type == FieldType.Logical:
fsl.append('B') fsl.append('B')
elif field_desc.field_type == FieldType.Memo: elif field_desc.field_type == FieldType.Memo:
fsl.append('%ds' % field_desc.size) fsl.append('{size:d}s'.format(size=field_desc.size))
elif field_desc.field_type == FieldType.Blob: elif field_desc.field_type == FieldType.Blob:
fsl.append('%ds' % field_desc.size) fsl.append('{size:d}s'.format(size=field_desc.size))
elif field_desc.field_type == FieldType.Timestamp: elif field_desc.field_type == FieldType.Timestamp:
fsl.append('Q') fsl.append('Q')
else: else:
fsl.append('%ds' % field_desc.size) fsl.append('{size:d}s'.format(size=field_desc.size))
self.record_structure = struct.Struct(''.join(fsl)) self.record_structure = struct.Struct(''.join(fsl))
self.field_descriptions = field_descriptions self.field_descriptions = field_descriptions

View File

@ -121,6 +121,7 @@ class FoilPresenterImport(SongImport):
for file_path in self.import_source: for file_path in self.import_source:
if self.stop_import_flag: if self.stop_import_flag:
return return
# TODO: Verify format() with template strings
self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % os.path.basename(file_path)) self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % os.path.basename(file_path))
try: try:
parsed_file = etree.parse(file_path, parser) parsed_file = etree.parse(file_path, parser)
@ -128,7 +129,7 @@ class FoilPresenterImport(SongImport):
self.foil_presenter.xml_to_song(xml) self.foil_presenter.xml_to_song(xml)
except etree.XMLSyntaxError: except etree.XMLSyntaxError:
self.log_error(file_path, SongStrings.XMLSyntaxError) self.log_error(file_path, SongStrings.XMLSyntaxError)
log.exception('XML syntax error in file %s' % file_path) log.exception('XML syntax error in file {path}'.format(path=file_path))
class FoilPresenter(object): class FoilPresenter(object):

View File

@ -102,8 +102,8 @@ class LyrixImport(SongImport):
else: else:
current_verse += '\n' + line current_verse += '\n' + line
except Exception as e: except Exception as e:
self.log_error(translate('SongsPlugin.LyrixImport', 'File %s' % file.name), self.log_error(translate('SongsPlugin.LyrixImport', 'File {name}').format(name=file.name),
translate('SongsPlugin.LyrixImport', 'Error: %s') % e) translate('SongsPlugin.LyrixImport', 'Error: {error}').format(error=e))
return return
self.title = song_title self.title = song_title
self.parse_author(author) self.parse_author(author)

View File

@ -23,6 +23,10 @@
The :mod:`mediashout` module provides the functionality for importing The :mod:`mediashout` module provides the functionality for importing
a MediaShout database into the OpenLP database. a MediaShout database into the OpenLP database.
""" """
# WARNING: See https://docs.python.org/2/library/sqlite3.html for value substitution
# in SQL statements
import pyodbc import pyodbc
from openlp.core.lib import translate from openlp.core.lib import translate
@ -47,8 +51,8 @@ class MediaShoutImport(SongImport):
Receive a single file to import. Receive a single file to import.
""" """
try: try:
conn = pyodbc.connect('DRIVER={Microsoft Access Driver (*.mdb)};DBQ=%s;PWD=6NOZ4eHK7k' % conn = pyodbc.connect('DRIVER={Microsoft Access Driver (*.mdb)};DBQ={source};'
self.import_source) 'PWD=6NOZ4eHK7k'.format(sorce=self.import_source))
except: except:
# Unfortunately no specific exception type # Unfortunately no specific exception type
self.log_error(self.import_source, translate('SongsPlugin.MediaShoutImport', self.log_error(self.import_source, translate('SongsPlugin.MediaShoutImport',
@ -61,16 +65,15 @@ class MediaShoutImport(SongImport):
for song in songs: for song in songs:
if self.stop_import_flag: if self.stop_import_flag:
break break
cursor.execute('SELECT Type, Number, Text FROM Verses WHERE Record = %s ORDER BY Type, Number' cursor.execute('SELECT Type, Number, Text FROM Verses WHERE Record = ? ORDER BY Type, Number', song.Record)
% song.Record)
verses = cursor.fetchall() verses = cursor.fetchall()
cursor.execute('SELECT Type, Number, POrder FROM PlayOrder WHERE Record = %s ORDER BY POrder' % song.Record) cursor.execute('SELECT Type, Number, POrder FROM PlayOrder WHERE Record = ? ORDER BY POrder', song.Record)
verse_order = cursor.fetchall() verse_order = cursor.fetchall()
cursor.execute('SELECT Name FROM Themes INNER JOIN SongThemes ON SongThemes.ThemeId = Themes.ThemeId ' cursor.execute('SELECT Name FROM Themes INNER JOIN SongThemes ON SongThemes.ThemeId = Themes.ThemeId '
'WHERE SongThemes.Record = %s' % song.Record) 'WHERE SongThemes.Record = ?', song.Record)
topics = cursor.fetchall() topics = cursor.fetchall()
cursor.execute('SELECT Name FROM Groups INNER JOIN SongGroups ON SongGroups.GroupId = Groups.GroupId ' cursor.execute('SELECT Name FROM Groups INNER JOIN SongGroups ON SongGroups.GroupId = Groups.GroupId '
'WHERE SongGroups.Record = %s' % song.Record) 'WHERE SongGroups.Record = ?', song.Record)
topics += cursor.fetchall() topics += cursor.fetchall()
self.process_song(song, verses, verse_order, topics) self.process_song(song, verses, verse_order, topics)

View File

@ -102,7 +102,7 @@ class OpenLPSongImport(SongImport):
self.log_error(self.import_source, translate('SongsPlugin.OpenLPSongImport', self.log_error(self.import_source, translate('SongsPlugin.OpenLPSongImport',
'Not a valid OpenLP 2 song database.')) 'Not a valid OpenLP 2 song database.'))
return return
self.import_source = 'sqlite:///%s' % self.import_source self.import_source = 'sqlite:///{url}'.format(url=self.import_source)
# Load the db file and reflect it # Load the db file and reflect it
engine = create_engine(self.import_source) engine = create_engine(self.import_source)
source_meta = MetaData() source_meta = MetaData()
@ -239,8 +239,10 @@ class OpenLPSongImport(SongImport):
self.manager.save_object(new_song) self.manager.save_object(new_song)
if progress_dialog: if progress_dialog:
progress_dialog.setValue(progress_dialog.value() + 1) progress_dialog.setValue(progress_dialog.value() + 1)
# TODO: Verify format() with template strings
progress_dialog.setLabelText(WizardStrings.ImportingType % new_song.title) progress_dialog.setLabelText(WizardStrings.ImportingType % new_song.title)
else: else:
# TODO: Verify format() with template strings
self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % new_song.title) self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % new_song.title)
if self.stop_import_flag: if self.stop_import_flag:
break break

View File

@ -58,6 +58,7 @@ class OpenLyricsImport(SongImport):
for file_path in self.import_source: for file_path in self.import_source:
if self.stop_import_flag: if self.stop_import_flag:
return return
# TODO: Verify format() with template strings
self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % os.path.basename(file_path)) self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % os.path.basename(file_path))
try: try:
# Pass a file object, because lxml does not cope with some # Pass a file object, because lxml does not cope with some
@ -66,9 +67,10 @@ class OpenLyricsImport(SongImport):
xml = etree.tostring(parsed_file).decode() xml = etree.tostring(parsed_file).decode()
self.open_lyrics.xml_to_song(xml) self.open_lyrics.xml_to_song(xml)
except etree.XMLSyntaxError: except etree.XMLSyntaxError:
log.exception('XML syntax error in file %s' % file_path) log.exception('XML syntax error in file {path}'.format(file_path))
self.log_error(file_path, SongStrings.XMLSyntaxError) self.log_error(file_path, SongStrings.XMLSyntaxError)
except OpenLyricsError as exception: except OpenLyricsError as exception:
log.exception('OpenLyricsException %d in file %s: %s' % log.exception('OpenLyricsException {error:d} in file {name}: {text}'.format(error=exception.type,
(exception.type, file_path, exception.log_message)) name=file_path,
text=exception.log_message))
self.log_error(file_path, exception.display_message) self.log_error(file_path, exception.display_message)

View File

@ -161,7 +161,7 @@ class OpenOfficeImport(SongImport):
else: else:
self.import_wizard.increment_progress_bar('Processing file ' + file_path, 0) self.import_wizard.increment_progress_bar('Processing file ' + file_path, 0)
except AttributeError: except AttributeError:
log.exception("open_ooo_file failed: %s", url) log.exception("open_ooo_file failed: {url}".format(url=url))
return return
def create_property(self, name, value): def create_property(self, name, value):

View File

@ -254,8 +254,8 @@ class OpenSongImport(SongImport):
length = 0 length = 0
while length < len(verse_num) and verse_num[length].isnumeric(): while length < len(verse_num) and verse_num[length].isnumeric():
length += 1 length += 1
verse_def = '%s%s' % (verse_tag, verse_num[:length]) verse_def = '{tag}{number}'.format(tag=verse_tag, number=verse_num[:length])
verse_joints[verse_def] = '%s\n[---]\n%s' % (verse_joints[verse_def], lines) \ verse_joints[verse_def] = '{verse}\n[---]\n{lines}'.format(verse=verse_joints[verse_def], lines=lines) \
if verse_def in verse_joints else lines if verse_def in verse_joints else lines
# Parsing the dictionary produces the elements in a non-intuitive order. While it "works", it's not a # Parsing the dictionary produces the elements in a non-intuitive order. While it "works", it's not a
# natural layout should the user come back to edit the song. Instead we sort by the verse type, so that we # natural layout should the user come back to edit the song. Instead we sort by the verse type, so that we
@ -287,11 +287,11 @@ class OpenSongImport(SongImport):
verse_num = '1' verse_num = '1'
verse_index = VerseType.from_loose_input(verse_tag) verse_index = VerseType.from_loose_input(verse_tag)
verse_tag = VerseType.tags[verse_index] verse_tag = VerseType.tags[verse_index]
verse_def = '%s%s' % (verse_tag, verse_num) verse_def = '{tag}{number}'.format(tag=verse_tag, number=verse_num)
if verse_num in verses.get(verse_tag, {}): if verse_num in verses.get(verse_tag, {}):
self.verse_order_list.append(verse_def) self.verse_order_list.append(verse_def)
else: else:
log.info('Got order %s but not in verse tags, dropping this item from presentation order', log.info('Got order {order} but not in verse tags, dropping this item from presentation '
verse_def) 'order'.format(order=verse_def))
if not self.finish(): if not self.finish():
self.log_error(file.name) self.log_error(file.name)

View File

@ -23,6 +23,10 @@
The :mod:`opspro` module provides the functionality for importing The :mod:`opspro` module provides the functionality for importing
a OPS Pro database into the OpenLP database. a OPS Pro database into the OpenLP database.
""" """
# WARNING: See https://docs.python.org/2/library/sqlite3.html for value substitution
# in SQL statements
import logging import logging
import re import re
import pyodbc import pyodbc
@ -51,10 +55,11 @@ class OPSProImport(SongImport):
""" """
password = self.extract_mdb_password() password = self.extract_mdb_password()
try: try:
conn = pyodbc.connect('DRIVER={Microsoft Access Driver (*.mdb)};DBQ=%s;PWD=%s' % (self.import_source, conn = pyodbc.connect('DRIVER={Microsoft Access Driver (*.mdb)};DBQ={source};'
password)) 'PWD={password}'.format(source=self.import_source, password=password))
except (pyodbc.DatabaseError, pyodbc.IntegrityError, pyodbc.InternalError, pyodbc.OperationalError) as e: except (pyodbc.DatabaseError, pyodbc.IntegrityError, pyodbc.InternalError, pyodbc.OperationalError) as e:
log.warning('Unable to connect the OPS Pro database %s. %s', self.import_source, str(e)) log.warning('Unable to connect the OPS Pro database {source}. {error}'.format(source=self.import_source,
error=str(e)))
# Unfortunately no specific exception type # Unfortunately no specific exception type
self.log_error(self.import_source, translate('SongsPlugin.OPSProImport', self.log_error(self.import_source, translate('SongsPlugin.OPSProImport',
'Unable to connect the OPS Pro database.')) 'Unable to connect the OPS Pro database.'))
@ -68,19 +73,19 @@ class OPSProImport(SongImport):
if self.stop_import_flag: if self.stop_import_flag:
break break
# Type means: 0=Original, 1=Projection, 2=Own # Type means: 0=Original, 1=Projection, 2=Own
cursor.execute('SELECT Lyrics, Type, IsDualLanguage FROM Lyrics WHERE SongID = %d AND Type < 2 ' cursor.execute('SELECT Lyrics, Type, IsDualLanguage FROM Lyrics WHERE SongID = ? AND Type < 2 '
'ORDER BY Type DESC' % song.ID) 'ORDER BY Type DESC', song.ID)
lyrics = cursor.fetchone() lyrics = cursor.fetchone()
cursor.execute('SELECT CategoryName FROM Category INNER JOIN SongCategory ' cursor.execute('SELECT CategoryName FROM Category INNER JOIN SongCategory '
'ON Category.ID = SongCategory.CategoryID WHERE SongCategory.SongID = %d ' 'ON Category.ID = SongCategory.CategoryID WHERE SongCategory.SongID = ? '
'ORDER BY CategoryName' % song.ID) 'ORDER BY CategoryName', song.ID)
topics = cursor.fetchall() topics = cursor.fetchall()
try: try:
self.process_song(song, lyrics, topics) self.process_song(song, lyrics, topics)
except Exception as e: except Exception as e:
self.log_error(self.import_source, self.log_error(self.import_source,
translate('SongsPlugin.OPSProImport', '"%s" could not be imported. %s') translate('SongsPlugin.OPSProImport',
% (song.Title, e)) '"{title}" could not be imported. {error}').format(title=song.Title, error=e))
def process_song(self, song, lyrics, topics): def process_song(self, song, lyrics, topics):
""" """

View File

@ -41,6 +41,7 @@ class PowerPraiseImport(SongImport):
for file_path in self.import_source: for file_path in self.import_source:
if self.stop_import_flag: if self.stop_import_flag:
return return
# TODO: Verify format() with template strings
self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % os.path.basename(file_path)) self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % os.path.basename(file_path))
root = objectify.parse(open(file_path, 'rb')).getroot() root = objectify.parse(open(file_path, 'rb')).getroot()
self.process_song(root) self.process_song(root)
@ -66,7 +67,7 @@ class PowerPraiseImport(SongImport):
else: else:
verse_def = 'o' verse_def = 'o'
verse_count[verse_def] = verse_count.get(verse_def, 0) + 1 verse_count[verse_def] = verse_count.get(verse_def, 0) + 1
verse_def = '%s%d' % (verse_def, verse_count[verse_def]) verse_def = '{verse}{count:d}'.format(verse=verse_def, count=verse_count[verse_def])
verse_text = [] verse_text = []
for slide in part.slide: for slide in part.slide:
if not hasattr(slide, 'line'): if not hasattr(slide, 'line'):

View File

@ -96,7 +96,7 @@ class PowerSongImport(SongImport):
self.import_source = '' self.import_source = ''
if not self.import_source or not isinstance(self.import_source, list): if not self.import_source or not isinstance(self.import_source, list):
self.log_error(translate('SongsPlugin.PowerSongImport', 'No songs to import.'), self.log_error(translate('SongsPlugin.PowerSongImport', 'No songs to import.'),
translate('SongsPlugin.PowerSongImport', 'No %s files found.') % ps_string) translate('SongsPlugin.PowerSongImport', 'No {text} files found.').format(text=ps_string))
return return
self.import_wizard.progress_bar.setMaximum(len(self.import_source)) self.import_wizard.progress_bar.setMaximum(len(self.import_source))
for file in self.import_source: for file in self.import_source:
@ -113,9 +113,9 @@ class PowerSongImport(SongImport):
field = self._read_string(song_data) field = self._read_string(song_data)
except ValueError: except ValueError:
parse_error = True parse_error = True
self.log_error(os.path.basename(file), str( self.log_error(os.path.basename(file),
translate('SongsPlugin.PowerSongImport', 'Invalid %s file. Unexpected byte value.')) % translate('SongsPlugin.PowerSongImport',
ps_string) 'Invalid {text} file. Unexpected byte value.').format(text=ps_string))
break break
else: else:
if label == 'TITLE': if label == 'TITLE':
@ -131,19 +131,20 @@ class PowerSongImport(SongImport):
continue continue
# Check that file had TITLE field # Check that file had TITLE field
if not self.title: if not self.title:
self.log_error(os.path.basename(file), str( self.log_error(os.path.basename(file),
translate('SongsPlugin.PowerSongImport', 'Invalid %s file. Missing "TITLE" header.')) % ps_string) translate('SongsPlugin.PowerSongImport',
'Invalid {text} file. Missing "TITLE" header.').format(text=ps_string))
continue continue
# Check that file had COPYRIGHTLINE label # Check that file had COPYRIGHTLINE label
if not found_copyright: if not found_copyright:
self.log_error(self.title, str( self.log_error(self.title,
translate('SongsPlugin.PowerSongImport', 'Invalid %s file. Missing "COPYRIGHTLINE" header.')) % translate('SongsPlugin.PowerSongImport',
ps_string) 'Invalid {text} file. Missing "COPYRIGHTLINE" header.').format(text=ps_string))
continue continue
# Check that file had at least one verse # Check that file had at least one verse
if not self.verses: if not self.verses:
self.log_error(self.title, str( self.log_error(self.title,
translate('SongsPlugin.PowerSongImport', 'Verses not found. Missing "PART" header.'))) translate('SongsPlugin.PowerSongImport', 'Verses not found. Missing "PART" header.'))
continue continue
if not self.finish(): if not self.finish():
self.log_error(self.title) self.log_error(self.title)

View File

@ -44,6 +44,7 @@ class PresentationManagerImport(SongImport):
for file_path in self.import_source: for file_path in self.import_source:
if self.stop_import_flag: if self.stop_import_flag:
return return
# TODO: Verify format() with template strings
self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % os.path.basename(file_path)) self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % os.path.basename(file_path))
try: try:
tree = etree.parse(file_path, parser=etree.XMLParser(recover=True)) tree = etree.parse(file_path, parser=etree.XMLParser(recover=True))
@ -90,7 +91,7 @@ class PresentationManagerImport(SongImport):
verse_def = 'o' verse_def = 'o'
if not is_duplicate: # Only increment verse number if no duplicate if not is_duplicate: # Only increment verse number if no duplicate
verse_count[verse_def] = verse_count.get(verse_def, 0) + 1 verse_count[verse_def] = verse_count.get(verse_def, 0) + 1
verse_def = '%s%d' % (verse_def, verse_count[verse_def]) verse_def = '{verse}{count:d}'.format(verse=verse_def, count=verse_count[verse_def])
if not is_duplicate: # Only add verse if no duplicate if not is_duplicate: # Only add verse if no duplicate
self.add_verse(str(verse).strip(), verse_def) self.add_verse(str(verse).strip(), verse_def)
verse_order_list.append(verse_def) verse_order_list.append(verse_def)

View File

@ -46,6 +46,7 @@ class ProPresenterImport(SongImport):
for file_path in self.import_source: for file_path in self.import_source:
if self.stop_import_flag: if self.stop_import_flag:
return return
# TODO: Verify format() with template strings
self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % os.path.basename(file_path)) self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % os.path.basename(file_path))
root = objectify.parse(open(file_path, 'rb')).getroot() root = objectify.parse(open(file_path, 'rb')).getroot()
self.process_song(root, file_path) self.process_song(root, file_path)
@ -87,7 +88,7 @@ class ProPresenterImport(SongImport):
RTFData = slide.displayElements.RVTextElement.get('RTFData') RTFData = slide.displayElements.RVTextElement.get('RTFData')
rtf = base64.standard_b64decode(RTFData) rtf = base64.standard_b64decode(RTFData)
words, encoding = strip_rtf(rtf.decode()) words, encoding = strip_rtf(rtf.decode())
self.add_verse(words, "v%d" % count) self.add_verse(words, "v{count}".format(count=count))
# ProPresenter 5 # ProPresenter 5
elif(self.version >= 500 and self.version < 600): elif(self.version >= 500 and self.version < 600):
@ -103,7 +104,7 @@ class ProPresenterImport(SongImport):
RTFData = slide.displayElements.RVTextElement.get('RTFData') RTFData = slide.displayElements.RVTextElement.get('RTFData')
rtf = base64.standard_b64decode(RTFData) rtf = base64.standard_b64decode(RTFData)
words, encoding = strip_rtf(rtf.decode()) words, encoding = strip_rtf(rtf.decode())
self.add_verse(words, "v%d" % count) self.add_verse(words, "v{count:d}".format(count=count))
# ProPresenter 6 # ProPresenter 6
elif(self.version >= 600 and self.version < 700): elif(self.version >= 600 and self.version < 700):
@ -127,7 +128,7 @@ class ProPresenterImport(SongImport):
words, encoding = strip_rtf(data.decode()) words, encoding = strip_rtf(data.decode())
break break
if words: if words:
self.add_verse(words, "v%d" % count) self.add_verse(words, "v{count:d}".format(count=count))
if not self.finish(): if not self.finish():
self.log_error(self.import_source) self.log_error(self.import_source)

View File

@ -117,7 +117,7 @@ class SongImport(QtCore.QObject):
self.import_wizard.error_report_text_edit.setVisible(True) self.import_wizard.error_report_text_edit.setVisible(True)
self.import_wizard.error_copy_to_button.setVisible(True) self.import_wizard.error_copy_to_button.setVisible(True)
self.import_wizard.error_save_to_button.setVisible(True) self.import_wizard.error_save_to_button.setVisible(True)
self.import_wizard.error_report_text_edit.append('- %s (%s)' % (file_path, reason)) self.import_wizard.error_report_text_edit.append('- {path} ({error})'.format(path=file_path, error=reason))
def stop_import(self): def stop_import(self):
""" """
@ -326,10 +326,11 @@ class SongImport(QtCore.QObject):
if not self.check_complete(): if not self.check_complete():
self.set_defaults() self.set_defaults()
return False return False
log.info('committing song %s to database', self.title) log.info('committing song {title} to database'.format(title=self.title))
song = Song() song = Song()
song.title = self.title song.title = self.title
if self.import_wizard is not None: if self.import_wizard is not None:
# TODO: Verify format() with template variables
self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % song.title) self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % song.title)
song.alternate_title = self.alternate_title song.alternate_title = self.alternate_title
# Values will be set when cleaning the song. # Values will be set when cleaning the song.
@ -344,11 +345,11 @@ class SongImport(QtCore.QObject):
if verse_def[0].lower() in VerseType.tags: if verse_def[0].lower() in VerseType.tags:
verse_tag = verse_def[0].lower() verse_tag = verse_def[0].lower()
else: else:
new_verse_def = '%s%d' % (VerseType.tags[VerseType.Other], other_count) new_verse_def = '{tag}{count:d}'.format(tag=VerseType.tags[VerseType.Other], count=other_count)
verses_changed_to_other[verse_def] = new_verse_def verses_changed_to_other[verse_def] = new_verse_def
other_count += 1 other_count += 1
verse_tag = VerseType.tags[VerseType.Other] verse_tag = VerseType.tags[VerseType.Other]
log.info('Versetype %s changing to %s', verse_def, new_verse_def) log.info('Versetype {old} changing to {new}'.format(old=verse_def, new=new_verse_def))
verse_def = new_verse_def verse_def = new_verse_def
sxml.add_verse_to_lyrics(verse_tag, verse_def[1:], verse_text, lang) sxml.add_verse_to_lyrics(verse_tag, verse_def[1:], verse_text, lang)
song.lyrics = str(sxml.extract_xml(), 'utf-8') song.lyrics = str(sxml.extract_xml(), 'utf-8')

View File

@ -101,6 +101,7 @@ class SongShowPlusImport(SongImport):
self.other_count = 0 self.other_count = 0
self.other_list = {} self.other_list = {}
file_name = os.path.split(file)[1] file_name = os.path.split(file)[1]
# TODO: Verify format() with template variables
self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % file_name, 0) self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % file_name, 0)
song_data = open(file, 'rb') song_data = open(file, 'rb')
while True: while True:
@ -145,13 +146,16 @@ class SongShowPlusImport(SongImport):
if match: if match:
self.ccli_number = int(match.group()) self.ccli_number = int(match.group())
else: else:
log.warning("Can't parse CCLI Number from string: %s" % self.decode(data)) log.warning("Can't parse CCLI Number from string: {text}".format(text=self.decode(data)))
elif block_key == VERSE: elif block_key == VERSE:
self.add_verse(self.decode(data), "%s%s" % (VerseType.tags[VerseType.Verse], verse_no)) self.add_verse(self.decode(data), "{tag}{number}".format(tag=VerseType.tags[VerseType.Verse],
number=verse_no))
elif block_key == CHORUS: elif block_key == CHORUS:
self.add_verse(self.decode(data), "%s%s" % (VerseType.tags[VerseType.Chorus], verse_no)) self.add_verse(self.decode(data), "{tag}{number}".format(tag=VerseType.tags[VerseType.Chorus],
number=verse_no))
elif block_key == BRIDGE: elif block_key == BRIDGE:
self.add_verse(self.decode(data), "%s%s" % (VerseType.tags[VerseType.Bridge], verse_no)) self.add_verse(self.decode(data), "{tag}{number}".format(tag=VerseType.tags[VerseType.Bridge],
number=verse_no))
elif block_key == TOPIC: elif block_key == TOPIC:
self.topics.append(self.decode(data)) self.topics.append(self.decode(data))
elif block_key == COMMENTS: elif block_key == COMMENTS:
@ -170,7 +174,7 @@ class SongShowPlusImport(SongImport):
verse_tag = self.to_openlp_verse_tag(verse_name) verse_tag = self.to_openlp_verse_tag(verse_name)
self.add_verse(self.decode(data), verse_tag) self.add_verse(self.decode(data), verse_tag)
else: else:
log.debug("Unrecognised blockKey: %s, data: %s" % (block_key, data)) log.debug("Unrecognised blockKey: {key}, data: {data}".format(key=block_key, data=data))
song_data.seek(next_block_starts) song_data.seek(next_block_starts)
self.verse_order_list = self.ssp_verse_order_list self.verse_order_list = self.ssp_verse_order_list
song_data.close() song_data.close()

View File

@ -141,7 +141,7 @@ class SundayPlusImport(SongImport):
if len(value): if len(value):
verse_type = VerseType.tags[VerseType.from_loose_input(value[0])] verse_type = VerseType.tags[VerseType.from_loose_input(value[0])]
if len(value) >= 2 and value[-1] in ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']: if len(value) >= 2 and value[-1] in ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']:
verse_type = "%s%s" % (verse_type, value[-1]) verse_type = "{verse}{value}".format(verse=verse_type, value=value[-1])
elif name == 'HOTKEY': elif name == 'HOTKEY':
value = self.decode(value).strip() value = self.decode(value).strip()
# HOTKEY always appears after MARKER_NAME, so it # HOTKEY always appears after MARKER_NAME, so it

View File

@ -115,8 +115,8 @@ class VideoPsalmImport(SongImport):
for verse in song['Verses']: for verse in song['Verses']:
self.add_verse(verse['Text'], 'v') self.add_verse(verse['Text'], 'v')
if not self.finish(): if not self.finish():
self.log_error('Could not import %s' % self.title) self.log_error('Could not import {title}'.format(title=self.title))
except Exception as e: except Exception as e:
self.log_error(translate('SongsPlugin.VideoPsalmImport', 'File %s' % file.name), self.log_error(translate('SongsPlugin.VideoPsalmImport', 'File {name}').format(name=file.name),
translate('SongsPlugin.VideoPsalmImport', 'Error: %s') % e) translate('SongsPlugin.VideoPsalmImport', 'Error: {error}').format(error=e))
song_file.close() song_file.close()

View File

@ -108,8 +108,8 @@ class WordsOfWorshipImport(SongImport):
if song_data.read(19).decode() != 'WoW File\nSong Words': if song_data.read(19).decode() != 'WoW File\nSong Words':
self.log_error(source, self.log_error(source,
translate('SongsPlugin.WordsofWorshipSongImport', translate('SongsPlugin.WordsofWorshipSongImport',
'Invalid Words of Worship song file. Missing "%s" header.') 'Invalid Words of Worship song file. Missing "{text}" '
% 'WoW File\\nSong Words') 'header.').format(text='WoW File\\nSong Words'))
continue continue
# Seek to byte which stores number of blocks in the song # Seek to byte which stores number of blocks in the song
song_data.seek(56) song_data.seek(56)
@ -118,8 +118,8 @@ class WordsOfWorshipImport(SongImport):
if song_data.read(16).decode() != 'CSongDoc::CBlock': if song_data.read(16).decode() != 'CSongDoc::CBlock':
self.log_error(source, self.log_error(source,
translate('SongsPlugin.WordsofWorshipSongImport', translate('SongsPlugin.WordsofWorshipSongImport',
'Invalid Words of Worship song file. Missing "%s" string.') 'Invalid Words of Worship song file. Missing "{text}" '
% 'CSongDoc::CBlock') 'string.').format(text='CSongDoc::CBlock'))
continue continue
# Seek to the beginning of the first block # Seek to the beginning of the first block
song_data.seek(82) song_data.seek(82)

View File

@ -91,11 +91,11 @@ class WorshipAssistantImport(SongImport):
records = list(songs_reader) records = list(songs_reader)
except csv.Error as e: except csv.Error as e:
self.log_error(translate('SongsPlugin.WorshipAssistantImport', 'Error reading CSV file.'), self.log_error(translate('SongsPlugin.WorshipAssistantImport', 'Error reading CSV file.'),
translate('SongsPlugin.WorshipAssistantImport', 'Line %d: %s') % translate('SongsPlugin.WorshipAssistantImport',
(songs_reader.line_num, e)) 'Line {number:d}: {error}').format(number=songs_reader.line_num, error=e))
return return
num_records = len(records) num_records = len(records)
log.info('%s records found in CSV file' % num_records) log.info('{count} records found in CSV file'.format(count=num_records))
self.import_wizard.progress_bar.setMaximum(num_records) self.import_wizard.progress_bar.setMaximum(num_records)
# Create regex to strip html tags # Create regex to strip html tags
re_html_strip = re.compile(r'<[^>]+>') re_html_strip = re.compile(r'<[^>]+>')
@ -122,12 +122,14 @@ class WorshipAssistantImport(SongImport):
verse_order_list = [x.strip() for x in record['ROADMAP'].split(',')] verse_order_list = [x.strip() for x in record['ROADMAP'].split(',')]
lyrics = record['LYRICS2'] lyrics = record['LYRICS2']
except UnicodeDecodeError as e: except UnicodeDecodeError as e:
self.log_error(translate('SongsPlugin.WorshipAssistantImport', 'Record %d' % index), self.log_error(translate('SongsPlugin.WorshipAssistantImport', 'Record {count:d}').format(count=index),
translate('SongsPlugin.WorshipAssistantImport', 'Decoding error: %s') % e) translate('SongsPlugin.WorshipAssistantImport',
'Decoding error: {error}').format(error=e))
continue continue
except TypeError as e: except TypeError as e:
self.log_error(translate('SongsPlugin.WorshipAssistantImport', self.log_error(translate('SongsPlugin.WorshipAssistantImport',
'File not valid WorshipAssistant CSV format.'), 'TypeError: %s' % e) 'File not valid WorshipAssistant CSV format.'),
'TypeError: {error}'.format(error=e))
return return
verse = '' verse = ''
used_verses = [] used_verses = []
@ -180,6 +182,7 @@ class WorshipAssistantImport(SongImport):
cleaned_verse_order_list.append(verse) cleaned_verse_order_list.append(verse)
self.verse_order_list = cleaned_verse_order_list self.verse_order_list = cleaned_verse_order_list
if not self.finish(): if not self.finish():
self.log_error(translate('SongsPlugin.WorshipAssistantImport', 'Record %d') % index + self.log_error(translate('SongsPlugin.WorshipAssistantImport',
'Record {count:d}').format(count=index) +
(': "' + self.title + '"' if self.title else '')) (': "' + self.title + '"' if self.title else ''))
songs_file.close() songs_file.close()

View File

@ -49,9 +49,11 @@ class WorshipCenterProImport(SongImport):
Receive a single file to import. Receive a single file to import.
""" """
try: try:
conn = pyodbc.connect('DRIVER={Microsoft Access Driver (*.mdb)};DBQ=%s' % self.import_source) conn = pyodbc.connect('DRIVER={Microsoft Access Driver (*.mdb)};'
'DBQ={source}'.format(source=self.import_source))
except (pyodbc.DatabaseError, pyodbc.IntegrityError, pyodbc.InternalError, pyodbc.OperationalError) as e: except (pyodbc.DatabaseError, pyodbc.IntegrityError, pyodbc.InternalError, pyodbc.OperationalError) as e:
log.warning('Unable to connect the WorshipCenter Pro database %s. %s', self.import_source, str(e)) log.warning('Unable to connect the WorshipCenter Pro '
'database {source}. {error}'.format(source=self.import_source, error=str(e)))
# Unfortunately no specific exception type # Unfortunately no specific exception type
self.log_error(self.import_source, translate('SongsPlugin.WorshipCenterProImport', self.log_error(self.import_source, translate('SongsPlugin.WorshipCenterProImport',
'Unable to connect the WorshipCenter Pro database.')) 'Unable to connect the WorshipCenter Pro database.'))

View File

@ -84,10 +84,11 @@ class ZionWorxImport(SongImport):
records = list(songs_reader) records = list(songs_reader)
except csv.Error as e: except csv.Error as e:
self.log_error(translate('SongsPlugin.ZionWorxImport', 'Error reading CSV file.'), self.log_error(translate('SongsPlugin.ZionWorxImport', 'Error reading CSV file.'),
translate('SongsPlugin.ZionWorxImport', 'Line %d: %s') % (songs_reader.line_num, e)) translate('SongsPlugin.ZionWorxImport',
'Line {number:d}: {error}').format(number=songs_reader.line_num, error=e))
return return
num_records = len(records) num_records = len(records)
log.info('%s records found in CSV file' % num_records) log.info('{count} records found in CSV file'.format(count=num_records))
self.import_wizard.progress_bar.setMaximum(num_records) self.import_wizard.progress_bar.setMaximum(num_records)
for index, record in enumerate(records, 1): for index, record in enumerate(records, 1):
if self.stop_import_flag: if self.stop_import_flag:
@ -101,12 +102,12 @@ class ZionWorxImport(SongImport):
self.add_copyright(self._decode(record['Copyright'])) self.add_copyright(self._decode(record['Copyright']))
lyrics = self._decode(record['Lyrics']) lyrics = self._decode(record['Lyrics'])
except UnicodeDecodeError as e: except UnicodeDecodeError as e:
self.log_error(translate('SongsPlugin.ZionWorxImport', 'Record %d' % index), self.log_error(translate('SongsPlugin.ZionWorxImport', 'Record {index}').format(index=index),
translate('SongsPlugin.ZionWorxImport', 'Decoding error: %s') % e) translate('SongsPlugin.ZionWorxImport', 'Decoding error: {error}').format(error=e))
continue continue
except TypeError as e: except TypeError as e:
self.log_error(translate( self.log_error(translate('SongsPlugin.ZionWorxImport', 'File not valid ZionWorx CSV format.'),
'SongsPlugin.ZionWorxImport', 'File not valid ZionWorx CSV format.'), 'TypeError: %s' % e) 'TypeError: {error}'.format(error=e))
return return
verse = '' verse = ''
for line in lyrics.splitlines(): for line in lyrics.splitlines():

View File

@ -129,7 +129,7 @@ class SongMediaItem(MediaManagerItem):
self.display_copyright_symbol = Settings().value(self.settings_section + '/display copyright symbol') self.display_copyright_symbol = Settings().value(self.settings_section + '/display copyright symbol')
def retranslateUi(self): def retranslateUi(self):
self.search_text_label.setText('%s:' % UiStrings().Search) self.search_text_label.setText('{text}:'.format(text=UiStrings().Search))
self.search_text_button.setText(UiStrings().Search) self.search_text_button.setText(UiStrings().Search)
self.maintenance_action.setText(SongStrings.SongMaintenance) self.maintenance_action.setText(SongStrings.SongMaintenance)
self.maintenance_action.setToolTip(translate('SongsPlugin.MediaItem', self.maintenance_action.setToolTip(translate('SongsPlugin.MediaItem',
@ -166,12 +166,14 @@ class SongMediaItem(MediaManagerItem):
translate('SongsPlugin.MediaItem', 'CCLI number'), translate('SongsPlugin.MediaItem', 'CCLI number'),
translate('SongsPlugin.MediaItem', 'Search CCLI number...')) translate('SongsPlugin.MediaItem', 'Search CCLI number...'))
]) ])
self.search_text_edit.set_current_search_type(Settings().value('%s/last search type' % self.settings_section)) self.search_text_edit.set_current_search_type(
Settings().value('{section}/last search type'.format(section=self.settings_section)))
self.config_update() self.config_update()
def on_search_text_button_clicked(self): def on_search_text_button_clicked(self):
# Save the current search type to the configuration. # Save the current search type to the configuration.
Settings().setValue('%s/last search type' % self.settings_section, self.search_text_edit.current_search_type()) Settings().setValue('{section}/last search type'.format(section=self.settings_section),
self.search_text_edit.current_search_type())
# Reload the list considering the new search type. # Reload the list considering the new search type.
search_keywords = str(self.search_text_edit.displayText()) search_keywords = str(self.search_text_edit.displayText())
search_type = self.search_text_edit.current_search_type() search_type = self.search_text_edit.current_search_type()
@ -181,31 +183,31 @@ class SongMediaItem(MediaManagerItem):
self.display_results_song(search_results) self.display_results_song(search_results)
elif search_type == SongSearch.Titles: elif search_type == SongSearch.Titles:
log.debug('Titles Search') log.debug('Titles Search')
search_string = '%' + clean_string(search_keywords) + '%' search_string = '%{text}%'.format(text=clean_string(search_keywords))
search_results = self.plugin.manager.get_all_objects(Song, Song.search_title.like(search_string)) search_results = self.plugin.manager.get_all_objects(Song, Song.search_title.like(search_string))
self.display_results_song(search_results) self.display_results_song(search_results)
elif search_type == SongSearch.Lyrics: elif search_type == SongSearch.Lyrics:
log.debug('Lyrics Search') log.debug('Lyrics Search')
search_string = '%' + clean_string(search_keywords) + '%' search_string = '%{text}%'.format(text=clean_string(search_keywords))
search_results = self.plugin.manager.get_all_objects(Song, Song.search_lyrics.like(search_string)) search_results = self.plugin.manager.get_all_objects(Song, Song.search_lyrics.like(search_string))
self.display_results_song(search_results) self.display_results_song(search_results)
elif search_type == SongSearch.Authors: elif search_type == SongSearch.Authors:
log.debug('Authors Search') log.debug('Authors Search')
search_string = '%' + search_keywords + '%' search_string = '%{text}%'.format(text=search_keywords)
search_results = self.plugin.manager.get_all_objects( search_results = self.plugin.manager.get_all_objects(
Author, Author.display_name.like(search_string)) Author, Author.display_name.like(search_string))
self.display_results_author(search_results) self.display_results_author(search_results)
elif search_type == SongSearch.Topics: elif search_type == SongSearch.Topics:
log.debug('Topics Search') log.debug('Topics Search')
search_string = '%' + search_keywords + '%' search_string = '%{text}%'.format(text=search_keywords)
search_results = self.plugin.manager.get_all_objects( search_results = self.plugin.manager.get_all_objects(
Topic, Topic.name.like(search_string)) Topic, Topic.name.like(search_string))
self.display_results_topic(search_results) self.display_results_topic(search_results)
elif search_type == SongSearch.Books: elif search_type == SongSearch.Books:
log.debug('Songbook Search') log.debug('Songbook Search')
search_keywords = search_keywords.rpartition(' ') search_keywords = search_keywords.rpartition(' ')
search_book = search_keywords[0] + '%' search_book = '{text}%'.format(text=search_keywords[0])
search_entry = search_keywords[2] + '%' search_entry = '{text}%'.format(text=search_keywords[2])
search_results = (self.plugin.manager.session.query(SongBookEntry.entry, Book.name, Song.title, Song.id) search_results = (self.plugin.manager.session.query(SongBookEntry.entry, Book.name, Song.title, Song.id)
.join(Song) .join(Song)
.join(Book) .join(Book)
@ -214,26 +216,26 @@ class SongMediaItem(MediaManagerItem):
self.display_results_book(search_results) self.display_results_book(search_results)
elif search_type == SongSearch.Themes: elif search_type == SongSearch.Themes:
log.debug('Theme Search') log.debug('Theme Search')
search_string = '%' + search_keywords + '%' search_string = '%{text}%'.format(text=search_keywords)
search_results = self.plugin.manager.get_all_objects( search_results = self.plugin.manager.get_all_objects(
Song, Song.theme_name.like(search_string)) Song, Song.theme_name.like(search_string))
self.display_results_themes(search_results) self.display_results_themes(search_results)
elif search_type == SongSearch.Copyright: elif search_type == SongSearch.Copyright:
log.debug('Copyright Search') log.debug('Copyright Search')
search_string = '%' + search_keywords + '%' search_string = '%{text}%'.format(text=search_keywords)
search_results = self.plugin.manager.get_all_objects( search_results = self.plugin.manager.get_all_objects(
Song, and_(Song.copyright.like(search_string), Song.copyright != '')) Song, and_(Song.copyright.like(search_string), Song.copyright != ''))
self.display_results_song(search_results) self.display_results_song(search_results)
elif search_type == SongSearch.CCLInumber: elif search_type == SongSearch.CCLInumber:
log.debug('CCLI number Search') log.debug('CCLI number Search')
search_string = '%' + search_keywords + '%' search_string = '%{text}%'.format(text=search_keywords)
search_results = self.plugin.manager.get_all_objects( search_results = self.plugin.manager.get_all_objects(
Song, and_(Song.ccli_number.like(search_string), Song.ccli_number != '')) Song, and_(Song.ccli_number.like(search_string), Song.ccli_number != ''))
self.display_results_cclinumber(search_results) self.display_results_cclinumber(search_results)
self.check_search_result() self.check_search_result()
def search_entire(self, search_keywords): def search_entire(self, search_keywords):
search_string = '%' + clean_string(search_keywords) + '%' search_string = '%{text}%'.format(text=clean_string(search_keywords))
return self.plugin.manager.get_all_objects( return self.plugin.manager.get_all_objects(
Song, or_(Song.search_title.like(search_string), Song.search_lyrics.like(search_string), Song, or_(Song.search_title.like(search_string), Song.search_lyrics.like(search_string),
Song.comments.like(search_string))) Song.comments.like(search_string)))
@ -272,7 +274,8 @@ class SongMediaItem(MediaManagerItem):
if song.temporary: if song.temporary:
continue continue
author_list = [author.display_name for author in song.authors] author_list = [author.display_name for author in song.authors]
song_detail = '%s (%s)' % (song.title, create_separated_list(author_list)) if author_list else song.title text = create_separated_list(author_list) if author_list else song.title
song_detail = '{title} ({author})'.format(title=song.title, author=text)
song_name = QtWidgets.QListWidgetItem(song_detail) song_name = QtWidgets.QListWidgetItem(song_detail)
song_name.setData(QtCore.Qt.UserRole, song.id) song_name.setData(QtCore.Qt.UserRole, song.id)
self.list_view.addItem(song_name) self.list_view.addItem(song_name)
@ -305,7 +308,7 @@ class SongMediaItem(MediaManagerItem):
# Do not display temporary songs # Do not display temporary songs
if song.temporary: if song.temporary:
continue continue
song_detail = '%s (%s)' % (author.display_name, song.title) song_detail = '{author} ({title})'.format(author=author.display_name, title=song.title)
song_name = QtWidgets.QListWidgetItem(song_detail) song_name = QtWidgets.QListWidgetItem(song_detail)
song_name.setData(QtCore.Qt.UserRole, song.id) song_name.setData(QtCore.Qt.UserRole, song.id)
self.list_view.addItem(song_name) self.list_view.addItem(song_name)
@ -325,7 +328,8 @@ class SongMediaItem(MediaManagerItem):
self.list_view.clear() self.list_view.clear()
search_results.sort(key=get_songbook_key) search_results.sort(key=get_songbook_key)
for result in search_results: for result in search_results:
song_detail = '%s #%s: %s' % (result[1], result[0], result[2]) song_detail = '{result1} #{result0}: {result2}'.format(result1=result[1], result0=result[0],
result2=result[2])
song_name = QtWidgets.QListWidgetItem(song_detail) song_name = QtWidgets.QListWidgetItem(song_detail)
song_name.setData(QtCore.Qt.UserRole, result[3]) song_name.setData(QtCore.Qt.UserRole, result[3])
self.list_view.addItem(song_name) self.list_view.addItem(song_name)
@ -354,7 +358,7 @@ class SongMediaItem(MediaManagerItem):
# Do not display temporary songs # Do not display temporary songs
if song.temporary: if song.temporary:
continue continue
song_detail = '%s (%s)' % (topic.name, song.title) song_detail = '{topic} ({title})'.format(topic=topic.name, title=song.title)
song_name = QtWidgets.QListWidgetItem(song_detail) song_name = QtWidgets.QListWidgetItem(song_detail)
song_name.setData(QtCore.Qt.UserRole, song.id) song_name.setData(QtCore.Qt.UserRole, song.id)
self.list_view.addItem(song_name) self.list_view.addItem(song_name)
@ -377,7 +381,7 @@ class SongMediaItem(MediaManagerItem):
# Do not display temporary songs # Do not display temporary songs
if song.temporary: if song.temporary:
continue continue
song_detail = '%s (%s)' % (song.theme_name, song.title) song_detail = '{theme} ({song})'.format(theme=song.theme_name, song=song.title)
song_name = QtWidgets.QListWidgetItem(song_detail) song_name = QtWidgets.QListWidgetItem(song_detail)
song_name.setData(QtCore.Qt.UserRole, song.id) song_name.setData(QtCore.Qt.UserRole, song.id)
self.list_view.addItem(song_name) self.list_view.addItem(song_name)
@ -400,7 +404,7 @@ class SongMediaItem(MediaManagerItem):
# Do not display temporary songs # Do not display temporary songs
if song.temporary: if song.temporary:
continue continue
song_detail = '%s (%s)' % (song.ccli_number, song.title) song_detail = '{ccli} ({song})'.format(ccli=song.ccli_number, song=song.title)
song_name = QtWidgets.QListWidgetItem(song_detail) song_name = QtWidgets.QListWidgetItem(song_detail)
song_name.setData(QtCore.Qt.UserRole, song.id) song_name.setData(QtCore.Qt.UserRole, song.id)
self.list_view.addItem(song_name) self.list_view.addItem(song_name)
@ -456,7 +460,7 @@ class SongMediaItem(MediaManagerItem):
Called by ServiceManager or SlideController by event passing the Song Id in the payload along with an indicator Called by ServiceManager or SlideController by event passing the Song Id in the payload along with an indicator
to say which type of display is required. to say which type of display is required.
""" """
log.debug('on_remote_edit for song %s' % song_id) log.debug('on_remote_edit for song {song}'.format(song=song_id))
song_id = int(song_id) song_id = int(song_id)
valid = self.plugin.manager.get_object(Song, song_id) valid = self.plugin.manager.get_object(Song, song_id)
if valid: if valid:
@ -499,7 +503,8 @@ class SongMediaItem(MediaManagerItem):
if QtWidgets.QMessageBox.question( if QtWidgets.QMessageBox.question(
self, UiStrings().ConfirmDelete, self, UiStrings().ConfirmDelete,
translate('SongsPlugin.MediaItem', translate('SongsPlugin.MediaItem',
'Are you sure you want to delete the "%d" selected song(s)?') % len(items), 'Are you sure you want to delete the "{items:d}" '
'selected song(s)?').format(items=len(items)),
QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No), QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No),
QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.No: QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.No:
return return
@ -524,8 +529,9 @@ class SongMediaItem(MediaManagerItem):
old_song = self.plugin.manager.get_object(Song, item_id) old_song = self.plugin.manager.get_object(Song, item_id)
song_xml = self.open_lyrics.song_to_xml(old_song) song_xml = self.open_lyrics.song_to_xml(old_song)
new_song = self.open_lyrics.xml_to_song(song_xml) new_song = self.open_lyrics.xml_to_song(song_xml)
new_song.title = '%s <%s>' % \ new_song.title = '{title} <{text}>'.format(title=new_song.title,
(new_song.title, translate('SongsPlugin.MediaItem', 'copy', 'For song cloning')) text=translate('SongsPlugin.MediaItem',
'copy', 'For song cloning'))
# Copy audio files from the old to the new song # Copy audio files from the old to the new song
if len(old_song.media_files) > 0: if len(old_song.media_files) > 0:
save_path = os.path.join(AppLocation.get_section_data_path(self.plugin.name), 'audio', str(new_song.id)) save_path = os.path.join(AppLocation.get_section_data_path(self.plugin.name), 'audio', str(new_song.id))
@ -552,7 +558,8 @@ class SongMediaItem(MediaManagerItem):
:param remote: Triggered from remote :param remote: Triggered from remote
:param context: Why is it being generated :param context: Why is it being generated
""" """
log.debug('generate_slide_data: %s, %s, %s' % (service_item, item, self.remote_song)) log.debug('generate_slide_data: {service}, {item}, {remote}'.format(service=service_item, item=item,
remote=self.remote_song))
item_id = self._get_id_of_item_to_generate(item, self.remote_song) item_id = self._get_id_of_item_to_generate(item, self.remote_song)
service_item.add_capability(ItemCapabilities.CanEdit) service_item.add_capability(ItemCapabilities.CanEdit)
service_item.add_capability(ItemCapabilities.CanPreview) service_item.add_capability(ItemCapabilities.CanPreview)
@ -581,7 +588,7 @@ class SongMediaItem(MediaManagerItem):
if verse_index is None: if verse_index is None:
verse_index = VerseType.from_tag(verse_tag) verse_index = VerseType.from_tag(verse_tag)
verse_tag = VerseType.translated_tags[verse_index].upper() verse_tag = VerseType.translated_tags[verse_index].upper()
verse_def = '%s%s' % (verse_tag, verse[0]['label']) verse_def = '{tag}{label}'.format(tag=verse_tag, label=verse[0]['label'])
service_item.add_from_text(str(verse[1]), verse_def) service_item.add_from_text(str(verse[1]), verse_def)
else: else:
# Loop through the verse list and expand the song accordingly. # Loop through the verse list and expand the song accordingly.
@ -596,7 +603,7 @@ class SongMediaItem(MediaManagerItem):
else: else:
verse_index = VerseType.from_tag(verse[0]['type']) verse_index = VerseType.from_tag(verse[0]['type'])
verse_tag = VerseType.translated_tags[verse_index] verse_tag = VerseType.translated_tags[verse_index]
verse_def = '%s%s' % (verse_tag, verse[0]['label']) verse_def = '{tag}{label}'.format(tzg=verse_tag, text=verse[0]['label'])
service_item.add_from_text(verse[1], verse_def) service_item.add_from_text(verse[1], verse_def)
service_item.title = song.title service_item.title = song.title
author_list = self.generate_footer(service_item, song) author_list = self.generate_footer(service_item, song)
@ -639,23 +646,24 @@ class SongMediaItem(MediaManagerItem):
item.raw_footer = [] item.raw_footer = []
item.raw_footer.append(song.title) item.raw_footer.append(song.title)
if authors_none: if authors_none:
item.raw_footer.append("%s: %s" % (translate('OpenLP.Ui', 'Written by'), item.raw_footer.append("{text}: {authors}".format(text=translate('OpenLP.Ui', 'Written by'),
create_separated_list(authors_none))) authors=create_separated_list(authors_none)))
if authors_words_music: if authors_words_music:
item.raw_footer.append("%s: %s" % (AuthorType.Types[AuthorType.WordsAndMusic], item.raw_footer.append("{text}: {authors}".format(text=AuthorType.Types[AuthorType.WordsAndMusic],
create_separated_list(authors_words_music))) authors=create_separated_list(authors_words_music)))
if authors_words: if authors_words:
item.raw_footer.append("%s: %s" % (AuthorType.Types[AuthorType.Words], item.raw_footer.append("{text}: {authors}".format(text=AuthorType.Types[AuthorType.Words],
create_separated_list(authors_words))) authors=create_separated_list(authors_words)))
if authors_music: if authors_music:
item.raw_footer.append("%s: %s" % (AuthorType.Types[AuthorType.Music], item.raw_footer.append("{text}: {authors}".format(text=AuthorType.Types[AuthorType.Music],
create_separated_list(authors_music))) authors=create_separated_list(authors_music)))
if authors_translation: if authors_translation:
item.raw_footer.append("%s: %s" % (AuthorType.Types[AuthorType.Translation], item.raw_footer.append("{text}: {authors}".format(text=AuthorType.Types[AuthorType.Translation],
create_separated_list(authors_translation))) authors=create_separated_list(authors_translation)))
if song.copyright: if song.copyright:
if self.display_copyright_symbol: if self.display_copyright_symbol:
item.raw_footer.append("%s %s" % (SongStrings.CopyrightSymbol, song.copyright)) item.raw_footer.append("{symbol} {song}".format(symbol=SongStrings.CopyrightSymbol,
song=song.copyright))
else: else:
item.raw_footer.append(song.copyright) item.raw_footer.append(song.copyright)
if self.display_songbook and song.songbook_entries: if self.display_songbook and song.songbook_entries:

View File

@ -61,18 +61,20 @@ class OpenLyricsExport(RegistryProperties):
if self.parent.stop_export_flag: if self.parent.stop_export_flag:
return False return False
self.parent.increment_progress_bar( self.parent.increment_progress_bar(
translate('SongsPlugin.OpenLyricsExport', 'Exporting "%s"...') % song.title) translate('SongsPlugin.OpenLyricsExport', 'Exporting "{title}"...').format(title=song.title))
xml = open_lyrics.song_to_xml(song) xml = open_lyrics.song_to_xml(song)
tree = etree.ElementTree(etree.fromstring(xml.encode())) tree = etree.ElementTree(etree.fromstring(xml.encode()))
filename = '%s (%s)' % (song.title, ', '.join([author.display_name for author in song.authors])) filename = '{title} ({author})'.format(title=song.title,
author=', '.join([author.display_name for author in song.authors]))
filename = clean_filename(filename) filename = clean_filename(filename)
# Ensure the filename isn't too long for some filesystems # Ensure the filename isn't too long for some filesystems
filename_with_ext = '%s.xml' % filename[0:250 - len(self.save_path)] filename_with_ext = '{name}.xml'.format(name=filename[0:250 - len(self.save_path)])
# Make sure we're not overwriting an existing file # Make sure we're not overwriting an existing file
conflicts = 0 conflicts = 0
while os.path.exists(os.path.join(self.save_path, filename_with_ext)): while os.path.exists(os.path.join(self.save_path, filename_with_ext)):
conflicts += 1 conflicts += 1
filename_with_ext = '%s-%d.xml' % (filename[0:247 - len(self.save_path)], conflicts) filename_with_ext = '{name}-{extra}.xml'.format(name=filename[0:247 - len(self.save_path)],
extra=conflicts)
# Pass a file object, because lxml does not cope with some special # Pass a file object, because lxml does not cope with some special
# characters in the path (see lp:757673 and lp:744337). # characters in the path (see lp:757673 and lp:744337).
tree.write(open(os.path.join(self.save_path, filename_with_ext), 'wb'), encoding='utf-8', tree.write(open(os.path.join(self.save_path, filename_with_ext), 'wb'), encoding='utf-8',

View File

@ -70,6 +70,7 @@ from openlp.plugins.songs.lib.db import Author, AuthorType, Book, Song, Topic
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
NAMESPACE = 'http://openlyrics.info/namespace/2009/song' NAMESPACE = 'http://openlyrics.info/namespace/2009/song'
# TODO: Verify format() with template variable
NSMAP = '{' + NAMESPACE + '}' + '%s' NSMAP = '{' + NAMESPACE + '}' + '%s'
@ -126,7 +127,7 @@ class SongXML(object):
try: try:
self.song_xml = objectify.fromstring(xml) self.song_xml = objectify.fromstring(xml)
except etree.XMLSyntaxError: except etree.XMLSyntaxError:
log.exception('Invalid xml %s', xml) log.exception('Invalid xml {text}'.format(text=xml))
xml_iter = self.song_xml.getiterator() xml_iter = self.song_xml.getiterator()
for element in xml_iter: for element in xml_iter:
if element.tag == 'verse': if element.tag == 'verse':
@ -422,7 +423,7 @@ class OpenLyrics(object):
:param tags_element: Some tag elements :param tags_element: Some tag elements
""" """
available_tags = FormattingTags.get_html_tags() available_tags = FormattingTags.get_html_tags()
start_tag = '{%s}' % tag_name start_tag = '{{{name}}}'.format(name=tag_name)
for tag in available_tags: for tag in available_tags:
if tag['start tag'] == start_tag: if tag['start tag'] == start_tag:
# Create new formatting tag in openlyrics xml. # Create new formatting tag in openlyrics xml.
@ -449,18 +450,18 @@ class OpenLyrics(object):
xml_tags = tags_element.xpath('tag/attribute::name') xml_tags = tags_element.xpath('tag/attribute::name')
# Some formatting tag has only starting part e.g. <br>. Handle this case. # Some formatting tag has only starting part e.g. <br>. Handle this case.
if tag in end_tags: if tag in end_tags:
text = text.replace('{%s}' % tag, '<tag name="%s">' % tag) text = text.replace('{{{tag}}}'.format(tag=tag), '<tag name="{tag}">'.format(tag=tag))
else: else:
text = text.replace('{%s}' % tag, '<tag name="%s"/>' % tag) text = text.replace('{{{tag}}}'.format(tag=tag), '<tag name="{tag}"/>'.format(tag=tag))
# Add tag to <format> element if tag not present. # Add tag to <format> element if tag not present.
if tag not in xml_tags: if tag not in xml_tags:
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('{/%s}' % 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>%s</lines>' % text) element = etree.XML('<lines>{text}</lines>'.format(text=text))
verse_element.append(element) verse_element.append(element)
return element return element
@ -566,9 +567,9 @@ class OpenLyrics(object):
name = tag.get('name') name = tag.get('name')
if name is None: if name is None:
continue continue
start_tag = '{%s}' % name[:5] start_tag = '{{{name}}}'.format(name=name[:5])
# Some tags have only start tag e.g. {br} # Some tags have only start tag e.g. {br}
end_tag = '{/' + name[:5] + '}' if hasattr(tag, 'close') else '' end_tag = '{{/{name}}}'.format(name=name[:5]) if hasattr(tag, 'close') else ''
openlp_tag = { openlp_tag = {
'desc': name, 'desc': name,
'start tag': start_tag, 'start tag': start_tag,
@ -604,26 +605,30 @@ class OpenLyrics(object):
text = '' text = ''
use_endtag = True use_endtag = True
# Skip <comment> elements - not yet supported. # Skip <comment> elements - not yet supported.
# TODO: Verify format() with template variables
if element.tag == NSMAP % 'comment': if element.tag == NSMAP % 'comment':
if element.tail: if element.tail:
# Append tail text at chord element. # Append tail text at chord element.
text += element.tail text += element.tail
return text return text
# Skip <chord> element - not yet supported. # Skip <chord> element - not yet supported.
# TODO: Verify format() with template variables
elif element.tag == NSMAP % 'chord': elif element.tag == NSMAP % 'chord':
if element.tail: if element.tail:
# Append tail text at chord element. # Append tail text at chord element.
text += element.tail text += element.tail
return text return text
# Convert line breaks <br/> to \n. # Convert line breaks <br/> to \n.
# TODO: Verify format() with template variables
elif newlines and element.tag == NSMAP % 'br': elif newlines and element.tag == NSMAP % 'br':
text += '\n' text += '\n'
if element.tail: if element.tail:
text += element.tail text += element.tail
return text return text
# Start formatting tag. # Start formatting tag.
# TODO: Verify format() with template variables
if element.tag == NSMAP % 'tag': if element.tag == NSMAP % 'tag':
text += '{%s}' % element.get('name') text += '{{{name}}}'.format(name=element.get('name'))
# Some formattings may have only start tag. # Some formattings may have only start tag.
# Handle this case if element has no children and contains no text. # Handle this case if element has no children and contains no text.
if not element and not element.text: if not element and not element.text:
@ -636,8 +641,9 @@ class OpenLyrics(object):
# Use recursion since nested formatting tags are allowed. # Use recursion since nested formatting tags are allowed.
text += self._process_lines_mixed_content(child, newlines) text += self._process_lines_mixed_content(child, newlines)
# Append text from tail and add formatting end tag. # Append text from tail and add formatting end tag.
# TODO: Verify format() with template variables
if element.tag == NSMAP % 'tag' and use_endtag: if element.tag == NSMAP % 'tag' and use_endtag:
text += '{/%s}' % 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
@ -663,6 +669,7 @@ class OpenLyrics(object):
# Loop over the "line" elements removing comments and chords. # Loop over the "line" elements removing comments and chords.
for line in element: for line in element:
# Skip comment lines. # Skip comment lines.
# TODO: Verify format() with template variables
if line.tag == NSMAP % 'comment': if line.tag == NSMAP % 'comment':
continue continue
if text: if text:

View File

@ -78,7 +78,7 @@ class SongSelectImport(object):
try: try:
login_page = BeautifulSoup(self.opener.open(LOGIN_URL).read(), 'lxml') login_page = BeautifulSoup(self.opener.open(LOGIN_URL).read(), 'lxml')
except (TypeError, URLError) as e: except (TypeError, URLError) as e:
log.exception('Could not login to SongSelect, %s', e) log.exception('Could not login to SongSelect, {error}'.format(error=e))
return False return False
if callback: if callback:
callback() callback()
@ -92,7 +92,7 @@ class SongSelectImport(object):
try: try:
posted_page = BeautifulSoup(self.opener.open(LOGIN_URL, data.encode('utf-8')).read(), 'lxml') posted_page = BeautifulSoup(self.opener.open(LOGIN_URL, data.encode('utf-8')).read(), 'lxml')
except (TypeError, URLError) as e: except (TypeError, URLError) as e:
log.exception('Could not login to SongSelect, %s', e) log.exception('Could not login to SongSelect, {error}'.format(error=e))
return False return False
if callback: if callback:
callback() callback()
@ -105,7 +105,7 @@ class SongSelectImport(object):
try: try:
self.opener.open(LOGOUT_URL) self.opener.open(LOGOUT_URL)
except (TypeError, URLError) as e: except (TypeError, URLError) as e:
log.exception('Could not log of SongSelect, %s', e) log.exception('Could not log of SongSelect, {error}'.format(error=e))
def search(self, search_text, max_results, callback=None): def search(self, search_text, max_results, callback=None):
""" """
@ -127,7 +127,7 @@ class SongSelectImport(object):
results_page = BeautifulSoup(self.opener.open(SEARCH_URL + '?' + urlencode(params)).read(), 'lxml') results_page = BeautifulSoup(self.opener.open(SEARCH_URL + '?' + urlencode(params)).read(), 'lxml')
search_results = results_page.find_all('li', 'result pane') search_results = results_page.find_all('li', 'result pane')
except (TypeError, URLError) as e: except (TypeError, URLError) as e:
log.exception('Could not search SongSelect, %s', e) log.exception('Could not search SongSelect, {error}'.format(error=e))
search_results = None search_results = None
if not search_results: if not search_results:
break break
@ -158,7 +158,7 @@ class SongSelectImport(object):
try: try:
song_page = BeautifulSoup(self.opener.open(song['link']).read(), 'lxml') song_page = BeautifulSoup(self.opener.open(song['link']).read(), 'lxml')
except (TypeError, URLError) as e: except (TypeError, URLError) as e:
log.exception('Could not get song from SongSelect, %s', e) log.exception('Could not get song from SongSelect, {error}'.format(error=e))
return None return None
if callback: if callback:
callback() callback()
@ -203,7 +203,7 @@ class SongSelectImport(object):
verse_type = VerseType.from_loose_input(verse_type) verse_type = VerseType.from_loose_input(verse_type)
verse_number = int(verse_number) verse_number = int(verse_number)
song_xml.add_verse_to_lyrics(VerseType.tags[verse_type], verse_number, verse['lyrics']) song_xml.add_verse_to_lyrics(VerseType.tags[verse_type], verse_number, verse['lyrics'])
verse_order.append('%s%s' % (VerseType.tags[verse_type], verse_number)) verse_order.append('{tag}{number}'.format(tag=VerseType.tags[verse_type], number=verse_number))
db_song.verse_order = ' '.join(verse_order) db_song.verse_order = ' '.join(verse_order)
db_song.lyrics = song_xml.extract_xml() db_song.lyrics = song_xml.extract_xml()
clean_song(self.db_manager, db_song) clean_song(self.db_manager, db_song)

View File

@ -74,8 +74,8 @@ class SongsTab(SettingsTab):
'Import missing songs from service files')) 'Import missing songs from service files'))
self.display_songbook_check_box.setText(translate('SongsPlugin.SongsTab', 'Display songbook in footer')) self.display_songbook_check_box.setText(translate('SongsPlugin.SongsTab', 'Display songbook in footer'))
self.display_copyright_check_box.setText(translate('SongsPlugin.SongsTab', self.display_copyright_check_box.setText(translate('SongsPlugin.SongsTab',
'Display "%s" symbol before copyright info') % 'Display "{symbol}" symbol before copyright '
SongStrings.CopyrightSymbol) 'info').format(symbol=SongStrings.CopyrightSymbol))
def on_search_as_type_check_box_changed(self, check_state): def on_search_as_type_check_box_changed(self, check_state):
self.song_search = (check_state == QtCore.Qt.Checked) self.song_search = (check_state == QtCore.Qt.Checked)

View File

@ -81,9 +81,10 @@ class SongUsageDetailForm(QtWidgets.QDialog, Ui_SongUsageDetailDialog, RegistryP
) )
return return
check_directory_exists(path) check_directory_exists(path)
file_name = translate('SongUsagePlugin.SongUsageDetailForm', 'usage_detail_%s_%s.txt') % \ file_name = translate('SongUsagePlugin.SongUsageDetailForm',
(self.from_date_calendar.selectedDate().toString('ddMMyyyy'), 'usage_detail_{old}_{new}.txt'
self.to_date_calendar.selectedDate().toString('ddMMyyyy')) ).format(old=self.from_date_calendar.selectedDate().toString('ddMMyyyy'),
new=self.to_date_calendar.selectedDate().toString('ddMMyyyy'))
Settings().setValue(self.plugin.settings_section + '/from date', self.from_date_calendar.selectedDate()) Settings().setValue(self.plugin.settings_section + '/from date', self.from_date_calendar.selectedDate())
Settings().setValue(self.plugin.settings_section + '/to date', self.to_date_calendar.selectedDate()) Settings().setValue(self.plugin.settings_section + '/to date', self.to_date_calendar.selectedDate())
usage = self.plugin.manager.get_all_objects( usage = self.plugin.manager.get_all_objects(
@ -95,21 +96,23 @@ class SongUsageDetailForm(QtWidgets.QDialog, Ui_SongUsageDetailDialog, RegistryP
try: try:
file_handle = open(report_file_name, 'wb') file_handle = open(report_file_name, 'wb')
for instance in usage: for instance in usage:
record = '\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",' \ record = ('\"{date}\",\"{time}\",\"{title}\",\"{copyright}\",\"{ccli}\",\"{authors}\",'
'\"%s\",\"%s\"\n' % \ '\"{name}\",\"{source}\"\n').format(date=instance.usagedate, time=instance.usagetime,
(instance.usagedate, instance.usagetime, instance.title, instance.copyright, title=instance.title, copyright=instance.copyright,
instance.ccl_number, instance.authors, instance.plugin_name, instance.source) ccli=instance.ccl_number, authors=instance.authors,
name=instance.plugin_name, source=instance.source)
file_handle.write(record.encode('utf-8')) file_handle.write(record.encode('utf-8'))
self.main_window.information_message( self.main_window.information_message(
translate('SongUsagePlugin.SongUsageDetailForm', 'Report Creation'), translate('SongUsagePlugin.SongUsageDetailForm', 'Report Creation'),
translate('SongUsagePlugin.SongUsageDetailForm', translate('SongUsagePlugin.SongUsageDetailForm',
'Report \n%s \nhas been successfully created. ') % report_file_name 'Report \n{name} \nhas been successfully created. ').format(name=report_file_name)
) )
except OSError as ose: except OSError as ose:
log.exception('Failed to write out song usage records') log.exception('Failed to write out song usage records')
critical_error_message_box(translate('SongUsagePlugin.SongUsageDetailForm', 'Report Creation Failed'), critical_error_message_box(translate('SongUsagePlugin.SongUsageDetailForm', 'Report Creation Failed'),
translate('SongUsagePlugin.SongUsageDetailForm', translate('SongUsagePlugin.SongUsageDetailForm',
'An error occurred while creating the report: %s') % ose.strerror) 'An error occurred while creating the report: {error}'
).format(error=ose.strerror))
finally: finally:
if file_handle: if file_handle:
file_handle.close() file_handle.close()

View File

@ -29,7 +29,7 @@ from tests.helpers.testmixin import TestMixin
class TestInitFunctions(TestMixin, TestCase): class TestInitFunctions(TestMixin, TestCase):
def parse_options_basic_test(self): def test_parse_options_basic(self):
""" """
Test the parse options process works Test the parse options process works
@ -46,7 +46,7 @@ class TestInitFunctions(TestMixin, TestCase):
self.assertEquals(args.style, None, 'There are no style flags to be processed') self.assertEquals(args.style, None, 'There are no style flags to be processed')
self.assertEquals(args.rargs, [], 'The service file should be blank') self.assertEquals(args.rargs, [], 'The service file should be blank')
def parse_options_debug_test(self): def test_parse_options_debug(self):
""" """
Test the parse options process works for debug only Test the parse options process works for debug only
@ -63,7 +63,7 @@ class TestInitFunctions(TestMixin, TestCase):
self.assertEquals(args.style, None, 'There are no style flags to be processed') self.assertEquals(args.style, None, 'There are no style flags to be processed')
self.assertEquals(args.rargs, [], 'The service file should be blank') self.assertEquals(args.rargs, [], 'The service file should be blank')
def parse_options_debug_and_portable_test(self): def test_parse_options_debug_and_portable(self):
""" """
Test the parse options process works for debug and portable Test the parse options process works for debug and portable
@ -80,7 +80,7 @@ class TestInitFunctions(TestMixin, TestCase):
self.assertEquals(args.style, None, 'There are no style flags to be processed') self.assertEquals(args.style, None, 'There are no style flags to be processed')
self.assertEquals(args.rargs, [], 'The service file should be blank') self.assertEquals(args.rargs, [], 'The service file should be blank')
def parse_options_all_no_file_test(self): def test_parse_options_all_no_file(self):
""" """
Test the parse options process works with two options Test the parse options process works with two options
@ -97,7 +97,7 @@ class TestInitFunctions(TestMixin, TestCase):
self.assertEquals(args.style, None, 'There are no style flags to be processed') self.assertEquals(args.style, None, 'There are no style flags to be processed')
self.assertEquals(args.rargs, [], 'The service file should be blank') self.assertEquals(args.rargs, [], 'The service file should be blank')
def parse_options_file_test(self): def test_parse_options_file(self):
""" """
Test the parse options process works with a file Test the parse options process works with a file
@ -114,7 +114,7 @@ class TestInitFunctions(TestMixin, TestCase):
self.assertEquals(args.style, None, 'There are no style flags to be processed') self.assertEquals(args.style, None, 'There are no style flags to be processed')
self.assertEquals(args.rargs, 'dummy_temp', 'The service file should not be blank') self.assertEquals(args.rargs, 'dummy_temp', 'The service file should not be blank')
def parse_options_file_and_debug_test(self): def test_parse_options_file_and_debug(self):
""" """
Test the parse options process works with a file Test the parse options process works with a file

View File

@ -49,7 +49,7 @@ class TestCategoryActionList(TestCase):
""" """
del self.list del self.list
def contains_test(self): def test_contains(self):
""" """
Test the __contains__() method Test the __contains__() method
""" """
@ -61,7 +61,7 @@ class TestCategoryActionList(TestCase):
self.assertTrue(self.action1 in self.list) self.assertTrue(self.action1 in self.list)
self.assertFalse(self.action2 in self.list) self.assertFalse(self.action2 in self.list)
def len_test(self): def test_len(self):
""" """
Test the __len__ method Test the __len__ method
""" """
@ -77,7 +77,7 @@ class TestCategoryActionList(TestCase):
# THEN: Check the length. # THEN: Check the length.
self.assertEqual(len(self.list), 1, "The length should be 1.") self.assertEqual(len(self.list), 1, "The length should be 1.")
def append_test(self): def test_append(self):
""" """
Test the append() method Test the append() method
""" """
@ -92,7 +92,7 @@ class TestCategoryActionList(TestCase):
self.assertEqual(self.list.actions[0], (0, self.action1)) self.assertEqual(self.list.actions[0], (0, self.action1))
self.assertEqual(self.list.actions[1], (1, self.action2)) self.assertEqual(self.list.actions[1], (1, self.action2))
def add_test(self): def test_add(self):
""" """
Test the add() method Test the add() method
""" """
@ -111,7 +111,7 @@ class TestCategoryActionList(TestCase):
self.assertEqual(self.list.actions[0], (41, self.action2)) self.assertEqual(self.list.actions[0], (41, self.action2))
self.assertEqual(self.list.actions[1], (42, self.action1)) self.assertEqual(self.list.actions[1], (42, self.action1))
def iterator_test(self): def test_iterator(self):
""" """
Test the __iter__ and __next__ methods Test the __iter__ and __next__ methods
""" """
@ -126,7 +126,7 @@ class TestCategoryActionList(TestCase):
self.assertIs(l[0], self.action1) self.assertIs(l[0], self.action1)
self.assertIs(l[1], self.action2) self.assertIs(l[1], self.action2)
def remove_test(self): def test_remove(self):
""" """
Test the remove() method Test the remove() method
""" """

View File

@ -36,7 +36,7 @@ class TestAppLocation(TestCase):
""" """
A test suite to test out various methods around the AppLocation class. A test suite to test out various methods around the AppLocation class.
""" """
def get_data_path_test(self): def test_get_data_path(self):
""" """
Test the AppLocation.get_data_path() method Test the AppLocation.get_data_path() method
""" """
@ -60,7 +60,7 @@ class TestAppLocation(TestCase):
mocked_check_directory_exists.assert_called_with(os.path.join('test', 'dir')) mocked_check_directory_exists.assert_called_with(os.path.join('test', 'dir'))
self.assertEqual(os.path.join('test', 'dir'), data_path, 'Result should be "test/dir"') self.assertEqual(os.path.join('test', 'dir'), data_path, 'Result should be "test/dir"')
def get_data_path_with_custom_location_test(self): def test_get_data_path_with_custom_location(self):
""" """
Test the AppLocation.get_data_path() method when a custom location is set in the settings Test the AppLocation.get_data_path() method when a custom location is set in the settings
""" """
@ -80,7 +80,7 @@ class TestAppLocation(TestCase):
mocked_settings.value.assert_called_with('advanced/data path') mocked_settings.value.assert_called_with('advanced/data path')
self.assertEqual('custom/dir', data_path, 'Result should be "custom/dir"') self.assertEqual('custom/dir', data_path, 'Result should be "custom/dir"')
def get_files_no_section_no_extension_test(self): def test_get_files_no_section_no_extension(self):
""" """
Test the AppLocation.get_files() method with no parameters passed. Test the AppLocation.get_files() method with no parameters passed.
""" """
@ -96,7 +96,7 @@ class TestAppLocation(TestCase):
# Then: check if the file lists are identical. # Then: check if the file lists are identical.
self.assertListEqual(FILE_LIST, result, 'The file lists should be identical.') self.assertListEqual(FILE_LIST, result, 'The file lists should be identical.')
def get_files_test(self): def test_get_files(self):
""" """
Test the AppLocation.get_files() method with all parameters passed. Test the AppLocation.get_files() method with all parameters passed.
""" """
@ -115,7 +115,7 @@ class TestAppLocation(TestCase):
# Then: check if the file lists are identical. # Then: check if the file lists are identical.
self.assertListEqual(['file5.mp3', 'file6.mp3'], result, 'The file lists should be identical.') self.assertListEqual(['file5.mp3', 'file6.mp3'], result, 'The file lists should be identical.')
def get_section_data_path_test(self): def test_get_section_data_path(self):
""" """
Test the AppLocation.get_section_data_path() method Test the AppLocation.get_section_data_path() method
""" """
@ -132,7 +132,7 @@ class TestAppLocation(TestCase):
mocked_check_directory_exists.assert_called_with(os.path.join('test', 'dir', 'section')) mocked_check_directory_exists.assert_called_with(os.path.join('test', 'dir', 'section'))
self.assertEqual(os.path.join('test', 'dir', 'section'), data_path, 'Result should be "test/dir/section"') self.assertEqual(os.path.join('test', 'dir', 'section'), data_path, 'Result should be "test/dir/section"')
def get_directory_for_app_dir_test(self): def test_get_directory_for_app_dir(self):
""" """
Test the AppLocation.get_directory() method for AppLocation.AppDir Test the AppLocation.get_directory() method for AppLocation.AppDir
""" """
@ -146,7 +146,7 @@ class TestAppLocation(TestCase):
# THEN: check that the correct directory is returned # THEN: check that the correct directory is returned
self.assertEqual(os.path.join('app', 'dir'), directory, 'Directory should be "app/dir"') self.assertEqual(os.path.join('app', 'dir'), directory, 'Directory should be "app/dir"')
def get_directory_for_plugins_dir_test(self): def test_get_directory_for_plugins_dir(self):
""" """
Test the AppLocation.get_directory() method for AppLocation.PluginsDir Test the AppLocation.get_directory() method for AppLocation.PluginsDir
""" """
@ -167,7 +167,7 @@ class TestAppLocation(TestCase):
# THEN: The correct directory should be returned # THEN: The correct directory should be returned
self.assertEqual(os.path.join('plugins', 'dir'), directory, 'Directory should be "plugins/dir"') self.assertEqual(os.path.join('plugins', 'dir'), directory, 'Directory should be "plugins/dir"')
def get_frozen_path_in_unfrozen_app_test(self): def test_get_frozen_path_in_unfrozen_app(self):
""" """
Test the _get_frozen_path() function when the application is not frozen (compiled by PyInstaller) Test the _get_frozen_path() function when the application is not frozen (compiled by PyInstaller)
""" """
@ -181,7 +181,7 @@ class TestAppLocation(TestCase):
# THEN: The non-frozen parameter is returned # THEN: The non-frozen parameter is returned
self.assertEqual('not frozen', frozen_path, '_get_frozen_path should return "not frozen"') self.assertEqual('not frozen', frozen_path, '_get_frozen_path should return "not frozen"')
def get_frozen_path_in_frozen_app_test(self): def test_get_frozen_path_in_frozen_app(self):
""" """
Test the get_frozen_path() function when the application is frozen (compiled by PyInstaller) Test the get_frozen_path() function when the application is frozen (compiled by PyInstaller)
""" """

View File

@ -34,7 +34,7 @@ class TestCommonFunctions(TestCase):
""" """
A test suite to test out various functions in the openlp.core.common module. A test suite to test out various functions in the openlp.core.common module.
""" """
def check_directory_exists_test(self): def test_check_directory_exists(self):
""" """
Test the check_directory_exists() function Test the check_directory_exists() function
""" """
@ -73,7 +73,7 @@ class TestCommonFunctions(TestCase):
mocked_exists.assert_called_with(directory_to_check) mocked_exists.assert_called_with(directory_to_check)
self.assertRaises(ValueError, check_directory_exists, directory_to_check) self.assertRaises(ValueError, check_directory_exists, directory_to_check)
def de_hump_conversion_test(self): def test_de_hump_conversion(self):
""" """
Test the de_hump function with a class name Test the de_hump function with a class name
""" """
@ -86,7 +86,7 @@ class TestCommonFunctions(TestCase):
# THEN: the new string should be converted to python format # THEN: the new string should be converted to python format
self.assertTrue(new_string == "my_class", 'The class name should have been converted') self.assertTrue(new_string == "my_class", 'The class name should have been converted')
def de_hump_static_test(self): def test_de_hump_static(self):
""" """
Test the de_hump function with a python string Test the de_hump function with a python string
""" """
@ -99,7 +99,7 @@ class TestCommonFunctions(TestCase):
# THEN: the new string should be converted to python format # THEN: the new string should be converted to python format
self.assertTrue(new_string == "my_class", 'The class name should have been preserved') self.assertTrue(new_string == "my_class", 'The class name should have been preserved')
def trace_error_handler_test(self): def test_trace_error_handler(self):
""" """
Test the trace_error_handler() method Test the trace_error_handler() method
""" """
@ -115,7 +115,7 @@ class TestCommonFunctions(TestCase):
mocked_logger.error.assert_called_with( mocked_logger.error.assert_called_with(
'OpenLP Error trace\n File openlp.fake at line 56 \n\t called trace_error_handler_test') 'OpenLP Error trace\n File openlp.fake at line 56 \n\t called trace_error_handler_test')
def translate_test(self): def test_translate(self):
""" """
Test the translate() function Test the translate() function
""" """
@ -132,7 +132,7 @@ class TestCommonFunctions(TestCase):
mocked_translate.assert_called_with(context, text, comment) mocked_translate.assert_called_with(context, text, comment)
self.assertEqual('Translated string', result, 'The translated string should have been returned') self.assertEqual('Translated string', result, 'The translated string should have been returned')
def is_win_test(self): def test_is_win(self):
""" """
Test the is_win() function Test the is_win() function
""" """
@ -148,7 +148,7 @@ class TestCommonFunctions(TestCase):
self.assertFalse(is_macosx(), 'is_macosx() should return False') self.assertFalse(is_macosx(), 'is_macosx() should return False')
self.assertFalse(is_linux(), 'is_linux() should return False') self.assertFalse(is_linux(), 'is_linux() should return False')
def is_macosx_test(self): def test_is_macosx(self):
""" """
Test the is_macosx() function Test the is_macosx() function
""" """
@ -164,7 +164,7 @@ class TestCommonFunctions(TestCase):
self.assertFalse(is_win(), 'is_win() should return False') self.assertFalse(is_win(), 'is_win() should return False')
self.assertFalse(is_linux(), 'is_linux() should return False') self.assertFalse(is_linux(), 'is_linux() should return False')
def is_linux_test(self): def test_is_linux(self):
""" """
Test the is_linux() function Test the is_linux() function
""" """
@ -180,7 +180,7 @@ class TestCommonFunctions(TestCase):
self.assertFalse(is_win(), 'is_win() should return False') self.assertFalse(is_win(), 'is_win() should return False')
self.assertFalse(is_macosx(), 'is_macosx() should return False') self.assertFalse(is_macosx(), 'is_macosx() should return False')
def clean_button_text_test(self): def test_clean_button_text(self):
""" """
Test the clean_button_text() function. Test the clean_button_text() function.
""" """

View File

@ -67,7 +67,7 @@ class TestUtilsDBFunctions(TestCase):
time.sleep(1) time.sleep(1)
retries += 1 retries += 1
def delete_column_test(self): def test_delete_column(self):
""" """
Test deleting a single column in a table Test deleting a single column in a table
""" """
@ -85,7 +85,7 @@ class TestUtilsDBFunctions(TestCase):
if column.name == 'song_book_id': if column.name == 'song_book_id':
self.fail("The column 'song_book_id' should have been deleted.") self.fail("The column 'song_book_id' should have been deleted.")
def delete_columns_test(self): def test_delete_columns(self):
""" """
Test deleting multiple columns in a table Test deleting multiple columns in a table
""" """

View File

@ -48,7 +48,7 @@ class TestInit(TestCase, TestMixin):
""" """
self.destroy_settings() self.destroy_settings()
def add_actions_empty_list_test(self): def test_add_actions_empty_list(self):
""" """
Test that no actions are added when the list is empty Test that no actions are added when the list is empty
""" """
@ -63,7 +63,7 @@ class TestInit(TestCase, TestMixin):
self.assertEqual(0, mocked_target.addSeparator.call_count, 'addSeparator method should not have been called') self.assertEqual(0, mocked_target.addSeparator.call_count, 'addSeparator method should not have been called')
self.assertEqual(0, mocked_target.addAction.call_count, 'addAction method should not have been called') self.assertEqual(0, mocked_target.addAction.call_count, 'addAction method should not have been called')
def add_actions_none_action_test(self): def test_add_actions_none_action(self):
""" """
Test that a separator is added when a None action is in the list Test that a separator is added when a None action is in the list
""" """
@ -78,7 +78,7 @@ class TestInit(TestCase, TestMixin):
mocked_target.addSeparator.assert_called_with() mocked_target.addSeparator.assert_called_with()
self.assertEqual(0, mocked_target.addAction.call_count, 'addAction method should not have been called') self.assertEqual(0, mocked_target.addAction.call_count, 'addAction method should not have been called')
def add_actions_add_action_test(self): def test_add_actions_add_action(self):
""" """
Test that an action is added when a valid action is in the list Test that an action is added when a valid action is in the list
""" """
@ -93,7 +93,7 @@ class TestInit(TestCase, TestMixin):
self.assertEqual(0, mocked_target.addSeparator.call_count, 'addSeparator method should not have been called') self.assertEqual(0, mocked_target.addSeparator.call_count, 'addSeparator method should not have been called')
mocked_target.addAction.assert_called_with('action') mocked_target.addAction.assert_called_with('action')
def add_actions_action_and_none_test(self): def test_add_actions_action_and_none(self):
""" """
Test that an action and a separator are added when a valid action and None are in the list Test that an action and a separator are added when a valid action and None are in the list
""" """
@ -108,7 +108,7 @@ class TestInit(TestCase, TestMixin):
mocked_target.addSeparator.assert_called_with() mocked_target.addSeparator.assert_called_with()
mocked_target.addAction.assert_called_with('action') mocked_target.addAction.assert_called_with('action')
def get_uno_instance_pipe_test(self): def test_get_uno_instance_pipe(self):
""" """
Test that when the UNO connection type is "pipe" the resolver is given the "pipe" URI Test that when the UNO connection type is "pipe" the resolver is given the "pipe" URI
""" """
@ -121,7 +121,7 @@ class TestInit(TestCase, TestMixin):
# THEN: the resolve method is called with the correct argument # THEN: the resolve method is called with the correct argument
mock_resolver.resolve.assert_called_with('uno:pipe,name=openlp_pipe;urp;StarOffice.ComponentContext') mock_resolver.resolve.assert_called_with('uno:pipe,name=openlp_pipe;urp;StarOffice.ComponentContext')
def get_uno_instance_socket_test(self): def test_get_uno_instance_socket(self):
""" """
Test that when the UNO connection type is other than "pipe" the resolver is given the "socket" URI Test that when the UNO connection type is other than "pipe" the resolver is given the "socket" URI
""" """
@ -134,7 +134,7 @@ class TestInit(TestCase, TestMixin):
# THEN: the resolve method is called with the correct argument # THEN: the resolve method is called with the correct argument
mock_resolver.resolve.assert_called_with('uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext') mock_resolver.resolve.assert_called_with('uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext')
def get_uno_command_libreoffice_command_exists_test(self): def test_get_uno_command_libreoffice_command_exists(self):
""" """
Test the ``get_uno_command`` function uses the libreoffice command when available. Test the ``get_uno_command`` function uses the libreoffice command when available.
:return: :return:
@ -151,7 +151,7 @@ class TestInit(TestCase, TestMixin):
'libreoffice --nologo --norestore --minimized --nodefault --nofirststartwizard' 'libreoffice --nologo --norestore --minimized --nodefault --nofirststartwizard'
' "--accept=pipe,name=openlp_pipe;urp;"') ' "--accept=pipe,name=openlp_pipe;urp;"')
def get_uno_command_only_soffice_command_exists_test(self): def test_get_uno_command_only_soffice_command_exists(self):
""" """
Test the ``get_uno_command`` function uses the soffice command when the libreoffice command is not available. Test the ``get_uno_command`` function uses the soffice command when the libreoffice command is not available.
:return: :return:
@ -169,7 +169,7 @@ class TestInit(TestCase, TestMixin):
self.assertEquals(result, 'soffice --nologo --norestore --minimized --nodefault --nofirststartwizard' self.assertEquals(result, 'soffice --nologo --norestore --minimized --nodefault --nofirststartwizard'
' "--accept=pipe,name=openlp_pipe;urp;"') ' "--accept=pipe,name=openlp_pipe;urp;"')
def get_uno_command_when_no_command_exists_test(self): def test_get_uno_command_when_no_command_exists(self):
""" """
Test the ``get_uno_command`` function raises an FileNotFoundError when neither the libreoffice or soffice Test the ``get_uno_command`` function raises an FileNotFoundError when neither the libreoffice or soffice
commands are available. commands are available.
@ -183,7 +183,7 @@ class TestInit(TestCase, TestMixin):
# THEN: a FileNotFoundError exception should be raised # THEN: a FileNotFoundError exception should be raised
self.assertRaises(FileNotFoundError, get_uno_command) self.assertRaises(FileNotFoundError, get_uno_command)
def get_uno_command_connection_type_test(self): def test_get_uno_command_connection_type(self):
""" """
Test the ``get_uno_command`` function when the connection type is anything other than pipe. Test the ``get_uno_command`` function when the connection type is anything other than pipe.
:return: :return:
@ -198,7 +198,7 @@ class TestInit(TestCase, TestMixin):
self.assertEqual(result, 'libreoffice --nologo --norestore --minimized --nodefault --nofirststartwizard' self.assertEqual(result, 'libreoffice --nologo --norestore --minimized --nodefault --nofirststartwizard'
' "--accept=socket,host=localhost,port=2002;urp;"') ' "--accept=socket,host=localhost,port=2002;urp;"')
def get_filesystem_encoding_sys_function_not_called_test(self): def test_get_filesystem_encoding_sys_function_not_called(self):
""" """
Test the get_filesystem_encoding() function does not call the sys.getdefaultencoding() function Test the get_filesystem_encoding() function does not call the sys.getdefaultencoding() function
""" """
@ -215,7 +215,7 @@ class TestInit(TestCase, TestMixin):
self.assertEqual(0, mocked_getdefaultencoding.called, 'getdefaultencoding should not have been called') self.assertEqual(0, mocked_getdefaultencoding.called, 'getdefaultencoding should not have been called')
self.assertEqual('cp1252', result, 'The result should be "cp1252"') self.assertEqual('cp1252', result, 'The result should be "cp1252"')
def get_filesystem_encoding_sys_function_is_called_test(self): def test_get_filesystem_encoding_sys_function_is_called(self):
""" """
Test the get_filesystem_encoding() function calls the sys.getdefaultencoding() function Test the get_filesystem_encoding() function calls the sys.getdefaultencoding() function
""" """
@ -233,7 +233,7 @@ class TestInit(TestCase, TestMixin):
mocked_getdefaultencoding.assert_called_with() mocked_getdefaultencoding.assert_called_with()
self.assertEqual('utf-8', result, 'The result should be "utf-8"') self.assertEqual('utf-8', result, 'The result should be "utf-8"')
def split_filename_with_file_path_test(self): def test_split_filename_with_file_path(self):
""" """
Test the split_filename() function with a path to a file Test the split_filename() function with a path to a file
""" """
@ -253,7 +253,7 @@ class TestInit(TestCase, TestMixin):
# THEN: A tuple should be returned. # THEN: A tuple should be returned.
self.assertEqual(wanted_result, result, 'A tuple with the dir and file name should have been returned') self.assertEqual(wanted_result, result, 'A tuple with the dir and file name should have been returned')
def split_filename_with_dir_path_test(self): def test_split_filename_with_dir_path(self):
""" """
Test the split_filename() function with a path to a directory Test the split_filename() function with a path to a directory
""" """
@ -274,7 +274,7 @@ class TestInit(TestCase, TestMixin):
self.assertEqual(wanted_result, result, self.assertEqual(wanted_result, result,
'A two-entry tuple with the directory and file name (empty) should have been returned.') 'A two-entry tuple with the directory and file name (empty) should have been returned.')
def clean_filename_test(self): def test_clean_filename(self):
""" """
Test the clean_filename() function Test the clean_filename() function
""" """
@ -288,7 +288,7 @@ class TestInit(TestCase, TestMixin):
# THEN: The file name should be cleaned. # THEN: The file name should be cleaned.
self.assertEqual(wanted_name, result, 'The file name should not contain any special characters.') self.assertEqual(wanted_name, result, 'The file name should not contain any special characters.')
def delete_file_no_path_test(self): def test_delete_file_no_path(self):
""" """
Test the delete_file function when called with out a valid path Test the delete_file function when called with out a valid path
""" """
@ -299,7 +299,7 @@ class TestInit(TestCase, TestMixin):
# THEN: delete_file should return False # THEN: delete_file should return False
self.assertFalse(result, "delete_file should return False when called with ''") self.assertFalse(result, "delete_file should return False when called with ''")
def delete_file_path_success_test(self): def test_delete_file_path_success(self):
""" """
Test the delete_file function when it successfully deletes a file Test the delete_file function when it successfully deletes a file
""" """
@ -312,7 +312,7 @@ class TestInit(TestCase, TestMixin):
# THEN: delete_file should return True # THEN: delete_file should return True
self.assertTrue(result, 'delete_file should return True when it successfully deletes a file') self.assertTrue(result, 'delete_file should return True when it successfully deletes a file')
def delete_file_path_no_file_exists_test(self): def test_delete_file_path_no_file_exists(self):
""" """
Test the delete_file function when the file to remove does not exist Test the delete_file function when the file to remove does not exist
""" """
@ -325,7 +325,7 @@ class TestInit(TestCase, TestMixin):
# THEN: delete_file should return True # THEN: delete_file should return True
self.assertTrue(result, 'delete_file should return True when the file doesnt exist') self.assertTrue(result, 'delete_file should return True when the file doesnt exist')
def delete_file_path_exception_test(self): def test_delete_file_path_exception(self):
""" """
Test the delete_file function when os.remove raises an exception Test the delete_file function when os.remove raises an exception
""" """

View File

@ -33,7 +33,7 @@ class TestLanguageManager(TestCase):
A test suite to test out various methods around the common __init__ class. A test suite to test out various methods around the common __init__ class.
""" """
def get_locale_key_test(self): def test_get_locale_key(self):
""" """
Test the get_locale_key(string) function Test the get_locale_key(string) function
""" """
@ -50,7 +50,7 @@ class TestLanguageManager(TestCase):
self.assertEqual(['Aushang', '\u00C4u\u00DFerung', 'Auszug'], sorted_list, self.assertEqual(['Aushang', '\u00C4u\u00DFerung', 'Auszug'], sorted_list,
'Strings should be sorted properly') 'Strings should be sorted properly')
def get_natural_key_test(self): def test_get_natural_key(self):
""" """
Test the get_natural_key(string) function Test the get_natural_key(string) function
""" """

View File

@ -33,7 +33,7 @@ TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '../', '..',
class TestRegistry(TestCase): class TestRegistry(TestCase):
def registry_service_test(self): def test_registry_service(self):
""" """
Test the registry creation and its usage Test the registry creation and its usage
""" """
@ -59,13 +59,13 @@ class TestRegistry(TestCase):
temp = Registry().get('test2') temp = Registry().get('test2')
self.assertEqual(temp, None, 'None should have been returned for missing service') self.assertEqual(temp, None, 'None should have been returned for missing service')
# WHEN I try to replace a component I should be allowed (testing only) # WHEN I try to replace a component I should be allowed
Registry().remove('test1') Registry().remove('test1')
# THEN I will get an exception # THEN I will get an exception
temp = Registry().get('test1') temp = Registry().get('test1')
self.assertEqual(temp, None, 'None should have been returned for deleted service') self.assertEqual(temp, None, 'None should have been returned for deleted service')
def registry_function_test(self): def test_registry_function(self):
""" """
Test the registry function creation and their usages Test the registry function creation and their usages
""" """
@ -93,7 +93,44 @@ class TestRegistry(TestCase):
# THEN: I expect then function to have been called and a return given # THEN: I expect then function to have been called and a return given
self.assertEqual(return_value[0], 'function_2', 'A return value is provided and matches') self.assertEqual(return_value[0], 'function_2', 'A return value is provided and matches')
def remove_function_test(self): def test_registry_working_flags(self):
"""
Test the registry working flags creation and its usage
"""
# GIVEN: A new registry
Registry.create()
# WHEN: I add a working flag it should save it
my_data = 'Lamas'
my_data2 = 'More Lamas'
Registry().set_flag('test1', my_data)
# THEN: we should be able retrieve the saved component
temp = Registry().get_flag('test1')
self.assertEquals(temp, my_data, 'The value should have been saved')
# WHEN: I add a component for the second time I am not mad.
# THEN and I will not get an exception
Registry().set_flag('test1', my_data2)
temp = Registry().get_flag('test1')
self.assertEquals(temp, my_data2, 'The value should have been updated')
# WHEN I try to get back a non existent Working Flag
# THEN I will get an exception
with self.assertRaises(KeyError) as context1:
temp = Registry().get_flag('test2')
self.assertEqual(context1.exception.args[0], 'Working Flag test2 not found in list',
'KeyError exception should have been thrown for missing working flag')
# WHEN I try to replace a working flag I should be allowed
Registry().remove_flag('test1')
# THEN I will get an exception
with self.assertRaises(KeyError) as context:
temp = Registry().get_flag('test1')
self.assertEqual(context.exception.args[0], 'Working Flag test1 not found in list',
'KeyError exception should have been thrown for duplicate working flag')
def test_remove_function(self):
""" """
Test the remove_function() method Test the remove_function() method
""" """

View File

@ -32,7 +32,7 @@ TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '../', '..',
class TestRegistryMixin(TestCase): class TestRegistryMixin(TestCase):
def registry_mixin_missing_test(self): def test_registry_mixin_missing(self):
""" """
Test the registry creation and its usage Test the registry creation and its usage
""" """
@ -45,7 +45,7 @@ class TestRegistryMixin(TestCase):
# THEN: The following methods are missing # THEN: The following methods are missing
self.assertEqual(len(Registry().functions_list), 0), 'The function should not be in the dict anymore.' self.assertEqual(len(Registry().functions_list), 0), 'The function should not be in the dict anymore.'
def registry_mixin_present_test(self): def test_registry_mixin_present(self):
""" """
Test the registry creation and its usage Test the registry creation and its usage
""" """

View File

@ -25,7 +25,8 @@ Test the registry properties
from unittest import TestCase from unittest import TestCase
from openlp.core.common import Registry, RegistryProperties from openlp.core.common import Registry, RegistryProperties
from tests.functional import MagicMock
from tests.functional import MagicMock, patch
class TestRegistryProperties(TestCase, RegistryProperties): class TestRegistryProperties(TestCase, RegistryProperties):
@ -38,7 +39,7 @@ class TestRegistryProperties(TestCase, RegistryProperties):
""" """
Registry.create() Registry.create()
def no_application_test(self): def test_no_application(self):
""" """
Test property if no registry value assigned Test property if no registry value assigned
""" """
@ -47,13 +48,31 @@ class TestRegistryProperties(TestCase, RegistryProperties):
# THEN the application should be none # THEN the application should be none
self.assertEqual(self.application, None, 'The application value should be None') self.assertEqual(self.application, None, 'The application value should be None')
def application_test(self): def test_application(self):
""" """
Test property if registry value assigned Test property if registry value assigned
""" """
# GIVEN an Empty Registry # GIVEN an Empty Registry
application = MagicMock() application = MagicMock()
# WHEN the application is registered # WHEN the application is registered
Registry().register('application', application) Registry().register('application', application)
# THEN the application should be none # THEN the application should be none
self.assertEqual(self.application, application, 'The application value should match') self.assertEqual(self.application, application, 'The application value should match')
@patch('openlp.core.common.registryproperties.is_win')
def test_application_on_windows(self, mocked_is_win):
"""
Test property if registry value assigned on Windows
"""
# GIVEN an Empty Registry and we're on Windows
application = MagicMock()
mocked_is_win.return_value = True
# WHEN the application is registered
Registry().register('application', application)
# THEN the application should be none
self.assertEqual(self.application, application, 'The application value should match')

View File

@ -47,7 +47,7 @@ class TestSettings(TestCase, TestMixin):
""" """
self.destroy_settings() self.destroy_settings()
def recent_files_conv_test(self): def test_recent_files_conv(self):
""" """
Test that recent_files_conv, converts various possible types of values correctly. Test that recent_files_conv, converts various possible types of values correctly.
""" """
@ -66,7 +66,7 @@ class TestSettings(TestCase, TestMixin):
# THEN: The actual result should be the same as the expected result # THEN: The actual result should be the same as the expected result
self.assertEqual(actual_result, expected_result) self.assertEqual(actual_result, expected_result)
def settings_basic_test(self): def test_settings_basic(self):
""" """
Test the Settings creation and its default usage Test the Settings creation and its default usage
""" """
@ -84,7 +84,7 @@ class TestSettings(TestCase, TestMixin):
# THEN the new value is returned when re-read # THEN the new value is returned when re-read
self.assertTrue(Settings().value('core/has run wizard'), 'The saved value should have been returned') self.assertTrue(Settings().value('core/has run wizard'), 'The saved value should have been returned')
def settings_override_test(self): def test_settings_override(self):
""" """
Test the Settings creation and its override usage Test the Settings creation and its override usage
""" """
@ -106,7 +106,7 @@ class TestSettings(TestCase, TestMixin):
# THEN the new value is returned when re-read # THEN the new value is returned when re-read
self.assertEqual('very short', Settings().value('test/extend'), 'The saved value should be returned') self.assertEqual('very short', Settings().value('test/extend'), 'The saved value should be returned')
def settings_override_with_group_test(self): def test_settings_override_with_group(self):
""" """
Test the Settings creation and its override usage - with groups Test the Settings creation and its override usage - with groups
""" """
@ -130,7 +130,7 @@ class TestSettings(TestCase, TestMixin):
# THEN the new value is returned when re-read # THEN the new value is returned when re-read
self.assertEqual('very short', Settings().value('test/extend'), 'The saved value should be returned') self.assertEqual('very short', Settings().value('test/extend'), 'The saved value should be returned')
def settings_nonexisting_test(self): def test_settings_nonexisting(self):
""" """
Test the Settings on query for non-existing value Test the Settings on query for non-existing value
""" """
@ -142,7 +142,7 @@ class TestSettings(TestCase, TestMixin):
# THEN: An exception with the non-existing key should be thrown # THEN: An exception with the non-existing key should be thrown
self.assertEqual(str(cm.exception), "'core/does not exists'", 'We should get an exception') self.assertEqual(str(cm.exception), "'core/does not exists'", 'We should get an exception')
def extend_default_settings_test(self): def test_extend_default_settings(self):
""" """
Test that the extend_default_settings method extends the default settings Test that the extend_default_settings method extends the default settings
""" """

Some files were not shown because too many files have changed in this diff Show More