This commit is contained in:
Raoul Snyman 2017-10-09 19:29:56 -07:00
commit bc0ade07e0
77 changed files with 1243 additions and 804 deletions

View File

@ -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 <a href="{qr}">download</a> 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 <a href="{qr}">download</a> to install the iOS app from the App '
'Store.').format(qr='https://itunes.apple.com/app/id1096218725'))
'Scan the QR code or click <a href="{qr}">download</a> 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)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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=''):
"""

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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('<hhxb8xh17xh', db_file.read(35))
if header_size != 0x800 or block_size < 1 or block_size > 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

View File

@ -82,10 +82,8 @@ The XML of `Foilpresenter <http://foilpresenter.de/>`_ songs is of the format::
</kategorien>
</foilpresenterfolie>
"""
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:

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -72,6 +72,7 @@ log = logging.getLogger(__name__)
NAMESPACE = 'http://openlyrics.info/namespace/2009/song'
NSMAP = '{{' + NAMESPACE + '}}{tag}'
NEWPAGETAG = '<p style="page-break-after: always;"/>'
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), '</tag>')
# Replace \n with <br/>.
text = text.replace('\n', '<br/>')
text = text.replace('[--}{--]', NEWPAGETAG)
element = etree.XML('<lines>{text}</lines>'.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'))

View File

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

View File

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

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 766 B

446
resources/images/app_qr.svg Normal file
View File

@ -0,0 +1,446 @@
<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" baseProfile="full" width="296" height="296" viewBox="0 0 296 296"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events">
<desc></desc>
<rect width="296" height="296" fill="#ffffff" cx="0" cy="0" />
<defs>
<rect id="p" width="8" height="8" />
</defs>
<g fill="#000000">
<use x="32" y="32" xlink:href="#p" />
<use x="32" y="40" xlink:href="#p" />
<use x="32" y="48" xlink:href="#p" />
<use x="32" y="56" xlink:href="#p" />
<use x="32" y="64" xlink:href="#p" />
<use x="32" y="72" xlink:href="#p" />
<use x="32" y="80" xlink:href="#p" />
<use x="32" y="96" xlink:href="#p" />
<use x="32" y="104" xlink:href="#p" />
<use x="32" y="120" xlink:href="#p" />
<use x="32" y="128" xlink:href="#p" />
<use x="32" y="168" xlink:href="#p" />
<use x="32" y="184" xlink:href="#p" />
<use x="32" y="192" xlink:href="#p" />
<use x="32" y="208" xlink:href="#p" />
<use x="32" y="216" xlink:href="#p" />
<use x="32" y="224" xlink:href="#p" />
<use x="32" y="232" xlink:href="#p" />
<use x="32" y="240" xlink:href="#p" />
<use x="32" y="248" xlink:href="#p" />
<use x="32" y="256" xlink:href="#p" />
<use x="40" y="32" xlink:href="#p" />
<use x="40" y="80" xlink:href="#p" />
<use x="40" y="96" xlink:href="#p" />
<use x="40" y="104" xlink:href="#p" />
<use x="40" y="120" xlink:href="#p" />
<use x="40" y="136" xlink:href="#p" />
<use x="40" y="152" xlink:href="#p" />
<use x="40" y="160" xlink:href="#p" />
<use x="40" y="176" xlink:href="#p" />
<use x="40" y="184" xlink:href="#p" />
<use x="40" y="208" xlink:href="#p" />
<use x="40" y="256" xlink:href="#p" />
<use x="48" y="32" xlink:href="#p" />
<use x="48" y="48" xlink:href="#p" />
<use x="48" y="56" xlink:href="#p" />
<use x="48" y="64" xlink:href="#p" />
<use x="48" y="80" xlink:href="#p" />
<use x="48" y="96" xlink:href="#p" />
<use x="48" y="104" xlink:href="#p" />
<use x="48" y="112" xlink:href="#p" />
<use x="48" y="120" xlink:href="#p" />
<use x="48" y="128" xlink:href="#p" />
<use x="48" y="136" xlink:href="#p" />
<use x="48" y="152" xlink:href="#p" />
<use x="48" y="160" xlink:href="#p" />
<use x="48" y="184" xlink:href="#p" />
<use x="48" y="192" xlink:href="#p" />
<use x="48" y="208" xlink:href="#p" />
<use x="48" y="224" xlink:href="#p" />
<use x="48" y="232" xlink:href="#p" />
<use x="48" y="240" xlink:href="#p" />
<use x="48" y="256" xlink:href="#p" />
<use x="56" y="32" xlink:href="#p" />
<use x="56" y="48" xlink:href="#p" />
<use x="56" y="56" xlink:href="#p" />
<use x="56" y="64" xlink:href="#p" />
<use x="56" y="80" xlink:href="#p" />
<use x="56" y="96" xlink:href="#p" />
<use x="56" y="112" xlink:href="#p" />
<use x="56" y="128" xlink:href="#p" />
<use x="56" y="136" xlink:href="#p" />
<use x="56" y="176" xlink:href="#p" />
<use x="56" y="192" xlink:href="#p" />
<use x="56" y="208" xlink:href="#p" />
<use x="56" y="224" xlink:href="#p" />
<use x="56" y="232" xlink:href="#p" />
<use x="56" y="240" xlink:href="#p" />
<use x="56" y="256" xlink:href="#p" />
<use x="64" y="32" xlink:href="#p" />
<use x="64" y="48" xlink:href="#p" />
<use x="64" y="56" xlink:href="#p" />
<use x="64" y="64" xlink:href="#p" />
<use x="64" y="80" xlink:href="#p" />
<use x="64" y="120" xlink:href="#p" />
<use x="64" y="128" xlink:href="#p" />
<use x="64" y="136" xlink:href="#p" />
<use x="64" y="152" xlink:href="#p" />
<use x="64" y="168" xlink:href="#p" />
<use x="64" y="176" xlink:href="#p" />
<use x="64" y="208" xlink:href="#p" />
<use x="64" y="224" xlink:href="#p" />
<use x="64" y="232" xlink:href="#p" />
<use x="64" y="240" xlink:href="#p" />
<use x="64" y="256" xlink:href="#p" />
<use x="72" y="32" xlink:href="#p" />
<use x="72" y="80" xlink:href="#p" />
<use x="72" y="120" xlink:href="#p" />
<use x="72" y="136" xlink:href="#p" />
<use x="72" y="160" xlink:href="#p" />
<use x="72" y="176" xlink:href="#p" />
<use x="72" y="208" xlink:href="#p" />
<use x="72" y="256" xlink:href="#p" />
<use x="80" y="32" xlink:href="#p" />
<use x="80" y="40" xlink:href="#p" />
<use x="80" y="48" xlink:href="#p" />
<use x="80" y="56" xlink:href="#p" />
<use x="80" y="64" xlink:href="#p" />
<use x="80" y="72" xlink:href="#p" />
<use x="80" y="80" xlink:href="#p" />
<use x="80" y="96" xlink:href="#p" />
<use x="80" y="112" xlink:href="#p" />
<use x="80" y="128" xlink:href="#p" />
<use x="80" y="144" xlink:href="#p" />
<use x="80" y="160" xlink:href="#p" />
<use x="80" y="176" xlink:href="#p" />
<use x="80" y="192" xlink:href="#p" />
<use x="80" y="208" xlink:href="#p" />
<use x="80" y="216" xlink:href="#p" />
<use x="80" y="224" xlink:href="#p" />
<use x="80" y="232" xlink:href="#p" />
<use x="80" y="240" xlink:href="#p" />
<use x="80" y="248" xlink:href="#p" />
<use x="80" y="256" xlink:href="#p" />
<use x="88" y="112" xlink:href="#p" />
<use x="88" y="120" xlink:href="#p" />
<use x="88" y="160" xlink:href="#p" />
<use x="88" y="168" xlink:href="#p" />
<use x="88" y="192" xlink:href="#p" />
<use x="96" y="32" xlink:href="#p" />
<use x="96" y="48" xlink:href="#p" />
<use x="96" y="56" xlink:href="#p" />
<use x="96" y="64" xlink:href="#p" />
<use x="96" y="80" xlink:href="#p" />
<use x="96" y="96" xlink:href="#p" />
<use x="96" y="112" xlink:href="#p" />
<use x="96" y="120" xlink:href="#p" />
<use x="96" y="136" xlink:href="#p" />
<use x="96" y="152" xlink:href="#p" />
<use x="96" y="168" xlink:href="#p" />
<use x="96" y="176" xlink:href="#p" />
<use x="96" y="200" xlink:href="#p" />
<use x="96" y="232" xlink:href="#p" />
<use x="96" y="240" xlink:href="#p" />
<use x="96" y="248" xlink:href="#p" />
<use x="96" y="256" xlink:href="#p" />
<use x="104" y="40" xlink:href="#p" />
<use x="104" y="48" xlink:href="#p" />
<use x="104" y="56" xlink:href="#p" />
<use x="104" y="112" xlink:href="#p" />
<use x="104" y="120" xlink:href="#p" />
<use x="104" y="128" xlink:href="#p" />
<use x="104" y="168" xlink:href="#p" />
<use x="104" y="176" xlink:href="#p" />
<use x="104" y="192" xlink:href="#p" />
<use x="104" y="200" xlink:href="#p" />
<use x="104" y="208" xlink:href="#p" />
<use x="104" y="216" xlink:href="#p" />
<use x="104" y="224" xlink:href="#p" />
<use x="104" y="232" xlink:href="#p" />
<use x="104" y="240" xlink:href="#p" />
<use x="104" y="248" xlink:href="#p" />
<use x="104" y="256" xlink:href="#p" />
<use x="112" y="32" xlink:href="#p" />
<use x="112" y="40" xlink:href="#p" />
<use x="112" y="48" xlink:href="#p" />
<use x="112" y="56" xlink:href="#p" />
<use x="112" y="72" xlink:href="#p" />
<use x="112" y="80" xlink:href="#p" />
<use x="112" y="88" xlink:href="#p" />
<use x="112" y="96" xlink:href="#p" />
<use x="112" y="104" xlink:href="#p" />
<use x="112" y="112" xlink:href="#p" />
<use x="112" y="120" xlink:href="#p" />
<use x="112" y="128" xlink:href="#p" />
<use x="112" y="200" xlink:href="#p" />
<use x="112" y="208" xlink:href="#p" />
<use x="112" y="216" xlink:href="#p" />
<use x="112" y="232" xlink:href="#p" />
<use x="112" y="240" xlink:href="#p" />
<use x="120" y="40" xlink:href="#p" />
<use x="120" y="64" xlink:href="#p" />
<use x="120" y="72" xlink:href="#p" />
<use x="120" y="96" xlink:href="#p" />
<use x="120" y="112" xlink:href="#p" />
<use x="120" y="136" xlink:href="#p" />
<use x="120" y="144" xlink:href="#p" />
<use x="120" y="152" xlink:href="#p" />
<use x="120" y="184" xlink:href="#p" />
<use x="120" y="216" xlink:href="#p" />
<use x="120" y="224" xlink:href="#p" />
<use x="120" y="232" xlink:href="#p" />
<use x="120" y="256" xlink:href="#p" />
<use x="128" y="32" xlink:href="#p" />
<use x="128" y="40" xlink:href="#p" />
<use x="128" y="56" xlink:href="#p" />
<use x="128" y="64" xlink:href="#p" />
<use x="128" y="80" xlink:href="#p" />
<use x="128" y="88" xlink:href="#p" />
<use x="128" y="96" xlink:href="#p" />
<use x="128" y="104" xlink:href="#p" />
<use x="128" y="112" xlink:href="#p" />
<use x="128" y="128" xlink:href="#p" />
<use x="128" y="136" xlink:href="#p" />
<use x="128" y="160" xlink:href="#p" />
<use x="128" y="216" xlink:href="#p" />
<use x="128" y="240" xlink:href="#p" />
<use x="128" y="248" xlink:href="#p" />
<use x="136" y="32" xlink:href="#p" />
<use x="136" y="40" xlink:href="#p" />
<use x="136" y="48" xlink:href="#p" />
<use x="136" y="56" xlink:href="#p" />
<use x="136" y="72" xlink:href="#p" />
<use x="136" y="120" xlink:href="#p" />
<use x="136" y="128" xlink:href="#p" />
<use x="136" y="176" xlink:href="#p" />
<use x="136" y="184" xlink:href="#p" />
<use x="136" y="192" xlink:href="#p" />
<use x="136" y="216" xlink:href="#p" />
<use x="136" y="232" xlink:href="#p" />
<use x="136" y="248" xlink:href="#p" />
<use x="144" y="48" xlink:href="#p" />
<use x="144" y="56" xlink:href="#p" />
<use x="144" y="64" xlink:href="#p" />
<use x="144" y="80" xlink:href="#p" />
<use x="144" y="96" xlink:href="#p" />
<use x="144" y="128" xlink:href="#p" />
<use x="144" y="152" xlink:href="#p" />
<use x="144" y="160" xlink:href="#p" />
<use x="144" y="176" xlink:href="#p" />
<use x="144" y="184" xlink:href="#p" />
<use x="144" y="192" xlink:href="#p" />
<use x="144" y="208" xlink:href="#p" />
<use x="144" y="216" xlink:href="#p" />
<use x="144" y="240" xlink:href="#p" />
<use x="152" y="64" xlink:href="#p" />
<use x="152" y="88" xlink:href="#p" />
<use x="152" y="96" xlink:href="#p" />
<use x="152" y="120" xlink:href="#p" />
<use x="152" y="128" xlink:href="#p" />
<use x="152" y="136" xlink:href="#p" />
<use x="152" y="160" xlink:href="#p" />
<use x="152" y="168" xlink:href="#p" />
<use x="152" y="176" xlink:href="#p" />
<use x="152" y="232" xlink:href="#p" />
<use x="152" y="248" xlink:href="#p" />
<use x="160" y="32" xlink:href="#p" />
<use x="160" y="64" xlink:href="#p" />
<use x="160" y="72" xlink:href="#p" />
<use x="160" y="80" xlink:href="#p" />
<use x="160" y="88" xlink:href="#p" />
<use x="160" y="96" xlink:href="#p" />
<use x="160" y="104" xlink:href="#p" />
<use x="160" y="112" xlink:href="#p" />
<use x="160" y="128" xlink:href="#p" />
<use x="160" y="136" xlink:href="#p" />
<use x="160" y="152" xlink:href="#p" />
<use x="160" y="184" xlink:href="#p" />
<use x="160" y="192" xlink:href="#p" />
<use x="160" y="200" xlink:href="#p" />
<use x="160" y="208" xlink:href="#p" />
<use x="160" y="240" xlink:href="#p" />
<use x="160" y="256" xlink:href="#p" />
<use x="168" y="56" xlink:href="#p" />
<use x="168" y="88" xlink:href="#p" />
<use x="168" y="96" xlink:href="#p" />
<use x="168" y="104" xlink:href="#p" />
<use x="168" y="112" xlink:href="#p" />
<use x="168" y="128" xlink:href="#p" />
<use x="168" y="136" xlink:href="#p" />
<use x="168" y="168" xlink:href="#p" />
<use x="168" y="184" xlink:href="#p" />
<use x="168" y="192" xlink:href="#p" />
<use x="168" y="208" xlink:href="#p" />
<use x="168" y="224" xlink:href="#p" />
<use x="168" y="232" xlink:href="#p" />
<use x="176" y="32" xlink:href="#p" />
<use x="176" y="80" xlink:href="#p" />
<use x="176" y="88" xlink:href="#p" />
<use x="176" y="96" xlink:href="#p" />
<use x="176" y="104" xlink:href="#p" />
<use x="176" y="112" xlink:href="#p" />
<use x="176" y="144" xlink:href="#p" />
<use x="176" y="184" xlink:href="#p" />
<use x="176" y="192" xlink:href="#p" />
<use x="176" y="200" xlink:href="#p" />
<use x="176" y="216" xlink:href="#p" />
<use x="176" y="232" xlink:href="#p" />
<use x="176" y="248" xlink:href="#p" />
<use x="176" y="256" xlink:href="#p" />
<use x="184" y="32" xlink:href="#p" />
<use x="184" y="56" xlink:href="#p" />
<use x="184" y="64" xlink:href="#p" />
<use x="184" y="72" xlink:href="#p" />
<use x="184" y="104" xlink:href="#p" />
<use x="184" y="136" xlink:href="#p" />
<use x="184" y="144" xlink:href="#p" />
<use x="184" y="152" xlink:href="#p" />
<use x="184" y="160" xlink:href="#p" />
<use x="184" y="184" xlink:href="#p" />
<use x="184" y="192" xlink:href="#p" />
<use x="184" y="208" xlink:href="#p" />
<use x="184" y="240" xlink:href="#p" />
<use x="184" y="248" xlink:href="#p" />
<use x="184" y="256" xlink:href="#p" />
<use x="192" y="32" xlink:href="#p" />
<use x="192" y="48" xlink:href="#p" />
<use x="192" y="72" xlink:href="#p" />
<use x="192" y="80" xlink:href="#p" />
<use x="192" y="104" xlink:href="#p" />
<use x="192" y="136" xlink:href="#p" />
<use x="192" y="144" xlink:href="#p" />
<use x="192" y="152" xlink:href="#p" />
<use x="192" y="160" xlink:href="#p" />
<use x="192" y="176" xlink:href="#p" />
<use x="192" y="192" xlink:href="#p" />
<use x="192" y="200" xlink:href="#p" />
<use x="192" y="208" xlink:href="#p" />
<use x="192" y="216" xlink:href="#p" />
<use x="192" y="224" xlink:href="#p" />
<use x="192" y="248" xlink:href="#p" />
<use x="192" y="256" xlink:href="#p" />
<use x="200" y="96" xlink:href="#p" />
<use x="200" y="104" xlink:href="#p" />
<use x="200" y="112" xlink:href="#p" />
<use x="200" y="136" xlink:href="#p" />
<use x="200" y="152" xlink:href="#p" />
<use x="200" y="192" xlink:href="#p" />
<use x="200" y="224" xlink:href="#p" />
<use x="200" y="232" xlink:href="#p" />
<use x="200" y="256" xlink:href="#p" />
<use x="208" y="32" xlink:href="#p" />
<use x="208" y="40" xlink:href="#p" />
<use x="208" y="48" xlink:href="#p" />
<use x="208" y="56" xlink:href="#p" />
<use x="208" y="64" xlink:href="#p" />
<use x="208" y="72" xlink:href="#p" />
<use x="208" y="80" xlink:href="#p" />
<use x="208" y="104" xlink:href="#p" />
<use x="208" y="112" xlink:href="#p" />
<use x="208" y="120" xlink:href="#p" />
<use x="208" y="136" xlink:href="#p" />
<use x="208" y="144" xlink:href="#p" />
<use x="208" y="176" xlink:href="#p" />
<use x="208" y="184" xlink:href="#p" />
<use x="208" y="192" xlink:href="#p" />
<use x="208" y="208" xlink:href="#p" />
<use x="208" y="224" xlink:href="#p" />
<use x="208" y="248" xlink:href="#p" />
<use x="208" y="256" xlink:href="#p" />
<use x="216" y="32" xlink:href="#p" />
<use x="216" y="80" xlink:href="#p" />
<use x="216" y="104" xlink:href="#p" />
<use x="216" y="112" xlink:href="#p" />
<use x="216" y="120" xlink:href="#p" />
<use x="216" y="144" xlink:href="#p" />
<use x="216" y="152" xlink:href="#p" />
<use x="216" y="160" xlink:href="#p" />
<use x="216" y="168" xlink:href="#p" />
<use x="216" y="184" xlink:href="#p" />
<use x="216" y="192" xlink:href="#p" />
<use x="216" y="224" xlink:href="#p" />
<use x="216" y="240" xlink:href="#p" />
<use x="216" y="248" xlink:href="#p" />
<use x="224" y="32" xlink:href="#p" />
<use x="224" y="48" xlink:href="#p" />
<use x="224" y="56" xlink:href="#p" />
<use x="224" y="64" xlink:href="#p" />
<use x="224" y="80" xlink:href="#p" />
<use x="224" y="96" xlink:href="#p" />
<use x="224" y="104" xlink:href="#p" />
<use x="224" y="112" xlink:href="#p" />
<use x="224" y="120" xlink:href="#p" />
<use x="224" y="152" xlink:href="#p" />
<use x="224" y="160" xlink:href="#p" />
<use x="224" y="176" xlink:href="#p" />
<use x="224" y="192" xlink:href="#p" />
<use x="224" y="200" xlink:href="#p" />
<use x="224" y="208" xlink:href="#p" />
<use x="224" y="216" xlink:href="#p" />
<use x="224" y="224" xlink:href="#p" />
<use x="224" y="232" xlink:href="#p" />
<use x="224" y="256" xlink:href="#p" />
<use x="232" y="32" xlink:href="#p" />
<use x="232" y="48" xlink:href="#p" />
<use x="232" y="56" xlink:href="#p" />
<use x="232" y="64" xlink:href="#p" />
<use x="232" y="80" xlink:href="#p" />
<use x="232" y="96" xlink:href="#p" />
<use x="232" y="128" xlink:href="#p" />
<use x="232" y="160" xlink:href="#p" />
<use x="232" y="168" xlink:href="#p" />
<use x="232" y="192" xlink:href="#p" />
<use x="232" y="200" xlink:href="#p" />
<use x="232" y="208" xlink:href="#p" />
<use x="232" y="216" xlink:href="#p" />
<use x="232" y="232" xlink:href="#p" />
<use x="232" y="248" xlink:href="#p" />
<use x="240" y="32" xlink:href="#p" />
<use x="240" y="48" xlink:href="#p" />
<use x="240" y="56" xlink:href="#p" />
<use x="240" y="64" xlink:href="#p" />
<use x="240" y="80" xlink:href="#p" />
<use x="240" y="96" xlink:href="#p" />
<use x="240" y="112" xlink:href="#p" />
<use x="240" y="128" xlink:href="#p" />
<use x="240" y="136" xlink:href="#p" />
<use x="240" y="144" xlink:href="#p" />
<use x="240" y="168" xlink:href="#p" />
<use x="240" y="176" xlink:href="#p" />
<use x="240" y="184" xlink:href="#p" />
<use x="240" y="192" xlink:href="#p" />
<use x="240" y="200" xlink:href="#p" />
<use x="240" y="224" xlink:href="#p" />
<use x="240" y="240" xlink:href="#p" />
<use x="248" y="32" xlink:href="#p" />
<use x="248" y="80" xlink:href="#p" />
<use x="248" y="112" xlink:href="#p" />
<use x="248" y="136" xlink:href="#p" />
<use x="248" y="144" xlink:href="#p" />
<use x="248" y="152" xlink:href="#p" />
<use x="248" y="160" xlink:href="#p" />
<use x="248" y="168" xlink:href="#p" />
<use x="248" y="200" xlink:href="#p" />
<use x="248" y="208" xlink:href="#p" />
<use x="248" y="224" xlink:href="#p" />
<use x="248" y="248" xlink:href="#p" />
<use x="248" y="256" xlink:href="#p" />
<use x="256" y="32" xlink:href="#p" />
<use x="256" y="40" xlink:href="#p" />
<use x="256" y="48" xlink:href="#p" />
<use x="256" y="56" xlink:href="#p" />
<use x="256" y="64" xlink:href="#p" />
<use x="256" y="72" xlink:href="#p" />
<use x="256" y="80" xlink:href="#p" />
<use x="256" y="96" xlink:href="#p" />
<use x="256" y="104" xlink:href="#p" />
<use x="256" y="120" xlink:href="#p" />
<use x="256" y="136" xlink:href="#p" />
<use x="256" y="144" xlink:href="#p" />
<use x="256" y="200" xlink:href="#p" />
<use x="256" y="216" xlink:href="#p" />
<use x="256" y="224" xlink:href="#p" />
<use x="256" y="232" xlink:href="#p" />
<use x="256" y="240" xlink:href="#p" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 758 B

View File

@ -183,7 +183,6 @@
<file>projector_warmup.png</file>
</qresource>
<qresource prefix="remotes">
<file>android_app_qr.png</file>
<file>ios_app_qr.png</file>
</qresource>
</RCC>
<file>app_qr.svg</file>
</qresource>
</RCC>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 = []