From 9fdcd30c65feb0dc347bd91cd5275f4ac2fece64 Mon Sep 17 00:00:00 2001 From: Ken Roberts Date: Fri, 27 May 2016 01:13:14 -0700 Subject: [PATCH 1/5] Convert strings from python2 to python3 in plugins part 3 --- openlp/plugins/remotes/lib/httprouter.py | 36 ++++---- openlp/plugins/remotes/lib/httpserver.py | 8 +- openlp/plugins/remotes/lib/remotetab.py | 28 +++---- .../songs/forms/duplicatesongremovalform.py | 5 +- openlp/plugins/songs/forms/editsongform.py | 62 +++++++------- openlp/plugins/songs/forms/editverseform.py | 10 +-- openlp/plugins/songs/forms/mediafilesform.py | 2 +- openlp/plugins/songs/forms/songexportform.py | 7 +- openlp/plugins/songs/forms/songimportform.py | 6 +- .../songs/forms/songmaintenanceform.py | 28 ++++--- .../plugins/songs/forms/songselectdialog.py | 3 +- openlp/plugins/songs/forms/songselectform.py | 6 +- openlp/plugins/songs/lib/__init__.py | 4 +- openlp/plugins/songs/lib/db.py | 8 +- openlp/plugins/songs/lib/importer.py | 71 +++++++++------- .../plugins/songs/lib/importers/cclifile.py | 14 ++-- .../plugins/songs/lib/importers/dreambeam.py | 10 ++- .../plugins/songs/lib/importers/easyslides.py | 20 +++-- .../songs/lib/importers/easyworship.py | 27 +++--- .../songs/lib/importers/foilpresenter.py | 3 +- openlp/plugins/songs/lib/importers/lyrix.py | 4 +- .../plugins/songs/lib/importers/mediashout.py | 17 ++-- openlp/plugins/songs/lib/importers/openlp.py | 4 +- .../plugins/songs/lib/importers/openlyrics.py | 8 +- .../plugins/songs/lib/importers/openoffice.py | 2 +- .../plugins/songs/lib/importers/opensong.py | 10 +-- openlp/plugins/songs/lib/importers/opspro.py | 23 ++++-- .../songs/lib/importers/powerpraise.py | 3 +- .../plugins/songs/lib/importers/powersong.py | 23 +++--- .../lib/importers/presentationmanager.py | 3 +- .../songs/lib/importers/propresenter.py | 7 +- .../plugins/songs/lib/importers/songimport.py | 9 +- .../songs/lib/importers/songshowplus.py | 14 ++-- .../plugins/songs/lib/importers/sundayplus.py | 2 +- .../plugins/songs/lib/importers/videopsalm.py | 6 +- .../songs/lib/importers/wordsofworship.py | 8 +- .../songs/lib/importers/worshipassistant.py | 17 ++-- .../songs/lib/importers/worshipcenterpro.py | 6 +- .../plugins/songs/lib/importers/zionworx.py | 13 +-- openlp/plugins/songs/lib/mediaitem.py | 82 ++++++++++--------- openlp/plugins/songs/lib/openlyricsexport.py | 10 ++- openlp/plugins/songs/lib/openlyricsxml.py | 27 +++--- openlp/plugins/songs/lib/songselect.py | 12 +-- openlp/plugins/songs/lib/songstab.py | 4 +- .../openlp_core_lib/test_projectordb.py | 19 +++++ 45 files changed, 400 insertions(+), 291 deletions(-) diff --git a/openlp/plugins/remotes/lib/httprouter.py b/openlp/plugins/remotes/lib/httprouter.py index 5d14a5637..174dc570a 100644 --- a/openlp/plugins/remotes/lib/httprouter.py +++ b/openlp/plugins/remotes/lib/httprouter.py @@ -141,7 +141,8 @@ class HttpRouter(RegistryProperties): """ 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: self.auth = base64.b64encode(auth_code) except TypeError: @@ -189,7 +190,7 @@ class HttpRouter(RegistryProperties): if self.headers['Authorization'] is None: self.do_authorisation() 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.call_function(function, *args) else: @@ -231,7 +232,7 @@ class HttpRouter(RegistryProperties): for route, func in self.routes: match = re.match(route, url_path_split.path) if match: - log.debug('Route "%s" matched "%s"', route, url_path) + log.debug('Route "{route}" matched "{path}"'.format(route=route, path=url_path)) args = [] for param in match.groups(): args.append(param) @@ -319,9 +320,9 @@ class HttpRouter(RegistryProperties): stage = translate('RemotePlugin.Mobile', 'Stage View') live = translate('RemotePlugin.Mobile', 'Live View') self.template_vars = { - 'app_title': "%s %s" % (UiStrings().OLPV2x, remote), - 'stage_title': "%s %s" % (UiStrings().OLPV2x, stage), - 'live_title': "%s %s" % (UiStrings().OLPV2x, live), + 'app_title': "{main} {remote}".format(main=UiStrings().OLPV2x, remote=remote), + 'stage_title': "{main} {stage}".format(main=UiStrings().OLPV2x, stage=stage), + 'live_title': "{main} {live}".format(main=UiStrings().OLPV2x, live=live), 'service_manager': translate('RemotePlugin.Mobile', 'Service Manager'), 'slide_controller': translate('RemotePlugin.Mobile', 'Slide Controller'), 'alerts': translate('RemotePlugin.Mobile', 'Alerts'), @@ -354,7 +355,7 @@ class HttpRouter(RegistryProperties): :param file_name: file name with path :return: """ - log.debug('serve file request %s' % file_name) + log.debug('serve file request {name}'.format(name=file_name)) parts = file_name.split('/') if len(parts) == 1: 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) else: file_handle = open(path, 'rb') - log.debug('Opened %s' % path) + log.debug('Opened {path}'.format(path=path)) content = file_handle.read() except IOError: - log.exception('Failed to open %s' % path) + log.exception('Failed to open {path}'.format(path=path)) return self.do_not_found() finally: 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. 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: file_name = 'index.html' if '.' not in file_name: @@ -433,7 +434,9 @@ class HttpRouter(RegistryProperties): :param dimensions: image size :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'] # -1 means use the default dimension in ImageManager width = -1 @@ -539,7 +542,7 @@ class HttpRouter(RegistryProperties): :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 data = [] 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 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: try: data = json.loads(self.request_data)['request']['id'] @@ -623,7 +627,7 @@ class HttpRouter(RegistryProperties): :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: try: data = int(json.loads(self.request_data)['request']['id']) @@ -680,7 +684,7 @@ class HttpRouter(RegistryProperties): return self.do_http_error() plugin = self.plugin_manager.get_plugin_by_name(plugin_name) 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() def add_to_service(self, plugin_name): @@ -696,5 +700,5 @@ class HttpRouter(RegistryProperties): plugin = self.plugin_manager.get_plugin_by_name(plugin_name) if plugin.status == PluginStatus.Active and plugin.media_item: 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() diff --git a/openlp/plugins/remotes/lib/httpserver.py b/openlp/plugins/remotes/lib/httpserver.py index 88d1097dc..7cc90fc07 100644 --- a/openlp/plugins/remotes/lib/httpserver.py +++ b/openlp/plugins/remotes/lib/httpserver.py @@ -136,11 +136,13 @@ class OpenLPServer(RegistryProperties): while loop < 4: try: 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 except OSError: - log.debug("failed to start http server thread state %d %s" % - (loop, self.http_thread.isRunning())) + log.debug("failed to start http server thread state " + "{loop:d} {running}".format(loop=loop, running=self.http_thread.isRunning())) loop += 1 time.sleep(0.1) except: diff --git a/openlp/plugins/remotes/lib/remotetab.py b/openlp/plugins/remotes/lib/remotetab.py index af64e401f..45a1533af 100644 --- a/openlp/plugins/remotes/lib/remotetab.py +++ b/openlp/plugins/remotes/lib/remotetab.py @@ -192,14 +192,14 @@ class RemoteTab(SettingsTab): 'Show thumbnails of non-text slides in remote and stage view.')) self.android_app_group_box.setTitle(translate('RemotePlugin.RemoteTab', 'Android App')) self.android_qr_description_label.setText( - translate('RemotePlugin.RemoteTab', 'Scan the QR code or click download to install the ' - 'Android app from Google Play.') % - 'https://play.google.com/store/apps/details?id=org.openlp.android2') + translate('RemotePlugin.RemoteTab', + 'Scan the QR code or click download to install the Android app from Google ' + '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_qr_description_label.setText( - translate('RemotePlugin.RemoteTab', 'Scan the QR code or click download to install the ' - 'iOS app from the App Store.') % - 'https://itunes.apple.com/app/id1096218725') + translate('RemotePlugin.RemoteTab', + 'Scan the QR code or click download to install the iOS app from the App ' + 'Store.').format(qr='https://itunes.apple.com/app/id1096218725')) self.https_settings_group_box.setTitle(translate('RemotePlugin.RemoteTab', 'HTTPS Server')) self.https_error_label.setText( 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 """ ip_address = self.get_ip_address(self.address_edit.text()) - http_url = 'http://%s:%s/' % (ip_address, self.port_spin_box.value()) - https_url = 'https://%s:%s/' % (ip_address, self.https_port_spin_box.value()) - self.remote_url.setText('%s' % (http_url, http_url)) - self.remote_https_url.setText('%s' % (https_url, https_url)) + http_url = 'http://{url}:{text}/'.format(url=ip_address, text=self.port_spin_box.value()) + https_url = 'https://{url}:{text}/'.format(url=ip_address, text=self.https_port_spin_box.value()) + self.remote_url.setText('{url}'.format(url=http_url)) + self.remote_https_url.setText('{url}'.format(url=https_url)) http_url_temp = http_url + 'stage' https_url_temp = https_url + 'stage' - self.stage_url.setText('%s' % (http_url_temp, http_url_temp)) - self.stage_https_url.setText('%s' % (https_url_temp, https_url_temp)) + self.stage_url.setText('{url}'.format(url=http_url_temp)) + self.stage_https_url.setText('{url}'.format(url=https_url_temp)) http_url_temp = http_url + 'main' https_url_temp = https_url + 'main' - self.live_url.setText('%s' % (http_url_temp, http_url_temp)) - self.live_https_url.setText('%s' % (https_url_temp, https_url_temp)) + self.live_url.setText('{url}'.format(url=http_url_temp)) + self.live_https_url.setText('{url}'.format(url=https_url_temp)) def get_ip_address(self, ip_address): """ diff --git a/openlp/plugins/songs/forms/duplicatesongremovalform.py b/openlp/plugins/songs/forms/duplicatesongremovalform.py index 26de9507f..ee048855f 100644 --- a/openlp/plugins/songs/forms/duplicatesongremovalform.py +++ b/openlp/plugins/songs/forms/duplicatesongremovalform.py @@ -130,6 +130,7 @@ class DuplicateSongRemovalForm(OpenLPWizard, RegistryProperties): Song wizard localisation. """ self.setWindowTitle(translate('Wizard', 'Wizard')) + # TODO: Check format() using template strings self.title_label.setText(WizardStrings.HeaderStyle % translate('OpenLP.Ui', 'Welcome to the Duplicate Song Removal Wizard')) self.information_label.setText( @@ -148,8 +149,8 @@ class DuplicateSongRemovalForm(OpenLPWizard, RegistryProperties): Set the wizard review page header text. """ self.review_page.setTitle( - translate('Wizard', 'Review duplicate songs (%s/%s)') % - (self.review_current_count, self.review_total_count)) + translate('Wizard', 'Review duplicate songs ({current}/{total})').format(current=self.review_current_count, + total=self.review_total_count)) def custom_page_changed(self, page_id): """ diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index b33788a4c..200e1436f 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -50,7 +50,7 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties): """ 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): """ @@ -185,20 +185,23 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties): verse = verse.data(QtCore.Qt.UserRole) if verse not in verse_names: 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): if item not in verses: invalid_verses.append(order_names[count]) if invalid_verses: valid = create_separated_list(verse_names) if len(invalid_verses) > 1: - msg = translate('SongsPlugin.EditSongForm', 'There are no verses corresponding to "%(invalid)s". ' - 'Valid entries are %(valid)s.\nPlease enter the verses separated by spaces.') % \ - {'invalid': ', '.join(invalid_verses), 'valid': valid} + msg = translate('SongsPlugin.EditSongForm', + 'There are no verses corresponding to "{invalid}". Valid entries are {valid}.\n' + 'Please enter the verses separated by spaces.' + ).format(invalid=', '.join(invalid_verses), valid=valid) else: - msg = translate('SongsPlugin.EditSongForm', 'There is no verse corresponding to "%(invalid)s".' - 'Valid entries are %(valid)s.\nPlease enter the verses separated by spaces.') % \ - {'invalid': invalid_verses[0], 'valid': valid} + msg = translate('SongsPlugin.EditSongForm', + 'There is no verse corresponding to "{invalid}". Valid entries are {valid}.\n' + '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'), message=msg) return len(invalid_verses) == 0 @@ -242,23 +245,24 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties): field = item.data(QtCore.Qt.UserRole) verse_tags.append(field) 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: critical_error_message_box( message=translate('SongsPlugin.EditSongForm', - 'There are misplaced formatting tags in the following verses:\n\n%s\n\n' - 'Please correct these tags before continuing.' % ', '.join(misplaced_tags))) + 'There are misplaced formatting tags in the following verses:\n\n{tag}\n\n' + 'Please correct these tags before continuing.').format(tag=', '.join(misplaced_tags))) return False for tag in verse_tags: if verse_tags.count(tag) > 26: # lp#1310523: OpenLyrics allows only a-z variants of one verse: # http://openlyrics.info/dataformat.html#verse-name critical_error_message_box(message=translate( - 'SongsPlugin.EditSongForm', 'You have %(count)s verses named %(name)s %(number)s. ' - 'You can have at most 26 verses with the same name' % - {'count': verse_tags.count(tag), - 'name': VerseType.translated_name(tag[0]), - 'number': tag[1:]})) + 'SongsPlugin.EditSongForm', + 'You have {count} verses named {name} {number}. You can have at most ' + '26 verses with the same name').format(count=verse_tags.count(tag), + name=VerseType.translated_name(tag[0]), + number=tag[1:])) return False return True @@ -313,7 +317,7 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties): self.song.verse_order = re.sub('([' + verse.upper() + verse.lower() + '])(\W|$)', r'\g<1>1\2', self.song.verse_order) 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 def keyPressEvent(self, event): @@ -492,7 +496,7 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties): verse[0]['type'] = VerseType.tags[index] if verse[0]['label'] == '': 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.setData(QtCore.Qt.UserRole, verse_def) 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): self.verse_list_widget.setRowCount(self.verse_list_widget.rowCount() + 1) 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) self.verse_list_widget.setItem(count, 0, item) if self.song.verse_order: @@ -514,7 +518,7 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties): if verse_index is None: verse_index = VerseType.from_tag(verse_def[0]) 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)) else: self.verse_order_edit.setText('') @@ -554,7 +558,7 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties): item = self.verse_list_widget.item(row, 0) verse_def = item.data(QtCore.Qt.UserRole) 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) self.verse_list_widget.setVerticalHeaderLabels(row_label) self.verse_list_widget.resizeRowsToContents() @@ -742,7 +746,7 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties): self.verse_form.set_verse('', True) if self.verse_form.exec(): 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.setData(QtCore.Qt.UserRole, verse_def) 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) if self.verse_form.exec(): 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.setText(after_text) # 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) verse_tag = VerseType.translated_name(field[0]) 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 += '\n' self.verse_form.set_verse(verse_list) @@ -828,7 +832,7 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties): verse_num = match.group(1) else: verse_num = '1' - verse_def = '%s%s' % (verse_tag, verse_num) + verse_def = '{tag}{number}'.format(tag=verse_tag, number=verse_num) else: if parts.endswith('\n'): parts = parts.rstrip('\n') @@ -919,7 +923,7 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties): """ 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)'), '', filters) for filename in file_names: @@ -1027,7 +1031,7 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties): for item in order_text.split(): verse_tag = VerseType.tags[VerseType.from_translated_tag(item[0])] 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.ccli_number = self.ccli_number_edit.text() theme_name = self.theme_combo_box.currentText() @@ -1082,12 +1086,12 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties): try: os.remove(audio) except: - log.exception('Could not remove file: %s', audio) + log.exception('Could not remove file: {audio}'.format(audio=audio)) if not files: try: os.rmdir(save_path) 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) self.manager.save_object(self.song) self.media_item.auto_select_id = self.song.id diff --git a/openlp/plugins/songs/forms/editverseform.py b/openlp/plugins/songs/forms/editverseform.py index 9ff9ef54b..454fdb71e 100644 --- a/openlp/plugins/songs/forms/editverseform.py +++ b/openlp/plugins/songs/forms/editverseform.py @@ -59,7 +59,7 @@ class EditVerseForm(QtWidgets.QDialog, Ui_EditVerseDialog): if self.verse_text_edit.textCursor().columnNumber() != 0: self.verse_text_edit.insertPlainText('\n') 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() def on_split_button_clicked(self): @@ -107,7 +107,7 @@ class EditVerseForm(QtWidgets.QDialog, Ui_EditVerseDialog): self.verse_type_combo_box.currentIndex()] if not text: return - position = text.rfind('---[%s' % verse_name, 0, position) + position = text.rfind('---[{verse}'.format(verse=verse_name), 0, position) if position == -1: self.verse_number_box.setValue(1) return @@ -124,7 +124,7 @@ class EditVerseForm(QtWidgets.QDialog, Ui_EditVerseDialog): verse_num = 1 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 @@ -142,7 +142,7 @@ class EditVerseForm(QtWidgets.QDialog, Ui_EditVerseDialog): self.insert_button.setVisible(False) else: 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_number_box.setValue(1) self.insert_button.setVisible(True) @@ -167,5 +167,5 @@ class EditVerseForm(QtWidgets.QDialog, Ui_EditVerseDialog): """ text = self.verse_text_edit.toPlainText() 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 diff --git a/openlp/plugins/songs/forms/mediafilesform.py b/openlp/plugins/songs/forms/mediafilesform.py index 8920bc0ef..9593976fd 100644 --- a/openlp/plugins/songs/forms/mediafilesform.py +++ b/openlp/plugins/songs/forms/mediafilesform.py @@ -34,7 +34,7 @@ class MediaFilesForm(QtWidgets.QDialog, Ui_MediaFilesDialog): """ 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): super(MediaFilesForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint) diff --git a/openlp/plugins/songs/forms/songexportform.py b/openlp/plugins/songs/forms/songexportform.py index e8a559c44..27f317963 100644 --- a/openlp/plugins/songs/forms/songexportform.py +++ b/openlp/plugins/songs/forms/songexportform.py @@ -143,6 +143,7 @@ class SongExportForm(OpenLPWizard): Song wizard localisation. """ self.setWindowTitle(translate('SongsPlugin.ExportWizardForm', 'Song Export Wizard')) + # TODO: Verify format() with template variables self.title_label.setText(WizardStrings.HeaderStyle % translate('OpenLP.Ui', 'Welcome to the Song Export Wizard')) 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.setSubTitle(translate('SongsPlugin.ExportWizardForm', '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.check_button.setText(translate('SongsPlugin.ExportWizardForm', 'Check All')) self.export_song_page.setTitle(translate('SongsPlugin.ExportWizardForm', 'Select Directory')) @@ -223,7 +224,7 @@ class SongExportForm(OpenLPWizard): if song.temporary: continue 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.setData(QtCore.Qt.UserRole, song) 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.')) except OSError as ose: 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): """ diff --git a/openlp/plugins/songs/forms/songimportform.py b/openlp/plugins/songs/forms/songimportform.py index 7a6af3981..2539973da 100644 --- a/openlp/plugins/songs/forms/songimportform.py +++ b/openlp/plugins/songs/forms/songimportform.py @@ -132,6 +132,7 @@ class SongImportForm(OpenLPWizard, RegistryProperties): Song wizard localisation. """ self.setWindowTitle(translate('SongsPlugin.ImportWizardForm', 'Song Import Wizard')) + # TODO: Verify format() with template variables self.title_label.setText(WizardStrings.HeaderStyle % translate('OpenLP.Ui', 'Welcome to the Song Import Wizard')) self.information_label.setText( @@ -236,7 +237,7 @@ class SongImportForm(OpenLPWizard, RegistryProperties): """ if filters: filters += ';;' - filters += '%s (*)' % UiStrings().AllFiles + filters += '{text} (*)'.format(text=UiStrings().AllFiles) file_names = FileDialog.getOpenFileNames( self, title, 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') file_path_edit = self.format_widgets[this_format]['file_path_edit'] if select_mode == SongFormatSelect.SingleFile: + # TODO: Verify format() with template variables self.get_file_name( WizardStrings.OpenTypeFile % format_name, file_path_edit, 'last directory import', ext_filter) elif select_mode == SongFormatSelect.SingleFolder: + # TODO: Verify format() with template variables self.get_folder(WizardStrings.OpenTypeFolder % format_name, file_path_edit, 'last directory import') def on_add_button_clicked(self): @@ -283,6 +286,7 @@ class SongImportForm(OpenLPWizard, RegistryProperties): this_format = self.current_format select_mode, format_name, ext_filter, custom_title = \ 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 if select_mode == SongFormatSelect.MultipleFiles: self.get_files(title, self.format_widgets[this_format]['file_list_widget'], ext_filter) diff --git a/openlp/plugins/songs/forms/songmaintenanceform.py b/openlp/plugins/songs/forms/songmaintenanceform.py index 74462e6d0..a9b871c7e 100644 --- a/openlp/plugins/songs/forms/songmaintenanceform.py +++ b/openlp/plugins/songs/forms/songmaintenanceform.py @@ -164,7 +164,8 @@ class SongMaintenanceForm(QtWidgets.QDialog, Ui_SongMaintenanceDialog, RegistryP books = self.manager.get_all_objects(Book) books.sort(key=get_book_key) 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) self.song_books_list_widget.addItem(book_name) @@ -310,11 +311,12 @@ class SongMaintenanceForm(QtWidgets.QDialog, Ui_SongMaintenanceDialog, RegistryP else: critical_error_message_box( message=translate('SongsPlugin.SongMaintenanceForm', 'Could not save your changes.')) - elif critical_error_message_box(message=translate( - 'SongsPlugin.SongMaintenanceForm', 'The author %s already exists. Would you like to make songs with ' - 'author %s use the existing author %s?') % - (author.display_name, temp_display_name, author.display_name), parent=self, question=True) == \ - QtWidgets.QMessageBox.Yes: + elif critical_error_message_box( + message=translate( + 'SongsPlugin.SongMaintenanceForm', + 'The author {original} already exists. Would you like to make songs with author {new} use the ' + '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) else: # 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( message=translate('SongsPlugin.SongMaintenanceForm', 'Could not save your changes.')) elif critical_error_message_box( - message=translate('SongsPlugin.SongMaintenanceForm', - 'The topic %s already exists. Would you like to make songs with topic %s use the ' - 'existing topic %s?') % (topic.name, temp_name, topic.name), + message=translate('SongsPlugin.SongMaintenanceForm', + 'The topic {original} already exists. Would you like to make songs with ' + 'topic {new} use the existing topic {original}?').format(original=topic.name, + new=temp_name), parent=self, question=True) == QtWidgets.QMessageBox.Yes: self._merge_objects(topic, self.merge_topics, self.reset_topics) else: @@ -384,9 +387,10 @@ class SongMaintenanceForm(QtWidgets.QDialog, Ui_SongMaintenanceDialog, RegistryP critical_error_message_box( message=translate('SongsPlugin.SongMaintenanceForm', 'Could not save your changes.')) elif critical_error_message_box( - message=translate('SongsPlugin.SongMaintenanceForm', - 'The book %s already exists. Would you like to make ' - 'songs with book %s use the existing book %s?') % (book.name, temp_name, book.name), + message=translate('SongsPlugin.SongMaintenanceForm', + 'The book {original} already exists. Would you like to make songs with ' + 'book {new} use the existing book {original}?').format(original=book.name, + new=temp_name), parent=self, question=True) == QtWidgets.QMessageBox.Yes: self._merge_objects(book, self.merge_song_books, self.reset_song_books) else: diff --git a/openlp/plugins/songs/forms/songselectdialog.py b/openlp/plugins/songs/forms/songselectdialog.py index 833ee39ec..9ad07d50a 100644 --- a/openlp/plugins/songs/forms/songselectdialog.py +++ b/openlp/plugins/songs/forms/songselectdialog.py @@ -242,7 +242,8 @@ class Ui_SongSelectDialog(object): self.search_label.setText(translate('SongsPlugin.SongSelectForm', 'Search Text:')) self.search_button.setText(translate('SongsPlugin.SongSelectForm', 'Search')) 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.view_button.setText(translate('SongsPlugin.SongSelectForm', 'View')) self.title_label.setText(translate('SongsPlugin.SongSelectForm', 'Title:')) diff --git a/openlp/plugins/songs/forms/songselectform.py b/openlp/plugins/songs/forms/songselectform.py index 84ced5383..57bdefdeb 100755 --- a/openlp/plugins/songs/forms/songselectform.py +++ b/openlp/plugins/songs/forms/songselectform.py @@ -305,7 +305,8 @@ class SongSelectForm(QtWidgets.QDialog, Ui_SongSelectDialog): self.search_progress_bar.setValue(0) self.set_progress_visible(True) 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.song_count = 0 search_history = self.search_combobox.getItems() @@ -343,7 +344,8 @@ class SongSelectForm(QtWidgets.QDialog, Ui_SongSelectDialog): :param song: """ 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']) + ')' song_item = QtWidgets.QListWidgetItem(item_title, self.search_results_widget) song_item.setData(QtCore.Qt.UserRole, song) diff --git a/openlp/plugins/songs/lib/__init__.py b/openlp/plugins/songs/lib/__init__.py index 0e03bfe7c..4d8586204 100644 --- a/openlp/plugins/songs/lib/__init__.py +++ b/openlp/plugins/songs/lib/__init__.py @@ -534,11 +534,11 @@ def delete_song(song_id, song_plugin): try: os.remove(media_file.file_name) 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: save_path = os.path.join(AppLocation.get_section_data_path(song_plugin.name), 'audio', str(song_id)) if os.path.exists(save_path): os.rmdir(save_path) 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) diff --git a/openlp/plugins/songs/lib/db.py b/openlp/plugins/songs/lib/db.py index 3026915e4..24b9fe880 100644 --- a/openlp/plugins/songs/lib/db.py +++ b/openlp/plugins/songs/lib/db.py @@ -39,7 +39,7 @@ class Author(BaseModel): """ def get_display_name(self, author_type=None): 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 @@ -105,7 +105,9 @@ class Book(BaseModel): Book model """ def __repr__(self): - return '' % (str(self.id), self.name, self.publisher) + return ''.format(myid=self.id, + name=self.name, + publisher=self.publisher) class MediaFile(BaseModel): @@ -187,7 +189,7 @@ class SongBookEntry(BaseModel): @staticmethod def get_display_name(songbook_name, entry): if entry: - return "%s #%s" % (songbook_name, entry) + return "{name} #{entry}".format(name=songbook_name, entry=entry) return songbook_name diff --git a/openlp/plugins/songs/lib/importer.py b/openlp/plugins/songs/lib/importer.py index 7b9101306..8f7654161 100644 --- a/openlp/plugins/songs/lib/importer.py +++ b/openlp/plugins/songs/lib/importer.py @@ -56,13 +56,13 @@ try: from .importers.songsoffellowship import SongsOfFellowshipImport HAS_SOF = True except ImportError: - log.exception('Error importing %s', 'SongsOfFellowshipImport') + log.exception('Error importing {text}'.format(text='SongsOfFellowshipImport')) HAS_SOF = False try: from .importers.openoffice import OpenOfficeImport HAS_OOO = True except ImportError: - log.exception('Error importing %s', 'OooImport') + log.exception('Error importing {text}'.format(text='OooImport')) HAS_OOO = False HAS_MEDIASHOUT = False if is_win(): @@ -70,21 +70,21 @@ if is_win(): from .importers.mediashout import MediaShoutImport HAS_MEDIASHOUT = True except ImportError: - log.exception('Error importing %s', 'MediaShoutImport') + log.exception('Error importing {text}'.format(text='MediaShoutImport')) HAS_WORSHIPCENTERPRO = False if is_win(): try: from .importers.worshipcenterpro import WorshipCenterProImport HAS_WORSHIPCENTERPRO = True except ImportError: - log.exception('Error importing %s', 'WorshipCenterProImport') + log.exception('Error importing {text}'.format(text='WorshipCenterProImport')) HAS_OPSPRO = False if is_win(): try: from .importers.opspro import OPSProImport HAS_OPSPRO = True except ImportError: - log.exception('Error importing %s', 'OPSProImport') + log.exception('Error importing {text}'.format(text='OPSProImport')) class SongFormatSelect(object): @@ -198,7 +198,7 @@ class SongFormat(object): 'class': OpenLyricsImport, 'name': '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') }, OpenLP2: { @@ -206,7 +206,7 @@ class SongFormat(object): 'name': UiStrings().OLPV2, 'prefix': 'openLP2', 'selectMode': SongFormatSelect.SingleFile, - 'filter': '%s (*.sqlite)' % (translate('SongsPlugin.ImportWizardForm', 'OpenLP 2 Databases')) + 'filter': '{text} (*.sqlite)'.format(text=translate('SongsPlugin.ImportWizardForm', 'OpenLP 2 Databases')) }, Generic: { 'name': translate('SongsPlugin.ImportWizardForm', 'Generic Document/Presentation'), @@ -221,46 +221,50 @@ class SongFormat(object): 'class': CCLIFileImport, 'name': 'CCLI/SongSelect', '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: { 'class': DreamBeamImport, 'name': 'DreamBeam', 'prefix': 'dreamBeam', - 'filter': '%s (*.xml)' % translate('SongsPlugin.ImportWizardForm', 'DreamBeam Song Files') + 'filter': '{text} (*.xml)'.format(text=translate('SongsPlugin.ImportWizardForm', 'DreamBeam Song Files')) }, EasySlides: { 'class': EasySlidesImport, 'name': 'EasySlides', 'prefix': 'easySlides', 'selectMode': SongFormatSelect.SingleFile, - 'filter': '%s (*.xml)' % translate('SongsPlugin.ImportWizardForm', 'EasySlides XML File') + 'filter': '{text} (*.xml)'.format(text=translate('SongsPlugin.ImportWizardForm', 'EasySlides XML File')) }, EasyWorshipDB: { 'class': EasyWorshipSongImport, 'name': 'EasyWorship Song Database', 'prefix': 'ew', 'selectMode': SongFormatSelect.SingleFile, - 'filter': '%s (*.db)' % translate('SongsPlugin.ImportWizardForm', 'EasyWorship Song Database') + 'filter': '{text} (*.db)'.format(text=translate('SongsPlugin.ImportWizardForm', + 'EasyWorship Song Database')) }, EasyWorshipService: { 'class': EasyWorshipSongImport, 'name': 'EasyWorship Service File', 'prefix': 'ew', 'selectMode': SongFormatSelect.SingleFile, - 'filter': '%s (*.ews)' % translate('SongsPlugin.ImportWizardForm', 'EasyWorship Service File') + 'filter': '{text} (*.ews)'.format(text=translate('SongsPlugin.ImportWizardForm', + 'EasyWorship Service File')) }, FoilPresenter: { 'class': FoilPresenterImport, 'name': 'Foilpresenter', 'prefix': 'foilPresenter', - 'filter': '%s (*.foil)' % translate('SongsPlugin.ImportWizardForm', 'Foilpresenter Song Files') + 'filter': '{text} (*.foil)'.format(text=translate('SongsPlugin.ImportWizardForm', + 'Foilpresenter Song Files')) }, Lyrix: { 'class': LyrixImport, 'name': '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)') }, MediaShout: { @@ -268,7 +272,7 @@ class SongFormat(object): 'prefix': 'mediaShout', 'canDisable': True, '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', 'The MediaShout importer is only supported on Windows. It has ' 'been disabled due to a missing Python module. If you want to ' @@ -285,7 +289,7 @@ class SongFormat(object): 'prefix': 'OPSPro', 'canDisable': True, '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', '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 ' @@ -295,7 +299,7 @@ class SongFormat(object): 'class': PowerPraiseImport, 'name': 'PowerPraise', 'prefix': 'powerPraise', - 'filter': '%s (*.ppl)' % translate('SongsPlugin.ImportWizardForm', 'PowerPraise Song Files') + 'filter': '{text} (*.ppl)'.format(text=translate('SongsPlugin.ImportWizardForm', 'PowerPraise Song Files')) }, PowerSong: { 'class': PowerSongImport, @@ -309,26 +313,29 @@ class SongFormat(object): 'class': PresentationManagerImport, 'name': 'PresentationManager', 'prefix': 'presentationManager', - 'filter': '%s (*.sng)' % translate('SongsPlugin.ImportWizardForm', 'PresentationManager Song Files') + 'filter': '{text} (*.sng)'.format(text=translate('SongsPlugin.ImportWizardForm', + 'PresentationManager Song Files')) }, ProPresenter: { 'class': ProPresenterImport, 'name': 'ProPresenter 4, 5 and 6', '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: { 'class': SongBeamerImport, 'name': 'SongBeamer', 'prefix': 'songBeamer', - 'filter': '%s (*.sng)' % translate('SongsPlugin.ImportWizardForm', 'SongBeamer Files') + 'filter': '{text} (*.sng)'.format(text=translate('SongsPlugin.ImportWizardForm', + 'SongBeamer Files')) }, SongPro: { 'class': SongProImport, 'name': 'SongPro', 'prefix': 'songPro', '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)'), 'descriptionText': translate('SongsPlugin.ImportWizardForm', 'In SongPro, export your songs using the File -> Export menu') @@ -337,13 +344,15 @@ class SongFormat(object): 'class': SongShowPlusImport, 'name': 'SongShow Plus', '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: { 'name': 'Songs of Fellowship', 'prefix': 'songsOfFellowship', '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', 'The Songs of Fellowship importer has been disabled because ' 'OpenLP cannot access OpenOffice or LibreOffice.') @@ -352,30 +361,33 @@ class SongFormat(object): 'class': SundayPlusImport, 'name': 'SundayPlus', 'prefix': 'sundayPlus', - 'filter': '%s (*.ptf)' % translate('SongsPlugin.ImportWizardForm', 'SundayPlus Song Files') + 'filter': '{text} (*.ptf)'.format(text=translate('SongsPlugin.ImportWizardForm', 'SundayPlus Song Files')) }, VideoPsalm: { 'class': VideoPsalmImport, 'name': 'VideoPsalm', 'prefix': 'videopsalm', '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'), '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: { 'class': WordsOfWorshipImport, 'name': 'Words of Worship', '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: { 'class': WorshipAssistantImport, 'name': 'Worship Assistant 0', 'prefix': 'worshipAssistant', '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)'), 'descriptionText': translate('SongsPlugin.ImportWizardForm', 'In Worship Assistant, export your Database to a CSV file.') @@ -385,7 +397,8 @@ class SongFormat(object): 'prefix': 'worshipCenterPro', 'canDisable': True, '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', '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 ' diff --git a/openlp/plugins/songs/lib/importers/cclifile.py b/openlp/plugins/songs/lib/importers/cclifile.py index 24008cc5d..800509ab2 100644 --- a/openlp/plugins/songs/lib/importers/cclifile.py +++ b/openlp/plugins/songs/lib/importers/cclifile.py @@ -58,7 +58,7 @@ class CCLIFileImport(SongImport): self.import_wizard.progress_bar.setMaximum(len(self.import_source)) for filename in self.import_source: filename = str(filename) - log.debug('Importing CCLI File: %s', filename) + log.debug('Importing CCLI File: {name}'.format(name=filename)) if os.path.isfile(filename): detect_file = open(filename, 'rb') detect_content = detect_file.read(2048) @@ -76,17 +76,17 @@ class CCLIFileImport(SongImport): infile.close() ext = os.path.splitext(filename)[1] 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): self.log_error(filename) 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): self.log_error(filename) else: self.log_error(filename, translate('SongsPlugin.CCLIFileImport', 'The file does not have a valid ' 'extension.')) - log.info('Extension %s is not valid', filename) + log.info('Extension {name} is not valid'.format(name=filename)) if self.stop_import_flag: return @@ -146,7 +146,7 @@ class CCLIFileImport(SongImport): :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_topics = '' for line in text_list: @@ -193,7 +193,7 @@ class CCLIFileImport(SongImport): if check_first_verse_line: if verse_lines[0].startswith('(PRE-CHORUS'): 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] elif verse_lines[0].startswith('(BRIDGE'): verse_type = VerseType.tags[VerseType.Bridge] @@ -248,7 +248,7 @@ class CCLIFileImport(SongImport): # 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 check_first_verse_line = False verse_text = '' diff --git a/openlp/plugins/songs/lib/importers/dreambeam.py b/openlp/plugins/songs/lib/importers/dreambeam.py index db785df7d..9371adc56 100644 --- a/openlp/plugins/songs/lib/importers/dreambeam.py +++ b/openlp/plugins/songs/lib/importers/dreambeam.py @@ -90,7 +90,7 @@ class DreamBeamImport(SongImport): try: parsed_file = etree.parse(open(file, 'r'), parser) 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) continue xml = etree.tostring(parsed_file).decode() @@ -115,15 +115,17 @@ class DreamBeamImport(SongImport): verse_type = lyrics_item.get('Type') verse_number = lyrics_item.get('Number') 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'): self.song_book_name = str(song_xml.Collection.text) if hasattr(song_xml, 'Number'): self.song_number = str(song_xml.Number.text) if hasattr(song_xml, 'Sequence'): for lyrics_sequence_item in (song_xml.Sequence.iterchildren()): - self.verse_order_list.append("%s%s" % (lyrics_sequence_item.get('Type')[:1], - lyrics_sequence_item.get('Number'))) + item = lyrics_sequence_item.get('Type')[:1] + self.verse_order_list.append("{item}{number}".format(item=item), + lyrics_sequence_item.get('Number')) if hasattr(song_xml, 'Notes'): self.comments = str(song_xml.Notes.text) else: diff --git a/openlp/plugins/songs/lib/importers/easyslides.py b/openlp/plugins/songs/lib/importers/easyslides.py index 3137e2baa..907a6c90f 100644 --- a/openlp/plugins/songs/lib/importers/easyslides.py +++ b/openlp/plugins/songs/lib/importers/easyslides.py @@ -45,7 +45,7 @@ class EasySlidesImport(SongImport): super(EasySlidesImport, self).__init__(manager, **kwargs) 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) parsed_file = etree.parse(self.import_source, parser) xml = etree.tostring(parsed_file).decode() @@ -96,10 +96,10 @@ class EasySlidesImport(SongImport): try: setattr(self, self_attribute, str(import_attribute).strip()) except UnicodeDecodeError: - log.exception('UnicodeDecodeError decoding %s' % import_attribute) + log.exception('UnicodeDecodeError decoding {attribute}'.format(attribute=import_attribute)) self._success = False except AttributeError: - log.exception('No attribute %s' % import_attribute) + log.exception('No attribute {attribute}'.format(attribute=import_attribute)) if mandatory: self._success = False @@ -119,7 +119,7 @@ class EasySlidesImport(SongImport): try: self.add_copyright(str(element).strip()) 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 except AttributeError: pass @@ -157,9 +157,10 @@ class EasySlidesImport(SongImport): separators = (separator_lines > 0) # the number of different regions in song - 1 if len(region_lines) > 1: - log.info('EasySlidesImport: the file contained a song named "%s"' - 'with more than two regions, but only two regions are tested, encountered regions were: %s', - self.title, ','.join(list(region_lines.keys()))) + log.info('EasySlidesImport: the file contained a song named "{title}"' + 'with more than two regions, but only two regions are tested, ' + 'encountered regions were: {keys}'.format(title=self.title, + keys=','.join(list(region_lines.keys())))) # if the song has regions regions = (len(region_lines) > 0) # if the regions are inside verses @@ -232,7 +233,7 @@ class EasySlidesImport(SongImport): for [reg, vt, vn, inst] in our_verse_order: if self._list_has(verses, [reg, vt, vn, inst]): # this is false, but needs user input - versetag = '%s%s' % (vt, vn) + versetag = '{tag}{number}'.format(tag=vt, number=vn) versetags.append(versetag) lines = '\n'.join(verses[reg][vt][vn][inst]) self.add_verse(lines, versetag) @@ -259,7 +260,8 @@ class EasySlidesImport(SongImport): if tag in versetags: self.verse_order_list.append(tag) 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: log.exception('Unicode decode error while decoding Sequence') self._success = False diff --git a/openlp/plugins/songs/lib/importers/easyworship.py b/openlp/plugins/songs/lib/importers/easyworship.py index 25bbc37ff..cdffe9292 100644 --- a/openlp/plugins/songs/lib/importers/easyworship.py +++ b/openlp/plugins/songs/lib/importers/easyworship.py @@ -171,15 +171,16 @@ class EasyWorshipSongImport(SongImport): if copyright: self.copyright += ', ' self.copyright += translate('SongsPlugin.EasyWorshipSongImport', - 'Administered by %s') % admin + 'Administered by {admin}').format(admin=admin) # Set the SongImport object members. self.set_song_import_object(authors, inflated_content) if self.stop_import_flag: break if self.entry_error_log: self.log_error(self.import_source, - translate('SongsPlugin.EasyWorshipSongImport', '"%s" could not be imported. %s') - % (self.title, self.entry_error_log)) + translate('SongsPlugin.EasyWorshipSongImport', + '"{title}" could not be imported. {entry}').format(title=self.title, + entry=self.entry_error_log)) self.entry_error_log = '' elif not self.finish(): self.log_error(self.import_source) @@ -306,7 +307,7 @@ class EasyWorshipSongImport(SongImport): if copy: self.copyright += ', ' self.copyright += translate('SongsPlugin.EasyWorshipSongImport', - 'Administered by %s') % admin.decode(self.encoding) + 'Administered by {admin}').format(admin=admin.decode(self.encoding)) if ccli: self.ccli_number = ccli.decode(self.encoding) if authors: @@ -319,15 +320,17 @@ class EasyWorshipSongImport(SongImport): break if self.entry_error_log: self.log_error(self.import_source, - translate('SongsPlugin.EasyWorshipSongImport', '"%s" could not be imported. %s') - % (self.title, self.entry_error_log)) + translate('SongsPlugin.EasyWorshipSongImport', + '"{title}" could not be imported. ' + '{entry}').format(title=self.title, entry=self.entry_error_log)) self.entry_error_log = '' elif not self.finish(): self.log_error(self.import_source) except Exception as e: self.log_error(self.import_source, - translate('SongsPlugin.EasyWorshipSongImport', '"%s" could not be imported. %s') - % (self.title, e)) + translate('SongsPlugin.EasyWorshipSongImport', + '"{title}" could not be imported. {error}').format(title=self.title, + error=e)) db_file.close() self.memo_file.close() @@ -421,7 +424,7 @@ class EasyWorshipSongImport(SongImport): fsl = ['>'] for field_desc in field_descriptions: 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: fsl.append('H') elif field_desc.field_type == FieldType.Int32: @@ -429,13 +432,13 @@ class EasyWorshipSongImport(SongImport): elif field_desc.field_type == FieldType.Logical: fsl.append('B') 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: - fsl.append('%ds' % field_desc.size) + fsl.append('{size:d}s'.format(size=field_desc.size)) elif field_desc.field_type == FieldType.Timestamp: fsl.append('Q') 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.field_descriptions = field_descriptions diff --git a/openlp/plugins/songs/lib/importers/foilpresenter.py b/openlp/plugins/songs/lib/importers/foilpresenter.py index 061f50a9f..011dece24 100644 --- a/openlp/plugins/songs/lib/importers/foilpresenter.py +++ b/openlp/plugins/songs/lib/importers/foilpresenter.py @@ -121,6 +121,7 @@ class FoilPresenterImport(SongImport): for file_path in self.import_source: if self.stop_import_flag: return + # TODO: Verify format() with template strings self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % os.path.basename(file_path)) try: parsed_file = etree.parse(file_path, parser) @@ -128,7 +129,7 @@ class FoilPresenterImport(SongImport): self.foil_presenter.xml_to_song(xml) except etree.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): diff --git a/openlp/plugins/songs/lib/importers/lyrix.py b/openlp/plugins/songs/lib/importers/lyrix.py index 28904354f..387213f57 100644 --- a/openlp/plugins/songs/lib/importers/lyrix.py +++ b/openlp/plugins/songs/lib/importers/lyrix.py @@ -102,8 +102,8 @@ class LyrixImport(SongImport): else: current_verse += '\n' + line except Exception as e: - self.log_error(translate('SongsPlugin.LyrixImport', 'File %s' % file.name), - translate('SongsPlugin.LyrixImport', 'Error: %s') % e) + self.log_error(translate('SongsPlugin.LyrixImport', 'File {name}').format(name=file.name), + translate('SongsPlugin.LyrixImport', 'Error: {error}').format(error=e)) return self.title = song_title self.parse_author(author) diff --git a/openlp/plugins/songs/lib/importers/mediashout.py b/openlp/plugins/songs/lib/importers/mediashout.py index 323d99acd..cb3a19640 100644 --- a/openlp/plugins/songs/lib/importers/mediashout.py +++ b/openlp/plugins/songs/lib/importers/mediashout.py @@ -23,6 +23,10 @@ The :mod:`mediashout` module provides the functionality for importing 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 from openlp.core.lib import translate @@ -47,8 +51,8 @@ class MediaShoutImport(SongImport): Receive a single file to import. """ try: - conn = pyodbc.connect('DRIVER={Microsoft Access Driver (*.mdb)};DBQ=%s;PWD=6NOZ4eHK7k' % - self.import_source) + conn = pyodbc.connect('DRIVER={Microsoft Access Driver (*.mdb)};DBQ={source};' + 'PWD=6NOZ4eHK7k'.format(sorce=self.import_source)) except: # Unfortunately no specific exception type self.log_error(self.import_source, translate('SongsPlugin.MediaShoutImport', @@ -61,16 +65,15 @@ class MediaShoutImport(SongImport): for song in songs: if self.stop_import_flag: break - cursor.execute('SELECT Type, Number, Text FROM Verses WHERE Record = %s ORDER BY Type, Number' - % song.Record) + cursor.execute('SELECT Type, Number, Text FROM Verses WHERE Record = ? ORDER BY Type, Number', song.Record) 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() 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() 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() self.process_song(song, verses, verse_order, topics) diff --git a/openlp/plugins/songs/lib/importers/openlp.py b/openlp/plugins/songs/lib/importers/openlp.py index 20c603e28..0bc1655a9 100644 --- a/openlp/plugins/songs/lib/importers/openlp.py +++ b/openlp/plugins/songs/lib/importers/openlp.py @@ -102,7 +102,7 @@ class OpenLPSongImport(SongImport): self.log_error(self.import_source, translate('SongsPlugin.OpenLPSongImport', 'Not a valid OpenLP 2 song database.')) 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 engine = create_engine(self.import_source) source_meta = MetaData() @@ -239,8 +239,10 @@ class OpenLPSongImport(SongImport): self.manager.save_object(new_song) if progress_dialog: progress_dialog.setValue(progress_dialog.value() + 1) + # TODO: Verify format() with template strings progress_dialog.setLabelText(WizardStrings.ImportingType % new_song.title) else: + # TODO: Verify format() with template strings self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % new_song.title) if self.stop_import_flag: break diff --git a/openlp/plugins/songs/lib/importers/openlyrics.py b/openlp/plugins/songs/lib/importers/openlyrics.py index f60023cdf..84b667ce4 100644 --- a/openlp/plugins/songs/lib/importers/openlyrics.py +++ b/openlp/plugins/songs/lib/importers/openlyrics.py @@ -58,6 +58,7 @@ class OpenLyricsImport(SongImport): for file_path in self.import_source: if self.stop_import_flag: return + # TODO: Verify format() with template strings self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % os.path.basename(file_path)) try: # Pass a file object, because lxml does not cope with some @@ -66,9 +67,10 @@ class OpenLyricsImport(SongImport): xml = etree.tostring(parsed_file).decode() self.open_lyrics.xml_to_song(xml) 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) except OpenLyricsError as exception: - log.exception('OpenLyricsException %d in file %s: %s' % - (exception.type, file_path, exception.log_message)) + log.exception('OpenLyricsException {error:d} in file {name}: {text}'.format(error=exception.type, + name=file_path, + text=exception.log_message)) self.log_error(file_path, exception.display_message) diff --git a/openlp/plugins/songs/lib/importers/openoffice.py b/openlp/plugins/songs/lib/importers/openoffice.py index b21f3a7d3..c1a949d1e 100644 --- a/openlp/plugins/songs/lib/importers/openoffice.py +++ b/openlp/plugins/songs/lib/importers/openoffice.py @@ -161,7 +161,7 @@ class OpenOfficeImport(SongImport): else: self.import_wizard.increment_progress_bar('Processing file ' + file_path, 0) except AttributeError: - log.exception("open_ooo_file failed: %s", url) + log.exception("open_ooo_file failed: {url}".format(url=url)) return def create_property(self, name, value): diff --git a/openlp/plugins/songs/lib/importers/opensong.py b/openlp/plugins/songs/lib/importers/opensong.py index 3c0644ff6..a1bfacbcb 100644 --- a/openlp/plugins/songs/lib/importers/opensong.py +++ b/openlp/plugins/songs/lib/importers/opensong.py @@ -254,8 +254,8 @@ class OpenSongImport(SongImport): length = 0 while length < len(verse_num) and verse_num[length].isnumeric(): length += 1 - verse_def = '%s%s' % (verse_tag, verse_num[:length]) - verse_joints[verse_def] = '%s\n[---]\n%s' % (verse_joints[verse_def], lines) \ + verse_def = '{tag}{number}'.format(tag=verse_tag, number=verse_num[:length]) + verse_joints[verse_def] = '{verse}\n[---]\n{lines}'.format(verse=verse_joints[verse_def], lines=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 # 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_index = VerseType.from_loose_input(verse_tag) 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, {}): self.verse_order_list.append(verse_def) else: - log.info('Got order %s but not in verse tags, dropping this item from presentation order', - verse_def) + log.info('Got order {order} but not in verse tags, dropping this item from presentation ' + 'order'.format(order=verse_def)) if not self.finish(): self.log_error(file.name) diff --git a/openlp/plugins/songs/lib/importers/opspro.py b/openlp/plugins/songs/lib/importers/opspro.py index 08bb1bcc9..8f9674deb 100644 --- a/openlp/plugins/songs/lib/importers/opspro.py +++ b/openlp/plugins/songs/lib/importers/opspro.py @@ -23,6 +23,10 @@ The :mod:`opspro` module provides the functionality for importing 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 re import pyodbc @@ -51,10 +55,11 @@ class OPSProImport(SongImport): """ password = self.extract_mdb_password() try: - conn = pyodbc.connect('DRIVER={Microsoft Access Driver (*.mdb)};DBQ=%s;PWD=%s' % (self.import_source, - password)) + conn = pyodbc.connect('DRIVER={Microsoft Access Driver (*.mdb)};DBQ={source};' + 'PWD={password}'.format(source=self.import_source, password=password)) 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 self.log_error(self.import_source, translate('SongsPlugin.OPSProImport', 'Unable to connect the OPS Pro database.')) @@ -68,19 +73,19 @@ class OPSProImport(SongImport): if self.stop_import_flag: break # Type means: 0=Original, 1=Projection, 2=Own - cursor.execute('SELECT Lyrics, Type, IsDualLanguage FROM Lyrics WHERE SongID = %d AND Type < 2 ' - 'ORDER BY Type DESC' % song.ID) + cursor.execute('SELECT Lyrics, Type, IsDualLanguage FROM Lyrics WHERE SongID = ? AND Type < 2 ' + 'ORDER BY Type DESC', song.ID) lyrics = cursor.fetchone() cursor.execute('SELECT CategoryName FROM Category INNER JOIN SongCategory ' - 'ON Category.ID = SongCategory.CategoryID WHERE SongCategory.SongID = %d ' - 'ORDER BY CategoryName' % song.ID) + 'ON Category.ID = SongCategory.CategoryID WHERE SongCategory.SongID = ? ' + 'ORDER BY CategoryName', song.ID) topics = cursor.fetchall() try: self.process_song(song, lyrics, topics) except Exception as e: self.log_error(self.import_source, - translate('SongsPlugin.OPSProImport', '"%s" could not be imported. %s') - % (song.Title, e)) + translate('SongsPlugin.OPSProImport', + '"{title}" could not be imported. {error}').format(title=song.Title, error=e)) def process_song(self, song, lyrics, topics): """ diff --git a/openlp/plugins/songs/lib/importers/powerpraise.py b/openlp/plugins/songs/lib/importers/powerpraise.py index 93a360542..b11ac0dda 100644 --- a/openlp/plugins/songs/lib/importers/powerpraise.py +++ b/openlp/plugins/songs/lib/importers/powerpraise.py @@ -41,6 +41,7 @@ class PowerPraiseImport(SongImport): for file_path in self.import_source: if self.stop_import_flag: return + # TODO: Verify format() with template strings self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % os.path.basename(file_path)) root = objectify.parse(open(file_path, 'rb')).getroot() self.process_song(root) @@ -66,7 +67,7 @@ class PowerPraiseImport(SongImport): else: verse_def = 'o' 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 = [] for slide in part.slide: if not hasattr(slide, 'line'): diff --git a/openlp/plugins/songs/lib/importers/powersong.py b/openlp/plugins/songs/lib/importers/powersong.py index a6ff674e8..be40bf873 100644 --- a/openlp/plugins/songs/lib/importers/powersong.py +++ b/openlp/plugins/songs/lib/importers/powersong.py @@ -96,7 +96,7 @@ class PowerSongImport(SongImport): self.import_source = '' if not self.import_source or not isinstance(self.import_source, list): 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 self.import_wizard.progress_bar.setMaximum(len(self.import_source)) for file in self.import_source: @@ -113,9 +113,9 @@ class PowerSongImport(SongImport): field = self._read_string(song_data) except ValueError: parse_error = True - self.log_error(os.path.basename(file), str( - translate('SongsPlugin.PowerSongImport', 'Invalid %s file. Unexpected byte value.')) % - ps_string) + self.log_error(os.path.basename(file), + translate('SongsPlugin.PowerSongImport', + 'Invalid {text} file. Unexpected byte value.').format(text=ps_string)) break else: if label == 'TITLE': @@ -131,19 +131,20 @@ class PowerSongImport(SongImport): continue # Check that file had TITLE field if not self.title: - self.log_error(os.path.basename(file), str( - translate('SongsPlugin.PowerSongImport', 'Invalid %s file. Missing "TITLE" header.')) % ps_string) + self.log_error(os.path.basename(file), + translate('SongsPlugin.PowerSongImport', + 'Invalid {text} file. Missing "TITLE" header.').format(text=ps_string)) continue # Check that file had COPYRIGHTLINE label if not found_copyright: - self.log_error(self.title, str( - translate('SongsPlugin.PowerSongImport', 'Invalid %s file. Missing "COPYRIGHTLINE" header.')) % - ps_string) + self.log_error(self.title, + translate('SongsPlugin.PowerSongImport', + 'Invalid {text} file. Missing "COPYRIGHTLINE" header.').format(text=ps_string)) continue # Check that file had at least one verse if not self.verses: - self.log_error(self.title, str( - translate('SongsPlugin.PowerSongImport', 'Verses not found. Missing "PART" header.'))) + self.log_error(self.title, + translate('SongsPlugin.PowerSongImport', 'Verses not found. Missing "PART" header.')) continue if not self.finish(): self.log_error(self.title) diff --git a/openlp/plugins/songs/lib/importers/presentationmanager.py b/openlp/plugins/songs/lib/importers/presentationmanager.py index c26f11312..06ab3f60d 100644 --- a/openlp/plugins/songs/lib/importers/presentationmanager.py +++ b/openlp/plugins/songs/lib/importers/presentationmanager.py @@ -44,6 +44,7 @@ class PresentationManagerImport(SongImport): for file_path in self.import_source: if self.stop_import_flag: return + # TODO: Verify format() with template strings self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % os.path.basename(file_path)) try: tree = etree.parse(file_path, parser=etree.XMLParser(recover=True)) @@ -90,7 +91,7 @@ class PresentationManagerImport(SongImport): verse_def = 'o' if not is_duplicate: # Only increment verse number if no duplicate 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 self.add_verse(str(verse).strip(), verse_def) verse_order_list.append(verse_def) diff --git a/openlp/plugins/songs/lib/importers/propresenter.py b/openlp/plugins/songs/lib/importers/propresenter.py index 55e05a08f..02d9fa73b 100644 --- a/openlp/plugins/songs/lib/importers/propresenter.py +++ b/openlp/plugins/songs/lib/importers/propresenter.py @@ -46,6 +46,7 @@ class ProPresenterImport(SongImport): for file_path in self.import_source: if self.stop_import_flag: return + # TODO: Verify format() with template strings self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % os.path.basename(file_path)) root = objectify.parse(open(file_path, 'rb')).getroot() self.process_song(root, file_path) @@ -87,7 +88,7 @@ class ProPresenterImport(SongImport): RTFData = slide.displayElements.RVTextElement.get('RTFData') rtf = base64.standard_b64decode(RTFData) words, encoding = strip_rtf(rtf.decode()) - self.add_verse(words, "v%d" % count) + self.add_verse(words, "v{count}".format(count=count)) # ProPresenter 5 elif(self.version >= 500 and self.version < 600): @@ -103,7 +104,7 @@ class ProPresenterImport(SongImport): RTFData = slide.displayElements.RVTextElement.get('RTFData') rtf = base64.standard_b64decode(RTFData) 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 elif(self.version >= 600 and self.version < 700): @@ -127,7 +128,7 @@ class ProPresenterImport(SongImport): words, encoding = strip_rtf(data.decode()) break if words: - self.add_verse(words, "v%d" % count) + self.add_verse(words, "v{count:d}".format(count=count)) if not self.finish(): self.log_error(self.import_source) diff --git a/openlp/plugins/songs/lib/importers/songimport.py b/openlp/plugins/songs/lib/importers/songimport.py index 835386b26..c9ef382a7 100644 --- a/openlp/plugins/songs/lib/importers/songimport.py +++ b/openlp/plugins/songs/lib/importers/songimport.py @@ -117,7 +117,7 @@ class SongImport(QtCore.QObject): self.import_wizard.error_report_text_edit.setVisible(True) self.import_wizard.error_copy_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): """ @@ -326,10 +326,11 @@ class SongImport(QtCore.QObject): if not self.check_complete(): self.set_defaults() 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.title = self.title if self.import_wizard is not None: + # TODO: Verify format() with template variables self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % song.title) song.alternate_title = self.alternate_title # Values will be set when cleaning the song. @@ -344,11 +345,11 @@ class SongImport(QtCore.QObject): if verse_def[0].lower() in VerseType.tags: verse_tag = verse_def[0].lower() 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 other_count += 1 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 sxml.add_verse_to_lyrics(verse_tag, verse_def[1:], verse_text, lang) song.lyrics = str(sxml.extract_xml(), 'utf-8') diff --git a/openlp/plugins/songs/lib/importers/songshowplus.py b/openlp/plugins/songs/lib/importers/songshowplus.py index d9a205e22..02b35fd9e 100644 --- a/openlp/plugins/songs/lib/importers/songshowplus.py +++ b/openlp/plugins/songs/lib/importers/songshowplus.py @@ -101,6 +101,7 @@ class SongShowPlusImport(SongImport): self.other_count = 0 self.other_list = {} file_name = os.path.split(file)[1] + # TODO: Verify format() with template variables self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % file_name, 0) song_data = open(file, 'rb') while True: @@ -145,13 +146,16 @@ class SongShowPlusImport(SongImport): if match: self.ccli_number = int(match.group()) 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: - 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: - 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: - 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: self.topics.append(self.decode(data)) elif block_key == COMMENTS: @@ -170,7 +174,7 @@ class SongShowPlusImport(SongImport): verse_tag = self.to_openlp_verse_tag(verse_name) self.add_verse(self.decode(data), verse_tag) 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) self.verse_order_list = self.ssp_verse_order_list song_data.close() diff --git a/openlp/plugins/songs/lib/importers/sundayplus.py b/openlp/plugins/songs/lib/importers/sundayplus.py index 1f10004e7..c79006fff 100644 --- a/openlp/plugins/songs/lib/importers/sundayplus.py +++ b/openlp/plugins/songs/lib/importers/sundayplus.py @@ -141,7 +141,7 @@ class SundayPlusImport(SongImport): if len(value): 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']: - verse_type = "%s%s" % (verse_type, value[-1]) + verse_type = "{verse}{value}".format(verse=verse_type, value=value[-1]) elif name == 'HOTKEY': value = self.decode(value).strip() # HOTKEY always appears after MARKER_NAME, so it diff --git a/openlp/plugins/songs/lib/importers/videopsalm.py b/openlp/plugins/songs/lib/importers/videopsalm.py index 0bc581239..60c8e27e4 100644 --- a/openlp/plugins/songs/lib/importers/videopsalm.py +++ b/openlp/plugins/songs/lib/importers/videopsalm.py @@ -115,8 +115,8 @@ class VideoPsalmImport(SongImport): for verse in song['Verses']: self.add_verse(verse['Text'], 'v') 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: - self.log_error(translate('SongsPlugin.VideoPsalmImport', 'File %s' % file.name), - translate('SongsPlugin.VideoPsalmImport', 'Error: %s') % e) + self.log_error(translate('SongsPlugin.VideoPsalmImport', 'File {name}').format(name=file.name), + translate('SongsPlugin.VideoPsalmImport', 'Error: {error}').format(error=e)) song_file.close() diff --git a/openlp/plugins/songs/lib/importers/wordsofworship.py b/openlp/plugins/songs/lib/importers/wordsofworship.py index 6135ae2b8..43d84179e 100644 --- a/openlp/plugins/songs/lib/importers/wordsofworship.py +++ b/openlp/plugins/songs/lib/importers/wordsofworship.py @@ -108,8 +108,8 @@ class WordsOfWorshipImport(SongImport): if song_data.read(19).decode() != 'WoW File\nSong Words': self.log_error(source, translate('SongsPlugin.WordsofWorshipSongImport', - 'Invalid Words of Worship song file. Missing "%s" header.') - % 'WoW File\\nSong Words') + 'Invalid Words of Worship song file. Missing "{text}" ' + 'header.').format(text='WoW File\\nSong Words')) continue # Seek to byte which stores number of blocks in the song song_data.seek(56) @@ -118,8 +118,8 @@ class WordsOfWorshipImport(SongImport): if song_data.read(16).decode() != 'CSongDoc::CBlock': self.log_error(source, translate('SongsPlugin.WordsofWorshipSongImport', - 'Invalid Words of Worship song file. Missing "%s" string.') - % 'CSongDoc::CBlock') + 'Invalid Words of Worship song file. Missing "{text}" ' + 'string.').format(text='CSongDoc::CBlock')) continue # Seek to the beginning of the first block song_data.seek(82) diff --git a/openlp/plugins/songs/lib/importers/worshipassistant.py b/openlp/plugins/songs/lib/importers/worshipassistant.py index 671b6d9cf..2374131c9 100644 --- a/openlp/plugins/songs/lib/importers/worshipassistant.py +++ b/openlp/plugins/songs/lib/importers/worshipassistant.py @@ -91,11 +91,11 @@ class WorshipAssistantImport(SongImport): records = list(songs_reader) except csv.Error as e: self.log_error(translate('SongsPlugin.WorshipAssistantImport', 'Error reading CSV file.'), - translate('SongsPlugin.WorshipAssistantImport', 'Line %d: %s') % - (songs_reader.line_num, e)) + translate('SongsPlugin.WorshipAssistantImport', + 'Line {number:d}: {error}').format(number=songs_reader.line_num, error=e)) return 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) # Create regex to strip html tags re_html_strip = re.compile(r'<[^>]+>') @@ -122,12 +122,14 @@ class WorshipAssistantImport(SongImport): verse_order_list = [x.strip() for x in record['ROADMAP'].split(',')] lyrics = record['LYRICS2'] except UnicodeDecodeError as e: - self.log_error(translate('SongsPlugin.WorshipAssistantImport', 'Record %d' % index), - translate('SongsPlugin.WorshipAssistantImport', 'Decoding error: %s') % e) + self.log_error(translate('SongsPlugin.WorshipAssistantImport', 'Record {count:d}').format(count=index), + translate('SongsPlugin.WorshipAssistantImport', + 'Decoding error: {error}').format(error=e)) continue except TypeError as e: 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 verse = '' used_verses = [] @@ -180,6 +182,7 @@ class WorshipAssistantImport(SongImport): cleaned_verse_order_list.append(verse) self.verse_order_list = cleaned_verse_order_list 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 '')) songs_file.close() diff --git a/openlp/plugins/songs/lib/importers/worshipcenterpro.py b/openlp/plugins/songs/lib/importers/worshipcenterpro.py index e932b8895..df04823e8 100644 --- a/openlp/plugins/songs/lib/importers/worshipcenterpro.py +++ b/openlp/plugins/songs/lib/importers/worshipcenterpro.py @@ -49,9 +49,11 @@ class WorshipCenterProImport(SongImport): Receive a single file to import. """ 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: - 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 self.log_error(self.import_source, translate('SongsPlugin.WorshipCenterProImport', 'Unable to connect the WorshipCenter Pro database.')) diff --git a/openlp/plugins/songs/lib/importers/zionworx.py b/openlp/plugins/songs/lib/importers/zionworx.py index 429c8a55a..30ac8c250 100644 --- a/openlp/plugins/songs/lib/importers/zionworx.py +++ b/openlp/plugins/songs/lib/importers/zionworx.py @@ -84,10 +84,11 @@ class ZionWorxImport(SongImport): records = list(songs_reader) except csv.Error as e: 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 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) for index, record in enumerate(records, 1): if self.stop_import_flag: @@ -101,12 +102,12 @@ class ZionWorxImport(SongImport): self.add_copyright(self._decode(record['Copyright'])) lyrics = self._decode(record['Lyrics']) except UnicodeDecodeError as e: - self.log_error(translate('SongsPlugin.ZionWorxImport', 'Record %d' % index), - translate('SongsPlugin.ZionWorxImport', 'Decoding error: %s') % e) + self.log_error(translate('SongsPlugin.ZionWorxImport', 'Record {index}').format(index=index), + translate('SongsPlugin.ZionWorxImport', 'Decoding error: {error}').format(error=e)) continue except TypeError as e: - self.log_error(translate( - 'SongsPlugin.ZionWorxImport', 'File not valid ZionWorx CSV format.'), 'TypeError: %s' % e) + self.log_error(translate('SongsPlugin.ZionWorxImport', 'File not valid ZionWorx CSV format.'), + 'TypeError: {error}'.format(error=e)) return verse = '' for line in lyrics.splitlines(): diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index 11deeb31d..f52abb86f 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -129,7 +129,7 @@ class SongMediaItem(MediaManagerItem): self.display_copyright_symbol = Settings().value(self.settings_section + '/display copyright symbol') 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.maintenance_action.setText(SongStrings.SongMaintenance) self.maintenance_action.setToolTip(translate('SongsPlugin.MediaItem', @@ -166,12 +166,14 @@ class SongMediaItem(MediaManagerItem): translate('SongsPlugin.MediaItem', '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() def on_search_text_button_clicked(self): # 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. search_keywords = str(self.search_text_edit.displayText()) search_type = self.search_text_edit.current_search_type() @@ -181,31 +183,31 @@ class SongMediaItem(MediaManagerItem): self.display_results_song(search_results) elif search_type == SongSearch.Titles: 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)) self.display_results_song(search_results) elif search_type == SongSearch.Lyrics: 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)) self.display_results_song(search_results) elif search_type == SongSearch.Authors: log.debug('Authors Search') - search_string = '%' + search_keywords + '%' + search_string = '%{text}%'.format(text=search_keywords) search_results = self.plugin.manager.get_all_objects( Author, Author.display_name.like(search_string)) self.display_results_author(search_results) elif search_type == SongSearch.Topics: log.debug('Topics Search') - search_string = '%' + search_keywords + '%' + search_string = '%{text}%'.format(text=search_keywords) search_results = self.plugin.manager.get_all_objects( Topic, Topic.name.like(search_string)) self.display_results_topic(search_results) elif search_type == SongSearch.Books: log.debug('Songbook Search') search_keywords = search_keywords.rpartition(' ') - search_book = search_keywords[0] + '%' - search_entry = search_keywords[2] + '%' + search_book = '{text}%'.format(text=search_keywords[0]) + search_entry = '{text}%'.format(text=search_keywords[2]) search_results = (self.plugin.manager.session.query(SongBookEntry.entry, Book.name, Song.title, Song.id) .join(Song) .join(Book) @@ -214,26 +216,26 @@ class SongMediaItem(MediaManagerItem): self.display_results_book(search_results) elif search_type == SongSearch.Themes: log.debug('Theme Search') - search_string = '%' + search_keywords + '%' + search_string = '%{text}%'.format(text=search_keywords) search_results = self.plugin.manager.get_all_objects( Song, Song.theme_name.like(search_string)) self.display_results_themes(search_results) elif search_type == SongSearch.Copyright: log.debug('Copyright Search') - search_string = '%' + search_keywords + '%' + search_string = '%{text}%'.format(text=search_keywords) search_results = self.plugin.manager.get_all_objects( Song, and_(Song.copyright.like(search_string), Song.copyright != '')) self.display_results_song(search_results) elif search_type == SongSearch.CCLInumber: log.debug('CCLI number Search') - search_string = '%' + search_keywords + '%' + search_string = '%{text}%'.format(text=search_keywords) search_results = self.plugin.manager.get_all_objects( Song, and_(Song.ccli_number.like(search_string), Song.ccli_number != '')) self.display_results_cclinumber(search_results) self.check_search_result() 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( Song, or_(Song.search_title.like(search_string), Song.search_lyrics.like(search_string), Song.comments.like(search_string))) @@ -272,7 +274,8 @@ class SongMediaItem(MediaManagerItem): if song.temporary: continue 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.setData(QtCore.Qt.UserRole, song.id) self.list_view.addItem(song_name) @@ -305,7 +308,7 @@ class SongMediaItem(MediaManagerItem): # Do not display temporary songs if song.temporary: 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.setData(QtCore.Qt.UserRole, song.id) self.list_view.addItem(song_name) @@ -325,7 +328,8 @@ class SongMediaItem(MediaManagerItem): self.list_view.clear() search_results.sort(key=get_songbook_key) 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.setData(QtCore.Qt.UserRole, result[3]) self.list_view.addItem(song_name) @@ -354,7 +358,7 @@ class SongMediaItem(MediaManagerItem): # Do not display temporary songs if song.temporary: 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.setData(QtCore.Qt.UserRole, song.id) self.list_view.addItem(song_name) @@ -377,7 +381,7 @@ class SongMediaItem(MediaManagerItem): # Do not display temporary songs if song.temporary: 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.setData(QtCore.Qt.UserRole, song.id) self.list_view.addItem(song_name) @@ -400,7 +404,7 @@ class SongMediaItem(MediaManagerItem): # Do not display temporary songs if song.temporary: 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.setData(QtCore.Qt.UserRole, song.id) 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 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) valid = self.plugin.manager.get_object(Song, song_id) if valid: @@ -499,7 +503,8 @@ class SongMediaItem(MediaManagerItem): if QtWidgets.QMessageBox.question( self, UiStrings().ConfirmDelete, 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.Yes) == QtWidgets.QMessageBox.No: return @@ -524,8 +529,9 @@ class SongMediaItem(MediaManagerItem): old_song = self.plugin.manager.get_object(Song, item_id) song_xml = self.open_lyrics.song_to_xml(old_song) new_song = self.open_lyrics.xml_to_song(song_xml) - new_song.title = '%s <%s>' % \ - (new_song.title, translate('SongsPlugin.MediaItem', 'copy', 'For song cloning')) + new_song.title = '{title} <{text}>'.format(title=new_song.title, + text=translate('SongsPlugin.MediaItem', + 'copy', 'For song cloning')) # Copy audio files from the old to the new song 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)) @@ -552,7 +558,8 @@ class SongMediaItem(MediaManagerItem): :param remote: Triggered from remote :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) service_item.add_capability(ItemCapabilities.CanEdit) service_item.add_capability(ItemCapabilities.CanPreview) @@ -581,7 +588,7 @@ class SongMediaItem(MediaManagerItem): if verse_index is None: verse_index = VerseType.from_tag(verse_tag) 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) else: # Loop through the verse list and expand the song accordingly. @@ -596,7 +603,7 @@ class SongMediaItem(MediaManagerItem): else: verse_index = VerseType.from_tag(verse[0]['type']) 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.title = song.title author_list = self.generate_footer(service_item, song) @@ -639,23 +646,24 @@ class SongMediaItem(MediaManagerItem): item.raw_footer = [] item.raw_footer.append(song.title) if authors_none: - item.raw_footer.append("%s: %s" % (translate('OpenLP.Ui', 'Written by'), - create_separated_list(authors_none))) + item.raw_footer.append("{text}: {authors}".format(text=translate('OpenLP.Ui', 'Written by'), + authors=create_separated_list(authors_none))) if authors_words_music: - item.raw_footer.append("%s: %s" % (AuthorType.Types[AuthorType.WordsAndMusic], - create_separated_list(authors_words_music))) + item.raw_footer.append("{text}: {authors}".format(text=AuthorType.Types[AuthorType.WordsAndMusic], + authors=create_separated_list(authors_words_music))) if authors_words: - item.raw_footer.append("%s: %s" % (AuthorType.Types[AuthorType.Words], - create_separated_list(authors_words))) + item.raw_footer.append("{text}: {authors}".format(text=AuthorType.Types[AuthorType.Words], + authors=create_separated_list(authors_words))) if authors_music: - item.raw_footer.append("%s: %s" % (AuthorType.Types[AuthorType.Music], - create_separated_list(authors_music))) + item.raw_footer.append("{text}: {authors}".format(text=AuthorType.Types[AuthorType.Music], + authors=create_separated_list(authors_music))) if authors_translation: - item.raw_footer.append("%s: %s" % (AuthorType.Types[AuthorType.Translation], - create_separated_list(authors_translation))) + item.raw_footer.append("{text}: {authors}".format(text=AuthorType.Types[AuthorType.Translation], + authors=create_separated_list(authors_translation))) if song.copyright: 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: item.raw_footer.append(song.copyright) if self.display_songbook and song.songbook_entries: diff --git a/openlp/plugins/songs/lib/openlyricsexport.py b/openlp/plugins/songs/lib/openlyricsexport.py index d5ca31e18..db1d02265 100644 --- a/openlp/plugins/songs/lib/openlyricsexport.py +++ b/openlp/plugins/songs/lib/openlyricsexport.py @@ -61,18 +61,20 @@ class OpenLyricsExport(RegistryProperties): if self.parent.stop_export_flag: return False 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) 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) # 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 conflicts = 0 while os.path.exists(os.path.join(self.save_path, filename_with_ext)): 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 # 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', diff --git a/openlp/plugins/songs/lib/openlyricsxml.py b/openlp/plugins/songs/lib/openlyricsxml.py index 806df438d..51165da32 100644 --- a/openlp/plugins/songs/lib/openlyricsxml.py +++ b/openlp/plugins/songs/lib/openlyricsxml.py @@ -70,6 +70,7 @@ from openlp.plugins.songs.lib.db import Author, AuthorType, Book, Song, Topic log = logging.getLogger(__name__) NAMESPACE = 'http://openlyrics.info/namespace/2009/song' +# TODO: Verify format() with template variable NSMAP = '{' + NAMESPACE + '}' + '%s' @@ -126,7 +127,7 @@ class SongXML(object): try: self.song_xml = objectify.fromstring(xml) except etree.XMLSyntaxError: - log.exception('Invalid xml %s', xml) + log.exception('Invalid xml {text}'.format(text=xml)) xml_iter = self.song_xml.getiterator() for element in xml_iter: if element.tag == 'verse': @@ -422,7 +423,7 @@ class OpenLyrics(object): :param tags_element: Some tag elements """ available_tags = FormattingTags.get_html_tags() - start_tag = '{%s}' % tag_name + start_tag = '{{{name}}}'.format(name=tag_name) for tag in available_tags: if tag['start tag'] == start_tag: # Create new formatting tag in openlyrics xml. @@ -449,18 +450,18 @@ class OpenLyrics(object): xml_tags = tags_element.xpath('tag/attribute::name') # Some formatting tag has only starting part e.g.
. Handle this case. if tag in end_tags: - text = text.replace('{%s}' % tag, '' % tag) + text = text.replace('{{{tag}}}'.format(tag=tag), ''.format(tag=tag)) else: - text = text.replace('{%s}' % tag, '' % tag) + text = text.replace('{{{tag}}}'.format(tag=tag), ''.format(tag=tag)) # Add tag to element if tag not present. if tag not in xml_tags: self._add_tag_to_formatting(tag, tags_element) # Replace end tags. for tag in end_tags: - text = text.replace('{/%s}' % tag, '') + text = text.replace('{/{tag}}'.format(tag=tag), '') # Replace \n with
. text = text.replace('\n', '
') - element = etree.XML('%s' % text) + element = etree.XML('{text}'.format(text=text)) verse_element.append(element) return element @@ -566,9 +567,9 @@ class OpenLyrics(object): name = tag.get('name') if name is None: continue - start_tag = '{%s}' % name[:5] + start_tag = '{{{name}}}'.format(name=name[:5]) # 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 = { 'desc': name, 'start tag': start_tag, @@ -604,26 +605,30 @@ class OpenLyrics(object): text = '' use_endtag = True # Skip elements - not yet supported. + # TODO: Verify format() with template variables if element.tag == NSMAP % 'comment': if element.tail: # Append tail text at chord element. text += element.tail return text # Skip element - not yet supported. + # TODO: Verify format() with template variables elif element.tag == NSMAP % 'chord': if element.tail: # Append tail text at chord element. text += element.tail return text # Convert line breaks
to \n. + # TODO: Verify format() with template variables elif newlines and element.tag == NSMAP % 'br': text += '\n' if element.tail: text += element.tail return text # Start formatting tag. + # TODO: Verify format() with template variables if element.tag == NSMAP % 'tag': - text += '{%s}' % element.get('name') + text += '{{{name}}}'.format(name=element.get('name')) # Some formattings may have only start tag. # Handle this case if element has no children and contains no text. if not element and not element.text: @@ -636,8 +641,9 @@ class OpenLyrics(object): # Use recursion since nested formatting tags are allowed. text += self._process_lines_mixed_content(child, newlines) # Append text from tail and add formatting end tag. + # TODO: Verify format() with template variables if element.tag == NSMAP % 'tag' and use_endtag: - text += '{/%s}' % element.get('name') + text += '{/{{name}}}'.format(name=element.get('name')) # Append text from tail. if element.tail: text += element.tail @@ -663,6 +669,7 @@ class OpenLyrics(object): # Loop over the "line" elements removing comments and chords. for line in element: # Skip comment lines. + # TODO: Verify format() with template variables if line.tag == NSMAP % 'comment': continue if text: diff --git a/openlp/plugins/songs/lib/songselect.py b/openlp/plugins/songs/lib/songselect.py index ca417b504..60f4383d2 100644 --- a/openlp/plugins/songs/lib/songselect.py +++ b/openlp/plugins/songs/lib/songselect.py @@ -78,7 +78,7 @@ class SongSelectImport(object): try: login_page = BeautifulSoup(self.opener.open(LOGIN_URL).read(), 'lxml') 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 if callback: callback() @@ -92,7 +92,7 @@ class SongSelectImport(object): try: posted_page = BeautifulSoup(self.opener.open(LOGIN_URL, data.encode('utf-8')).read(), 'lxml') 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 if callback: callback() @@ -105,7 +105,7 @@ class SongSelectImport(object): try: self.opener.open(LOGOUT_URL) 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): """ @@ -127,7 +127,7 @@ class SongSelectImport(object): results_page = BeautifulSoup(self.opener.open(SEARCH_URL + '?' + urlencode(params)).read(), 'lxml') search_results = results_page.find_all('li', 'result pane') 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 if not search_results: break @@ -158,7 +158,7 @@ class SongSelectImport(object): try: song_page = BeautifulSoup(self.opener.open(song['link']).read(), 'lxml') 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 if callback: callback() @@ -203,7 +203,7 @@ class SongSelectImport(object): verse_type = VerseType.from_loose_input(verse_type) verse_number = int(verse_number) 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.lyrics = song_xml.extract_xml() clean_song(self.db_manager, db_song) diff --git a/openlp/plugins/songs/lib/songstab.py b/openlp/plugins/songs/lib/songstab.py index 5a8c18a50..61739abeb 100644 --- a/openlp/plugins/songs/lib/songstab.py +++ b/openlp/plugins/songs/lib/songstab.py @@ -74,8 +74,8 @@ class SongsTab(SettingsTab): 'Import missing songs from service files')) self.display_songbook_check_box.setText(translate('SongsPlugin.SongsTab', 'Display songbook in footer')) self.display_copyright_check_box.setText(translate('SongsPlugin.SongsTab', - 'Display "%s" symbol before copyright info') % - SongStrings.CopyrightSymbol) + 'Display "{symbol}" symbol before copyright ' + 'info').format(symbol=SongStrings.CopyrightSymbol)) def on_search_as_type_check_box_changed(self, check_state): self.song_search = (check_state == QtCore.Qt.Checked) diff --git a/tests/functional/openlp_core_lib/test_projectordb.py b/tests/functional/openlp_core_lib/test_projectordb.py index 7f3f927aa..7c677177e 100644 --- a/tests/functional/openlp_core_lib/test_projectordb.py +++ b/tests/functional/openlp_core_lib/test_projectordb.py @@ -265,3 +265,22 @@ class TestProjectorDB(TestCase): 'manufacturer="IN YOUR DREAMS", model="OpenLP", other="None", sources="None", ' 'source_list="[]") >', 'Projector.__repr__() should have returned a proper representation string') + + def projectorsource_repr_test(self): + """ + Test ProjectorSource.__repr__() text + """ + # GIVEN: test setup + projector1 = Projector(**TEST1_DATA) + self.projector.add_projector(projector1) + item = self.projector.get_projector_by_id(projector1.id) + item_id = item.id + + # WHEN: A source entry is saved for item + source = ProjectorSource(projector_id=item_id, code='11', text='First RGB source') + self.projector.add_source(source) + + # THEN: __repr__ should return a proper string + self.assertEqual(str(source), + '', + 'ProjectorSource.__repr__)_ should have returned a proper representation string') From 96333b3f082d3d1c6ab4f2b6c0b13397904cd41e Mon Sep 17 00:00:00 2001 From: Ken Roberts Date: Fri, 27 May 2016 01:29:31 -0700 Subject: [PATCH 2/5] Oops in foramt - missed escaping brace(s) --- openlp/plugins/songs/lib/openlyricsxml.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openlp/plugins/songs/lib/openlyricsxml.py b/openlp/plugins/songs/lib/openlyricsxml.py index 51165da32..5adffb300 100644 --- a/openlp/plugins/songs/lib/openlyricsxml.py +++ b/openlp/plugins/songs/lib/openlyricsxml.py @@ -458,7 +458,7 @@ class OpenLyrics(object): self._add_tag_to_formatting(tag, tags_element) # Replace end tags. for tag in end_tags: - text = text.replace('{/{tag}}'.format(tag=tag), '
') + text = text.replace('{/{tag}}}'.format(tag=tag), '
') # Replace \n with
. text = text.replace('\n', '
') element = etree.XML('{text}'.format(text=text)) @@ -643,7 +643,7 @@ class OpenLyrics(object): # Append text from tail and add formatting end tag. # TODO: Verify format() with template variables if element.tag == NSMAP % 'tag' and use_endtag: - text += '{/{{name}}}'.format(name=element.get('name')) + text += '{/{name}}}'.format(name=element.get('name')) # Append text from tail. if element.tail: text += element.tail From 546030b64db4162af72d169dbc6b692a27aa69cb Mon Sep 17 00:00:00 2001 From: Ken Roberts Date: Fri, 27 May 2016 22:50:31 -0700 Subject: [PATCH 3/5] Convert strings to python3 in plugins part 4 --- openlp/core/lib/projector/pjlink1.py | 4 +- .../songusage/forms/songusagedetailform.py | 21 ++--- .../openlp_core_lib/test_projector_pjlink1.py | 78 +++++++++++++++++-- 3 files changed, 87 insertions(+), 16 deletions(-) diff --git a/openlp/core/lib/projector/pjlink1.py b/openlp/core/lib/projector/pjlink1.py index bbf2ccc64..ce06b3625 100644 --- a/openlp/core/lib/projector/pjlink1.py +++ b/openlp/core/lib/projector/pjlink1.py @@ -58,7 +58,7 @@ SocketSTate = QAbstractSocket.SocketState PJLINK_PREFIX = '%' 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 @@ -160,8 +160,10 @@ class PJLink1(QTcpSocket): self.source = None self.other_info = None if hasattr(self, 'timer'): + log.debug('({ip}): Calling timer.stop()'.format(ip=self.ip)) self.timer.stop() if hasattr(self, 'socket_timer'): + log.debug('({ip}): Calling socket_timer.stop()'.format(ip=self.ip)) self.socket_timer.stop() self.send_queue = [] self.send_busy = False diff --git a/openlp/plugins/songusage/forms/songusagedetailform.py b/openlp/plugins/songusage/forms/songusagedetailform.py index 8edf9775f..541c139c7 100644 --- a/openlp/plugins/songusage/forms/songusagedetailform.py +++ b/openlp/plugins/songusage/forms/songusagedetailform.py @@ -81,9 +81,10 @@ class SongUsageDetailForm(QtWidgets.QDialog, Ui_SongUsageDetailDialog, RegistryP ) return check_directory_exists(path) - file_name = translate('SongUsagePlugin.SongUsageDetailForm', 'usage_detail_%s_%s.txt') % \ - (self.from_date_calendar.selectedDate().toString('ddMMyyyy'), - self.to_date_calendar.selectedDate().toString('ddMMyyyy')) + file_name = translate('SongUsagePlugin.SongUsageDetailForm', + 'usage_detail_{old}_{new}.txt' + ).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 + '/to date', self.to_date_calendar.selectedDate()) usage = self.plugin.manager.get_all_objects( @@ -95,21 +96,23 @@ class SongUsageDetailForm(QtWidgets.QDialog, Ui_SongUsageDetailDialog, RegistryP try: file_handle = open(report_file_name, 'wb') for instance in usage: - record = '\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",' \ - '\"%s\",\"%s\"\n' % \ - (instance.usagedate, instance.usagetime, instance.title, instance.copyright, - instance.ccl_number, instance.authors, instance.plugin_name, instance.source) + record = ('\"{date}\",\"{time}\",\"{title}\",\"{copyright}\",\"{ccli}\",\"{authors}\",' + '\"{name}\",\"{source}\"\n').format(date=instance.usagedate, time=instance.usagetime, + title=instance.title, copyright=instance.copyright, + ccli=instance.ccl_number, authors=instance.authors, + name=instance.plugin_name, source=instance.source) file_handle.write(record.encode('utf-8')) self.main_window.information_message( translate('SongUsagePlugin.SongUsageDetailForm', 'Report Creation'), 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: log.exception('Failed to write out song usage records') critical_error_message_box(translate('SongUsagePlugin.SongUsageDetailForm', 'Report Creation Failed'), 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: if file_handle: file_handle.close() diff --git a/tests/functional/openlp_core_lib/test_projector_pjlink1.py b/tests/functional/openlp_core_lib/test_projector_pjlink1.py index ac1059656..45dbde093 100644 --- a/tests/functional/openlp_core_lib/test_projector_pjlink1.py +++ b/tests/functional/openlp_core_lib/test_projector_pjlink1.py @@ -34,6 +34,18 @@ from tests.resources.projector.data import TEST_PIN, TEST_SALT, TEST_CONNECT_AUT pjlink_test = PJLink1(name='test', ip='127.0.0.1', pin=TEST_PIN, no_poll=True) +class DummyTimer(object): + ''' + Dummy class to fake timers + ''' + def __init__(self, *args, **kwargs): + pass + + def start(self, *args, **kwargs): + pass + + def stop(self, *args, **kwargs): + pass class TestPJLink(TestCase): """ @@ -43,13 +55,10 @@ class TestPJLink(TestCase): @patch.object(pjlink_test, 'send_command') @patch.object(pjlink_test, 'waitForReadyRead') @patch('openlp.core.common.qmd5_hash') - def authenticated_connection_call_test(self, - mock_qmd5_hash, - mock_waitForReadyRead, - mock_send_command, + def authenticated_connection_call_test(self, mock_qmd5_hash, mock_waitForReadyRead, mock_send_command, mock_readyRead): """ - Fix for projector connect with PJLink authentication exception. Ticket 92187. + Ticket 92187: Fix for projector connect with PJLink authentication exception. """ # GIVEN: Test object pjlink = pjlink_test @@ -63,9 +72,23 @@ class TestPJLink(TestCase): self.assertTrue(mock_qmd5_hash.called_with(TEST_PIN, "Connection request should have been called with TEST_PIN")) + def projector_class_test(self): + """ + Test class version from projector + """ + # GIVEN: Test object + pjlink = pjlink_test + + # WHEN: Process class response + pjlink.process_clss('1') + + # THEN: Projector class should be set to 1 + self.assertEquals(pjlink.pjlink_class, '1', + 'Projector should have returned class=1') + def non_standard_class_reply_test(self): """ - bugfix 1550891 - CLSS request returns non-standard 'Class N' reply + Bugfix 1550891: CLSS request returns non-standard 'Class N' reply """ # GIVEN: Test object pjlink = pjlink_test @@ -264,3 +287,46 @@ class TestPJLink(TestCase): # THEN: Input selected should reflect current input self.assertEquals(pjlink.source, '1', 'Input source should be set to "1"') + + def projector_reset_information_test(self): + """ + Test reset_information() resets all information and stops timers + """ + # GIVEN: Test object and test data + pjlink = pjlink_test + pjlink.power = S_ON + pjlink.pjlink_name = 'OPENLPTEST' + pjlink.manufacturer = 'PJLINK' + pjlink.model = '1' + pjlink.shutter = True + pjlink.mute = True + pjlink.lamp = True + pjlink.fan = True + pjlink.source_available = True + pjlink.other_info = 'ANOTHER TEST' + pjlink.send_queue = True + pjlink.send_busy = True + pjlink.timer = DummyTimer() + pjlink.socket_timer = DummyTimer() + + # WHEN: reset_information() is called + with patch.object(pjlink.timer, 'stop') as mock_timer: + with patch.object(pjlink.socket_timer, 'stop') as mock_socket_timer: + pjlink.reset_information() + + # THEN: All information should be reset and timers stopped + self.assertEquals(pjlink.power, S_OFF, 'Projector power should be OFF') + self.assertIsNone(pjlink.pjlink_name, 'Projector pjlink_name should be None') + self.assertIsNone(pjlink.manufacturer, 'Projector manufacturer should be None') + self.assertIsNone(pjlink.model, 'Projector model should be None') + self.assertIsNone(pjlink.shutter, 'Projector shutter should be None') + self.assertIsNone(pjlink.mute, 'Projector shuttter should be None') + self.assertIsNone(pjlink.lamp, 'Projector lamp should be None') + self.assertIsNone(pjlink.fan, 'Projector fan should be None') + self.assertIsNone(pjlink.source_available, 'Projector source_available should be None') + self.assertIsNone(pjlink.source, 'Projector source should be None') + self.assertIsNone(pjlink.other_info, 'Projector other_info should be None') + self.assertEquals(pjlink.send_queue, [], 'Projector send_queue should be an empty list') + self.assertFalse(pjlink.send_busy, 'Projector send_busy should be False') + self.assertTrue(mock_timer.called, 'Projector timer.stop() should have been called') + self.assertTrue(mock_socket_timer.called, 'Projector socket_timer.stop() should have been called') From c27b676c8c762ebb6380123c73faea108caf41f8 Mon Sep 17 00:00:00 2001 From: Ken Roberts Date: Fri, 27 May 2016 22:55:54 -0700 Subject: [PATCH 4/5] pep8 --- tests/functional/openlp_core_lib/test_projector_pjlink1.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/functional/openlp_core_lib/test_projector_pjlink1.py b/tests/functional/openlp_core_lib/test_projector_pjlink1.py index 45dbde093..7f7f0e9cf 100644 --- a/tests/functional/openlp_core_lib/test_projector_pjlink1.py +++ b/tests/functional/openlp_core_lib/test_projector_pjlink1.py @@ -34,6 +34,7 @@ from tests.resources.projector.data import TEST_PIN, TEST_SALT, TEST_CONNECT_AUT pjlink_test = PJLink1(name='test', ip='127.0.0.1', pin=TEST_PIN, no_poll=True) + class DummyTimer(object): ''' Dummy class to fake timers @@ -47,6 +48,7 @@ class DummyTimer(object): def stop(self, *args, **kwargs): pass + class TestPJLink(TestCase): """ Tests for the PJLink module From 5a6c4f90a10600801c028e30d352f2fb7e64e4f6 Mon Sep 17 00:00:00 2001 From: Raoul Snyman Date: Tue, 31 May 2016 18:10:31 +0200 Subject: [PATCH 5/5] Fix formatting problems --- openlp/core/ui/media/__init__.py | 1 + openlp/plugins/bibles/lib/mediaitem.py | 2 +- .../test_registryproperties.py | 21 ++++++++++++++++++- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/openlp/core/ui/media/__init__.py b/openlp/core/ui/media/__init__.py index 9e2f61ae8..248aca6f2 100644 --- a/openlp/core/ui/media/__init__.py +++ b/openlp/core/ui/media/__init__.py @@ -134,6 +134,7 @@ def format_milliseconds(milliseconds): :param milliseconds: Milliseconds to format :return: Time string in format: hh.mm.ss,ttt """ + milliseconds = int(milliseconds) seconds, millis = divmod(milliseconds, 1000) minutes, seconds = divmod(seconds, 60) hours, minutes = divmod(minutes, 60) diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index d6e7eb62e..d5304e867 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -407,7 +407,7 @@ class BibleMediaItem(MediaManagerItem): self.initialise_chapter_verse(bible, first_book['name'], first_book['book_reference_id']) def initialise_chapter_verse(self, bible, book, book_ref_id): - log.debug('initialise_chapter_verse {bible}, {book), {ref}'.format(bible=bible, book=book, ref=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) 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) diff --git a/tests/functional/openlp_core_common/test_registryproperties.py b/tests/functional/openlp_core_common/test_registryproperties.py index c9cf9c1c0..0f0184876 100644 --- a/tests/functional/openlp_core_common/test_registryproperties.py +++ b/tests/functional/openlp_core_common/test_registryproperties.py @@ -25,7 +25,8 @@ Test the registry properties from unittest import TestCase from openlp.core.common import Registry, RegistryProperties -from tests.functional import MagicMock + +from tests.functional import MagicMock, patch class TestRegistryProperties(TestCase, RegistryProperties): @@ -53,7 +54,25 @@ class TestRegistryProperties(TestCase, RegistryProperties): """ # GIVEN an Empty Registry application = MagicMock() + # 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') + + @patch('openlp.core.common.registryproperties.is_win') + def application_on_windows_test(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') +