diff --git a/openlp/core/api/tab.py b/openlp/core/api/tab.py index 038878c03..ddca61176 100644 --- a/openlp/core/api/tab.py +++ b/openlp/core/api/tab.py @@ -133,36 +133,21 @@ class ApiTab(SettingsTab): self.master_version_value.setObjectName('master_version_value') self.update_site_layout.addRow(self.master_version_label, self.master_version_value) self.left_layout.addWidget(self.update_site_group_box) - self.android_app_group_box = QtWidgets.QGroupBox(self.right_column) - self.android_app_group_box.setObjectName('android_app_group_box') - self.right_layout.addWidget(self.android_app_group_box) - self.android_qr_layout = QtWidgets.QVBoxLayout(self.android_app_group_box) - self.android_qr_layout.setObjectName('android_qr_layout') - self.android_qr_code_label = QtWidgets.QLabel(self.android_app_group_box) - self.android_qr_code_label.setPixmap(QtGui.QPixmap(':/remotes/android_app_qr.png')) - self.android_qr_code_label.setAlignment(QtCore.Qt.AlignCenter) - self.android_qr_code_label.setObjectName('android_qr_code_label') - self.android_qr_layout.addWidget(self.android_qr_code_label) - self.android_qr_description_label = QtWidgets.QLabel(self.android_app_group_box) - self.android_qr_description_label.setObjectName('android_qr_description_label') - self.android_qr_description_label.setOpenExternalLinks(True) - self.android_qr_description_label.setWordWrap(True) - self.android_qr_layout.addWidget(self.android_qr_description_label) - self.ios_app_group_box = QtWidgets.QGroupBox(self.right_column) - self.ios_app_group_box.setObjectName('ios_app_group_box') - self.right_layout.addWidget(self.ios_app_group_box) - self.ios_qr_layout = QtWidgets.QVBoxLayout(self.ios_app_group_box) - self.ios_qr_layout.setObjectName('ios_qr_layout') - self.ios_qr_code_label = QtWidgets.QLabel(self.ios_app_group_box) - self.ios_qr_code_label.setPixmap(QtGui.QPixmap(':/remotes/ios_app_qr.png')) - self.ios_qr_code_label.setAlignment(QtCore.Qt.AlignCenter) - self.ios_qr_code_label.setObjectName('ios_qr_code_label') - self.ios_qr_layout.addWidget(self.ios_qr_code_label) - self.ios_qr_description_label = QtWidgets.QLabel(self.ios_app_group_box) - self.ios_qr_description_label.setObjectName('ios_qr_description_label') - self.ios_qr_description_label.setOpenExternalLinks(True) - self.ios_qr_description_label.setWordWrap(True) - self.ios_qr_layout.addWidget(self.ios_qr_description_label) + self.app_group_box = QtWidgets.QGroupBox(self.right_column) + self.app_group_box.setObjectName('app_group_box') + self.right_layout.addWidget(self.app_group_box) + self.app_qr_layout = QtWidgets.QVBoxLayout(self.app_group_box) + self.app_qr_layout.setObjectName('app_qr_layout') + self.app_qr_code_label = QtWidgets.QLabel(self.app_group_box) + self.app_qr_code_label.setPixmap(QtGui.QPixmap(':/remotes/app_qr.svg')) + self.app_qr_code_label.setAlignment(QtCore.Qt.AlignCenter) + self.app_qr_code_label.setObjectName('app_qr_code_label') + self.app_qr_layout.addWidget(self.app_qr_code_label) + self.app_qr_description_label = QtWidgets.QLabel(self.app_group_box) + self.app_qr_description_label.setObjectName('app_qr_description_label') + self.app_qr_description_label.setOpenExternalLinks(True) + self.app_qr_description_label.setWordWrap(True) + self.app_qr_layout.addWidget(self.app_qr_description_label) self.left_layout.addStretch() self.right_layout.addStretch() self.twelve_hour_check_box.stateChanged.connect(self.on_twelve_hour_check_box_changed) @@ -199,16 +184,11 @@ class ApiTab(SettingsTab): self.twelve_hour_check_box.setText(translate('RemotePlugin.RemoteTab', 'Display stage time in 12h format')) self.thumbnails_check_box.setText(translate('RemotePlugin.RemoteTab', '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( + self.app_group_box.setTitle(translate('RemotePlugin.RemoteTab', 'Remote App')) + self.app_qr_description_label.setText( 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.').format(qr='https://itunes.apple.com/app/id1096218725')) + 'Scan the QR code or click download to download an app for your mobile device' + ).format(qr='https://openlp.org/#mobile-app-downloads')) self.user_login_group_box.setTitle(translate('RemotePlugin.RemoteTab', 'User Authentication')) self.aa = UiStrings() self.update_site_group_box.setTitle(UiStrings().WebDownloadText) diff --git a/openlp/core/lib/renderer.py b/openlp/core/lib/renderer.py index acf7177dd..fa617a38b 100644 --- a/openlp/core/lib/renderer.py +++ b/openlp/core/lib/renderer.py @@ -245,6 +245,9 @@ class Renderer(OpenLPMixin, RegistryMixin, RegistryProperties): elif item.is_capable(ItemCapabilities.CanSoftBreak): pages = [] if '[---]' in text: + # Remove Overflow split if at start of the text + if text.startswith('[---]'): + text = text[5:] # Remove two or more option slide breaks next to each other (causing infinite loop). while '\n[---]\n[---]\n' in text: text = text.replace('\n[---]\n[---]\n', '\n[---]\n') diff --git a/openlp/core/lib/serviceitem.py b/openlp/core/lib/serviceitem.py index 692faf8ea..22b1f9b5f 100644 --- a/openlp/core/lib/serviceitem.py +++ b/openlp/core/lib/serviceitem.py @@ -429,10 +429,10 @@ class ServiceItem(RegistryProperties): self.background_audio = [] for filename in header['background_audio']: # Give them real file paths. - filepath = filename + filepath = str(filename) if path: # Windows can handle both forward and backward slashes, so we use ntpath to get the basename - filepath = os.path.join(path, ntpath.basename(filename)) + filepath = os.path.join(path, ntpath.basename(str(filename))) self.background_audio.append(filepath) self.theme_overwritten = header.get('theme_overwritten', False) if self.service_item_type == ServiceItemType.Text: diff --git a/openlp/core/ui/lib/listpreviewwidget.py b/openlp/core/ui/lib/listpreviewwidget.py index ab258ec35..d03261e8c 100644 --- a/openlp/core/ui/lib/listpreviewwidget.py +++ b/openlp/core/ui/lib/listpreviewwidget.py @@ -210,21 +210,21 @@ class ListPreviewWidget(QtWidgets.QTableWidget, RegistryProperties): Switches to the given row. """ # Retrieve setting - autoscrolling = Settings().value('advanced/autoscrolling') + auto_scrolling = Settings().value('advanced/autoscrolling') # Check if auto-scroll disabled (None) and validate value as dict containing 'dist' and 'pos' # 'dist' represents the slide to scroll to relative to the new slide (-1 = previous, 0 = current, 1 = next) # 'pos' represents the vert position of of the slide (0 = in view, 1 = top, 2 = middle, 3 = bottom) - if not (isinstance(autoscrolling, dict) and 'dist' in autoscrolling and 'pos' in autoscrolling and - isinstance(autoscrolling['dist'], int) and isinstance(autoscrolling['pos'], int)): + if not (isinstance(auto_scrolling, dict) and 'dist' in auto_scrolling and 'pos' in auto_scrolling and + isinstance(auto_scrolling['dist'], int) and isinstance(auto_scrolling['pos'], int)): return # prevent scrolling past list bounds - scroll_to_slide = slide + autoscrolling['dist'] + scroll_to_slide = slide + auto_scrolling['dist'] if scroll_to_slide < 0: scroll_to_slide = 0 if scroll_to_slide >= self.slide_count(): scroll_to_slide = self.slide_count() - 1 # Scroll to item if possible. - self.scrollToItem(self.item(scroll_to_slide, 0), autoscrolling['pos']) + self.scrollToItem(self.item(scroll_to_slide, 0), auto_scrolling['pos']) self.selectRow(slide) def current_slide_number(self): diff --git a/openlp/core/ui/maindisplay.py b/openlp/core/ui/maindisplay.py index 55547f161..46e9a82e2 100644 --- a/openlp/core/ui/maindisplay.py +++ b/openlp/core/ui/maindisplay.py @@ -686,7 +686,7 @@ class AudioPlayer(OpenLPMixin, QtCore.QObject): if not isinstance(file_names, list): file_names = [file_names] for file_name in file_names: - self.playlist.addMedia(QtMultimedia.QMediaContent(QtCore.QUrl.fromLocalFile(file_name))) + self.playlist.addMedia(QtMultimedia.QMediaContent(QtCore.QUrl.fromLocalFile(str(file_name)))) def next(self): """ diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 7f0caabd8..ff6ab9a47 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -564,7 +564,7 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ServiceMa service_item = item['service_item'].get_service_repr(self._save_lite) if service_item['header']['background_audio']: for i, file_name in enumerate(service_item['header']['background_audio']): - new_file = os.path.join('audio', item['service_item'].unique_identifier, file_name) + new_file = os.path.join('audio', item['service_item'].unique_identifier, str(file_name)) audio_files.append((file_name, new_file)) service_item['header']['background_audio'][i] = new_file # Add the service item to the service. @@ -589,6 +589,8 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ServiceMa for write_from in write_list: zip_file.write(write_from, write_from) for audio_from, audio_to in audio_files: + audio_from = str(audio_from) + audio_to = str(audio_to) if audio_from.startswith('audio'): # When items are saved, they get new unique_identifier. Let's copy the file to the new location. # Unused files can be ignored, OpenLP automatically cleans up the service manager dir on exit. diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index 467389db6..10530f011 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -871,7 +871,7 @@ class SlideController(DisplayController, RegistryProperties): self.track_menu.clear() for counter in range(len(self.service_item.background_audio)): action = self.track_menu.addAction( - os.path.basename(self.service_item.background_audio[counter])) + os.path.basename(str(self.service_item.background_audio[counter]))) action.setData(counter) action.triggered.connect(self.on_track_triggered) self.display.audio_player.repeat = \ diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index 9031dcc61..bdb1a9353 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -25,25 +25,23 @@ used to edit songs. """ import logging import re -import os -import shutil from PyQt5 import QtCore, QtWidgets from openlp.core.common.applocation import AppLocation from openlp.core.common.i18n import UiStrings, translate, get_natural_key -from openlp.core.common.path import Path, path_to_str, create_paths +from openlp.core.common.path import create_paths, copyfile from openlp.core.common.registry import Registry, RegistryProperties from openlp.core.lib import PluginStatus, MediaType, create_separated_list from openlp.core.lib.ui import set_case_insensitive_completer, critical_error_message_box, find_and_set_in_combo_box from openlp.core.ui.lib.filedialog import FileDialog -from openlp.plugins.songs.lib import VerseType, clean_song -from openlp.plugins.songs.lib.db import Book, Song, Author, AuthorType, Topic, MediaFile, SongBookEntry -from openlp.plugins.songs.lib.ui import SongStrings -from openlp.plugins.songs.lib.openlyricsxml import SongXML from openlp.plugins.songs.forms.editsongdialog import Ui_EditSongDialog from openlp.plugins.songs.forms.editverseform import EditVerseForm from openlp.plugins.songs.forms.mediafilesform import MediaFilesForm +from openlp.plugins.songs.lib import VerseType, clean_song +from openlp.plugins.songs.lib.db import Book, Song, Author, AuthorType, Topic, MediaFile, SongBookEntry +from openlp.plugins.songs.lib.openlyricsxml import SongXML +from openlp.plugins.songs.lib.ui import SongStrings log = logging.getLogger(__name__) @@ -545,9 +543,9 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties): songbook_entry.entry) self.audio_list_widget.clear() for media in self.song.media_files: - media_file = QtWidgets.QListWidgetItem(os.path.split(media.file_name)[1]) - media_file.setData(QtCore.Qt.UserRole, media.file_name) - self.audio_list_widget.addItem(media_file) + item = QtWidgets.QListWidgetItem(media.file_path.name) + item.setData(QtCore.Qt.UserRole, media.file_path) + self.audio_list_widget.addItem(item) self.title_edit.setFocus() # Hide or show the preview button. self.preview_button.setVisible(preview) @@ -927,12 +925,11 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties): Loads file(s) from the filesystem. """ filters = '{text} (*)'.format(text=UiStrings().AllFiles) - file_paths, selected_filter = FileDialog.getOpenFileNames( - self, translate('SongsPlugin.EditSongForm', 'Open File(s)'), Path(), filters) + file_paths, filter_used = FileDialog.getOpenFileNames( + parent=self, caption=translate('SongsPlugin.EditSongForm', 'Open File(s)'), filter=filters) for file_path in file_paths: - filename = path_to_str(file_path) - item = QtWidgets.QListWidgetItem(os.path.split(str(filename))[1]) - item.setData(QtCore.Qt.UserRole, filename) + item = QtWidgets.QListWidgetItem(file_path.name) + item.setData(QtCore.Qt.UserRole, file_path) self.audio_list_widget.addItem(item) def on_audio_add_from_media_button_clicked(self): @@ -940,9 +937,9 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties): Loads file(s) from the media plugin. """ if self.media_form.exec(): - for filename in self.media_form.get_selected_files(): - item = QtWidgets.QListWidgetItem(os.path.split(str(filename))[1]) - item.setData(QtCore.Qt.UserRole, filename) + for file_path in self.media_form.get_selected_files(): + item = QtWidgets.QListWidgetItem(file_path.name) + item.setData(QtCore.Qt.UserRole, file_path) self.audio_list_widget.addItem(item) def on_audio_remove_button_clicked(self): @@ -1066,34 +1063,33 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties): # Save the song here because we need a valid id for the audio files. clean_song(self.manager, self.song) self.manager.save_object(self.song) - audio_files = [a.file_name for a in self.song.media_files] - log.debug(audio_files) - save_path = os.path.join(str(AppLocation.get_section_data_path(self.media_item.plugin.name)), 'audio', - str(self.song.id)) - create_paths(Path(save_path)) + audio_paths = [a.file_path for a in self.song.media_files] + log.debug(audio_paths) + save_path = AppLocation.get_section_data_path(self.media_item.plugin.name) / 'audio' / str(self.song.id) + create_paths(save_path) self.song.media_files = [] - files = [] + file_paths = [] for row in range(self.audio_list_widget.count()): item = self.audio_list_widget.item(row) - filename = item.data(QtCore.Qt.UserRole) - if not filename.startswith(save_path): - old_file, filename = filename, os.path.join(save_path, os.path.split(filename)[1]) - shutil.copyfile(old_file, filename) - files.append(filename) + file_path = item.data(QtCore.Qt.UserRole) + if save_path not in file_path.parents: + old_file_path, file_path = file_path, save_path / file_path.name + copyfile(old_file_path, file_path) + file_paths.append(file_path) media_file = MediaFile() - media_file.file_name = filename + media_file.file_path = file_path media_file.type = 'audio' media_file.weight = row self.song.media_files.append(media_file) - for audio in audio_files: - if audio not in files: + for audio_path in audio_paths: + if audio_path not in file_paths: try: - os.remove(audio) + audio_path.unlink() except: - log.exception('Could not remove file: {audio}'.format(audio=audio)) - if not files: + log.exception('Could not remove file: {audio}'.format(audio=audio_path)) + if not file_paths: try: - os.rmdir(save_path) + save_path.rmdir() except OSError: log.exception('Could not remove directory: {path}'.format(path=save_path)) clean_song(self.manager, self.song) diff --git a/openlp/plugins/songs/forms/editversedialog.py b/openlp/plugins/songs/forms/editversedialog.py index ee8a2175f..63ca0cf27 100644 --- a/openlp/plugins/songs/forms/editversedialog.py +++ b/openlp/plugins/songs/forms/editversedialog.py @@ -43,10 +43,14 @@ class Ui_EditVerseDialog(object): self.dialog_layout.addWidget(self.verse_text_edit) self.verse_type_layout = QtWidgets.QHBoxLayout() self.verse_type_layout.setObjectName('verse_type_layout') - self.split_button = QtWidgets.QPushButton(edit_verse_dialog) - self.split_button.setIcon(build_icon(':/general/general_add.png')) - self.split_button.setObjectName('split_button') - self.verse_type_layout.addWidget(self.split_button) + self.forced_split_button = QtWidgets.QPushButton(edit_verse_dialog) + self.forced_split_button.setIcon(build_icon(':/general/general_add.png')) + self.forced_split_button.setObjectName('forced_split_button') + self.verse_type_layout.addWidget(self.forced_split_button) + self.overflow_split_button = QtWidgets.QPushButton(edit_verse_dialog) + self.overflow_split_button.setIcon(build_icon(':/general/general_add.png')) + self.overflow_split_button.setObjectName('overflow_split_button') + self.verse_type_layout.addWidget(self.overflow_split_button) self.verse_type_label = QtWidgets.QLabel(edit_verse_dialog) self.verse_type_label.setObjectName('verse_type_label') self.verse_type_layout.addWidget(self.verse_type_label) @@ -94,8 +98,11 @@ class Ui_EditVerseDialog(object): self.verse_type_combo_box.setItemText(VerseType.Intro, VerseType.translated_names[VerseType.Intro]) self.verse_type_combo_box.setItemText(VerseType.Ending, VerseType.translated_names[VerseType.Ending]) self.verse_type_combo_box.setItemText(VerseType.Other, VerseType.translated_names[VerseType.Other]) - self.split_button.setText(UiStrings().Split) - self.split_button.setToolTip(UiStrings().SplitToolTip) + self.overflow_split_button.setText(UiStrings().Split) + self.overflow_split_button.setToolTip(UiStrings().SplitToolTip) + self.forced_split_button.setText(translate('SongsPlugin.EditVerseForm', '&Forced Split')) + self.forced_split_button.setToolTip(translate('SongsPlugin.EditVerseForm', 'Split the verse when displayed ' + 'regardless of the screen size.')) self.insert_button.setText(translate('SongsPlugin.EditVerseForm', '&Insert')) self.insert_button.setToolTip(translate('SongsPlugin.EditVerseForm', 'Split a slide into two by inserting a verse splitter.')) diff --git a/openlp/plugins/songs/forms/editverseform.py b/openlp/plugins/songs/forms/editverseform.py index 26e6d1cb4..09ba1e300 100644 --- a/openlp/plugins/songs/forms/editverseform.py +++ b/openlp/plugins/songs/forms/editverseform.py @@ -49,12 +49,13 @@ class EditVerseForm(QtWidgets.QDialog, Ui_EditVerseDialog): self.setupUi(self) self.has_single_verse = False self.insert_button.clicked.connect(self.on_insert_button_clicked) - self.split_button.clicked.connect(self.on_split_button_clicked) + self.overflow_split_button.clicked.connect(self.on_overflow_split_button_clicked) self.verse_text_edit.cursorPositionChanged.connect(self.on_cursor_position_changed) self.verse_type_combo_box.currentIndexChanged.connect(self.on_verse_type_combo_box_changed) + self.forced_split_button.clicked.connect(self.on_forced_split_button_clicked) if Settings().value('songs/enable chords'): - self.transpose_down_button.clicked.connect(self.on_transepose_down_button_clicked) - self.transpose_up_button.clicked.connect(self.on_transepose_up_button_clicked) + self.transpose_down_button.clicked.connect(self.on_transpose_down_button_clicked) + self.transpose_up_button.clicked.connect(self.on_transpose_up_button_clicked) def insert_verse(self, verse_tag, verse_num=1): """ @@ -69,13 +70,27 @@ class EditVerseForm(QtWidgets.QDialog, Ui_EditVerseDialog): 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): + def on_overflow_split_button_clicked(self): """ - The split button has been pressed + The optional split button has been pressed so we need add the split + """ + self._add_splitter_to_text('[---]') + + def on_forced_split_button_clicked(self): + """ + The force split button has been pressed so we need add the split + """ + self._add_splitter_to_text('[--}{--]') + + def _add_splitter_to_text(self, insert_string): + """ + Add a custom splitter to the song text + + :param insert_string: The string to insert + :return: """ text = self.verse_text_edit.toPlainText() position = self.verse_text_edit.textCursor().position() - insert_string = '[---]' if position and text[position - 1] != '\n': insert_string = '\n' + insert_string if position == len(text) or text[position] != '\n': @@ -102,7 +117,7 @@ class EditVerseForm(QtWidgets.QDialog, Ui_EditVerseDialog): """ self.update_suggested_verse_number() - def on_transepose_up_button_clicked(self): + def on_transpose_up_button_clicked(self): """ The transpose up button clicked """ @@ -119,7 +134,7 @@ class EditVerseForm(QtWidgets.QDialog, Ui_EditVerseDialog): self.verse_text_edit.setFocus() self.verse_text_edit.moveCursor(QtGui.QTextCursor.End) - def on_transepose_down_button_clicked(self): + def on_transpose_down_button_clicked(self): """ The transpose down button clicked """ diff --git a/openlp/plugins/songs/forms/mediafilesform.py b/openlp/plugins/songs/forms/mediafilesform.py index 10e9334f7..8718471ea 100644 --- a/openlp/plugins/songs/forms/mediafilesform.py +++ b/openlp/plugins/songs/forms/mediafilesform.py @@ -41,12 +41,19 @@ class MediaFilesForm(QtWidgets.QDialog, Ui_MediaFilesDialog): QtCore.Qt.WindowCloseButtonHint) self.setupUi(self) - def populate_files(self, files): + def populate_files(self, file_paths): + """ + :param list[openlp.core.common.path.Path] file_paths: + :return: + """ self.file_list_widget.clear() - for file in files: - item = QtWidgets.QListWidgetItem(os.path.split(file)[1]) - item.setData(QtCore.Qt.UserRole, file) + for file_path in file_paths: + item = QtWidgets.QListWidgetItem(file_path.name) + item.setData(QtCore.Qt.UserRole, file_path) self.file_list_widget.addItem(item) def get_selected_files(self): + """ + :rtype: list[openlp.core.common.path.Path] + """ return [item.data(QtCore.Qt.UserRole) for item in self.file_list_widget.selectedItems()] diff --git a/openlp/plugins/songs/forms/songexportform.py b/openlp/plugins/songs/forms/songexportform.py index 0df4868f2..28ca8054c 100644 --- a/openlp/plugins/songs/forms/songexportform.py +++ b/openlp/plugins/songs/forms/songexportform.py @@ -29,8 +29,10 @@ from PyQt5 import QtCore, QtWidgets from openlp.core.common.i18n import UiStrings, translate from openlp.core.common.registry import Registry -from openlp.core.lib import create_separated_list, build_icon +from openlp.core.common.settings import Settings +from openlp.core.lib import create_separated_list from openlp.core.lib.ui import critical_error_message_box +from openlp.core.ui.lib import PathEdit, PathType from openlp.core.ui.lib.wizard import OpenLPWizard, WizardStrings from openlp.plugins.songs.lib.db import Song from openlp.plugins.songs.lib.openlyricsexport import OpenLyricsExport @@ -77,7 +79,6 @@ class SongExportForm(OpenLPWizard): self.search_line_edit.textEdited.connect(self.on_search_line_edit_changed) self.uncheck_button.clicked.connect(self.on_uncheck_button_clicked) self.check_button.clicked.connect(self.on_check_button_clicked) - self.directory_button.clicked.connect(self.on_directory_button_clicked) def add_custom_pages(self): """ @@ -121,21 +122,15 @@ class SongExportForm(OpenLPWizard): self.grid_layout.setObjectName('range_layout') self.selected_list_widget = QtWidgets.QListWidget(self.export_song_page) self.selected_list_widget.setObjectName('selected_list_widget') - self.grid_layout.addWidget(self.selected_list_widget, 1, 0, 1, 1) - # FIXME: self.horizontal_layout is already defined above?!?!? Replace with Path Eidt! - self.horizontal_layout = QtWidgets.QHBoxLayout() - self.horizontal_layout.setObjectName('horizontal_layout') + self.grid_layout.addWidget(self.selected_list_widget, 1, 0, 1, 2) + self.output_directory_path_edit = PathEdit( + self.export_song_page, PathType.Directories, + dialog_caption=translate('SongsPlugin.ExportWizardForm', 'Select Destination Folder'), show_revert=False) + self.output_directory_path_edit.path = Settings().value('songs/last directory export') self.directory_label = QtWidgets.QLabel(self.export_song_page) self.directory_label.setObjectName('directory_label') - self.horizontal_layout.addWidget(self.directory_label) - self.directory_line_edit = QtWidgets.QLineEdit(self.export_song_page) - self.directory_line_edit.setObjectName('directory_line_edit') - self.horizontal_layout.addWidget(self.directory_line_edit) - self.directory_button = QtWidgets.QToolButton(self.export_song_page) - self.directory_button.setIcon(build_icon(':/exports/export_load.png')) - self.directory_button.setObjectName('directory_button') - self.horizontal_layout.addWidget(self.directory_button) - self.grid_layout.addLayout(self.horizontal_layout, 0, 0, 1, 1) + self.grid_layout.addWidget(self.directory_label, 0, 0) + self.grid_layout.addWidget(self.output_directory_path_edit, 0, 1) self.export_song_layout.addLayout(self.grid_layout) self.addPage(self.export_song_page) @@ -189,11 +184,12 @@ class SongExportForm(OpenLPWizard): self.selected_list_widget.addItem(song) return True elif self.currentPage() == self.export_song_page: - if not self.directory_line_edit.text(): + if not self.output_directory_path_edit.path: critical_error_message_box( translate('SongsPlugin.ExportWizardForm', 'No Save Location specified'), translate('SongsPlugin.ExportWizardForm', 'You need to specify a directory.')) return False + Settings().setValue('songs/last directory export', self.output_directory_path_edit.path) return True elif self.currentPage() == self.progress_page: self.available_list_widget.clear() @@ -212,8 +208,6 @@ class SongExportForm(OpenLPWizard): self.finish_button.setVisible(False) self.cancel_button.setVisible(True) self.available_list_widget.clear() - self.selected_list_widget.clear() - self.directory_line_edit.clear() self.search_line_edit.clear() # Load the list of songs. self.application.set_busy_cursor() @@ -248,7 +242,7 @@ class SongExportForm(OpenLPWizard): song.data(QtCore.Qt.UserRole) for song in find_list_widget_items(self.selected_list_widget) ] - exporter = OpenLyricsExport(self, songs, self.directory_line_edit.text()) + exporter = OpenLyricsExport(self, songs, self.output_directory_path_edit.path) try: if exporter.do_export(): self.progress_label.setText( @@ -292,15 +286,6 @@ class SongExportForm(OpenLPWizard): if not item.isHidden(): item.setCheckState(QtCore.Qt.Checked) - def on_directory_button_clicked(self): - """ - Called when the *directory_button* was clicked. Opens a dialog and writes - the path to *directory_line_edit*. - """ - self.get_folder( - translate('SongsPlugin.ExportWizardForm', 'Select Destination Folder'), - self.directory_line_edit, 'last directory export') - def find_list_widget_items(list_widget, text=''): """ diff --git a/openlp/plugins/songs/forms/songimportform.py b/openlp/plugins/songs/forms/songimportform.py index c2f6c8373..acfa4b5b8 100644 --- a/openlp/plugins/songs/forms/songimportform.py +++ b/openlp/plugins/songs/forms/songimportform.py @@ -23,15 +23,14 @@ The song import functions for OpenLP. """ import logging -import os from PyQt5 import QtCore, QtWidgets from openlp.core.common.i18n import UiStrings, translate -from openlp.core.common.path import path_to_str from openlp.core.common.registry import RegistryProperties from openlp.core.common.settings import Settings from openlp.core.lib.ui import critical_error_message_box +from openlp.core.ui.lib import PathEdit, PathType from openlp.core.ui.lib.filedialog import FileDialog from openlp.core.ui.lib.wizard import OpenLPWizard, WizardStrings from openlp.plugins.songs.lib.importer import SongFormat, SongFormatSelect @@ -94,9 +93,7 @@ class SongImportForm(OpenLPWizard, RegistryProperties): self.format_widgets[song_format]['addButton'].clicked.connect(self.on_add_button_clicked) self.format_widgets[song_format]['removeButton'].clicked.connect(self.on_remove_button_clicked) else: - self.format_widgets[song_format]['browseButton'].clicked.connect(self.on_browse_button_clicked) - self.format_widgets[song_format]['file_path_edit'].textChanged.\ - connect(self.on_filepath_edit_text_changed) + self.format_widgets[song_format]['path_edit'].pathChanged.connect(self.on_path_edit_path_changed) def add_custom_pages(self): """ @@ -156,7 +153,6 @@ class SongImportForm(OpenLPWizard, RegistryProperties): self.format_widgets[format_list]['removeButton'].setText( translate('SongsPlugin.ImportWizardForm', 'Remove File(s)')) else: - self.format_widgets[format_list]['browseButton'].setText(UiStrings().Browse) f_label = 'Filename:' if select_mode == SongFormatSelect.SingleFolder: f_label = 'Folder:' @@ -173,16 +169,11 @@ class SongImportForm(OpenLPWizard, RegistryProperties): self.error_save_to_button.setText(translate('SongsPlugin.ImportWizardForm', 'Save to File')) # Align all QFormLayouts towards each other. formats = [f for f in SongFormat.get_format_list() if 'filepathLabel' in self.format_widgets[f]] - labels = [self.format_widgets[f]['filepathLabel'] for f in formats] + labels = [self.format_widgets[f]['filepathLabel'] for f in formats] + [self.format_label] # Get max width of all labels - max_label_width = max(self.format_label.minimumSizeHint().width(), - max([label.minimumSizeHint().width() for label in labels])) - self.format_spacer.changeSize(max_label_width, 0, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) - spacers = [self.format_widgets[f]['filepathSpacer'] for f in formats] - for index, spacer in enumerate(spacers): - spacer.changeSize( - max_label_width - labels[index].minimumSizeHint().width(), 0, - QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + max_label_width = max(labels, key=lambda label: label.minimumSizeHint().width()).minimumSizeHint().width() + for label in labels: + label.setFixedWidth(max_label_width) # Align descriptionLabels with rest of layout for format_list in SongFormat.get_format_list(): if SongFormat.get(format_list, 'descriptionText') is not None: @@ -209,13 +200,13 @@ class SongImportForm(OpenLPWizard, RegistryProperties): Settings().setValue('songs/last import type', this_format) select_mode, class_, error_msg = SongFormat.get(this_format, 'selectMode', 'class', 'invalidSourceMsg') if select_mode == SongFormatSelect.MultipleFiles: - import_source = self.get_list_of_files(self.format_widgets[this_format]['file_list_widget']) + import_source = self.get_list_of_paths(self.format_widgets[this_format]['file_list_widget']) error_title = UiStrings().IFSp focus_button = self.format_widgets[this_format]['addButton'] else: - import_source = self.format_widgets[this_format]['file_path_edit'].text() + import_source = self.format_widgets[this_format]['path_edit'].path error_title = (UiStrings().IFSs if select_mode == SongFormatSelect.SingleFile else UiStrings().IFdSs) - focus_button = self.format_widgets[this_format]['browseButton'] + focus_button = self.format_widgets[this_format]['path_edit'] if not class_.is_valid_source(import_source): critical_error_message_box(error_title, error_msg) focus_button.setFocus() @@ -238,20 +229,23 @@ class SongImportForm(OpenLPWizard, RegistryProperties): if filters: filters += ';;' filters += '{text} (*)'.format(text=UiStrings().AllFiles) - file_paths, selected_filter = FileDialog.getOpenFileNames( - self, title, Settings().value(self.plugin.settings_section + '/last directory import'), filters) + file_paths, filter_used = FileDialog.getOpenFileNames( + self, title, + Settings().value(self.plugin.settings_section + '/last directory import'), filters) + for file_path in file_paths: + list_item = QtWidgets.QListWidgetItem(str(file_path)) + list_item.setData(QtCore.Qt.UserRole, file_path) + listbox.addItem(list_item) if file_paths: - file_names = [path_to_str(file_path) for file_path in file_paths] - listbox.addItems(file_names) Settings().setValue(self.plugin.settings_section + '/last directory import', file_paths[0].parent) - def get_list_of_files(self, list_box): + def get_list_of_paths(self, list_box): """ Return a list of file from the list_box :param list_box: The source list box """ - return [list_box.item(i).text() for i in range(list_box.count())] + return [list_box.item(i).data(QtCore.Qt.UserRole) for i in range(list_box.count())] def remove_selected_items(self, list_box): """ @@ -263,20 +257,6 @@ class SongImportForm(OpenLPWizard, RegistryProperties): item = list_box.takeItem(list_box.row(item)) del item - def on_browse_button_clicked(self): - """ - Browse for files or a directory. - """ - this_format = self.current_format - 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: - self.get_file_name(WizardStrings.OpenTypeFile.format(file_type=format_name), - file_path_edit, 'last directory import', ext_filter) - elif select_mode == SongFormatSelect.SingleFolder: - self.get_folder( - WizardStrings.OpenTypeFolder.format(folder_name=format_name), file_path_edit, 'last directory import') - def on_add_button_clicked(self): """ Add a file or directory. @@ -296,7 +276,7 @@ class SongImportForm(OpenLPWizard, RegistryProperties): self.remove_selected_items(self.format_widgets[self.current_format]['file_list_widget']) self.source_page.completeChanged.emit() - def on_filepath_edit_text_changed(self): + def on_path_edit_path_changed(self): """ Called when the content of the Filename/Folder edit box changes. """ @@ -317,8 +297,6 @@ class SongImportForm(OpenLPWizard, RegistryProperties): select_mode = SongFormat.get(format_list, 'selectMode') if select_mode == SongFormatSelect.MultipleFiles: self.format_widgets[format_list]['file_list_widget'].clear() - else: - self.format_widgets[format_list]['file_path_edit'].setText('') self.error_report_text_edit.clear() self.error_report_text_edit.setHidden(True) self.error_copy_to_button.setHidden(True) @@ -341,14 +319,14 @@ class SongImportForm(OpenLPWizard, RegistryProperties): select_mode = SongFormat.get(source_format, 'selectMode') if select_mode == SongFormatSelect.SingleFile: importer = self.plugin.import_songs(source_format, - filename=self.format_widgets[source_format]['file_path_edit'].text()) + file_path=self.format_widgets[source_format]['path_edit'].path) elif select_mode == SongFormatSelect.SingleFolder: importer = self.plugin.import_songs(source_format, - folder=self.format_widgets[source_format]['file_path_edit'].text()) + folder_path=self.format_widgets[source_format]['path_edit'].path) else: importer = self.plugin.import_songs( source_format, - filenames=self.get_list_of_files(self.format_widgets[source_format]['file_list_widget'])) + file_paths=self.get_list_of_paths(self.format_widgets[source_format]['file_list_widget'])) importer.do_import() self.progress_label.setText(WizardStrings.FinishedImport) @@ -366,18 +344,17 @@ class SongImportForm(OpenLPWizard, RegistryProperties): """ file_path, filter_used = FileDialog.getSaveFileName( self, Settings().value(self.plugin.settings_section + '/last directory import')) - if not file_path: + if file_path is None: return - with file_path.open('w', encoding='utf-8') as report_file: - report_file.write(self.error_report_text_edit.toPlainText()) + file_path.write_text(self.error_report_text_edit.toPlainText(), encoding='utf-8') def add_file_select_item(self): """ Add a file selection page. """ this_format = self.current_format - prefix, can_disable, description_text, select_mode = \ - SongFormat.get(this_format, 'prefix', 'canDisable', 'descriptionText', 'selectMode') + format_name, prefix, can_disable, description_text, select_mode, filters = \ + SongFormat.get(this_format, 'name', 'prefix', 'canDisable', 'descriptionText', 'selectMode', 'filter') page = QtWidgets.QWidget() page.setObjectName(prefix + 'Page') if can_disable: @@ -403,26 +380,23 @@ class SongImportForm(OpenLPWizard, RegistryProperties): if select_mode == SongFormatSelect.SingleFile or select_mode == SongFormatSelect.SingleFolder: file_path_layout = QtWidgets.QHBoxLayout() file_path_layout.setObjectName(prefix + '_file_path_layout') - file_path_layout.setContentsMargins(0, self.format_v_spacing, 0, 0) file_path_label = QtWidgets.QLabel(import_widget) - file_path_label.setObjectName(prefix + 'FilepathLabel') file_path_layout.addWidget(file_path_label) - file_path_spacer = QtWidgets.QSpacerItem(0, 0, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) - file_path_layout.addSpacerItem(file_path_spacer) - file_path_edit = QtWidgets.QLineEdit(import_widget) - file_path_edit.setObjectName(prefix + '_file_path_edit') - file_path_layout.addWidget(file_path_edit) - browse_button = QtWidgets.QToolButton(import_widget) - browse_button.setIcon(self.open_icon) - browse_button.setObjectName(prefix + 'BrowseButton') - file_path_layout.addWidget(browse_button) + if select_mode == SongFormatSelect.SingleFile: + path_type = PathType.Files + dialog_caption = WizardStrings.OpenTypeFile.format(file_type=format_name) + else: + path_type = PathType.Directories + dialog_caption = WizardStrings.OpenTypeFolder.format(folder_name=format_name) + path_edit = PathEdit( + parent=import_widget, path_type=path_type, dialog_caption=dialog_caption, show_revert=False) + path_edit.filters = path_edit.filters + filters + path_edit.path = Settings().value(self.plugin.settings_section + '/last directory import') + file_path_layout.addWidget(path_edit) import_layout.addLayout(file_path_layout) import_layout.addSpacerItem(self.stack_spacer) self.format_widgets[this_format]['filepathLabel'] = file_path_label - self.format_widgets[this_format]['filepathSpacer'] = file_path_spacer - self.format_widgets[this_format]['file_path_layout'] = file_path_layout - self.format_widgets[this_format]['file_path_edit'] = file_path_edit - self.format_widgets[this_format]['browseButton'] = browse_button + self.format_widgets[this_format]['path_edit'] = path_edit elif select_mode == SongFormatSelect.MultipleFiles: file_list_widget = QtWidgets.QListWidget(import_widget) file_list_widget.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) @@ -496,6 +470,8 @@ class SongImportSourcePage(QtWidgets.QWizardPage): * or if SingleFolder mode, the specified folder exists When this method returns True, the wizard's Next button is enabled. + + :rtype: bool """ wizard = self.wizard() this_format = wizard.current_format @@ -505,10 +481,10 @@ class SongImportSourcePage(QtWidgets.QWizardPage): if wizard.format_widgets[this_format]['file_list_widget'].count() > 0: return True else: - file_path = str(wizard.format_widgets[this_format]['file_path_edit'].text()) + file_path = wizard.format_widgets[this_format]['path_edit'].path if file_path: - if select_mode == SongFormatSelect.SingleFile and os.path.isfile(file_path): + if select_mode == SongFormatSelect.SingleFile and file_path.is_file(): return True - elif select_mode == SongFormatSelect.SingleFolder and os.path.isdir(file_path): + elif select_mode == SongFormatSelect.SingleFolder and file_path.is_dir(): return True return False diff --git a/openlp/plugins/songs/lib/__init__.py b/openlp/plugins/songs/lib/__init__.py index f73ecb907..f88aa8678 100644 --- a/openlp/plugins/songs/lib/__init__.py +++ b/openlp/plugins/songs/lib/__init__.py @@ -537,24 +537,24 @@ def delete_song(song_id, song_plugin): media_files = song_plugin.manager.get_all_objects(MediaFile, MediaFile.song_id == song_id) for media_file in media_files: try: - os.remove(media_file.file_name) + media_file.file_path.unlink() except OSError: - log.exception('Could not remove file: {name}'.format(name=media_file.file_name)) + log.exception('Could not remove file: {name}'.format(name=media_file.file_path)) try: - save_path = os.path.join(str(AppLocation.get_section_data_path(song_plugin.name)), 'audio', str(song_id)) - if os.path.exists(save_path): - os.rmdir(save_path) + save_path = AppLocation.get_section_data_path(song_plugin.name) / 'audio' / str(song_id) + if save_path.exists(): + save_path.rmdir() except OSError: log.exception('Could not remove directory: {path}'.format(path=save_path)) song_plugin.manager.delete_object(Song, song_id) -def transpose_lyrics(lyrics, transepose_value): +def transpose_lyrics(lyrics, transpose_value): """ - Transepose lyrics + Transpose lyrics - :param lyrcs: The lyrics to be transposed - :param transepose_value: The value to transpose the lyrics with + :param lyrics: The lyrics to be transposed + :param transpose_value: The value to transpose the lyrics with :return: The transposed lyrics """ # Split text by verse delimiter - both normal and optional @@ -565,16 +565,17 @@ def transpose_lyrics(lyrics, transepose_value): if verse.startswith('---[') or verse == '[---]': transposed_lyrics += verse else: - transposed_lyrics += transpose_verse(verse, transepose_value, notation) + transposed_lyrics += transpose_verse(verse, transpose_value, notation) return transposed_lyrics -def transpose_verse(verse_text, transepose_value, notation): +def transpose_verse(verse_text, transpose_value, notation): """ - Transepose lyrics + Transpose Verse - :param lyrcs: The lyrics to be transposed - :param transepose_value: The value to transpose the lyrics with + :param verse_text: The lyrics to be transposed + :param transpose_value: The value to transpose the lyrics with + :param notation: which notation to use :return: The transposed lyrics """ if '[' not in verse_text: @@ -592,11 +593,11 @@ def transpose_verse(verse_text, transepose_value, notation): if word == ']': in_tag = False transposed_lyrics += word - elif word == '/': + elif word == '/' or word == '--}{--': transposed_lyrics += word else: # This MUST be a chord - transposed_lyrics += transpose_chord(word, transepose_value, notation) + transposed_lyrics += transpose_chord(word, transpose_value, notation) # If still inside a chord tag something is wrong! if in_tag: return verse_text @@ -632,36 +633,36 @@ def transpose_chord(chord, transpose_value, notation): for i in range(0, len(chord_split)): if i > 0: transposed_chord += '/' - currentchord = chord_split[i] - if currentchord and currentchord[0] == '(': + current_chord = chord_split[i] + if current_chord and current_chord[0] == '(': transposed_chord += '(' - if len(currentchord) > 1: - currentchord = currentchord[1:] + if len(current_chord) > 1: + current_chord = current_chord[1:] else: - currentchord = '' - if len(currentchord) > 0: - if len(currentchord) > 1: - if '#b'.find(currentchord[1]) == -1: - note = currentchord[0:1] - rest = currentchord[1:] + current_chord = '' + if len(current_chord) > 0: + if len(current_chord) > 1: + if '#b'.find(current_chord[1]) == -1: + note = current_chord[0:1] + rest = current_chord[1:] else: - note = currentchord[0:2] - rest = currentchord[2:] + note = current_chord[0:2] + rest = current_chord[2:] else: - note = currentchord + note = current_chord rest = '' - notenumber = notes_flat.index(note) if note not in notes_sharp else notes_sharp.index(note) - notenumber += transpose_value - while notenumber > 11: - notenumber -= 12 - while notenumber < 0: - notenumber += 12 + note_number = notes_flat.index(note) if note not in notes_sharp else notes_sharp.index(note) + note_number += transpose_value + while note_number > 11: + note_number -= 12 + while note_number < 0: + note_number += 12 if i == 0: - current_chord = notes_sharp[notenumber] if notes_preferred[notenumber] == '#' else notes_flat[ - notenumber] + current_chord = notes_sharp[note_number] if notes_preferred[note_number] == '#' else notes_flat[ + note_number] last_chord = current_chord else: - current_chord = notes_flat[notenumber] if last_chord not in notes_sharp else notes_sharp[notenumber] + current_chord = notes_flat[note_number] if last_chord not in notes_sharp else notes_sharp[note_number] if not (note not in notes_flat and note not in notes_sharp): transposed_chord += current_chord + rest else: diff --git a/openlp/plugins/songs/lib/db.py b/openlp/plugins/songs/lib/db.py index 51aa507ac..0963fd084 100644 --- a/openlp/plugins/songs/lib/db.py +++ b/openlp/plugins/songs/lib/db.py @@ -23,13 +23,12 @@ The :mod:`db` module provides the database and schema that is the backend for the Songs plugin """ - from sqlalchemy import Column, ForeignKey, Table, types from sqlalchemy.orm import mapper, relation, reconstructor from sqlalchemy.sql.expression import func, text -from openlp.core.lib.db import BaseModel, init_db from openlp.core.common.i18n import translate, get_natural_key +from openlp.core.lib.db import BaseModel, PathType, init_db class Author(BaseModel): @@ -237,7 +236,7 @@ def init_schema(url): **media_files Table** * id - * file_name + * _file_path * type **song_books Table** @@ -304,7 +303,7 @@ def init_schema(url): 'media_files', metadata, Column('id', types.Integer(), primary_key=True), Column('song_id', types.Integer(), ForeignKey('songs.id'), default=None), - Column('file_name', types.Unicode(255), nullable=False), + Column('file_path', PathType, nullable=False), Column('type', types.Unicode(64), nullable=False, default='audio'), Column('weight', types.Integer(), default=0) ) diff --git a/openlp/plugins/songs/lib/importers/cclifile.py b/openlp/plugins/songs/lib/importers/cclifile.py index 1137d6b13..10a8b09e1 100644 --- a/openlp/plugins/songs/lib/importers/cclifile.py +++ b/openlp/plugins/songs/lib/importers/cclifile.py @@ -19,11 +19,9 @@ # with this program; if not, write to the Free Software Foundation, Inc., 59 # # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### - -import logging -import os import chardet import codecs +import logging from openlp.core.common.i18n import translate from openlp.plugins.songs.lib import VerseType @@ -48,7 +46,7 @@ class CCLIFileImport(SongImport): :param manager: The song manager for the running OpenLP installation. :param kwargs: The files to be imported. """ - super(CCLIFileImport, self).__init__(manager, **kwargs) + super().__init__(manager, **kwargs) def do_import(self): """ @@ -56,37 +54,35 @@ class CCLIFileImport(SongImport): """ log.debug('Starting CCLI File Import') self.import_wizard.progress_bar.setMaximum(len(self.import_source)) - for filename in self.import_source: - filename = str(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) - try: - str(detect_content, 'utf-8') - details = {'confidence': 1, 'encoding': 'utf-8'} - except UnicodeDecodeError: - details = chardet.detect(detect_content) - detect_file.close() - infile = codecs.open(filename, 'r', details['encoding']) - if not infile.read(1) == '\ufeff': + for file_path in self.import_source: + log.debug('Importing CCLI File: {name}'.format(name=file_path)) + if file_path.is_file(): + with file_path.open('rb') as detect_file: + detect_content = detect_file.read(2048) + try: + str(detect_content, 'utf-8') + details = {'confidence': 1, 'encoding': 'utf-8'} + except UnicodeDecodeError: + details = chardet.detect(detect_content) + in_file = codecs.open(str(file_path), 'r', details['encoding']) + if not in_file.read(1) == '\ufeff': # not UTF or no BOM was found - infile.seek(0) - lines = infile.readlines() - infile.close() - ext = os.path.splitext(filename)[1] - if ext.lower() == '.usr' or ext.lower() == '.bin': - log.info('SongSelect USR format file found: {name}'.format(name=filename)) + in_file.seek(0) + lines = in_file.readlines() + in_file.close() + ext = file_path.suffix.lower() + if ext == '.usr' or ext == '.bin': + log.info('SongSelect USR format file found: {name}'.format(name=file_path)) if not self.do_import_usr_file(lines): - self.log_error(filename) - elif ext.lower() == '.txt': - log.info('SongSelect TEXT format file found: {name}'.format(name=filename)) + self.log_error(file_path) + elif ext == '.txt': + log.info('SongSelect TEXT format file found: {name}'.format(name=file_path)) if not self.do_import_txt_file(lines): - self.log_error(filename) + self.log_error(file_path) else: - self.log_error(filename, translate('SongsPlugin.CCLIFileImport', 'The file does not have a valid ' - 'extension.')) - log.info('Extension {name} is not valid'.format(name=filename)) + self.log_error(file_path, translate('SongsPlugin.CCLIFileImport', + 'The file does not have a valid extension.')) + log.info('Extension {name} is not valid'.format(name=file_path)) if self.stop_import_flag: return diff --git a/openlp/plugins/songs/lib/importers/chordpro.py b/openlp/plugins/songs/lib/importers/chordpro.py index b5d526634..0d4c4d4f2 100644 --- a/openlp/plugins/songs/lib/importers/chordpro.py +++ b/openlp/plugins/songs/lib/importers/chordpro.py @@ -45,12 +45,11 @@ class ChordProImport(SongImport): """ def do_import(self): self.import_wizard.progress_bar.setMaximum(len(self.import_source)) - for filename in self.import_source: + for file_path in self.import_source: if self.stop_import_flag: return - song_file = open(filename, 'rt') - self.do_import_file(song_file) - song_file.close() + with file_path.open('rt') as song_file: + self.do_import_file(song_file) def do_import_file(self, song_file): """ diff --git a/openlp/plugins/songs/lib/importers/dreambeam.py b/openlp/plugins/songs/lib/importers/dreambeam.py index bcca877f4..eb2ec1ded 100644 --- a/openlp/plugins/songs/lib/importers/dreambeam.py +++ b/openlp/plugins/songs/lib/importers/dreambeam.py @@ -78,27 +78,29 @@ class DreamBeamImport(SongImport): def do_import(self): """ - Receive a single file or a list of files to import. + Receive a single file_path or a list of files to import. """ if isinstance(self.import_source, list): self.import_wizard.progress_bar.setMaximum(len(self.import_source)) - for file in self.import_source: + for file_path in self.import_source: if self.stop_import_flag: return self.set_defaults() parser = etree.XMLParser(remove_blank_text=True) try: - parsed_file = etree.parse(open(file, 'r'), parser) + with file_path.open('r') as xml_file: + parsed_file = etree.parse(xml_file, parser) except etree.XMLSyntaxError: - log.exception('XML syntax error in file {name}'.format(name=file)) - self.log_error(file, SongStrings.XMLSyntaxError) + log.exception('XML syntax error in file_path {name}'.format(name=file_path)) + self.log_error(file_path, SongStrings.XMLSyntaxError) continue xml = etree.tostring(parsed_file).decode() song_xml = objectify.fromstring(xml) if song_xml.tag != 'DreamSong': self.log_error( - file, - translate('SongsPlugin.DreamBeamImport', 'Invalid DreamBeam song file. Missing DreamSong tag.')) + file_path, + translate('SongsPlugin.DreamBeamImport', + 'Invalid DreamBeam song file_path. Missing DreamSong tag.')) continue if hasattr(song_xml, 'Version'): self.version = float(song_xml.Version.text) @@ -144,4 +146,4 @@ class DreamBeamImport(SongImport): else: self.parse_author(author_copyright) if not self.finish(): - self.log_error(file) + self.log_error(file_path) diff --git a/openlp/plugins/songs/lib/importers/easyslides.py b/openlp/plugins/songs/lib/importers/easyslides.py index 7c2ffd2c9..a1ffb7b7c 100644 --- a/openlp/plugins/songs/lib/importers/easyslides.py +++ b/openlp/plugins/songs/lib/importers/easyslides.py @@ -47,7 +47,7 @@ class EasySlidesImport(SongImport): def do_import(self): log.info('Importing EasySlides XML file {source}'.format(source=self.import_source)) parser = etree.XMLParser(remove_blank_text=True, recover=True) - parsed_file = etree.parse(self.import_source, parser) + parsed_file = etree.parse(str(self.import_source), parser) xml = etree.tostring(parsed_file).decode() song_xml = objectify.fromstring(xml) self.import_wizard.progress_bar.setMaximum(len(song_xml.Item)) diff --git a/openlp/plugins/songs/lib/importers/easyworship.py b/openlp/plugins/songs/lib/importers/easyworship.py index 50b26a906..c82a7e6ad 100644 --- a/openlp/plugins/songs/lib/importers/easyworship.py +++ b/openlp/plugins/songs/lib/importers/easyworship.py @@ -22,15 +22,16 @@ """ The :mod:`easyworship` module provides the functionality for importing EasyWorship song databases into OpenLP. """ - -import os -import struct -import re -import zlib import logging +import os +import re +import struct +import zlib + import sqlite3 from openlp.core.common.i18n import translate +from openlp.core.common.path import Path from openlp.plugins.songs.lib import VerseType from openlp.plugins.songs.lib import retrieve_windows_encoding, strip_rtf from .songimport import SongImport @@ -76,9 +77,11 @@ class EasyWorshipSongImport(SongImport): """ Determines the type of file to import and calls the appropiate method """ - if self.import_source.lower().endswith('ews'): + self.import_source = Path(self.import_source) + ext = self.import_source.suffix.lower() + if ext == '.ews': self.import_ews() - elif self.import_source.endswith('DB'): + elif ext == '.db': self.import_db() else: self.import_sqlite_db() @@ -91,11 +94,11 @@ class EasyWorshipSongImport(SongImport): or here: http://wiki.openlp.org/Development:EasyWorship_EWS_Format """ # Open ews file if it exists - if not os.path.isfile(self.import_source): + if not self.import_source.is_file(): log.debug('Given ews file does not exists.') return # Make sure there is room for at least a header and one entry - if os.path.getsize(self.import_source) < 892: + if self.import_source.stat().st_size < 892: log.debug('Given ews file is to small to contain valid data.') return # Take a stab at how text is encoded @@ -104,7 +107,7 @@ class EasyWorshipSongImport(SongImport): if not self.encoding: log.debug('No encoding set.') return - self.ews_file = open(self.import_source, 'rb') + self.ews_file = self.import_source.open('rb') # EWS header, version '1.6'/' 3'/' 5': # Offset Field Data type Length Details # -------------------------------------------------------------------------------------------------- @@ -199,23 +202,22 @@ class EasyWorshipSongImport(SongImport): Import the songs from the database """ # Open the DB and MB files if they exist - import_source_mb = self.import_source.replace('.DB', '.MB') - if not os.path.isfile(self.import_source): + import_source_mb = self.import_source.with_suffix('.MB') + if not self.import_source.is_file(): self.log_error(self.import_source, translate('SongsPlugin.EasyWorshipSongImport', 'This file does not exist.')) return - if not os.path.isfile(import_source_mb): + if not import_source_mb.is_file(): self.log_error(self.import_source, translate('SongsPlugin.EasyWorshipSongImport', 'Could not find the "Songs.MB" file. It must be in the same ' 'folder as the "Songs.DB" file.')) return - db_size = os.path.getsize(self.import_source) - if db_size < 0x800: + if self.import_source.stat().st_size < 0x800: self.log_error(self.import_source, translate('SongsPlugin.EasyWorshipSongImport', 'This file is not a valid EasyWorship database.')) return - db_file = open(self.import_source, 'rb') - self.memo_file = open(import_source_mb, 'rb') + db_file = self.import_source.open('rb') + self.memo_file = import_source_mb.open('rb') # Don't accept files that are clearly not paradox files record_size, header_size, block_size, first_block, num_fields = struct.unpack(' 4: @@ -340,52 +342,34 @@ class EasyWorshipSongImport(SongImport): db_file.close() self.memo_file.close() - def _find_file(self, base_path, path_list): - """ - Find the specified file, with the option of the file being at any level in the specified directory structure. - - :param base_path: the location search in - :param path_list: the targeted file, preceded by directories that may be their parents relative to the base_path - :return: path for targeted file - """ - target_file = '' - while len(path_list) > 0: - target_file = os.path.join(path_list[-1], target_file) - path_list = path_list[:len(path_list) - 1] - full_path = os.path.join(base_path, target_file) - full_path = full_path[:len(full_path) - 1] - if os.path.isfile(full_path): - return full_path - return '' - def import_sqlite_db(self): """ Import the songs from an EasyWorship 6 SQLite database """ - songs_db_path = self._find_file(self.import_source, ["Databases", "Data", "Songs.db"]) - song_words_db_path = self._find_file(self.import_source, ["Databases", "Data", "SongWords.db"]) - invalid_dir_msg = 'This does not appear to be a valid Easy Worship 6 database directory.' + songs_db_path = next(self.import_source.rglob('Songs.db'), None) + song_words_db_path = next(self.import_source.rglob('SongWords.db'), None) + invalid_dir_msg = translate('SongsPlugin.EasyWorshipSongImport', + 'This does not appear to be a valid Easy Worship 6 database directory.') + invalid_db_msg = translate('SongsPlugin.EasyWorshipSongImport', 'This is not a valid Easy Worship 6 database.') # check to see if needed files are there - if not os.path.isfile(songs_db_path): - self.log_error(songs_db_path, translate('SongsPlugin.EasyWorshipSongImport', invalid_dir_msg)) + if not (songs_db_path and songs_db_path.is_file()): + self.log_error(self.import_source, invalid_dir_msg) return - if not os.path.isfile(song_words_db_path): - self.log_error(song_words_db_path, translate('SongsPlugin.EasyWorshipSongImport', invalid_dir_msg)) + if not (song_words_db_path and song_words_db_path.is_file()): + self.log_error(self.import_source, invalid_dir_msg) return # get database handles - songs_conn = sqlite3.connect(songs_db_path) - words_conn = sqlite3.connect(song_words_db_path) + songs_conn = sqlite3.connect(str(songs_db_path)) + words_conn = sqlite3.connect(str(song_words_db_path)) if songs_conn is None or words_conn is None: - self.log_error(self.import_source, translate('SongsPlugin.EasyWorshipSongImport', - 'This is not a valid Easy Worship 6 database.')) + self.log_error(self.import_source, invalid_db_msg) songs_conn.close() words_conn.close() return songs_db = songs_conn.cursor() words_db = words_conn.cursor() if songs_conn is None or words_conn is None: - self.log_error(self.import_source, translate('SongsPlugin.EasyWorshipSongImport', - 'This is not a valid Easy Worship 6 database.')) + self.log_error(self.import_source, invalid_db_msg) songs_conn.close() words_conn.close() return diff --git a/openlp/plugins/songs/lib/importers/foilpresenter.py b/openlp/plugins/songs/lib/importers/foilpresenter.py index 660aaa31a..860177172 100644 --- a/openlp/plugins/songs/lib/importers/foilpresenter.py +++ b/openlp/plugins/songs/lib/importers/foilpresenter.py @@ -82,10 +82,8 @@ The XML of `Foilpresenter `_ songs is of the format:: """ - import logging import re -import os from lxml import etree, objectify @@ -121,10 +119,9 @@ class FoilPresenterImport(SongImport): for file_path in self.import_source: if self.stop_import_flag: return - self.import_wizard.increment_progress_bar( - WizardStrings.ImportingType.format(source=os.path.basename(file_path))) + self.import_wizard.increment_progress_bar(WizardStrings.ImportingType.format(source=file_path.name)) try: - parsed_file = etree.parse(file_path, parser) + parsed_file = etree.parse(str(file_path), parser) xml = etree.tostring(parsed_file).decode() self.foil_presenter.xml_to_song(xml) except etree.XMLSyntaxError: diff --git a/openlp/plugins/songs/lib/importers/lyrix.py b/openlp/plugins/songs/lib/importers/lyrix.py index 31816326e..c8dbac24b 100644 --- a/openlp/plugins/songs/lib/importers/lyrix.py +++ b/openlp/plugins/songs/lib/importers/lyrix.py @@ -50,12 +50,11 @@ class LyrixImport(SongImport): if not isinstance(self.import_source, list): return self.import_wizard.progress_bar.setMaximum(len(self.import_source)) - for filename in self.import_source: + for file_path in self.import_source: if self.stop_import_flag: return - song_file = open(filename, 'rt', encoding='cp1251') - self.do_import_file(song_file) - song_file.close() + with file_path.open('rt', encoding='cp1251') as song_file: + self.do_import_file(song_file) def do_import_file(self, file): """ diff --git a/openlp/plugins/songs/lib/importers/openlp.py b/openlp/plugins/songs/lib/importers/openlp.py index c78bb6d3a..252b8fd8b 100644 --- a/openlp/plugins/songs/lib/importers/openlp.py +++ b/openlp/plugins/songs/lib/importers/openlp.py @@ -266,7 +266,7 @@ class OpenLPSongImport(SongImport): if has_media_files and song.media_files: for media_file in song.media_files: existing_media_file = self.manager.get_object_filtered( - MediaFile, MediaFile.file_name == media_file.file_name) + MediaFile, MediaFile.file_path == media_file.file_path) if existing_media_file: new_song.media_files.append(existing_media_file) else: diff --git a/openlp/plugins/songs/lib/importers/openlyrics.py b/openlp/plugins/songs/lib/importers/openlyrics.py index 09cc1ef91..44f5f96bf 100644 --- a/openlp/plugins/songs/lib/importers/openlyrics.py +++ b/openlp/plugins/songs/lib/importers/openlyrics.py @@ -23,9 +23,7 @@ The :mod:`openlyrics` module provides the functionality for importing songs which are saved as OpenLyrics files. """ - import logging -import os from lxml import etree @@ -58,12 +56,11 @@ class OpenLyricsImport(SongImport): for file_path in self.import_source: if self.stop_import_flag: return - self.import_wizard.increment_progress_bar( - WizardStrings.ImportingType.format(source=os.path.basename(file_path))) + self.import_wizard.increment_progress_bar(WizardStrings.ImportingType.format(source=file_path.name)) try: # Pass a file object, because lxml does not cope with some # special characters in the path (see lp:757673 and lp:744337). - parsed_file = etree.parse(open(file_path, 'rb'), parser) + parsed_file = etree.parse(file_path.open('rb'), parser) xml = etree.tostring(parsed_file).decode() self.open_lyrics.xml_to_song(xml) except etree.XMLSyntaxError: diff --git a/openlp/plugins/songs/lib/importers/openoffice.py b/openlp/plugins/songs/lib/importers/openoffice.py index 43a15a894..a097d8b85 100644 --- a/openlp/plugins/songs/lib/importers/openoffice.py +++ b/openlp/plugins/songs/lib/importers/openoffice.py @@ -20,7 +20,6 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### import logging -import os import time from PyQt5 import QtCore @@ -70,12 +69,11 @@ class OpenOfficeImport(SongImport): log.error(exc) return self.import_wizard.progress_bar.setMaximum(len(self.import_source)) - for filename in self.import_source: + for file_path in self.import_source: if self.stop_import_flag: break - filename = str(filename) - if os.path.isfile(filename): - self.open_ooo_file(filename) + if file_path.is_file(): + self.open_ooo_file(file_path) if self.document: self.process_ooo_document() self.close_ooo_file() @@ -144,12 +142,7 @@ class OpenOfficeImport(SongImport): Open the passed file in OpenOffice.org Impress """ self.file_path = file_path - if is_win(): - url = file_path.replace('\\', '/') - url = url.replace(':', '|').replace(' ', '%20') - url = 'file:///' + url - else: - url = uno.systemPathToFileUrl(file_path) + url = file_path.as_uri() properties = [] properties.append(self.create_property('Hidden', True)) properties = tuple(properties) @@ -159,7 +152,7 @@ class OpenOfficeImport(SongImport): self.document.supportsService("com.sun.star.text.TextDocument"): self.close_ooo_file() else: - self.import_wizard.increment_progress_bar('Processing file ' + file_path, 0) + self.import_wizard.increment_progress_bar('Processing file {file_path}'.format(file_path=file_path), 0) except AttributeError: log.exception("open_ooo_file failed: {url}".format(url=url)) return diff --git a/openlp/plugins/songs/lib/importers/opensong.py b/openlp/plugins/songs/lib/importers/opensong.py index c98f29a4e..e6924e7b2 100644 --- a/openlp/plugins/songs/lib/importers/opensong.py +++ b/openlp/plugins/songs/lib/importers/opensong.py @@ -116,12 +116,11 @@ class OpenSongImport(SongImport): if not isinstance(self.import_source, list): return self.import_wizard.progress_bar.setMaximum(len(self.import_source)) - for filename in self.import_source: + for file_path in self.import_source: if self.stop_import_flag: return - song_file = open(filename, 'rb') - self.do_import_file(song_file) - song_file.close() + with file_path.open('rb') as song_file: + self.do_import_file(song_file) def do_import_file(self, file): """ diff --git a/openlp/plugins/songs/lib/importers/opspro.py b/openlp/plugins/songs/lib/importers/opspro.py index 4e43e71f8..f7dba83b8 100644 --- a/openlp/plugins/songs/lib/importers/opspro.py +++ b/openlp/plugins/songs/lib/importers/opspro.py @@ -231,16 +231,15 @@ class OPSProImport(SongImport): xor_pattern_2k = (0xa1, 0xec, 0x7a, 0x9c, 0xe1, 0x28, 0x34, 0x8a, 0x73, 0x7b, 0xd2, 0xdf, 0x50) # Access97 XOR of the source xor_pattern_97 = (0x86, 0xfb, 0xec, 0x37, 0x5d, 0x44, 0x9c, 0xfa, 0xc6, 0x5e, 0x28, 0xe6, 0x13) - mdb = open(self.import_source, 'rb') - mdb.seek(0x14) - version = struct.unpack('B', mdb.read(1))[0] - # Get encrypted logo - mdb.seek(0x62) - EncrypFlag = struct.unpack('B', mdb.read(1))[0] - # Get encrypted password - mdb.seek(0x42) - encrypted_password = mdb.read(26) - mdb.close() + with self.import_source.open('rb') as mdb_file: + mdb_file.seek(0x14) + version = struct.unpack('B', mdb_file.read(1))[0] + # Get encrypted logo + mdb_file.seek(0x62) + EncrypFlag = struct.unpack('B', mdb_file.read(1))[0] + # Get encrypted password + mdb_file.seek(0x42) + encrypted_password = mdb_file.read(26) # "Decrypt" the password based on the version decrypted_password = '' if version < 0x01: diff --git a/openlp/plugins/songs/lib/importers/powerpraise.py b/openlp/plugins/songs/lib/importers/powerpraise.py index a652cf58c..a08652e3f 100644 --- a/openlp/plugins/songs/lib/importers/powerpraise.py +++ b/openlp/plugins/songs/lib/importers/powerpraise.py @@ -23,8 +23,6 @@ The :mod:`powerpraiseimport` module provides the functionality for importing Powerpraise song files into the current database. """ - -import os from lxml import objectify from openlp.core.ui.lib.wizard import WizardStrings @@ -41,10 +39,10 @@ class PowerPraiseImport(SongImport): for file_path in self.import_source: if self.stop_import_flag: return - self.import_wizard.increment_progress_bar( - WizardStrings.ImportingType.format(source=os.path.basename(file_path))) - root = objectify.parse(open(file_path, 'rb')).getroot() - self.process_song(root) + self.import_wizard.increment_progress_bar(WizardStrings.ImportingType.format(source=file_path.name)) + with file_path.open('rb') as xml_file: + root = objectify.parse(xml_file).getroot() + self.process_song(root) def process_song(self, root): self.set_defaults() diff --git a/openlp/plugins/songs/lib/importers/powersong.py b/openlp/plugins/songs/lib/importers/powersong.py index a500ccc7a..7fac4ef75 100644 --- a/openlp/plugins/songs/lib/importers/powersong.py +++ b/openlp/plugins/songs/lib/importers/powersong.py @@ -72,10 +72,14 @@ class PowerSongImport(SongImport): Checks if source is a PowerSong 1.0 folder: * is a directory * contains at least one \*.song file + + :param openlp.core.common.path.Path import_source: Should be a Path object that fulfills the above criteria + :return: If the source is valid + :rtype: bool """ - if os.path.isdir(import_source): - for file in os.listdir(import_source): - if fnmatch.fnmatch(file, '*.song'): + if import_source.is_dir(): + for file_path in import_source.iterdir(): + if file_path.suffix == '.song': return True return False diff --git a/openlp/plugins/songs/lib/importers/presentationmanager.py b/openlp/plugins/songs/lib/importers/presentationmanager.py index 675f6441c..e7fec2a6c 100644 --- a/openlp/plugins/songs/lib/importers/presentationmanager.py +++ b/openlp/plugins/songs/lib/importers/presentationmanager.py @@ -23,13 +23,12 @@ The :mod:`presentationmanager` module provides the functionality for importing Presentationmanager song files into the current database. """ -import os import re -import chardet from lxml import objectify, etree from openlp.core.common.i18n import translate +from openlp.core.common import get_file_encoding from openlp.core.ui.lib.wizard import WizardStrings from .songimport import SongImport @@ -44,17 +43,14 @@ class PresentationManagerImport(SongImport): for file_path in self.import_source: if self.stop_import_flag: return - self.import_wizard.increment_progress_bar( - WizardStrings.ImportingType.format(source=os.path.basename(file_path))) + self.import_wizard.increment_progress_bar(WizardStrings.ImportingType.format(source=file_path.name)) try: - tree = etree.parse(file_path, parser=etree.XMLParser(recover=True)) + tree = etree.parse(str(file_path), parser=etree.XMLParser(recover=True)) except etree.XMLSyntaxError: # Try to detect encoding and use it - file = open(file_path, mode='rb') - encoding = chardet.detect(file.read())['encoding'] - file.close() + encoding = get_file_encoding(file_path)['encoding'] # Open file with detected encoding and remove encoding declaration - text = open(file_path, mode='r', encoding=encoding).read() + text = file_path.read_text(encoding=encoding) text = re.sub('.+\?>\n', '', text) try: tree = etree.fromstring(text, parser=etree.XMLParser(recover=True)) @@ -80,6 +76,11 @@ class PresentationManagerImport(SongImport): return '' def process_song(self, root, file_path): + """ + :param root: + :param openlp.core.common.path.Path file_path: Path to the file to process + :rtype: None + """ self.set_defaults() attrs = None if hasattr(root, 'attributes'): @@ -123,4 +124,4 @@ class PresentationManagerImport(SongImport): self.verse_order_list = verse_order_list if not self.finish(): - self.log_error(os.path.basename(file_path)) + self.log_error(file_path.name) diff --git a/openlp/plugins/songs/lib/importers/propresenter.py b/openlp/plugins/songs/lib/importers/propresenter.py index 9a3fa372d..582b1a6ee 100644 --- a/openlp/plugins/songs/lib/importers/propresenter.py +++ b/openlp/plugins/songs/lib/importers/propresenter.py @@ -23,8 +23,6 @@ The :mod:`propresenter` module provides the functionality for importing ProPresenter song files into the current installation database. """ - -import os import base64 import logging from lxml import objectify @@ -47,11 +45,17 @@ class ProPresenterImport(SongImport): if self.stop_import_flag: return self.import_wizard.increment_progress_bar( - WizardStrings.ImportingType.format(source=os.path.basename(file_path))) - root = objectify.parse(open(file_path, 'rb')).getroot() - self.process_song(root, file_path) + WizardStrings.ImportingType.format(source=file_path.name)) + with file_path.open('rb') as xml_file: + root = objectify.parse(xml_file).getroot() + self.process_song(root, file_path) - def process_song(self, root, filename): + def process_song(self, root, file_path): + """ + :param root: + :param openlp.core.common.path.Path file_path: Path to the file thats being imported + :rtype: None + """ self.set_defaults() # Extract ProPresenter versionNumber @@ -64,9 +68,7 @@ class ProPresenterImport(SongImport): # Title self.title = root.get('CCLISongTitle') if not self.title or self.title == '': - self.title = os.path.basename(filename) - if self.title[-5:-1] == '.pro': - self.title = self.title[:-5] + self.title = file_path.stem # Notes self.comments = root.get('notes') # Author diff --git a/openlp/plugins/songs/lib/importers/songbeamer.py b/openlp/plugins/songs/lib/importers/songbeamer.py index 3d339f864..adc364325 100644 --- a/openlp/plugins/songs/lib/importers/songbeamer.py +++ b/openlp/plugins/songs/lib/importers/songbeamer.py @@ -113,7 +113,7 @@ class SongBeamerImport(SongImport): if not isinstance(self.import_source, list): return self.import_wizard.progress_bar.setMaximum(len(self.import_source)) - for import_file in self.import_source: + for file_path in self.import_source: # TODO: check that it is a valid SongBeamer file if self.stop_import_flag: return @@ -121,20 +121,19 @@ class SongBeamerImport(SongImport): self.current_verse = '' self.current_verse_type = VerseType.tags[VerseType.Verse] self.chord_table = None - file_name = os.path.split(import_file)[1] - if os.path.isfile(import_file): + if file_path.is_file(): # Detect the encoding - self.input_file_encoding = get_file_encoding(Path(import_file))['encoding'] + self.input_file_encoding = get_file_encoding(file_path)['encoding'] # The encoding should only be ANSI (cp1252), UTF-8, Unicode, Big-Endian-Unicode. # So if it doesn't start with 'u' we default to cp1252. See: # https://forum.songbeamer.com/viewtopic.php?p=419&sid=ca4814924e37c11e4438b7272a98b6f2 if not self.input_file_encoding.lower().startswith('u'): self.input_file_encoding = 'cp1252' - infile = open(import_file, 'rt', encoding=self.input_file_encoding) - song_data = infile.readlines() + with file_path.open(encoding=self.input_file_encoding) as song_file: + song_data = song_file.readlines() else: continue - self.title = file_name.split('.sng')[0] + self.title = file_path.stem read_verses = False # The first verse separator doesn't count, but the others does, so line count starts at -1 line_number = -1 @@ -186,7 +185,7 @@ class SongBeamerImport(SongImport): # inserted by songbeamer, but are manually added headings. So restart the loop, and # count tags as lines. self.set_defaults() - self.title = file_name.split('.sng')[0] + self.title = file_path.stem verse_tags_mode = VerseTagMode.ContainsNoTagsRestart read_verses = False # The first verseseparator doesn't count, but the others does, so linecount starts at -1 @@ -208,7 +207,7 @@ class SongBeamerImport(SongImport): self.replace_html_tags() self.add_verse(self.current_verse, self.current_verse_type) if not self.finish(): - self.log_error(import_file) + self.log_error(file_path) def insert_chords(self, line_number, line): """ @@ -416,14 +415,15 @@ class SongBeamerImport(SongImport): """ # The path is relative to SongBeamers Song folder if is_win(): - user_doc_folder = os.path.expandvars('$DOCUMENTS') + user_doc_path = Path(os.path.expandvars('$DOCUMENTS')) elif is_macosx(): - user_doc_folder = os.path.join(os.path.expanduser('~'), 'Documents') + user_doc_path = Path.home() / 'Documents' else: # SongBeamer only runs on mac and win... return - audio_file_path = os.path.normpath(os.path.join(user_doc_folder, 'SongBeamer', 'Songs', audio_file_path)) - if os.path.isfile(audio_file_path): + audio_file_path = user_doc_path / 'SongBeamer' / 'Songs' / audio_file_path + if audio_file_path.is_file(): self.add_media_file(audio_file_path) else: - log.debug('Could not import mediafile "%s" since it does not exists!' % audio_file_path) + log.debug('Could not import mediafile "{audio_file_path}" since it does not exists!' + .format(audio_file_path=audio_file_path)) diff --git a/openlp/plugins/songs/lib/importers/songimport.py b/openlp/plugins/songs/lib/importers/songimport.py index 3da60305e..e0cc5220e 100644 --- a/openlp/plugins/songs/lib/importers/songimport.py +++ b/openlp/plugins/songs/lib/importers/songimport.py @@ -22,14 +22,12 @@ import logging import re -import shutil -import os from PyQt5 import QtCore from openlp.core.common.applocation import AppLocation from openlp.core.common.i18n import translate -from openlp.core.common.path import Path, create_paths +from openlp.core.common.path import copyfile, create_paths from openlp.core.common.registry import Registry from openlp.core.ui.lib.wizard import WizardStrings from openlp.plugins.songs.lib import clean_song, VerseType @@ -64,14 +62,14 @@ class SongImport(QtCore.QObject): """ self.manager = manager QtCore.QObject.__init__(self) - if 'filename' in kwargs: - self.import_source = kwargs['filename'] - elif 'filenames' in kwargs: - self.import_source = kwargs['filenames'] - elif 'folder' in kwargs: - self.import_source = kwargs['folder'] + if 'file_path' in kwargs: + self.import_source = kwargs['file_path'] + elif 'file_paths' in kwargs: + self.import_source = kwargs['file_paths'] + elif 'folder_path' in kwargs: + self.import_source = kwargs['folder_path'] else: - raise KeyError('Keyword arguments "filename[s]" or "folder" not supplied.') + raise KeyError('Keyword arguments "file_path[s]" or "folder_path" not supplied.') log.debug(self.import_source) self.import_wizard = None self.song = None @@ -272,13 +270,13 @@ class SongImport(QtCore.QObject): return self.authors.append((author, type)) - def add_media_file(self, filename, weight=0): + def add_media_file(self, file_path, weight=0): """ Add a media file to the list """ - if filename in [x[0] for x in self.media_files]: + if file_path in [x[0] for x in self.media_files]: return - self.media_files.append((filename, weight)) + self.media_files.append((file_path, weight)) def add_verse(self, verse_text, verse_def='v', lang=None): """ @@ -405,29 +403,30 @@ class SongImport(QtCore.QObject): self.manager.save_object(song) # Now loop through the media files, copy them to the correct location, # and save the song again. - for filename, weight in self.media_files: - media_file = self.manager.get_object_filtered(MediaFile, MediaFile.file_name == filename) + for file_path, weight in self.media_files: + media_file = self.manager.get_object_filtered(MediaFile, MediaFile.file_path == file_path) if not media_file: - if os.path.dirname(filename): - filename = self.copy_media_file(song.id, filename) - song.media_files.append(MediaFile.populate(file_name=filename, weight=weight)) + if file_path.parent: + file_path = self.copy_media_file(song.id, file_path) + song.media_files.append(MediaFile.populate(file_path=file_path, weight=weight)) self.manager.save_object(song) self.set_defaults() return True - def copy_media_file(self, song_id, filename): + def copy_media_file(self, song_id, file_path): """ This method copies the media file to the correct location and returns the new file location. :param song_id: - :param filename: The file to copy. + :param openlp.core.common.path.Path file_path: The file to copy. + :return: The new location of the file + :rtype: openlp.core.common.path.Path """ if not hasattr(self, 'save_path'): - self.save_path = os.path.join(str(AppLocation.get_section_data_path(self.import_wizard.plugin.name)), - 'audio', str(song_id)) - create_paths(Path(self.save_path)) - if not filename.startswith(self.save_path): - old_file, filename = filename, os.path.join(self.save_path, os.path.split(filename)[1]) - shutil.copyfile(old_file, filename) - return filename + self.save_path = AppLocation.get_section_data_path(self.import_wizard.plugin.name) / 'audio' / str(song_id) + create_paths(self.save_path) + if self.save_path not in file_path.parents: + old_path, file_path = file_path, self.save_path / file_path.name + copyfile(old_path, file_path) + return file_path diff --git a/openlp/plugins/songs/lib/importers/songpro.py b/openlp/plugins/songs/lib/importers/songpro.py index 30f19128a..261c2c728 100644 --- a/openlp/plugins/songs/lib/importers/songpro.py +++ b/openlp/plugins/songs/lib/importers/songpro.py @@ -25,6 +25,7 @@ songs into the OpenLP database. """ import re +from openlp.core.common.path import Path from openlp.plugins.songs.lib import strip_rtf from openlp.plugins.songs.lib.importers.songimport import SongImport @@ -72,7 +73,8 @@ class SongProImport(SongImport): Receive a single file or a list of files to import. """ self.encoding = None - with open(self.import_source, 'rt', errors='ignore') as songs_file: + self.import_source = Path(self.import_source) + with self.import_source.open('rt', errors='ignore') as songs_file: self.import_wizard.progress_bar.setMaximum(0) tag = '' text = '' diff --git a/openlp/plugins/songs/lib/importers/songshowplus.py b/openlp/plugins/songs/lib/importers/songshowplus.py index b0841b672..2fcf414dd 100644 --- a/openlp/plugins/songs/lib/importers/songshowplus.py +++ b/openlp/plugins/songs/lib/importers/songshowplus.py @@ -23,7 +23,6 @@ The :mod:`songshowplus` module provides the functionality for importing SongShow Plus songs into the OpenLP database. """ -import os import logging import re import struct @@ -93,97 +92,95 @@ class SongShowPlusImport(SongImport): if not isinstance(self.import_source, list): return self.import_wizard.progress_bar.setMaximum(len(self.import_source)) - for file in self.import_source: + for file_path in self.import_source: if self.stop_import_flag: return self.ssp_verse_order_list = [] self.other_count = 0 self.other_list = {} - file_name = os.path.split(file)[1] - self.import_wizard.increment_progress_bar(WizardStrings.ImportingType.format(source=file_name), 0) - song_data = open(file, 'rb') - while True: - block_key, = struct.unpack("I", song_data.read(4)) - log.debug('block_key: %d' % block_key) - # The file ends with 4 NULL's - if block_key == 0: - break - next_block_starts, = struct.unpack("I", song_data.read(4)) - next_block_starts += song_data.tell() - if block_key in (VERSE, CHORUS, BRIDGE): - null, verse_no, = struct.unpack("BB", song_data.read(2)) - elif block_key == CUSTOM_VERSE: - null, verse_name_length, = struct.unpack("BB", song_data.read(2)) - verse_name = self.decode(song_data.read(verse_name_length)) - length_descriptor_size, = struct.unpack("B", song_data.read(1)) - log.debug('length_descriptor_size: %d' % length_descriptor_size) - # In the case of song_numbers the number is in the data from the - # current position to the next block starts - if block_key == SONG_NUMBER: - sn_bytes = song_data.read(length_descriptor_size - 1) - self.song_number = int.from_bytes(sn_bytes, byteorder='little') - continue - # Detect if/how long the length descriptor is - if length_descriptor_size == 12 or length_descriptor_size == 20: - length_descriptor, = struct.unpack("I", song_data.read(4)) - elif length_descriptor_size == 2: - length_descriptor = 1 - elif length_descriptor_size == 9: - length_descriptor = 0 - else: - length_descriptor, = struct.unpack("B", song_data.read(1)) - log.debug('length_descriptor: %d' % length_descriptor) - data = song_data.read(length_descriptor) - log.debug(data) - if block_key == TITLE: - self.title = self.decode(data) - elif block_key == AUTHOR: - authors = self.decode(data).split(" / ") - for author in authors: - if author.find(",") != -1: - author_parts = author.split(", ") - author = author_parts[1] + " " + author_parts[0] - self.parse_author(author) - elif block_key == COPYRIGHT: - self.add_copyright(self.decode(data)) - elif block_key == CCLI_NO: - # Try to get the CCLI number even if the field contains additional text - match = re.search(r'\d+', self.decode(data)) - if match: - self.ccli_number = int(match.group()) + self.import_wizard.increment_progress_bar(WizardStrings.ImportingType.format(source=file_path.name), 0) + with file_path.open('rb') as song_file: + while True: + block_key, = struct.unpack("I", song_file.read(4)) + log.debug('block_key: %d' % block_key) + # The file ends with 4 NULL's + if block_key == 0: + break + next_block_starts, = struct.unpack("I", song_file.read(4)) + next_block_starts += song_file.tell() + if block_key in (VERSE, CHORUS, BRIDGE): + null, verse_no, = struct.unpack("BB", song_file.read(2)) + elif block_key == CUSTOM_VERSE: + null, verse_name_length, = struct.unpack("BB", song_file.read(2)) + verse_name = self.decode(song_file.read(verse_name_length)) + length_descriptor_size, = struct.unpack("B", song_file.read(1)) + log.debug('length_descriptor_size: %d' % length_descriptor_size) + # In the case of song_numbers the number is in the data from the + # current position to the next block starts + if block_key == SONG_NUMBER: + sn_bytes = song_file.read(length_descriptor_size - 1) + self.song_number = int.from_bytes(sn_bytes, byteorder='little') + continue + # Detect if/how long the length descriptor is + if length_descriptor_size == 12 or length_descriptor_size == 20: + length_descriptor, = struct.unpack("I", song_file.read(4)) + elif length_descriptor_size == 2: + length_descriptor = 1 + elif length_descriptor_size == 9: + length_descriptor = 0 else: - 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), "{tag}{number}".format(tag=VerseType.tags[VerseType.Verse], - number=verse_no)) - elif block_key == CHORUS: - 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), "{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: - self.comments = self.decode(data) - elif block_key == VERSE_ORDER: - verse_tag = self.to_openlp_verse_tag(self.decode(data), True) - if verse_tag: - if not isinstance(verse_tag, str): - verse_tag = self.decode(verse_tag) - self.ssp_verse_order_list.append(verse_tag) - elif block_key == SONG_BOOK: - self.song_book_name = self.decode(data) - elif block_key == CUSTOM_VERSE: - verse_tag = self.to_openlp_verse_tag(verse_name) - self.add_verse(self.decode(data), verse_tag) - else: - 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() - if not self.finish(): - self.log_error(file) + length_descriptor, = struct.unpack("B", song_file.read(1)) + log.debug('length_descriptor: %d' % length_descriptor) + data = song_file.read(length_descriptor) + log.debug(data) + if block_key == TITLE: + self.title = self.decode(data) + elif block_key == AUTHOR: + authors = self.decode(data).split(" / ") + for author in authors: + if author.find(",") != -1: + author_parts = author.split(", ") + author = author_parts[1] + " " + author_parts[0] + self.parse_author(author) + elif block_key == COPYRIGHT: + self.add_copyright(self.decode(data)) + elif block_key == CCLI_NO: + # Try to get the CCLI number even if the field contains additional text + match = re.search(r'\d+', self.decode(data)) + if match: + self.ccli_number = int(match.group()) + else: + 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), "{tag}{number}".format(tag=VerseType.tags[VerseType.Verse], + number=verse_no)) + elif block_key == CHORUS: + 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), "{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: + self.comments = self.decode(data) + elif block_key == VERSE_ORDER: + verse_tag = self.to_openlp_verse_tag(self.decode(data), True) + if verse_tag: + if not isinstance(verse_tag, str): + verse_tag = self.decode(verse_tag) + self.ssp_verse_order_list.append(verse_tag) + elif block_key == SONG_BOOK: + self.song_book_name = self.decode(data) + elif block_key == CUSTOM_VERSE: + verse_tag = self.to_openlp_verse_tag(verse_name) + self.add_verse(self.decode(data), verse_tag) + else: + log.debug("Unrecognised blockKey: {key}, data: {data}".format(key=block_key, data=data)) + song_file.seek(next_block_starts) + self.verse_order_list = self.ssp_verse_order_list + if not self.finish(): + self.log_error(file_path) def to_openlp_verse_tag(self, verse_name, ignore_unique=False): """ diff --git a/openlp/plugins/songs/lib/importers/sundayplus.py b/openlp/plugins/songs/lib/importers/sundayplus.py index caa92abf4..e0ce16aa1 100644 --- a/openlp/plugins/songs/lib/importers/sundayplus.py +++ b/openlp/plugins/songs/lib/importers/sundayplus.py @@ -19,11 +19,8 @@ # with this program; if not, write to the Free Software Foundation, Inc., 59 # # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### - import os import re -import logging - from openlp.plugins.songs.lib import VerseType, retrieve_windows_encoding from openlp.plugins.songs.lib import strip_rtf @@ -60,12 +57,11 @@ class SundayPlusImport(SongImport): def do_import(self): self.import_wizard.progress_bar.setMaximum(len(self.import_source)) - for filename in self.import_source: + for file_path in self.import_source: if self.stop_import_flag: return - song_file = open(filename, 'rb') - self.do_import_file(song_file) - song_file.close() + with file_path.open('rb') as song_file: + self.do_import_file(song_file) def do_import_file(self, file): """ diff --git a/openlp/plugins/songs/lib/importers/videopsalm.py b/openlp/plugins/songs/lib/importers/videopsalm.py index 1191f7555..180dbd333 100644 --- a/openlp/plugins/songs/lib/importers/videopsalm.py +++ b/openlp/plugins/songs/lib/importers/videopsalm.py @@ -22,13 +22,13 @@ """ The :mod:`lyrix` module provides the functionality for importing songs which are exported from Lyrix.""" -import logging import json -import os +import logging import re from openlp.core.common.i18n import translate from openlp.core.common.settings import Settings +from openlp.core.common.path import Path from openlp.plugins.songs.lib.importers.songimport import SongImport from openlp.plugins.songs.lib.db import AuthorType @@ -50,11 +50,10 @@ class VideoPsalmImport(SongImport): """ Process the VideoPsalm file - pass in a file-like object, not a file path. """ + self.import_source = Path(self.import_source) self.set_defaults() - # Open SongBook file - song_file = open(self.import_source, 'rt', encoding='utf-8-sig') try: - file_content = song_file.read() + file_content = self.import_source.read_text(encoding='utf-8-sig') processed_content = '' inside_quotes = False # The VideoPsalm format is not valid json, it uses illegal line breaks and unquoted keys, this must be fixed @@ -89,7 +88,7 @@ class VideoPsalmImport(SongImport): songs = songbook['Songs'] self.import_wizard.progress_bar.setMaximum(len(songs)) songbook_name = songbook['Text'] - media_folder = os.path.normpath(os.path.join(os.path.dirname(song_file.name), '..', 'Audio')) + media_path = Path('..', 'Audio') for song in songs: self.song_book_name = songbook_name if 'Text' in song: @@ -114,7 +113,7 @@ class VideoPsalmImport(SongImport): if 'Theme' in song: self.topics = song['Theme'].splitlines() if 'AudioFile' in song: - self.add_media_file(os.path.join(media_folder, song['AudioFile'])) + self.add_media_file(media_path / song['AudioFile']) if 'Memo1' in song: self.add_comment(song['Memo1']) if 'Memo2' in song: @@ -132,4 +131,5 @@ class VideoPsalmImport(SongImport): if not self.finish(): self.log_error('Could not import {title}'.format(title=self.title)) except Exception as e: - self.log_error(song_file.name, translate('SongsPlugin.VideoPsalmImport', 'Error: {error}').format(error=e)) + self.log_error(self.import_source.name, + translate('SongsPlugin.VideoPsalmImport', 'Error: {error}').format(error=e)) diff --git a/openlp/plugins/songs/lib/importers/wordsofworship.py b/openlp/plugins/songs/lib/importers/wordsofworship.py index 624803700..e1c561361 100644 --- a/openlp/plugins/songs/lib/importers/wordsofworship.py +++ b/openlp/plugins/songs/lib/importers/wordsofworship.py @@ -25,6 +25,7 @@ Worship songs into the OpenLP database. """ import os import logging +from openlp.core.common.path import Path from openlp.core.common.i18n import translate from openlp.plugins.songs.lib.importers.songimport import SongImport @@ -100,62 +101,60 @@ class WordsOfWorshipImport(SongImport): """ if isinstance(self.import_source, list): self.import_wizard.progress_bar.setMaximum(len(self.import_source)) - for source in self.import_source: + for file_path in self.import_source: if self.stop_import_flag: return self.set_defaults() - song_data = open(source, 'rb') - if song_data.read(19).decode() != 'WoW File\nSong Words': - self.log_error(source, - translate('SongsPlugin.WordsofWorshipSongImport', - '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) - no_of_blocks = ord(song_data.read(1)) - song_data.seek(66) - if song_data.read(16).decode() != 'CSongDoc::CBlock': - self.log_error(source, - translate('SongsPlugin.WordsofWorshipSongImport', - '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) - for block in range(no_of_blocks): - skip_char_at_end = True - self.lines_to_read = ord(song_data.read(4)[:1]) - block_text = '' - while self.lines_to_read: - self.line_text = str(song_data.read(ord(song_data.read(1))), 'cp1252') - if skip_char_at_end: - skip_char = ord(song_data.read(1)) - # Check if we really should skip a char. In some wsg files we shouldn't - if skip_char != 0: - song_data.seek(-1, os.SEEK_CUR) - skip_char_at_end = False - if block_text: - block_text += '\n' - block_text += self.line_text - self.lines_to_read -= 1 - block_type = BLOCK_TYPES[ord(song_data.read(4)[:1])] - # Blocks are separated by 2 bytes, skip them, but not if - # this is the last block! - if block + 1 < no_of_blocks: - song_data.seek(2, os.SEEK_CUR) - self.add_verse(block_text, block_type) - # Now to extract the author - author_length = ord(song_data.read(1)) - if author_length: - self.parse_author(str(song_data.read(author_length), 'cp1252')) - # Finally the copyright - copyright_length = ord(song_data.read(1)) - if copyright_length: - self.add_copyright(str(song_data.read(copyright_length), 'cp1252')) - file_name = os.path.split(source)[1] - # Get the song title - self.title = file_name.rpartition('.')[0] - song_data.close() - if not self.finish(): - self.log_error(source) + with file_path.open('rb') as song_data: + if song_data.read(19).decode() != 'WoW File\nSong Words': + self.log_error(file_path, + translate('SongsPlugin.WordsofWorshipSongImport', + '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) + no_of_blocks = ord(song_data.read(1)) + song_data.seek(66) + if song_data.read(16).decode() != 'CSongDoc::CBlock': + self.log_error(file_path, + translate('SongsPlugin.WordsofWorshipSongImport', + '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) + for block in range(no_of_blocks): + skip_char_at_end = True + self.lines_to_read = ord(song_data.read(4)[:1]) + block_text = '' + while self.lines_to_read: + self.line_text = str(song_data.read(ord(song_data.read(1))), 'cp1252') + if skip_char_at_end: + skip_char = ord(song_data.read(1)) + # Check if we really should skip a char. In some wsg files we shouldn't + if skip_char != 0: + song_data.seek(-1, os.SEEK_CUR) + skip_char_at_end = False + if block_text: + block_text += '\n' + block_text += self.line_text + self.lines_to_read -= 1 + block_type = BLOCK_TYPES[ord(song_data.read(4)[:1])] + # Blocks are separated by 2 bytes, skip them, but not if + # this is the last block! + if block + 1 < no_of_blocks: + song_data.seek(2, os.SEEK_CUR) + self.add_verse(block_text, block_type) + # Now to extract the author + author_length = ord(song_data.read(1)) + if author_length: + self.parse_author(str(song_data.read(author_length), 'cp1252')) + # Finally the copyright + copyright_length = ord(song_data.read(1)) + if copyright_length: + self.add_copyright(str(song_data.read(copyright_length), 'cp1252')) + # Get the song title + self.title = file_path.stem + if not self.finish(): + self.log_error(file_path) diff --git a/openlp/plugins/songs/lib/importers/worshipassistant.py b/openlp/plugins/songs/lib/importers/worshipassistant.py index dbbd8f5fd..f9354427d 100644 --- a/openlp/plugins/songs/lib/importers/worshipassistant.py +++ b/openlp/plugins/songs/lib/importers/worshipassistant.py @@ -23,11 +23,11 @@ The :mod:`worshipassistant` module provides the functionality for importing Worship Assistant songs into the OpenLP database. """ -import chardet import csv import logging import re +from openlp.core.common import get_file_encoding from openlp.core.common.i18n import translate from openlp.plugins.songs.lib import VerseType from openlp.plugins.songs.lib.importers.songimport import SongImport @@ -81,19 +81,16 @@ class WorshipAssistantImport(SongImport): Receive a CSV file to import. """ # Get encoding - detect_file = open(self.import_source, 'rb') - detect_content = detect_file.read() - details = chardet.detect(detect_content) - detect_file.close() - songs_file = open(self.import_source, 'r', encoding=details['encoding']) - songs_reader = csv.DictReader(songs_file, escapechar='\\') - try: - records = list(songs_reader) - except csv.Error as e: - self.log_error(translate('SongsPlugin.WorshipAssistantImport', 'Error reading CSV file.'), - translate('SongsPlugin.WorshipAssistantImport', - 'Line {number:d}: {error}').format(number=songs_reader.line_num, error=e)) - return + encoding = get_file_encoding(self.import_source)['encoding'] + with self.import_source.open('r', encoding=encoding) as songs_file: + songs_reader = csv.DictReader(songs_file, escapechar='\\') + try: + records = list(songs_reader) + except csv.Error as e: + self.log_error(translate('SongsPlugin.WorshipAssistantImport', 'Error reading CSV file.'), + translate('SongsPlugin.WorshipAssistantImport', + 'Line {number:d}: {error}').format(number=songs_reader.line_num, error=e)) + return num_records = len(records) log.info('{count} records found in CSV file'.format(count=num_records)) self.import_wizard.progress_bar.setMaximum(num_records) @@ -185,4 +182,3 @@ class WorshipAssistantImport(SongImport): 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/zionworx.py b/openlp/plugins/songs/lib/importers/zionworx.py index 12645d313..5cfc0576d 100644 --- a/openlp/plugins/songs/lib/importers/zionworx.py +++ b/openlp/plugins/songs/lib/importers/zionworx.py @@ -76,7 +76,7 @@ class ZionWorxImport(SongImport): Receive a CSV file (from a ZionWorx database dump) to import. """ # Encoding should always be ISO-8859-1 - with open(self.import_source, 'rt', encoding='ISO-8859-1') as songs_file: + with self.import_source.open('rt', encoding='ISO-8859-1') as songs_file: field_names = ['SongNum', 'Title1', 'Title2', 'Lyrics', 'Writer', 'Copyright', 'Keywords', 'DefaultStyle'] songs_reader = csv.DictReader(songs_file, field_names) diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index d13d069a5..c6ad9c927 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -19,30 +19,28 @@ # with this program; if not, write to the Free Software Foundation, Inc., 59 # # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### - import logging import os -import shutil from PyQt5 import QtCore, QtWidgets from sqlalchemy.sql import and_, or_ from openlp.core.common.applocation import AppLocation from openlp.core.common.i18n import UiStrings, translate, get_natural_key -from openlp.core.common.path import Path, create_paths +from openlp.core.common.path import copyfile, create_paths from openlp.core.common.registry import Registry from openlp.core.common.settings import Settings from openlp.core.lib import MediaManagerItem, ItemCapabilities, PluginStatus, ServiceItemContext, \ check_item_selected, create_separated_list from openlp.core.lib.ui import create_widget_action from openlp.plugins.songs.forms.editsongform import EditSongForm -from openlp.plugins.songs.forms.songmaintenanceform import SongMaintenanceForm -from openlp.plugins.songs.forms.songimportform import SongImportForm from openlp.plugins.songs.forms.songexportform import SongExportForm +from openlp.plugins.songs.forms.songimportform import SongImportForm +from openlp.plugins.songs.forms.songmaintenanceform import SongMaintenanceForm from openlp.plugins.songs.lib import VerseType, clean_string, delete_song from openlp.plugins.songs.lib.db import Author, AuthorType, Song, Book, MediaFile, SongBookEntry, Topic -from openlp.plugins.songs.lib.ui import SongStrings from openlp.plugins.songs.lib.openlyricsxml import OpenLyrics, SongXML +from openlp.plugins.songs.lib.ui import SongStrings log = logging.getLogger(__name__) @@ -90,11 +88,11 @@ class SongMediaItem(MediaManagerItem): def _update_background_audio(self, song, item): song.media_files = [] for i, bga in enumerate(item.background_audio): - dest_file = os.path.join( - str(AppLocation.get_section_data_path(self.plugin.name)), 'audio', str(song.id), os.path.split(bga)[1]) - create_paths(Path(os.path.split(dest_file)[0])) - shutil.copyfile(os.path.join(str(AppLocation.get_section_data_path('servicemanager')), bga), dest_file) - song.media_files.append(MediaFile.populate(weight=i, file_name=dest_file)) + dest_path =\ + AppLocation.get_section_data_path(self.plugin.name) / 'audio' / str(song.id) / os.path.split(bga)[1] + create_paths(dest_path.parent) + copyfile(AppLocation.get_section_data_path('servicemanager') / bga, dest_path) + song.media_files.append(MediaFile.populate(weight=i, file_path=dest_path)) self.plugin.manager.save_object(song, True) def add_end_header_bar(self): @@ -536,14 +534,13 @@ class SongMediaItem(MediaManagerItem): '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( - str(AppLocation.get_section_data_path(self.plugin.name)), 'audio', str(new_song.id)) - create_paths(Path(save_path)) + save_path = AppLocation.get_section_data_path(self.plugin.name) / 'audio' / str(new_song.id) + create_paths(save_path) for media_file in old_song.media_files: - new_media_file_name = os.path.join(save_path, os.path.basename(media_file.file_name)) - shutil.copyfile(media_file.file_name, new_media_file_name) + new_media_file_path = save_path / media_file.file_path.name + copyfile(media_file.file_path, new_media_file_path) new_media_file = MediaFile() - new_media_file.file_name = new_media_file_name + new_media_file.file_path = new_media_file_path new_media_file.type = media_file.type new_media_file.weight = media_file.weight new_song.media_files.append(new_media_file) @@ -581,7 +578,7 @@ class SongMediaItem(MediaManagerItem): if not song.verse_order.strip(): for verse in verse_list: # We cannot use from_loose_input() here, because database is supposed to contain English lowercase - # singlechar tags. + # single char tags. verse_tag = verse[0]['type'] verse_index = None if len(verse_tag) > 1: @@ -592,7 +589,9 @@ class SongMediaItem(MediaManagerItem): verse_index = VerseType.from_tag(verse_tag) verse_tag = VerseType.translated_tags[verse_index].upper() verse_def = '{tag}{label}'.format(tag=verse_tag, label=verse[0]['label']) - service_item.add_from_text(str(verse[1]), verse_def) + force_verse = verse[1].split('[--}{--]\n') + for split_verse in force_verse: + service_item.add_from_text(split_verse, verse_def) else: # Loop through the verse list and expand the song accordingly. for order in song.verse_order.lower().split(): @@ -607,7 +606,9 @@ class SongMediaItem(MediaManagerItem): verse_index = VerseType.from_tag(verse[0]['type']) verse_tag = VerseType.translated_tags[verse_index] verse_def = '{tag}{label}'.format(tag=verse_tag, label=verse[0]['label']) - service_item.add_from_text(verse[1], verse_def) + force_verse = verse[1].split('[--}{--]\n') + for split_verse in force_verse: + service_item.add_from_text(split_verse, verse_def) service_item.title = song.title author_list = self.generate_footer(service_item, song) service_item.data_string = {'title': song.search_title, 'authors': ', '.join(author_list)} @@ -615,7 +616,7 @@ class SongMediaItem(MediaManagerItem): # Add the audio file to the service item. if song.media_files: service_item.add_capability(ItemCapabilities.HasBackgroundAudio) - service_item.background_audio = [m.file_name for m in song.media_files] + service_item.background_audio = [m.file_path for m in song.media_files] return True def generate_footer(self, item, song): diff --git a/openlp/plugins/songs/lib/openlyricsexport.py b/openlp/plugins/songs/lib/openlyricsexport.py index 9f2fd4848..ca7fe0bc7 100644 --- a/openlp/plugins/songs/lib/openlyricsexport.py +++ b/openlp/plugins/songs/lib/openlyricsexport.py @@ -24,13 +24,12 @@ The :mod:`openlyricsexport` module provides the functionality for exporting song format. """ import logging -import os from lxml import etree from openlp.core.common import clean_filename from openlp.core.common.i18n import translate -from openlp.core.common.path import Path, create_paths +from openlp.core.common.path import create_paths from openlp.core.common.registry import RegistryProperties from openlp.plugins.songs.lib.openlyricsxml import OpenLyrics @@ -44,13 +43,16 @@ class OpenLyricsExport(RegistryProperties): def __init__(self, parent, songs, save_path): """ Initialise the export. + + :param openlp.core.common.path.Path save_path: The directory to save the exported songs in + :rtype: None """ log.debug('initialise OpenLyricsExport') self.parent = parent self.manager = parent.plugin.manager self.songs = songs self.save_path = save_path - create_paths(Path(self.save_path)) + create_paths(self.save_path) def do_export(self): """ @@ -71,15 +73,15 @@ class OpenLyricsExport(RegistryProperties): 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 = '{name}.xml'.format(name=filename[0:250 - len(self.save_path)]) + path_length = len(str(self.save_path)) + filename_with_ext = '{name}.xml'.format(name=filename[0:250 - path_length]) # Make sure we're not overwriting an existing file conflicts = 0 - while os.path.exists(os.path.join(self.save_path, filename_with_ext)): + while (self.save_path / filename_with_ext).exists(): conflicts += 1 - filename_with_ext = '{name}-{extra}.xml'.format(name=filename[0:247 - len(self.save_path)], - extra=conflicts) + filename_with_ext = '{name}-{extra}.xml'.format(name=filename[0:247 - path_length], 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', - xml_declaration=True, pretty_print=True) + with (self.save_path / filename_with_ext).open('wb') as out_file: + tree.write(out_file, encoding='utf-8', xml_declaration=True, pretty_print=True) return True diff --git a/openlp/plugins/songs/lib/openlyricsxml.py b/openlp/plugins/songs/lib/openlyricsxml.py index 9647bfdcb..74d91068c 100644 --- a/openlp/plugins/songs/lib/openlyricsxml.py +++ b/openlp/plugins/songs/lib/openlyricsxml.py @@ -72,6 +72,7 @@ log = logging.getLogger(__name__) NAMESPACE = 'http://openlyrics.info/namespace/2009/song' NSMAP = '{{' + NAMESPACE + '}}{tag}' +NEWPAGETAG = '

' class SongXML(object): @@ -282,7 +283,7 @@ class OpenLyrics(object): tags_element = None match = re.search('\{/?\w+\}', song.lyrics, re.UNICODE) if match: - # Named 'format_' - 'format' is built-in fuction in Python. + # Named 'format_' - 'format' is built-in function in Python. format_ = etree.SubElement(song_xml, 'format') tags_element = etree.SubElement(format_, 'tags') tags_element.set('application', 'OpenLP') @@ -473,6 +474,7 @@ class OpenLyrics(object): text = text.replace('{{/{tag}}}'.format(tag=tag), '') # Replace \n with
. text = text.replace('\n', '
') + text = text.replace('[--}{--]', NEWPAGETAG) element = etree.XML('{text}'.format(text=text)) verse_element.append(element) return element @@ -635,6 +637,9 @@ class OpenLyrics(object): if element.tail: text += element.tail return text + elif newlines and element.tag == NSMAP.format(tag='p') and 'page-break-after' in str(element.attrib): + text += '[--}{--]' + return text # Start formatting tag. if element.tag == NSMAP.format(tag='tag'): text += '{{{name}}}'.format(name=element.get('name')) diff --git a/openlp/plugins/songs/lib/upgrade.py b/openlp/plugins/songs/lib/upgrade.py index 5b55d7985..bc7c95624 100644 --- a/openlp/plugins/songs/lib/upgrade.py +++ b/openlp/plugins/songs/lib/upgrade.py @@ -23,16 +23,20 @@ The :mod:`upgrade` module provides a way for the database and schema that is the backend for the Songs plugin """ +import json import logging from sqlalchemy import Table, Column, ForeignKey, types from sqlalchemy.sql.expression import func, false, null, text +from openlp.core.common import AppLocation from openlp.core.common.db import drop_columns -from openlp.core.lib.db import get_upgrade_op +from openlp.core.common.json import OpenLPJsonEncoder +from openlp.core.common.path import Path +from openlp.core.lib.db import PathType, get_upgrade_op log = logging.getLogger(__name__) -__version__ = 6 +__version__ = 7 # TODO: When removing an upgrade path the ftw-data needs updating to the minimum supported version @@ -162,3 +166,28 @@ def upgrade_6(session, metadata): op.drop_column('songs', 'song_number') # Finally, clean up our mess in people's databases op.execute('DELETE FROM songs_songbooks WHERE songbook_id = 0') + + +def upgrade_7(session, metadata): + """ + Version 7 upgrade - Move file path from old db to JSON encoded path to new db. Upgrade added in 2.5 dev + """ + log.debug('Starting upgrade_7 for file_path to JSON') + old_table = Table('media_files', metadata, autoload=True) + if 'file_path' not in [col.name for col in old_table.c.values()]: + op = get_upgrade_op(session) + op.add_column('media_files', Column('file_path', PathType())) + conn = op.get_bind() + results = conn.execute('SELECT * FROM media_files') + data_path = AppLocation.get_data_path() + for row in results.fetchall(): + file_path_json = json.dumps(Path(row.file_name), cls=OpenLPJsonEncoder, base_path=data_path) + sql = 'UPDATE media_files SET file_path = \'{file_path_json}\' WHERE id = {id}'.format( + file_path_json=file_path_json, id=row.id) + conn.execute(sql) + # Drop old columns + if metadata.bind.url.get_dialect().name == 'sqlite': + drop_columns(op, 'media_files', ['file_name', ]) + else: + op.drop_constraint('media_files', 'foreignkey') + op.drop_column('media_files', 'filenames') diff --git a/openlp/plugins/songs/reporting.py b/openlp/plugins/songs/reporting.py index 645823073..01fb0af6c 100644 --- a/openlp/plugins/songs/reporting.py +++ b/openlp/plugins/songs/reporting.py @@ -32,7 +32,6 @@ from openlp.core.lib.ui import critical_error_message_box from openlp.core.ui.lib.filedialog import FileDialog from openlp.plugins.songs.lib.db import Song - log = logging.getLogger(__name__) @@ -59,9 +58,9 @@ def report_song_list(): report_file_path.with_suffix('.csv') Registry().get('application').set_busy_cursor() try: - with report_file_path.open('wt') as file_handle: + with report_file_path.open('wt') as export_file: fieldnames = ('Title', 'Alternative Title', 'Copyright', 'Author(s)', 'Song Book', 'Topic') - writer = csv.DictWriter(file_handle, fieldnames=fieldnames, quoting=csv.QUOTE_ALL) + writer = csv.DictWriter(export_file, fieldnames=fieldnames, quoting=csv.QUOTE_ALL) headers = dict((n, n) for n in fieldnames) writer.writerow(headers) song_list = plugin.manager.get_all_objects(Song) diff --git a/openlp/plugins/songs/songsplugin.py b/openlp/plugins/songs/songsplugin.py index e976db7d3..b35cbe2c9 100644 --- a/openlp/plugins/songs/songsplugin.py +++ b/openlp/plugins/songs/songsplugin.py @@ -38,7 +38,6 @@ from openlp.core.common.registry import Registry from openlp.core.lib import Plugin, StringContent, build_icon from openlp.core.lib.db import Manager from openlp.core.lib.ui import create_action - from openlp.plugins.songs import reporting from openlp.plugins.songs.endpoint import api_songs_endpoint, songs_endpoint from openlp.plugins.songs.forms.duplicatesongremovalform import DuplicateSongRemovalForm @@ -51,7 +50,6 @@ from openlp.plugins.songs.lib.mediaitem import SongMediaItem from openlp.plugins.songs.lib.mediaitem import SongSearch from openlp.plugins.songs.lib.songstab import SongsTab - log = logging.getLogger(__name__) __default_settings__ = { 'songs/db type': 'sqlite', @@ -341,7 +339,7 @@ class SongsPlugin(Plugin): progress.forceShow() self.application.process_events() for db in song_dbs: - importer = OpenLPSongImport(self.manager, filename=db) + importer = OpenLPSongImport(self.manager, file_path=db) importer.do_import(progress) self.application.process_events() progress.setValue(song_count) diff --git a/resources/images/android_app_qr.png b/resources/images/android_app_qr.png deleted file mode 100644 index e7cd3fe92..000000000 Binary files a/resources/images/android_app_qr.png and /dev/null differ diff --git a/resources/images/app_qr.svg b/resources/images/app_qr.svg new file mode 100644 index 000000000..f384070b1 --- /dev/null +++ b/resources/images/app_qr.svg @@ -0,0 +1,446 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/images/ios_app_qr.png b/resources/images/ios_app_qr.png deleted file mode 100644 index c7244fc33..000000000 Binary files a/resources/images/ios_app_qr.png and /dev/null differ diff --git a/resources/images/openlp-2.qrc b/resources/images/openlp-2.qrc index 1a50dbe78..ee6dfe358 100644 --- a/resources/images/openlp-2.qrc +++ b/resources/images/openlp-2.qrc @@ -183,7 +183,6 @@ projector_warmup.png - android_app_qr.png - ios_app_qr.png - - + app_qr.svg + + \ No newline at end of file diff --git a/tests/functional/openlp_plugins/images/test_upgrade.py b/tests/functional/openlp_plugins/images/test_upgrade.py index 69ce912ed..471c33609 100644 --- a/tests/functional/openlp_plugins/images/test_upgrade.py +++ b/tests/functional/openlp_plugins/images/test_upgrade.py @@ -80,5 +80,6 @@ class TestImageDBUpgrade(TestCase, TestMixin): 2: Path('/', 'test', 'dir', 'image2.jpg'), 3: Path('/', 'test', 'dir', 'subdir', 'image3.jpg')} + self.assertEqual(len(upgraded_results), 3) for result in upgraded_results: self.assertEqual(expected_result_data[result.id], result.file_path) diff --git a/tests/functional/openlp_plugins/songs/test_chordproimport.py b/tests/functional/openlp_plugins/songs/test_chordproimport.py index b096fcb15..51ab19f25 100644 --- a/tests/functional/openlp_plugins/songs/test_chordproimport.py +++ b/tests/functional/openlp_plugins/songs/test_chordproimport.py @@ -24,6 +24,8 @@ This module contains tests for the OpenSong song importer. """ import os +from openlp.core.common.path import Path + from tests.helpers.songfileimport import SongImportTestHelper from unittest.mock import patch, MagicMock @@ -48,5 +50,5 @@ class TestChordProFileImport(SongImportTestHelper): mocked_returned_settings.value.side_effect = lambda value: True if value == 'songs/enable chords' else False mocked_settings.return_value = mocked_returned_settings # Do the test import - self.file_import([os.path.join(TEST_PATH, 'swing-low.chordpro')], + self.file_import([Path(TEST_PATH, 'swing-low.chordpro')], self.load_external_result_data(os.path.join(TEST_PATH, 'swing-low.json'))) diff --git a/tests/functional/openlp_plugins/songs/test_easyslidesimport.py b/tests/functional/openlp_plugins/songs/test_easyslidesimport.py index bfe9abcc2..6e0e6848b 100644 --- a/tests/functional/openlp_plugins/songs/test_easyslidesimport.py +++ b/tests/functional/openlp_plugins/songs/test_easyslidesimport.py @@ -21,9 +21,10 @@ """ This module contains tests for the EasySlides song importer. """ - import os +from openlp.core.common.path import Path + from tests.helpers.songfileimport import SongImportTestHelper TEST_PATH = os.path.abspath( @@ -41,7 +42,7 @@ class TestEasySlidesFileImport(SongImportTestHelper): """ Test that loading an EasySlides file works correctly on various files """ - self.file_import(os.path.join(TEST_PATH, 'amazing-grace.xml'), + self.file_import(Path(TEST_PATH, 'amazing-grace.xml'), self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace.json'))) - self.file_import(os.path.join(TEST_PATH, 'Export_2017-01-12_BB.xml'), + self.file_import(Path(TEST_PATH, 'Export_2017-01-12_BB.xml'), self.load_external_result_data(os.path.join(TEST_PATH, 'Export_2017-01-12_BB.json'))) diff --git a/tests/functional/openlp_plugins/songs/test_editverseform.py b/tests/functional/openlp_plugins/songs/test_editverseform.py index 92e88829d..10b339dc2 100644 --- a/tests/functional/openlp_plugins/songs/test_editverseform.py +++ b/tests/functional/openlp_plugins/songs/test_editverseform.py @@ -72,3 +72,31 @@ class TestEditVerseForm(TestCase, TestMixin): # THEN the verse number must not be changed self.assertEqual(3, self.edit_verse_form.verse_number_box.value(), 'The verse number should be 3') + + def test_on_divide_split_button_clicked(self): + """ + Test that divide adds text at the correct position + """ + # GIVEN some input values + self.edit_verse_form.verse_type_combo_box.currentIndex = MagicMock(return_value=4) + self.edit_verse_form.verse_text_edit.setPlainText('Text\n') + + # WHEN the method is called + self.edit_verse_form.on_forced_split_button_clicked() + # THEN the verse number must not be changed + self.assertEqual('[--}{--]\nText\n', self.edit_verse_form.verse_text_edit.toPlainText(), + 'The verse number should be [--}{--]\nText\n') + + def test_on_split_button_clicked(self): + """ + Test that divide adds text at the correct position + """ + # GIVEN some input values + self.edit_verse_form.verse_type_combo_box.currentIndex = MagicMock(return_value=4) + self.edit_verse_form.verse_text_edit.setPlainText('Text\n') + + # WHEN the method is called + self.edit_verse_form.on_overflow_split_button_clicked() + # THEN the verse number must not be changed + self.assertEqual('[---]\nText\n', self.edit_verse_form.verse_text_edit.toPlainText(), + 'The verse number should be [---]\nText\n') diff --git a/tests/functional/openlp_plugins/songs/test_ewimport.py b/tests/functional/openlp_plugins/songs/test_ewimport.py index 505d214b6..ae9f873c5 100644 --- a/tests/functional/openlp_plugins/songs/test_ewimport.py +++ b/tests/functional/openlp_plugins/songs/test_ewimport.py @@ -97,7 +97,7 @@ class EasyWorshipSongImportLogger(EasyWorshipSongImport): _title_assignment_list = [] def __init__(self, manager): - EasyWorshipSongImport.__init__(self, manager, filenames=[]) + EasyWorshipSongImport.__init__(self, manager, file_paths=[]) @property def title(self): @@ -180,7 +180,7 @@ class TestEasyWorshipSongImport(TestCase): mocked_manager = MagicMock() # WHEN: An importer object is created - importer = EasyWorshipSongImport(mocked_manager, filenames=[]) + importer = EasyWorshipSongImport(mocked_manager, file_paths=[]) # THEN: The importer object should not be None self.assertIsNotNone(importer, 'Import should not be none') @@ -192,7 +192,7 @@ class TestEasyWorshipSongImport(TestCase): # GIVEN: A mocked out SongImport class, a mocked out "manager" and a list of field descriptions. with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'): mocked_manager = MagicMock() - importer = EasyWorshipSongImport(mocked_manager, filenames=[]) + importer = EasyWorshipSongImport(mocked_manager, file_paths=[]) importer.field_descriptions = TEST_FIELD_DESCS # WHEN: Called with a field name that exists @@ -210,7 +210,7 @@ class TestEasyWorshipSongImport(TestCase): # GIVEN: A mocked out SongImport class, a mocked out "manager" and a list of field descriptions with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'): mocked_manager = MagicMock() - importer = EasyWorshipSongImport(mocked_manager, filenames=[]) + importer = EasyWorshipSongImport(mocked_manager, file_paths=[]) importer.field_descriptions = TEST_FIELD_DESCS # WHEN: Called with a field name that does not exist @@ -229,7 +229,7 @@ class TestEasyWorshipSongImport(TestCase): with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'), \ patch('openlp.plugins.songs.lib.importers.easyworship.struct') as mocked_struct: mocked_manager = MagicMock() - importer = EasyWorshipSongImport(mocked_manager, filenames=[]) + importer = EasyWorshipSongImport(mocked_manager, file_paths=[]) # WHEN: db_set_record_struct is called with a list of field descriptions return_value = importer.db_set_record_struct(TEST_FIELD_DESCS) @@ -246,7 +246,7 @@ class TestEasyWorshipSongImport(TestCase): # GIVEN: A mocked out SongImport class, a mocked out "manager", an encoding and some test data and known results with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'): mocked_manager = MagicMock() - importer = EasyWorshipSongImport(mocked_manager, filenames=[]) + importer = EasyWorshipSongImport(mocked_manager, file_paths=[]) importer.encoding = TEST_DATA_ENCODING importer.fields = TEST_FIELDS importer.field_descriptions = TEST_FIELD_DESCS @@ -270,7 +270,7 @@ class TestEasyWorshipSongImport(TestCase): with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'): mocked_manager = MagicMock() mocked_memo_file = MagicMock() - importer = EasyWorshipSongImport(mocked_manager, filenames=[]) + importer = EasyWorshipSongImport(mocked_manager, file_paths=[]) importer.memo_file = mocked_memo_file importer.encoding = TEST_DATA_ENCODING @@ -294,44 +294,25 @@ class TestEasyWorshipSongImport(TestCase): else: mocked_memo_file.seek.assert_any_call(call[0], call[1]) - def test_do_import_source(self): - """ - Test the :mod:`do_import` module opens the correct files - """ - # GIVEN: A mocked out SongImport class, a mocked out "manager" - with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'), \ - patch('openlp.plugins.songs.lib.importers.easyworship.os.path') as mocked_os_path: - mocked_manager = MagicMock() - importer = EasyWorshipSongImport(mocked_manager, filenames=[]) - mocked_os_path.isfile.side_effect = [True, False] - - # WHEN: Supplied with an import source - importer.import_source = 'Songs.DB' - - # THEN: do_import should return None having called os.path.isfile - self.assertIsNone(importer.do_import(), 'do_import should return None') - mocked_os_path.isfile.assert_any_call('Songs.DB') - mocked_os_path.isfile.assert_any_call('Songs.MB') - def test_do_import_source_invalid(self): """ Test the :mod:`do_import` module produces an error when Songs.MB not found. """ # GIVEN: A mocked out SongImport class, a mocked out "manager" with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'), \ - patch('openlp.plugins.songs.lib.importers.easyworship.os.path') as mocked_os_path: + patch('openlp.plugins.songs.lib.importers.easyworship.Path.is_file', side_effect=[True, False]): mocked_manager = MagicMock() - importer = EasyWorshipSongImport(mocked_manager, filenames=[]) - importer.log_error = MagicMock() - mocked_os_path.isfile.side_effect = [True, False] + importer = EasyWorshipSongImport(mocked_manager, file_paths=[]) + with patch.object(importer, 'log_error') as mocked_log_error: - # WHEN: do_import is supplied with an import source (Songs.MB missing) - importer.import_source = 'Songs.DB' - importer.do_import() + # WHEN: do_import is supplied with an import source (Songs.MB missing) + importer.import_source = 'Songs.DB' + importer.do_import() - # THEN: do_import should have logged an error that the Songs.MB file could not be found. - importer.log_error.assert_any_call(importer.import_source, 'Could not find the "Songs.MB" file. It must be ' - 'in the same folder as the "Songs.DB" file.') + # THEN: do_import should have logged an error that the Songs.MB file could not be found. + mocked_log_error.assert_any_call(importer.import_source, + 'Could not find the "Songs.MB" file. It must be in the same folder as ' + 'the "Songs.DB" file.') def test_do_import_database_validity(self): """ @@ -339,18 +320,19 @@ class TestEasyWorshipSongImport(TestCase): """ # GIVEN: A mocked out SongImport class, os.path and a mocked out "manager" with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'), \ - patch('openlp.plugins.songs.lib.importers.easyworship.os.path') as mocked_os_path: + patch('openlp.plugins.songs.lib.importers.easyworship.Path.is_file', return_value=True), \ + patch('openlp.plugins.songs.lib.importers.easyworship.Path.stat') as mocked_stat: + mocked_manager = MagicMock() - importer = EasyWorshipSongImport(mocked_manager, filenames=[]) - mocked_os_path.isfile.return_value = True + importer = EasyWorshipSongImport(mocked_manager, file_paths=[]) importer.import_source = 'Songs.DB' # WHEN: DB file size is less than 0x800 - mocked_os_path.getsize.return_value = 0x7FF + mocked_stat.return_value.st_size = 0x7FF - # THEN: do_import should return None having called os.path.isfile + # THEN: do_import should return None having called Path.stat() self.assertIsNone(importer.do_import(), 'do_import should return None when db_size is less than 0x800') - mocked_os_path.getsize.assert_any_call('Songs.DB') + mocked_stat.assert_called_once_with() def test_do_import_memo_validty(self): """ @@ -358,13 +340,12 @@ class TestEasyWorshipSongImport(TestCase): """ # GIVEN: A mocked out SongImport class, a mocked out "manager" with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'), \ - patch('openlp.plugins.songs.lib.importers.easyworship.os.path') as mocked_os_path, \ - patch('builtins.open') as mocked_open, \ + patch('openlp.plugins.songs.lib.importers.easyworship.Path.is_file', return_value=True), \ + patch('openlp.plugins.songs.lib.importers.easyworship.Path.stat', **{'return_value.st_size': 0x800}), \ + patch('openlp.plugins.songs.lib.importers.easyworship.Path.open') as mocked_open, \ patch('openlp.plugins.songs.lib.importers.easyworship.struct') as mocked_struct: mocked_manager = MagicMock() - importer = EasyWorshipSongImport(mocked_manager, filenames=[]) - mocked_os_path.isfile.return_value = True - mocked_os_path.getsize.return_value = 0x800 + importer = EasyWorshipSongImport(mocked_manager, file_paths=[]) importer.import_source = 'Songs.DB' # WHEN: Unpacking first 35 bytes of Memo file @@ -385,14 +366,14 @@ class TestEasyWorshipSongImport(TestCase): """ # GIVEN: A mocked out SongImport class, a mocked out "manager" with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'), \ - patch('openlp.plugins.songs.lib.importers.easyworship.os.path') as mocked_os_path, \ + patch('openlp.plugins.songs.lib.importers.easyworship.Path.is_file', return_value=True), \ + patch('openlp.plugins.songs.lib.importers.easyworship.Path.stat', **{'return_value.st_size': 0x800}), \ + patch('openlp.plugins.songs.lib.importers.easyworship.Path.open'), \ patch('builtins.open'), patch('openlp.plugins.songs.lib.importers.easyworship.struct') as mocked_struct, \ - patch('openlp.plugins.songs.lib.importers.easyworship.retrieve_windows_encoding') as \ + patch('openlp.plugins.songs.lib.importers.easyworship.retrieve_windows_encoding') as \ mocked_retrieve_windows_encoding: mocked_manager = MagicMock() - importer = EasyWorshipSongImport(mocked_manager, filenames=[]) - mocked_os_path.isfile.return_value = True - mocked_os_path.getsize.return_value = 0x800 + importer = EasyWorshipSongImport(mocked_manager, file_paths=[]) importer.import_source = 'Songs.DB' # WHEN: Unpacking the code page diff --git a/tests/functional/openlp_plugins/songs/test_lyriximport.py b/tests/functional/openlp_plugins/songs/test_lyriximport.py index 32644e033..2732b3154 100644 --- a/tests/functional/openlp_plugins/songs/test_lyriximport.py +++ b/tests/functional/openlp_plugins/songs/test_lyriximport.py @@ -22,7 +22,8 @@ This module contains tests for the LyriX song importer. """ import os -from unittest.mock import patch + +from openlp.core.common.path import Path from tests.helpers.songfileimport import SongImportTestHelper @@ -41,9 +42,9 @@ class TestLyrixFileImport(SongImportTestHelper): """ Test that loading an LyriX file works correctly on various files """ - self.file_import([os.path.join(TEST_PATH, 'A06.TXT')], + self.file_import([Path(TEST_PATH, 'A06.TXT')], self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace.json'))) - self.file_import([os.path.join(TEST_PATH, 'A002.TXT')], + self.file_import([Path(TEST_PATH, 'A002.TXT')], self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace2.json'))) - self.file_import([os.path.join(TEST_PATH, 'AO05.TXT')], + self.file_import([Path(TEST_PATH, 'AO05.TXT')], self.load_external_result_data(os.path.join(TEST_PATH, 'in die regterhand.json'))) diff --git a/tests/functional/openlp_plugins/songs/test_mediashout.py b/tests/functional/openlp_plugins/songs/test_mediashout.py index c501ba6ab..8fc452ea4 100644 --- a/tests/functional/openlp_plugins/songs/test_mediashout.py +++ b/tests/functional/openlp_plugins/songs/test_mediashout.py @@ -51,7 +51,7 @@ class TestMediaShoutImport(TestCase): """ # GIVEN: A MediaShoutImport class # WHEN: It is created - importer = MediaShoutImport(MagicMock(), filename='mediashout.db') + importer = MediaShoutImport(MagicMock(), file_path='mediashout.db') # THEN: It should not be None self.assertIsNotNone(importer) @@ -62,7 +62,7 @@ class TestMediaShoutImport(TestCase): Test that do_import exits early when unable to connect to the database """ # GIVEN: A MediaShoutImport instance - importer = MediaShoutImport(MagicMock(), filename='mediashout.db') + importer = MediaShoutImport(MagicMock(), file_path='mediashout.db') mocked_pyodbc.connect.side_effect = Exception('Unable to connect') # WHEN: do_import is called @@ -89,7 +89,7 @@ class TestMediaShoutImport(TestCase): group = GroupRecord('Hymns') # GIVEN: A MediaShoutImport instance and a bunch of stuff mocked out - importer = MediaShoutImport(MagicMock(), filename='mediashout.db') + importer = MediaShoutImport(MagicMock(), file_path='mediashout.db') mocked_cursor = MagicMock() mocked_cursor.fetchall.side_effect = [[song], [verse], [play_order], [theme], [group]] mocked_cursor.tables.fetchone.return_value = True @@ -124,7 +124,7 @@ class TestMediaShoutImport(TestCase): song = SongRecord(1, 'Amazing Grace', 'William Wilberforce', 'Public Domain', 1, '654321', '') # GIVEN: A MediaShoutImport instance and a bunch of stuff mocked out - importer = MediaShoutImport(MagicMock(), filename='mediashout.db') + importer = MediaShoutImport(MagicMock(), file_path='mediashout.db') mocked_cursor = MagicMock() mocked_cursor.fetchall.return_value = [song] mocked_connection = MagicMock() @@ -158,7 +158,7 @@ class TestMediaShoutImport(TestCase): play_order = PlayOrderRecord(0, 1, 1) theme = ThemeRecord('Grace') group = GroupRecord('Hymns') - importer = MediaShoutImport(MagicMock(), filename='mediashout.db') + importer = MediaShoutImport(MagicMock(), file_path='mediashout.db') # WHEN: A song is processed with patch.object(importer, 'set_defaults') as mocked_set_defaults, \ @@ -200,7 +200,7 @@ class TestMediaShoutImport(TestCase): play_order = PlayOrderRecord(0, 1, 1) theme = ThemeRecord('Grace') group = GroupRecord('Hymns') - importer = MediaShoutImport(MagicMock(), filename='mediashout.db') + importer = MediaShoutImport(MagicMock(), file_path='mediashout.db') # WHEN: A song is processed with patch.object(importer, 'set_defaults') as mocked_set_defaults, \ diff --git a/tests/functional/openlp_plugins/songs/test_openlpimporter.py b/tests/functional/openlp_plugins/songs/test_openlpimporter.py index 1b920c843..590d09b79 100644 --- a/tests/functional/openlp_plugins/songs/test_openlpimporter.py +++ b/tests/functional/openlp_plugins/songs/test_openlpimporter.py @@ -48,7 +48,7 @@ class TestOpenLPImport(TestCase): mocked_manager = MagicMock() # WHEN: An importer object is created - importer = OpenLPSongImport(mocked_manager, filenames=[]) + importer = OpenLPSongImport(mocked_manager, file_paths=[]) # THEN: The importer object should not be None self.assertIsNotNone(importer, 'Import should not be none') @@ -61,7 +61,7 @@ class TestOpenLPImport(TestCase): with patch('openlp.plugins.songs.lib.importers.openlp.SongImport'): mocked_manager = MagicMock() mocked_import_wizard = MagicMock() - importer = OpenLPSongImport(mocked_manager, filenames=[]) + importer = OpenLPSongImport(mocked_manager, file_paths=[]) importer.import_wizard = mocked_import_wizard importer.stop_import_flag = True diff --git a/tests/functional/openlp_plugins/songs/test_openlyricsexport.py b/tests/functional/openlp_plugins/songs/test_openlyricsexport.py index 191952c7a..0fd4767db 100644 --- a/tests/functional/openlp_plugins/songs/test_openlyricsexport.py +++ b/tests/functional/openlp_plugins/songs/test_openlyricsexport.py @@ -22,14 +22,14 @@ """ This module contains tests for the OpenLyrics song importer. """ -import os import shutil from tempfile import mkdtemp from unittest import TestCase from unittest.mock import MagicMock, patch -from openlp.plugins.songs.lib.openlyricsexport import OpenLyricsExport from openlp.core.common.registry import Registry +from openlp.core.common.path import Path, rmtree +from openlp.plugins.songs.lib.openlyricsexport import OpenLyricsExport from tests.helpers.testmixin import TestMixin @@ -43,13 +43,13 @@ class TestOpenLyricsExport(TestCase, TestMixin): Create the registry """ Registry.create() - self.temp_folder = mkdtemp() + self.temp_folder = Path(mkdtemp()) def tearDown(self): """ Cleanup """ - shutil.rmtree(self.temp_folder) + rmtree(self.temp_folder) def test_export_same_filename(self): """ @@ -73,7 +73,9 @@ class TestOpenLyricsExport(TestCase, TestMixin): ol_export.do_export() # THEN: The exporter should have created 2 files - self.assertTrue(os.path.exists(os.path.join(self.temp_folder, - '%s (%s).xml' % (song.title, author.display_name)))) - self.assertTrue(os.path.exists(os.path.join(self.temp_folder, - '%s (%s)-1.xml' % (song.title, author.display_name)))) + self.assertTrue((self.temp_folder / + '{title} ({display_name}).xml'.format( + title=song.title, display_name=author.display_name)).exists()) + self.assertTrue((self.temp_folder / + '{title} ({display_name})-1.xml'.format( + title=song.title, display_name=author.display_name)).exists()) diff --git a/tests/functional/openlp_plugins/songs/test_openlyricsimport.py b/tests/functional/openlp_plugins/songs/test_openlyricsimport.py index 10577c392..088711fbc 100644 --- a/tests/functional/openlp_plugins/songs/test_openlyricsimport.py +++ b/tests/functional/openlp_plugins/songs/test_openlyricsimport.py @@ -29,6 +29,7 @@ from unittest.mock import MagicMock, patch from lxml import etree, objectify +from openlp.core.common.path import Path from openlp.core.common.registry import Registry from openlp.core.common.settings import Settings from openlp.plugins.songs.lib.importers.openlyrics import OpenLyricsImport @@ -110,7 +111,7 @@ class TestOpenLyricsImport(TestCase, TestMixin): mocked_manager = MagicMock() # WHEN: An importer object is created - importer = OpenLyricsImport(mocked_manager, filenames=[]) + importer = OpenLyricsImport(mocked_manager, file_paths=[]) # THEN: The importer should be an instance of SongImport self.assertIsInstance(importer, SongImport) @@ -123,13 +124,13 @@ class TestOpenLyricsImport(TestCase, TestMixin): for song_file in SONG_TEST_DATA: mocked_manager = MagicMock() mocked_import_wizard = MagicMock() - importer = OpenLyricsImport(mocked_manager, filenames=[]) + importer = OpenLyricsImport(mocked_manager, file_paths=[]) importer.import_wizard = mocked_import_wizard importer.open_lyrics = MagicMock() importer.open_lyrics.xml_to_song = MagicMock() # WHEN: Importing each file - importer.import_source = [os.path.join(TEST_PATH, song_file)] + importer.import_source = [Path(TEST_PATH, song_file)] importer.do_import() # THEN: The xml_to_song() method should have been called diff --git a/tests/functional/openlp_plugins/songs/test_openoffice.py b/tests/functional/openlp_plugins/songs/test_openoffice.py index 8c6143a05..4172a553c 100644 --- a/tests/functional/openlp_plugins/songs/test_openoffice.py +++ b/tests/functional/openlp_plugins/songs/test_openoffice.py @@ -54,7 +54,7 @@ class TestOpenOfficeImport(TestCase, TestMixin): mocked_manager = MagicMock() # WHEN: An importer object is created - importer = OpenOfficeImport(mocked_manager, filenames=[]) + importer = OpenOfficeImport(mocked_manager, file_paths=[]) # THEN: The importer object should not be None self.assertIsNotNone(importer, 'Import should not be none') @@ -66,7 +66,7 @@ class TestOpenOfficeImport(TestCase, TestMixin): """ # GIVEN: A mocked out SongImport class, a mocked out "manager" and a document that raises an exception mocked_manager = MagicMock() - importer = OpenOfficeImport(mocked_manager, filenames=[]) + importer = OpenOfficeImport(mocked_manager, file_paths=[]) importer.document = MagicMock() importer.document.close = MagicMock(side_effect=Exception()) diff --git a/tests/functional/openlp_plugins/songs/test_opensongimport.py b/tests/functional/openlp_plugins/songs/test_opensongimport.py index a4cf5552f..a8582b3e4 100644 --- a/tests/functional/openlp_plugins/songs/test_opensongimport.py +++ b/tests/functional/openlp_plugins/songs/test_opensongimport.py @@ -27,6 +27,7 @@ from unittest import TestCase from unittest.mock import patch, MagicMock from openlp.core.common.registry import Registry +from openlp.core.common.path import Path from openlp.plugins.songs.lib.importers.opensong import OpenSongImport from tests.helpers.songfileimport import SongImportTestHelper @@ -52,15 +53,15 @@ class TestOpenSongFileImport(SongImportTestHelper): mocked_returned_settings.value.side_effect = lambda value: True if value == 'songs/enable chords' else False mocked_settings.return_value = mocked_returned_settings # Do the test import - self.file_import([os.path.join(TEST_PATH, 'Amazing Grace')], + self.file_import([Path(TEST_PATH, 'Amazing Grace')], self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace.json'))) - self.file_import([os.path.join(TEST_PATH, 'Beautiful Garden Of Prayer')], + self.file_import([Path(TEST_PATH, 'Beautiful Garden Of Prayer')], self.load_external_result_data(os.path.join(TEST_PATH, 'Beautiful Garden Of Prayer.json'))) - self.file_import([os.path.join(TEST_PATH, 'One, Two, Three, Four, Five')], + self.file_import([Path(TEST_PATH, 'One, Two, Three, Four, Five')], self.load_external_result_data(os.path.join(TEST_PATH, 'One, Two, Three, Four, Five.json'))) - self.file_import([os.path.join(TEST_PATH, 'Amazing Grace2')], + self.file_import([Path(TEST_PATH, 'Amazing Grace2')], self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace.json'))) - self.file_import([os.path.join(TEST_PATH, 'Amazing Grace with bad CCLI')], + self.file_import([Path(TEST_PATH, 'Amazing Grace with bad CCLI')], self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace without CCLI.json'))) @@ -83,7 +84,7 @@ class TestOpenSongImport(TestCase): mocked_manager = MagicMock() # WHEN: An importer object is created - importer = OpenSongImport(mocked_manager, filenames=[]) + importer = OpenSongImport(mocked_manager, file_paths=[]) # THEN: The importer object should not be None self.assertIsNotNone(importer, 'Import should not be none') @@ -96,7 +97,7 @@ class TestOpenSongImport(TestCase): with patch('openlp.plugins.songs.lib.importers.opensong.SongImport'): mocked_manager = MagicMock() mocked_import_wizard = MagicMock() - importer = OpenSongImport(mocked_manager, filenames=[]) + importer = OpenSongImport(mocked_manager, file_paths=[]) importer.import_wizard = mocked_import_wizard importer.stop_import_flag = True @@ -117,7 +118,7 @@ class TestOpenSongImport(TestCase): with patch('openlp.plugins.songs.lib.importers.opensong.SongImport'): mocked_manager = MagicMock() mocked_import_wizard = MagicMock() - importer = OpenSongImport(mocked_manager, filenames=[]) + importer = OpenSongImport(mocked_manager, file_paths=[]) importer.import_wizard = mocked_import_wizard importer.stop_import_flag = True diff --git a/tests/functional/openlp_plugins/songs/test_opsproimport.py b/tests/functional/openlp_plugins/songs/test_opsproimport.py index cb7b0b84d..8dac8eca4 100644 --- a/tests/functional/openlp_plugins/songs/test_opsproimport.py +++ b/tests/functional/openlp_plugins/songs/test_opsproimport.py @@ -86,7 +86,7 @@ class TestOpsProSongImport(TestCase): mocked_manager = MagicMock() # WHEN: An importer object is created - importer = OPSProImport(mocked_manager, filenames=[]) + importer = OPSProImport(mocked_manager, file_paths=[]) # THEN: The importer object should not be None self.assertIsNotNone(importer, 'Import should not be none') @@ -98,7 +98,7 @@ class TestOpsProSongImport(TestCase): """ # GIVEN: A mocked out SongImport class, a mocked out "manager" and a mocked song and lyrics entry mocked_manager = MagicMock() - importer = OPSProImport(mocked_manager, filenames=[]) + importer = OPSProImport(mocked_manager, file_paths=[]) importer.finish = MagicMock() song, lyrics = _build_data('you are so faithfull.txt', False) @@ -118,7 +118,7 @@ class TestOpsProSongImport(TestCase): """ # GIVEN: A mocked out SongImport class, a mocked out "manager" and a mocked song and lyrics entry mocked_manager = MagicMock() - importer = OPSProImport(mocked_manager, filenames=[]) + importer = OPSProImport(mocked_manager, file_paths=[]) importer.finish = MagicMock() song, lyrics = _build_data('amazing grace.txt', False) @@ -138,7 +138,7 @@ class TestOpsProSongImport(TestCase): """ # GIVEN: A mocked out SongImport class, a mocked out "manager" and a mocked song and lyrics entry mocked_manager = MagicMock() - importer = OPSProImport(mocked_manager, filenames=[]) + importer = OPSProImport(mocked_manager, file_paths=[]) importer.finish = MagicMock() song, lyrics = _build_data('amazing grace2.txt', True) @@ -158,7 +158,7 @@ class TestOpsProSongImport(TestCase): """ # GIVEN: A mocked out SongImport class, a mocked out "manager" and a mocked song and lyrics entry mocked_manager = MagicMock() - importer = OPSProImport(mocked_manager, filenames=[]) + importer = OPSProImport(mocked_manager, file_paths=[]) importer.finish = MagicMock() song, lyrics = _build_data('amazing grace3.txt', True) diff --git a/tests/functional/openlp_plugins/songs/test_powerpraiseimport.py b/tests/functional/openlp_plugins/songs/test_powerpraiseimport.py index 98838ba80..4df5806ae 100644 --- a/tests/functional/openlp_plugins/songs/test_powerpraiseimport.py +++ b/tests/functional/openlp_plugins/songs/test_powerpraiseimport.py @@ -23,11 +23,11 @@ The :mod:`powerpraiseimport` module provides the functionality for importing ProPresenter song files into the current installation database. """ - import os +from openlp.core.common.path import Path + from tests.helpers.songfileimport import SongImportTestHelper -from openlp.core.common.registry import Registry TEST_PATH = os.path.abspath( os.path.join(os.path.dirname(__file__), '..', '..', '..', 'resources', 'powerpraisesongs')) @@ -44,7 +44,7 @@ class TestPowerPraiseFileImport(SongImportTestHelper): """ Test that loading a PowerPraise file works correctly """ - self.file_import([os.path.join(TEST_PATH, 'Naher, mein Gott zu Dir.ppl')], + self.file_import([Path(TEST_PATH, 'Naher, mein Gott zu Dir.ppl')], self.load_external_result_data(os.path.join(TEST_PATH, 'Naher, mein Gott zu Dir.json'))) - self.file_import([os.path.join(TEST_PATH, 'You are so faithful.ppl')], + self.file_import([Path(TEST_PATH, 'You are so faithful.ppl')], self.load_external_result_data(os.path.join(TEST_PATH, 'You are so faithful.json'))) diff --git a/tests/functional/openlp_plugins/songs/test_presentationmanagerimport.py b/tests/functional/openlp_plugins/songs/test_presentationmanagerimport.py index c8c496dde..5391c31af 100644 --- a/tests/functional/openlp_plugins/songs/test_presentationmanagerimport.py +++ b/tests/functional/openlp_plugins/songs/test_presentationmanagerimport.py @@ -22,9 +22,10 @@ """ This module contains tests for the PresentationManager song importer. """ - import os +from openlp.core.common.path import Path + from tests.helpers.songfileimport import SongImportTestHelper TEST_PATH = os.path.abspath( @@ -42,7 +43,7 @@ class TestPresentationManagerFileImport(SongImportTestHelper): """ Test that loading a PresentationManager file works correctly """ - self.file_import([os.path.join(TEST_PATH, 'Great Is Thy Faithfulness.sng')], + self.file_import([Path(TEST_PATH, 'Great Is Thy Faithfulness.sng')], self.load_external_result_data(os.path.join(TEST_PATH, 'Great Is Thy Faithfulness.json'))) - self.file_import([os.path.join(TEST_PATH, 'Amazing Grace.sng')], + self.file_import([Path(TEST_PATH, 'Amazing Grace.sng')], self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace.json'))) diff --git a/tests/functional/openlp_plugins/songs/test_propresenterimport.py b/tests/functional/openlp_plugins/songs/test_propresenterimport.py index 777ff9e58..e93dd6d9f 100644 --- a/tests/functional/openlp_plugins/songs/test_propresenterimport.py +++ b/tests/functional/openlp_plugins/songs/test_propresenterimport.py @@ -23,9 +23,10 @@ The :mod:`propresenterimport` module provides the functionality for importing ProPresenter song files into the current installation database. """ - import os +from openlp.core.common.path import Path + from tests.helpers.songfileimport import SongImportTestHelper TEST_PATH = os.path.abspath( @@ -43,19 +44,19 @@ class TestProPresenterFileImport(SongImportTestHelper): """ Test that loading a ProPresenter 4 file works correctly """ - self.file_import([os.path.join(TEST_PATH, 'Amazing Grace.pro4')], + self.file_import([Path(TEST_PATH, 'Amazing Grace.pro4')], self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace.json'))) def test_pro5_song_import(self): """ Test that loading a ProPresenter 5 file works correctly """ - self.file_import([os.path.join(TEST_PATH, 'Amazing Grace.pro5')], + self.file_import([Path(TEST_PATH, 'Amazing Grace.pro5')], self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace.json'))) def test_pro6_song_import(self): """ Test that loading a ProPresenter 6 file works correctly """ - self.file_import([os.path.join(TEST_PATH, 'Amazing Grace.pro6')], + self.file_import([Path(TEST_PATH, 'Amazing Grace.pro6')], self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace.json'))) diff --git a/tests/functional/openlp_plugins/songs/test_songbeamerimport.py b/tests/functional/openlp_plugins/songs/test_songbeamerimport.py index 208022feb..165abd1a9 100644 --- a/tests/functional/openlp_plugins/songs/test_songbeamerimport.py +++ b/tests/functional/openlp_plugins/songs/test_songbeamerimport.py @@ -26,8 +26,9 @@ import os from unittest import TestCase from unittest.mock import MagicMock, patch -from openlp.plugins.songs.lib.importers.songbeamer import SongBeamerImport, SongBeamerTypes from openlp.core.common.registry import Registry +from openlp.core.common.path import Path +from openlp.plugins.songs.lib.importers.songbeamer import SongBeamerImport, SongBeamerTypes from tests.helpers.songfileimport import SongImportTestHelper @@ -51,18 +52,18 @@ class TestSongBeamerFileImport(SongImportTestHelper): mocked_returned_settings = MagicMock() mocked_returned_settings.value.side_effect = lambda value: True if value == 'songs/enable chords' else False mocked_settings.return_value = mocked_returned_settings - self.file_import([os.path.join(TEST_PATH, 'Amazing Grace.sng')], + self.file_import([Path(TEST_PATH, 'Amazing Grace.sng')], self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace.json'))) - self.file_import([os.path.join(TEST_PATH, 'Lobsinget dem Herrn.sng')], + self.file_import([Path(TEST_PATH, 'Lobsinget dem Herrn.sng')], self.load_external_result_data(os.path.join(TEST_PATH, 'Lobsinget dem Herrn.json'))) - self.file_import([os.path.join(TEST_PATH, 'When I Call On You.sng')], + self.file_import([Path(TEST_PATH, 'When I Call On You.sng')], self.load_external_result_data(os.path.join(TEST_PATH, 'When I Call On You.json'))) def test_cp1252_encoded_file(self): """ Test that a CP1252 encoded file get's decoded properly. """ - self.file_import([os.path.join(TEST_PATH, 'cp1252song.sng')], + self.file_import([Path(TEST_PATH, 'cp1252song.sng')], self.load_external_result_data(os.path.join(TEST_PATH, 'cp1252song.json'))) @@ -78,7 +79,7 @@ class TestSongBeamerImport(TestCase): self.song_import_patcher = patch('openlp.plugins.songs.lib.importers.songbeamer.SongImport') self.song_import_patcher.start() mocked_manager = MagicMock() - self.importer = SongBeamerImport(mocked_manager, filenames=[]) + self.importer = SongBeamerImport(mocked_manager, file_paths=[]) def tearDown(self): """ @@ -95,7 +96,7 @@ class TestSongBeamerImport(TestCase): mocked_manager = MagicMock() # WHEN: An importer object is created - importer = SongBeamerImport(mocked_manager, filenames=[]) + importer = SongBeamerImport(mocked_manager, file_paths=[]) # THEN: The importer object should not be None self.assertIsNotNone(importer, 'Import should not be none') diff --git a/tests/functional/openlp_plugins/songs/test_songproimport.py b/tests/functional/openlp_plugins/songs/test_songproimport.py index b41ae7b0e..4e874165b 100644 --- a/tests/functional/openlp_plugins/songs/test_songproimport.py +++ b/tests/functional/openlp_plugins/songs/test_songproimport.py @@ -23,9 +23,10 @@ The :mod:`songproimport` module provides the functionality for importing SongPro song files into the current installation database. """ - import os +from openlp.core.common.path import Path + from tests.helpers.songfileimport import SongImportTestHelper TEST_PATH = os.path.abspath( @@ -43,5 +44,5 @@ class TestSongProFileImport(SongImportTestHelper): """ Test that loading an SongPro file works correctly """ - self.file_import(os.path.join(TEST_PATH, 'amazing-grace.txt'), + self.file_import(Path(TEST_PATH, 'amazing-grace.txt'), self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace.json'))) diff --git a/tests/functional/openlp_plugins/songs/test_songselect.py b/tests/functional/openlp_plugins/songs/test_songselect.py index 8583e0a17..9a92e3788 100644 --- a/tests/functional/openlp_plugins/songs/test_songselect.py +++ b/tests/functional/openlp_plugins/songs/test_songselect.py @@ -31,6 +31,7 @@ from urllib.error import URLError from PyQt5 import QtWidgets from openlp.core import Registry +from openlp.core.common.path import Path from openlp.plugins.songs.forms.songselectform import SongSelectForm, SearchWorker from openlp.plugins.songs.lib import Song from openlp.plugins.songs.lib.songselect import SongSelectImport, LOGIN_PAGE, LOGOUT_URL, BASE_URL @@ -810,15 +811,15 @@ class TestSongSelectFileImport(SongImportTestHelper): def __init__(self, *args, **kwargs): self.importer_class_name = 'CCLIFileImport' self.importer_module_name = 'cclifile' - super(TestSongSelectFileImport, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def test_song_import(self): """ Test that loading an OpenSong file works correctly on various files """ - self.file_import([os.path.join(TEST_PATH, 'TestSong.bin')], + self.file_import([Path(TEST_PATH, 'TestSong.bin')], self.load_external_result_data(os.path.join(TEST_PATH, 'TestSong-bin.json'))) - self.file_import([os.path.join(TEST_PATH, 'TestSong.txt')], + self.file_import([Path(TEST_PATH, 'TestSong.txt')], self.load_external_result_data(os.path.join(TEST_PATH, 'TestSong-txt.json'))) diff --git a/tests/functional/openlp_plugins/songs/test_songshowplusimport.py b/tests/functional/openlp_plugins/songs/test_songshowplusimport.py index e7dd5d354..79e8ba593 100644 --- a/tests/functional/openlp_plugins/songs/test_songshowplusimport.py +++ b/tests/functional/openlp_plugins/songs/test_songshowplusimport.py @@ -26,6 +26,7 @@ import os from unittest import TestCase from unittest.mock import patch, MagicMock +from openlp.core.common.path import Path from openlp.plugins.songs.lib import VerseType from openlp.plugins.songs.lib.importers.songshowplus import SongShowPlusImport @@ -46,13 +47,13 @@ class TestSongShowPlusFileImport(SongImportTestHelper): """ Test that loading a SongShow Plus file works correctly on various files """ - self.file_import([os.path.join(TEST_PATH, 'Amazing Grace.sbsong')], + self.file_import([Path(TEST_PATH, 'Amazing Grace.sbsong')], self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace.json'))) - self.file_import([os.path.join(TEST_PATH, 'Beautiful Garden Of Prayer.sbsong')], + self.file_import([Path(TEST_PATH, 'Beautiful Garden Of Prayer.sbsong')], self.load_external_result_data(os.path.join(TEST_PATH, 'Beautiful Garden Of Prayer.json'))) - self.file_import([os.path.join(TEST_PATH, 'a mighty fortress is our god.sbsong')], + self.file_import([Path(TEST_PATH, 'a mighty fortress is our god.sbsong')], self.load_external_result_data(os.path.join(TEST_PATH, 'a mighty fortress is our god.json'))) - self.file_import([os.path.join(TEST_PATH, 'cleanse-me.sbsong')], + self.file_import([Path(TEST_PATH, 'cleanse-me.sbsong')], self.load_external_result_data(os.path.join(TEST_PATH, 'cleanse-me.json'))) @@ -69,7 +70,7 @@ class TestSongShowPlusImport(TestCase): mocked_manager = MagicMock() # WHEN: An importer object is created - importer = SongShowPlusImport(mocked_manager, filenames=[]) + importer = SongShowPlusImport(mocked_manager, file_paths=[]) # THEN: The importer object should not be None self.assertIsNotNone(importer, 'Import should not be none') @@ -82,7 +83,7 @@ class TestSongShowPlusImport(TestCase): with patch('openlp.plugins.songs.lib.importers.songshowplus.SongImport'): mocked_manager = MagicMock() mocked_import_wizard = MagicMock() - importer = SongShowPlusImport(mocked_manager, filenames=[]) + importer = SongShowPlusImport(mocked_manager, file_paths=[]) importer.import_wizard = mocked_import_wizard importer.stop_import_flag = True @@ -103,7 +104,7 @@ class TestSongShowPlusImport(TestCase): with patch('openlp.plugins.songs.lib.importers.songshowplus.SongImport'): mocked_manager = MagicMock() mocked_import_wizard = MagicMock() - importer = SongShowPlusImport(mocked_manager, filenames=[]) + importer = SongShowPlusImport(mocked_manager, file_paths=[]) importer.import_wizard = mocked_import_wizard importer.stop_import_flag = True @@ -123,7 +124,7 @@ class TestSongShowPlusImport(TestCase): # GIVEN: A mocked out SongImport class, and a mocked out "manager" with patch('openlp.plugins.songs.lib.importers.songshowplus.SongImport'): mocked_manager = MagicMock() - importer = SongShowPlusImport(mocked_manager, filenames=[]) + importer = SongShowPlusImport(mocked_manager, file_paths=[]) # WHEN: Supplied with the following arguments replicating verses being added test_values = [ @@ -151,7 +152,7 @@ class TestSongShowPlusImport(TestCase): # GIVEN: A mocked out SongImport class, and a mocked out "manager" with patch('openlp.plugins.songs.lib.importers.songshowplus.SongImport'): mocked_manager = MagicMock() - importer = SongShowPlusImport(mocked_manager, filenames=[]) + importer = SongShowPlusImport(mocked_manager, file_paths=[]) # WHEN: Supplied with the following arguments replicating a verse order being added test_values = [ diff --git a/tests/functional/openlp_plugins/songs/test_sundayplusimport.py b/tests/functional/openlp_plugins/songs/test_sundayplusimport.py index 9c6ef1a42..5a5977943 100644 --- a/tests/functional/openlp_plugins/songs/test_sundayplusimport.py +++ b/tests/functional/openlp_plugins/songs/test_sundayplusimport.py @@ -24,6 +24,8 @@ This module contains tests for the SundayPlus song importer. import os from unittest.mock import patch +from openlp.core.common.path import Path + from tests.helpers.songfileimport import SongImportTestHelper TEST_PATH = os.path.abspath( @@ -44,5 +46,5 @@ class TestSundayPlusFileImport(SongImportTestHelper): with patch('openlp.plugins.songs.lib.importers.sundayplus.retrieve_windows_encoding') as \ mocked_retrieve_windows_encoding: mocked_retrieve_windows_encoding.return_value = 'cp1252' - self.file_import([os.path.join(TEST_PATH, 'Amazing Grace.ptf')], + self.file_import([Path(TEST_PATH, 'Amazing Grace.ptf')], self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace.json'))) diff --git a/tests/functional/openlp_plugins/songs/test_videopsalm.py b/tests/functional/openlp_plugins/songs/test_videopsalm.py index c3e2a276f..69e4d9127 100644 --- a/tests/functional/openlp_plugins/songs/test_videopsalm.py +++ b/tests/functional/openlp_plugins/songs/test_videopsalm.py @@ -21,9 +21,10 @@ """ This module contains tests for the VideoPsalm song importer. """ - import os +from openlp.core.common.path import Path + from tests.helpers.songfileimport import SongImportTestHelper from unittest.mock import patch, MagicMock @@ -48,7 +49,7 @@ class TestVideoPsalmFileImport(SongImportTestHelper): mocked_returned_settings.value.side_effect = lambda value: True if value == 'songs/enable chords' else False mocked_settings.return_value = mocked_returned_settings # Do the test import - self.file_import(os.path.join(TEST_PATH, 'videopsalm-as-safe-a-stronghold.json'), + self.file_import(Path(TEST_PATH, 'videopsalm-as-safe-a-stronghold.json'), self.load_external_result_data(os.path.join(TEST_PATH, 'as-safe-a-stronghold.json'))) - self.file_import(os.path.join(TEST_PATH, 'videopsalm-as-safe-a-stronghold2.json'), + self.file_import(Path(TEST_PATH, 'videopsalm-as-safe-a-stronghold2.json'), self.load_external_result_data(os.path.join(TEST_PATH, 'as-safe-a-stronghold2.json'))) diff --git a/tests/functional/openlp_plugins/songs/test_wordsofworshipimport.py b/tests/functional/openlp_plugins/songs/test_wordsofworshipimport.py index ececf5dbb..c0cb9b47d 100644 --- a/tests/functional/openlp_plugins/songs/test_wordsofworshipimport.py +++ b/tests/functional/openlp_plugins/songs/test_wordsofworshipimport.py @@ -22,9 +22,10 @@ """ This module contains tests for the Words of Worship song importer. """ - import os +from openlp.core.common.path import Path + from tests.helpers.songfileimport import SongImportTestHelper from openlp.plugins.songs.lib.importers.wordsofworship import WordsOfWorshipImport @@ -43,10 +44,10 @@ class TestWordsOfWorshipFileImport(SongImportTestHelper): """ Test that loading a Words of Worship file works correctly """ - self.file_import([os.path.join(TEST_PATH, 'Amazing Grace (6 Verses).wow-song')], + self.file_import([Path(TEST_PATH, 'Amazing Grace (6 Verses).wow-song')], self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace (6 Verses).json'))) - self.file_import([os.path.join(TEST_PATH, 'When morning gilds the skies.wsg')], + self.file_import([Path(TEST_PATH, 'When morning gilds the skies.wsg')], self.load_external_result_data(os.path.join(TEST_PATH, 'When morning gilds the skies.json'))) - self.file_import([os.path.join(TEST_PATH, 'Holy Holy Holy Lord God Almighty.wow-song')], + self.file_import([Path(TEST_PATH, 'Holy Holy Holy Lord God Almighty.wow-song')], self.load_external_result_data(os.path.join(TEST_PATH, 'Holy Holy Holy Lord God Almighty.json'))) diff --git a/tests/functional/openlp_plugins/songs/test_worshipassistantimport.py b/tests/functional/openlp_plugins/songs/test_worshipassistantimport.py index 21e5a592f..be0179a98 100644 --- a/tests/functional/openlp_plugins/songs/test_worshipassistantimport.py +++ b/tests/functional/openlp_plugins/songs/test_worshipassistantimport.py @@ -23,9 +23,10 @@ The :mod:`worshipassistantimport` module provides the functionality for importing WorshipAssistant song files into the current installation database. """ - import os +from openlp.core.common.path import Path + from tests.helpers.songfileimport import SongImportTestHelper TEST_PATH = os.path.abspath( @@ -43,9 +44,9 @@ class TestWorshipAssistantFileImport(SongImportTestHelper): """ Test that loading an Worship Assistant file works correctly """ - self.file_import(os.path.join(TEST_PATH, 'du_herr.csv'), + self.file_import(Path(TEST_PATH, 'du_herr.csv'), self.load_external_result_data(os.path.join(TEST_PATH, 'du_herr.json'))) - self.file_import(os.path.join(TEST_PATH, 'would_you_be_free.csv'), + self.file_import(Path(TEST_PATH, 'would_you_be_free.csv'), self.load_external_result_data(os.path.join(TEST_PATH, 'would_you_be_free.json'))) - self.file_import(os.path.join(TEST_PATH, 'would_you_be_free2.csv'), + self.file_import(Path(TEST_PATH, 'would_you_be_free2.csv'), self.load_external_result_data(os.path.join(TEST_PATH, 'would_you_be_free.json'))) diff --git a/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py b/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py index 93deee67e..02c365a4b 100644 --- a/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py +++ b/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py @@ -55,7 +55,7 @@ if CAN_RUN_TESTS: _title_assignment_list = [] def __init__(self, manager): - WorshipCenterProImport.__init__(self, manager, filenames=[]) + WorshipCenterProImport.__init__(self, manager, file_paths=[]) @property def title(self): @@ -153,7 +153,7 @@ class TestWorshipCenterProSongImport(TestCase): mocked_manager = MagicMock() # WHEN: An importer object is created - importer = WorshipCenterProImport(mocked_manager, filenames=[]) + importer = WorshipCenterProImport(mocked_manager, file_paths=[]) # THEN: The importer object should not be None self.assertIsNotNone(importer, 'Import should not be none') @@ -170,7 +170,7 @@ class TestWorshipCenterProSongImport(TestCase): mocked_manager = MagicMock() mocked_log_error = MagicMock() mocked_translate.return_value = 'Translated Text' - importer = WorshipCenterProImport(mocked_manager, filenames=[]) + importer = WorshipCenterProImport(mocked_manager, file_paths=[]) importer.log_error = mocked_log_error importer.import_source = 'import_source' pyodbc_errors = [pyodbc.DatabaseError, pyodbc.IntegrityError, pyodbc.InternalError, pyodbc.OperationalError] diff --git a/tests/functional/openlp_plugins/songs/test_zionworximport.py b/tests/functional/openlp_plugins/songs/test_zionworximport.py index 8e0b45bee..42991382e 100644 --- a/tests/functional/openlp_plugins/songs/test_zionworximport.py +++ b/tests/functional/openlp_plugins/songs/test_zionworximport.py @@ -26,9 +26,10 @@ import os from unittest import TestCase from unittest.mock import MagicMock, patch +from openlp.core.common.registry import Registry +from openlp.core.common.path import Path from openlp.plugins.songs.lib.importers.zionworx import ZionWorxImport from openlp.plugins.songs.lib.importers.songimport import SongImport -from openlp.core.common.registry import Registry from tests.helpers.songfileimport import SongImportTestHelper @@ -55,7 +56,7 @@ class TestZionWorxImport(TestCase): mocked_manager = MagicMock() # WHEN: An importer object is created - importer = ZionWorxImport(mocked_manager, filenames=[]) + importer = ZionWorxImport(mocked_manager, file_paths=[]) # THEN: The importer should be an instance of SongImport self.assertIsInstance(importer, SongImport) @@ -72,5 +73,5 @@ class TestZionWorxFileImport(SongImportTestHelper): """ Test that loading an ZionWorx file works correctly on various files """ - self.file_import(os.path.join(TEST_PATH, 'zionworx.csv'), + self.file_import(Path(TEST_PATH, 'zionworx.csv'), self.load_external_result_data(os.path.join(TEST_PATH, 'zionworx.json'))) diff --git a/tests/helpers/songfileimport.py b/tests/helpers/songfileimport.py index 42768ed50..2128d28f9 100644 --- a/tests/helpers/songfileimport.py +++ b/tests/helpers/songfileimport.py @@ -89,7 +89,7 @@ class SongImportTestHelper(TestCase): """ Import the given file and check that it has imported correctly """ - importer = self.importer_class(self.mocked_manager, filenames=[source_file_name]) + importer = self.importer_class(self.mocked_manager, file_paths=[source_file_name]) importer.import_wizard = self.mocked_import_wizard importer.stop_import_flag = False importer.topics = []