forked from openlp/openlp
Head
This commit is contained in:
commit
b666196002
@ -6,6 +6,8 @@
|
|||||||
*.ropeproject
|
*.ropeproject
|
||||||
*.e4*
|
*.e4*
|
||||||
.eric4project
|
.eric4project
|
||||||
|
.komodotools
|
||||||
|
*.komodoproject
|
||||||
list
|
list
|
||||||
openlp.org 2.0.e4*
|
openlp.org 2.0.e4*
|
||||||
documentation/build/html
|
documentation/build/html
|
||||||
|
@ -194,6 +194,7 @@ class Manager(object):
|
|||||||
db_ver, up_ver = upgrade_db(self.db_url, upgrade_mod)
|
db_ver, up_ver = upgrade_db(self.db_url, upgrade_mod)
|
||||||
except (SQLAlchemyError, DBAPIError):
|
except (SQLAlchemyError, DBAPIError):
|
||||||
log.exception('Error loading database: %s', self.db_url)
|
log.exception('Error loading database: %s', self.db_url)
|
||||||
|
return
|
||||||
if db_ver > up_ver:
|
if db_ver > up_ver:
|
||||||
critical_error_message_box(
|
critical_error_message_box(
|
||||||
translate('OpenLP.Manager', 'Database Error'),
|
translate('OpenLP.Manager', 'Database Error'),
|
||||||
|
@ -511,7 +511,7 @@ class AdvancedTab(SettingsTab):
|
|||||||
"""
|
"""
|
||||||
Select an image for the default display screen.
|
Select an image for the default display screen.
|
||||||
"""
|
"""
|
||||||
file_filters = '%s;;%s (*.*) (*)' % (get_images_filter(), UiStrings().AllFiles)
|
file_filters = '%s;;%s (*.*)' % (get_images_filter(), UiStrings().AllFiles)
|
||||||
filename = QtGui.QFileDialog.getOpenFileName(self, translate('OpenLP.AdvancedTab', 'Open File'), '',
|
filename = QtGui.QFileDialog.getOpenFileName(self, translate('OpenLP.AdvancedTab', 'Open File'), '',
|
||||||
file_filters)
|
file_filters)
|
||||||
if filename:
|
if filename:
|
||||||
|
@ -228,7 +228,7 @@ class ExceptionForm(QtGui.QDialog, Ui_ExceptionDialog, RegistryProperties):
|
|||||||
"""
|
"""
|
||||||
files = QtGui.QFileDialog.getOpenFileName(self, translate('ImagePlugin.ExceptionDialog', 'Select Attachment'),
|
files = QtGui.QFileDialog.getOpenFileName(self, translate('ImagePlugin.ExceptionDialog', 'Select Attachment'),
|
||||||
Settings().value(self.settings_section + '/last directory'),
|
Settings().value(self.settings_section + '/last directory'),
|
||||||
'%s (*.*) (*)' % UiStrings().AllFiles)
|
'%s (*)' % UiStrings().AllFiles)
|
||||||
log.info('New files(s) %s', str(files))
|
log.info('New files(s) %s', str(files))
|
||||||
if files:
|
if files:
|
||||||
self.file_attachment = str(files)
|
self.file_attachment = str(files)
|
||||||
|
@ -114,10 +114,10 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard, RegistryProperties):
|
|||||||
"""
|
"""
|
||||||
Run the wizard.
|
Run the wizard.
|
||||||
"""
|
"""
|
||||||
self.setDefaults()
|
self.set_defaults()
|
||||||
return QtGui.QWizard.exec_(self)
|
return QtGui.QWizard.exec_(self)
|
||||||
|
|
||||||
def setDefaults(self):
|
def set_defaults(self):
|
||||||
"""
|
"""
|
||||||
Set up display at start of theme edit.
|
Set up display at start of theme edit.
|
||||||
"""
|
"""
|
||||||
|
@ -90,7 +90,7 @@ def get_media_players():
|
|||||||
overridden_player = 'auto'
|
overridden_player = 'auto'
|
||||||
else:
|
else:
|
||||||
overridden_player = ''
|
overridden_player = ''
|
||||||
saved_players_list = saved_players.replace('[', '').replace(']', '').split(',')
|
saved_players_list = saved_players.replace('[', '').replace(']', '').split(',') if saved_players else []
|
||||||
return saved_players_list, overridden_player
|
return saved_players_list, overridden_player
|
||||||
|
|
||||||
|
|
||||||
|
@ -90,7 +90,7 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard, RegistryProperties):
|
|||||||
self.footer_font_combo_box.activated.connect(self.update_theme)
|
self.footer_font_combo_box.activated.connect(self.update_theme)
|
||||||
self.footer_size_spin_box.valueChanged.connect(self.update_theme)
|
self.footer_size_spin_box.valueChanged.connect(self.update_theme)
|
||||||
|
|
||||||
def setDefaults(self):
|
def set_defaults(self):
|
||||||
"""
|
"""
|
||||||
Set up display at start of theme edit.
|
Set up display at start of theme edit.
|
||||||
"""
|
"""
|
||||||
@ -261,7 +261,7 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard, RegistryProperties):
|
|||||||
log.debug('Editing theme %s' % self.theme.theme_name)
|
log.debug('Editing theme %s' % self.theme.theme_name)
|
||||||
self.temp_background_filename = ''
|
self.temp_background_filename = ''
|
||||||
self.update_theme_allowed = False
|
self.update_theme_allowed = False
|
||||||
self.setDefaults()
|
self.set_defaults()
|
||||||
self.update_theme_allowed = True
|
self.update_theme_allowed = True
|
||||||
self.theme_name_label.setVisible(not edit)
|
self.theme_name_label.setVisible(not edit)
|
||||||
self.theme_name_edit.setVisible(not edit)
|
self.theme_name_edit.setVisible(not edit)
|
||||||
@ -432,7 +432,7 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard, RegistryProperties):
|
|||||||
Background Image button pushed.
|
Background Image button pushed.
|
||||||
"""
|
"""
|
||||||
images_filter = get_images_filter()
|
images_filter = get_images_filter()
|
||||||
images_filter = '%s;;%s (*.*) (*)' % (images_filter, UiStrings().AllFiles)
|
images_filter = '%s;;%s (*.*)' % (images_filter, UiStrings().AllFiles)
|
||||||
filename = QtGui.QFileDialog.getOpenFileName(self, translate('OpenLP.ThemeWizard', 'Select Image'), '',
|
filename = QtGui.QFileDialog.getOpenFileName(self, translate('OpenLP.ThemeWizard', 'Select Image'), '',
|
||||||
images_filter)
|
images_filter)
|
||||||
if filename:
|
if filename:
|
||||||
|
@ -197,7 +197,7 @@ class OpenLPWizard(QtGui.QWizard, RegistryProperties):
|
|||||||
"""
|
"""
|
||||||
Run the wizard.
|
Run the wizard.
|
||||||
"""
|
"""
|
||||||
self.setDefaults()
|
self.set_defaults()
|
||||||
return QtGui.QWizard.exec_(self)
|
return QtGui.QWizard.exec_(self)
|
||||||
|
|
||||||
def reject(self):
|
def reject(self):
|
||||||
|
@ -465,7 +465,7 @@ class BibleImportForm(OpenLPWizard):
|
|||||||
self.license_details_page.registerField('license_copyright', self.copyright_edit)
|
self.license_details_page.registerField('license_copyright', self.copyright_edit)
|
||||||
self.license_details_page.registerField('license_permissions', self.permissions_edit)
|
self.license_details_page.registerField('license_permissions', self.permissions_edit)
|
||||||
|
|
||||||
def setDefaults(self):
|
def set_defaults(self):
|
||||||
"""
|
"""
|
||||||
Set default values for the wizard pages.
|
Set default values for the wizard pages.
|
||||||
"""
|
"""
|
||||||
|
@ -307,7 +307,7 @@ class BibleUpgradeForm(OpenLPWizard):
|
|||||||
if self.currentPage() == self.progress_page:
|
if self.currentPage() == self.progress_page:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def setDefaults(self):
|
def set_defaults(self):
|
||||||
"""
|
"""
|
||||||
Set default values for the wizard pages.
|
Set default values for the wizard pages.
|
||||||
"""
|
"""
|
||||||
|
@ -75,7 +75,7 @@ class ImageMediaItem(MediaManagerItem):
|
|||||||
def retranslateUi(self):
|
def retranslateUi(self):
|
||||||
self.on_new_prompt = translate('ImagePlugin.MediaItem', 'Select Image(s)')
|
self.on_new_prompt = translate('ImagePlugin.MediaItem', 'Select Image(s)')
|
||||||
file_formats = get_images_filter()
|
file_formats = get_images_filter()
|
||||||
self.on_new_file_masks = '%s;;%s (*.*) (*)' % (file_formats, UiStrings().AllFiles)
|
self.on_new_file_masks = '%s;;%s (*)' % (file_formats, UiStrings().AllFiles)
|
||||||
self.add_group_action.setText(UiStrings().AddGroup)
|
self.add_group_action.setText(UiStrings().AddGroup)
|
||||||
self.add_group_action.setToolTip(UiStrings().AddGroup)
|
self.add_group_action.setToolTip(UiStrings().AddGroup)
|
||||||
self.replace_action.setText(UiStrings().ReplaceBG)
|
self.replace_action.setText(UiStrings().ReplaceBG)
|
||||||
|
@ -264,7 +264,7 @@ class DuplicateSongRemovalForm(OpenLPWizard, RegistryProperties):
|
|||||||
self.break_search = True
|
self.break_search = True
|
||||||
self.plugin.media_item.on_search_text_button_clicked()
|
self.plugin.media_item.on_search_text_button_clicked()
|
||||||
|
|
||||||
def setDefaults(self):
|
def set_defaults(self):
|
||||||
"""
|
"""
|
||||||
Set default form values for the song import wizard.
|
Set default form values for the song import wizard.
|
||||||
"""
|
"""
|
||||||
|
@ -118,13 +118,18 @@ class Ui_EditSongDialog(object):
|
|||||||
self.authors_group_box.setObjectName('authors_group_box')
|
self.authors_group_box.setObjectName('authors_group_box')
|
||||||
self.authors_layout = QtGui.QVBoxLayout(self.authors_group_box)
|
self.authors_layout = QtGui.QVBoxLayout(self.authors_group_box)
|
||||||
self.authors_layout.setObjectName('authors_layout')
|
self.authors_layout.setObjectName('authors_layout')
|
||||||
self.author_add_layout = QtGui.QHBoxLayout()
|
self.author_add_layout = QtGui.QVBoxLayout()
|
||||||
self.author_add_layout.setObjectName('author_add_layout')
|
self.author_add_layout.setObjectName('author_add_layout')
|
||||||
|
self.author_type_layout = QtGui.QHBoxLayout()
|
||||||
|
self.author_type_layout.setObjectName('author_type_layout')
|
||||||
self.authors_combo_box = create_combo_box(self.authors_group_box, 'authors_combo_box')
|
self.authors_combo_box = create_combo_box(self.authors_group_box, 'authors_combo_box')
|
||||||
self.author_add_layout.addWidget(self.authors_combo_box)
|
self.author_add_layout.addWidget(self.authors_combo_box)
|
||||||
|
self.author_types_combo_box = create_combo_box(self.authors_group_box, 'author_types_combo_box', editable=False)
|
||||||
|
self.author_type_layout.addWidget(self.author_types_combo_box)
|
||||||
self.author_add_button = QtGui.QPushButton(self.authors_group_box)
|
self.author_add_button = QtGui.QPushButton(self.authors_group_box)
|
||||||
self.author_add_button.setObjectName('author_add_button')
|
self.author_add_button.setObjectName('author_add_button')
|
||||||
self.author_add_layout.addWidget(self.author_add_button)
|
self.author_type_layout.addWidget(self.author_add_button)
|
||||||
|
self.author_add_layout.addLayout(self.author_type_layout)
|
||||||
self.authors_layout.addLayout(self.author_add_layout)
|
self.authors_layout.addLayout(self.author_add_layout)
|
||||||
self.authors_list_view = QtGui.QListWidget(self.authors_group_box)
|
self.authors_list_view = QtGui.QListWidget(self.authors_group_box)
|
||||||
self.authors_list_view.setAlternatingRowColors(True)
|
self.authors_list_view.setAlternatingRowColors(True)
|
||||||
@ -330,7 +335,7 @@ class Ui_EditSongDialog(object):
|
|||||||
translate('SongsPlugin.EditSongForm', '<strong>Warning:</strong> You have not entered a verse order.')
|
translate('SongsPlugin.EditSongForm', '<strong>Warning:</strong> You have not entered a verse order.')
|
||||||
|
|
||||||
|
|
||||||
def create_combo_box(parent, name):
|
def create_combo_box(parent, name, editable=True):
|
||||||
"""
|
"""
|
||||||
Utility method to generate a standard combo box for this dialog.
|
Utility method to generate a standard combo box for this dialog.
|
||||||
|
|
||||||
@ -340,7 +345,7 @@ def create_combo_box(parent, name):
|
|||||||
combo_box = QtGui.QComboBox(parent)
|
combo_box = QtGui.QComboBox(parent)
|
||||||
combo_box.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToMinimumContentsLength)
|
combo_box.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToMinimumContentsLength)
|
||||||
combo_box.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed)
|
combo_box.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed)
|
||||||
combo_box.setEditable(True)
|
combo_box.setEditable(editable)
|
||||||
combo_box.setInsertPolicy(QtGui.QComboBox.NoInsert)
|
combo_box.setInsertPolicy(QtGui.QComboBox.NoInsert)
|
||||||
combo_box.setObjectName(name)
|
combo_box.setObjectName(name)
|
||||||
return combo_box
|
return combo_box
|
||||||
|
@ -42,7 +42,7 @@ from openlp.core.common import Registry, RegistryProperties, AppLocation, UiStri
|
|||||||
from openlp.core.lib import FileDialog, PluginStatus, MediaType, create_separated_list
|
from openlp.core.lib import FileDialog, 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.lib.ui import set_case_insensitive_completer, critical_error_message_box, find_and_set_in_combo_box
|
||||||
from openlp.plugins.songs.lib import VerseType, clean_song
|
from openlp.plugins.songs.lib import VerseType, clean_song
|
||||||
from openlp.plugins.songs.lib.db import Book, Song, Author, Topic, MediaFile
|
from openlp.plugins.songs.lib.db import Book, Song, Author, AuthorSong, AuthorType, Topic, MediaFile
|
||||||
from openlp.plugins.songs.lib.ui import SongStrings
|
from openlp.plugins.songs.lib.ui import SongStrings
|
||||||
from openlp.plugins.songs.lib.xml import SongXML
|
from openlp.plugins.songs.lib.xml import SongXML
|
||||||
from openlp.plugins.songs.forms.editsongdialog import Ui_EditSongDialog
|
from openlp.plugins.songs.forms.editsongdialog import Ui_EditSongDialog
|
||||||
@ -122,12 +122,12 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties):
|
|||||||
combo.setItemData(row, obj.id)
|
combo.setItemData(row, obj.id)
|
||||||
set_case_insensitive_completer(cache, combo)
|
set_case_insensitive_completer(cache, combo)
|
||||||
|
|
||||||
def _add_author_to_list(self, author):
|
def _add_author_to_list(self, author, author_type):
|
||||||
"""
|
"""
|
||||||
Add an author to the author list.
|
Add an author to the author list.
|
||||||
"""
|
"""
|
||||||
author_item = QtGui.QListWidgetItem(str(author.display_name))
|
author_item = QtGui.QListWidgetItem(author.get_display_name(author_type))
|
||||||
author_item.setData(QtCore.Qt.UserRole, author.id)
|
author_item.setData(QtCore.Qt.UserRole, (author.id, author_type))
|
||||||
self.authors_list_view.addItem(author_item)
|
self.authors_list_view.addItem(author_item)
|
||||||
|
|
||||||
def _extract_verse_order(self, verse_order):
|
def _extract_verse_order(self, verse_order):
|
||||||
@ -217,8 +217,8 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties):
|
|||||||
if self.authors_list_view.count() == 0:
|
if self.authors_list_view.count() == 0:
|
||||||
self.song_tab_widget.setCurrentIndex(1)
|
self.song_tab_widget.setCurrentIndex(1)
|
||||||
self.authors_list_view.setFocus()
|
self.authors_list_view.setFocus()
|
||||||
critical_error_message_box(
|
critical_error_message_box(message=translate('SongsPlugin.EditSongForm',
|
||||||
message=translate('SongsPlugin.EditSongForm', 'You need to have an author for this song.'))
|
'You need to have an author for this song.'))
|
||||||
return False
|
return False
|
||||||
if self.verse_order_edit.text():
|
if self.verse_order_edit.text():
|
||||||
result = self._validate_verse_list(self.verse_order_edit.text(), self.verse_list_widget.rowCount())
|
result = self._validate_verse_list(self.verse_order_edit.text(), self.verse_list_widget.rowCount())
|
||||||
@ -302,6 +302,15 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties):
|
|||||||
self.authors.append(author.display_name)
|
self.authors.append(author.display_name)
|
||||||
set_case_insensitive_completer(self.authors, self.authors_combo_box)
|
set_case_insensitive_completer(self.authors, self.authors_combo_box)
|
||||||
|
|
||||||
|
# Types
|
||||||
|
self.author_types_combo_box.clear()
|
||||||
|
self.author_types_combo_box.addItem('')
|
||||||
|
# Don't iterate over the dictionary to give them this specific order
|
||||||
|
self.author_types_combo_box.addItem(AuthorType.Types[AuthorType.Words], AuthorType.Words)
|
||||||
|
self.author_types_combo_box.addItem(AuthorType.Types[AuthorType.Music], AuthorType.Music)
|
||||||
|
self.author_types_combo_box.addItem(AuthorType.Types[AuthorType.WordsAndMusic], AuthorType.WordsAndMusic)
|
||||||
|
self.author_types_combo_box.addItem(AuthorType.Types[AuthorType.Translation], AuthorType.Translation)
|
||||||
|
|
||||||
def load_topics(self):
|
def load_topics(self):
|
||||||
"""
|
"""
|
||||||
Load the topics into the combobox.
|
Load the topics into the combobox.
|
||||||
@ -454,10 +463,8 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties):
|
|||||||
self.tag_rows()
|
self.tag_rows()
|
||||||
# clear the results
|
# clear the results
|
||||||
self.authors_list_view.clear()
|
self.authors_list_view.clear()
|
||||||
for author in self.song.authors:
|
for author_song in self.song.authors_songs:
|
||||||
author_name = QtGui.QListWidgetItem(str(author.display_name))
|
self._add_author_to_list(author_song.author, author_song.author_type)
|
||||||
author_name.setData(QtCore.Qt.UserRole, author.id)
|
|
||||||
self.authors_list_view.addItem(author_name)
|
|
||||||
# clear the results
|
# clear the results
|
||||||
self.topics_list_view.clear()
|
self.topics_list_view.clear()
|
||||||
for topic in self.song.topics:
|
for topic in self.song.topics:
|
||||||
@ -496,6 +503,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties):
|
|||||||
"""
|
"""
|
||||||
item = int(self.authors_combo_box.currentIndex())
|
item = int(self.authors_combo_box.currentIndex())
|
||||||
text = self.authors_combo_box.currentText().strip(' \r\n\t')
|
text = self.authors_combo_box.currentText().strip(' \r\n\t')
|
||||||
|
author_type = self.author_types_combo_box.itemData(self.author_types_combo_box.currentIndex())
|
||||||
# This if statement is for OS X, which doesn't seem to work well with
|
# This if statement is for OS X, which doesn't seem to work well with
|
||||||
# the QCompleter auto-completion class. See bug #812628.
|
# the QCompleter auto-completion class. See bug #812628.
|
||||||
if text in self.authors:
|
if text in self.authors:
|
||||||
@ -513,7 +521,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties):
|
|||||||
author = Author.populate(first_name=text.rsplit(' ', 1)[0], last_name=text.rsplit(' ', 1)[1],
|
author = Author.populate(first_name=text.rsplit(' ', 1)[0], last_name=text.rsplit(' ', 1)[1],
|
||||||
display_name=text)
|
display_name=text)
|
||||||
self.manager.save_object(author)
|
self.manager.save_object(author)
|
||||||
self._add_author_to_list(author)
|
self._add_author_to_list(author, author_type)
|
||||||
self.load_authors()
|
self.load_authors()
|
||||||
self.authors_combo_box.setCurrentIndex(0)
|
self.authors_combo_box.setCurrentIndex(0)
|
||||||
else:
|
else:
|
||||||
@ -521,11 +529,11 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties):
|
|||||||
elif item > 0:
|
elif item > 0:
|
||||||
item_id = (self.authors_combo_box.itemData(item))
|
item_id = (self.authors_combo_box.itemData(item))
|
||||||
author = self.manager.get_object(Author, item_id)
|
author = self.manager.get_object(Author, item_id)
|
||||||
if self.authors_list_view.findItems(str(author.display_name), QtCore.Qt.MatchExactly):
|
if self.authors_list_view.findItems(author.get_display_name(author_type), QtCore.Qt.MatchExactly):
|
||||||
critical_error_message_box(
|
critical_error_message_box(
|
||||||
message=translate('SongsPlugin.EditSongForm', 'This author is already in the list.'))
|
message=translate('SongsPlugin.EditSongForm', 'This author is already in the list.'))
|
||||||
else:
|
else:
|
||||||
self._add_author_to_list(author)
|
self._add_author_to_list(author, author_type)
|
||||||
self.authors_combo_box.setCurrentIndex(0)
|
self.authors_combo_box.setCurrentIndex(0)
|
||||||
else:
|
else:
|
||||||
QtGui.QMessageBox.warning(
|
QtGui.QMessageBox.warning(
|
||||||
@ -905,13 +913,13 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties):
|
|||||||
else:
|
else:
|
||||||
self.song.theme_name = None
|
self.song.theme_name = None
|
||||||
self._process_lyrics()
|
self._process_lyrics()
|
||||||
self.song.authors = []
|
self.song.authors_songs = []
|
||||||
for row in range(self.authors_list_view.count()):
|
for row in range(self.authors_list_view.count()):
|
||||||
item = self.authors_list_view.item(row)
|
item = self.authors_list_view.item(row)
|
||||||
author_id = (item.data(QtCore.Qt.UserRole))
|
author_song = AuthorSong()
|
||||||
author = self.manager.get_object(Author, author_id)
|
author_song.author_id = item.data(QtCore.Qt.UserRole)[0]
|
||||||
if author is not None:
|
author_song.author_type = item.data(QtCore.Qt.UserRole)[1]
|
||||||
self.song.authors.append(author)
|
self.song.authors_songs.append(author_song)
|
||||||
self.song.topics = []
|
self.song.topics = []
|
||||||
for row in range(self.topics_list_view.count()):
|
for row in range(self.topics_list_view.count()):
|
||||||
item = self.topics_list_view.item(row)
|
item = self.topics_list_view.item(row)
|
||||||
|
@ -304,7 +304,7 @@ class SongImportForm(OpenLPWizard, RegistryProperties):
|
|||||||
"""
|
"""
|
||||||
self.source_page.emit(QtCore.SIGNAL('completeChanged()'))
|
self.source_page.emit(QtCore.SIGNAL('completeChanged()'))
|
||||||
|
|
||||||
def setDefaults(self):
|
def set_defaults(self):
|
||||||
"""
|
"""
|
||||||
Set default form values for the song import wizard.
|
Set default form values for the song import wizard.
|
||||||
"""
|
"""
|
||||||
|
@ -390,7 +390,7 @@ def clean_song(manager, song):
|
|||||||
verses = SongXML().get_verses(song.lyrics)
|
verses = SongXML().get_verses(song.lyrics)
|
||||||
song.search_lyrics = ' '.join([clean_string(verse[1]) for verse in verses])
|
song.search_lyrics = ' '.join([clean_string(verse[1]) for verse in verses])
|
||||||
# The song does not have any author, add one.
|
# The song does not have any author, add one.
|
||||||
if not song.authors:
|
if not song.authors and not song.authors_songs: # Need to check both relations
|
||||||
name = SongStrings.AuthorUnknown
|
name = SongStrings.AuthorUnknown
|
||||||
author = manager.get_object_filtered(Author, Author.display_name == name)
|
author = manager.get_object_filtered(Author, Author.display_name == name)
|
||||||
if author is None:
|
if author is None:
|
||||||
|
@ -35,19 +35,52 @@ import re
|
|||||||
|
|
||||||
from sqlalchemy import Column, ForeignKey, Table, types
|
from sqlalchemy import Column, ForeignKey, Table, types
|
||||||
from sqlalchemy.orm import mapper, relation, reconstructor
|
from sqlalchemy.orm import mapper, relation, reconstructor
|
||||||
from sqlalchemy.sql.expression import func
|
from sqlalchemy.sql.expression import func, text
|
||||||
|
|
||||||
from openlp.core.lib.db import BaseModel, init_db
|
from openlp.core.lib.db import BaseModel, init_db
|
||||||
from openlp.core.utils import get_natural_key
|
from openlp.core.utils import get_natural_key
|
||||||
|
from openlp.core.lib import translate
|
||||||
|
|
||||||
|
|
||||||
class Author(BaseModel):
|
class Author(BaseModel):
|
||||||
"""
|
"""
|
||||||
Author model
|
Author model
|
||||||
"""
|
"""
|
||||||
|
def get_display_name(self, author_type=None):
|
||||||
|
if author_type:
|
||||||
|
return "%s (%s)" % (self.display_name, AuthorType.Types[author_type])
|
||||||
|
return self.display_name
|
||||||
|
|
||||||
|
|
||||||
|
class AuthorSong(BaseModel):
|
||||||
|
"""
|
||||||
|
Relationship between Authors and Songs (many to many).
|
||||||
|
Need to define this relationship table explicit to get access to the
|
||||||
|
Association Object (author_type).
|
||||||
|
http://docs.sqlalchemy.org/en/latest/orm/relationships.html#association-object
|
||||||
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class AuthorType(object):
|
||||||
|
"""
|
||||||
|
Enumeration for Author types.
|
||||||
|
They are defined by OpenLyrics: http://openlyrics.info/dataformat.html#authors
|
||||||
|
|
||||||
|
The 'words+music' type is not an official type, but is provided for convenience.
|
||||||
|
"""
|
||||||
|
Words = 'words'
|
||||||
|
Music = 'music'
|
||||||
|
WordsAndMusic = 'words+music'
|
||||||
|
Translation = 'translation'
|
||||||
|
Types = {
|
||||||
|
Words: translate('OpenLP.Ui', 'Words'),
|
||||||
|
Music: translate('OpenLP.Ui', 'Music'),
|
||||||
|
WordsAndMusic: translate('OpenLP.Ui', 'Words and Music'),
|
||||||
|
Translation: translate('OpenLP.Ui', 'Translation')
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class Book(BaseModel):
|
class Book(BaseModel):
|
||||||
"""
|
"""
|
||||||
Book model
|
Book model
|
||||||
@ -67,6 +100,7 @@ class Song(BaseModel):
|
|||||||
"""
|
"""
|
||||||
Song model
|
Song model
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.sort_key = []
|
self.sort_key = []
|
||||||
|
|
||||||
@ -120,6 +154,7 @@ def init_schema(url):
|
|||||||
|
|
||||||
* author_id
|
* author_id
|
||||||
* song_id
|
* song_id
|
||||||
|
* author_type
|
||||||
|
|
||||||
**media_files Table**
|
**media_files Table**
|
||||||
* id
|
* id
|
||||||
@ -230,7 +265,8 @@ def init_schema(url):
|
|||||||
authors_songs_table = Table(
|
authors_songs_table = Table(
|
||||||
'authors_songs', metadata,
|
'authors_songs', metadata,
|
||||||
Column('author_id', types.Integer(), ForeignKey('authors.id'), primary_key=True),
|
Column('author_id', types.Integer(), ForeignKey('authors.id'), primary_key=True),
|
||||||
Column('song_id', types.Integer(), ForeignKey('songs.id'), primary_key=True)
|
Column('song_id', types.Integer(), ForeignKey('songs.id'), primary_key=True),
|
||||||
|
Column('author_type', types.String(), primary_key=True, nullable=False, server_default=text('""'))
|
||||||
)
|
)
|
||||||
|
|
||||||
# Definition of the "songs_topics" table
|
# Definition of the "songs_topics" table
|
||||||
@ -241,10 +277,15 @@ def init_schema(url):
|
|||||||
)
|
)
|
||||||
|
|
||||||
mapper(Author, authors_table)
|
mapper(Author, authors_table)
|
||||||
|
mapper(AuthorSong, authors_songs_table, properties={
|
||||||
|
'author': relation(Author)
|
||||||
|
})
|
||||||
mapper(Book, song_books_table)
|
mapper(Book, song_books_table)
|
||||||
mapper(MediaFile, media_files_table)
|
mapper(MediaFile, media_files_table)
|
||||||
mapper(Song, songs_table, properties={
|
mapper(Song, songs_table, properties={
|
||||||
'authors': relation(Author, backref='songs', secondary=authors_songs_table, lazy=False),
|
# Use the authors_songs relation when you need access to the 'author_type' attribute.
|
||||||
|
'authors_songs': relation(AuthorSong, cascade="all, delete-orphan"),
|
||||||
|
'authors': relation(Author, secondary=authors_songs_table),
|
||||||
'book': relation(Book, backref='songs'),
|
'book': relation(Book, backref='songs'),
|
||||||
'media_files': relation(MediaFile, backref='songs', order_by=media_files_table.c.weight),
|
'media_files': relation(MediaFile, backref='songs', order_by=media_files_table.c.weight),
|
||||||
'topics': relation(Topic, backref='songs', secondary=songs_topics_table)
|
'topics': relation(Topic, backref='songs', secondary=songs_topics_table)
|
||||||
|
@ -44,7 +44,7 @@ from openlp.plugins.songs.forms.songmaintenanceform import SongMaintenanceForm
|
|||||||
from openlp.plugins.songs.forms.songimportform import SongImportForm
|
from openlp.plugins.songs.forms.songimportform import SongImportForm
|
||||||
from openlp.plugins.songs.forms.songexportform import SongExportForm
|
from openlp.plugins.songs.forms.songexportform import SongExportForm
|
||||||
from openlp.plugins.songs.lib import VerseType, clean_string, delete_song
|
from openlp.plugins.songs.lib import VerseType, clean_string, delete_song
|
||||||
from openlp.plugins.songs.lib.db import Author, Song, Book, MediaFile
|
from openlp.plugins.songs.lib.db import Author, AuthorType, Song, Book, MediaFile
|
||||||
from openlp.plugins.songs.lib.ui import SongStrings
|
from openlp.plugins.songs.lib.ui import SongStrings
|
||||||
from openlp.plugins.songs.lib.xml import OpenLyrics, SongXML
|
from openlp.plugins.songs.lib.xml import OpenLyrics, SongXML
|
||||||
|
|
||||||
@ -234,8 +234,7 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
if song.temporary:
|
if song.temporary:
|
||||||
continue
|
continue
|
||||||
author_list = [author.display_name for author in song.authors]
|
author_list = [author.display_name for author in song.authors]
|
||||||
song_title = str(song.title)
|
song_detail = '%s (%s)' % (song.title, create_separated_list(author_list)) if author_list else song.title
|
||||||
song_detail = '%s (%s)' % (song_title, create_separated_list(author_list))
|
|
||||||
song_name = QtGui.QListWidgetItem(song_detail)
|
song_name = QtGui.QListWidgetItem(song_detail)
|
||||||
song_name.setData(QtCore.Qt.UserRole, song.id)
|
song_name.setData(QtCore.Qt.UserRole, song.id)
|
||||||
self.list_view.addItem(song_name)
|
self.list_view.addItem(song_name)
|
||||||
@ -464,23 +463,53 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
def generate_footer(self, item, song):
|
def generate_footer(self, item, song):
|
||||||
"""
|
"""
|
||||||
Generates the song footer based on a song and adds details to a service item.
|
Generates the song footer based on a song and adds details to a service item.
|
||||||
author_list is only required for initial song generation.
|
|
||||||
|
|
||||||
:param item: The service item to be amended
|
:param item: The service item to be amended
|
||||||
:param song: The song to be used to generate the footer
|
:param song: The song to be used to generate the footer
|
||||||
|
:return: List of all authors (only required for initial song generation)
|
||||||
"""
|
"""
|
||||||
author_list = [str(author.display_name) for author in song.authors]
|
authors_words = []
|
||||||
|
authors_music = []
|
||||||
|
authors_words_music = []
|
||||||
|
authors_translation = []
|
||||||
|
authors_none = []
|
||||||
|
for author_song in song.authors_songs:
|
||||||
|
if author_song.author_type == AuthorType.Words:
|
||||||
|
authors_words.append(author_song.author.display_name)
|
||||||
|
elif author_song.author_type == AuthorType.Music:
|
||||||
|
authors_music.append(author_song.author.display_name)
|
||||||
|
elif author_song.author_type == AuthorType.WordsAndMusic:
|
||||||
|
authors_words_music.append(author_song.author.display_name)
|
||||||
|
elif author_song.author_type == AuthorType.Translation:
|
||||||
|
authors_translation.append(author_song.author.display_name)
|
||||||
|
else:
|
||||||
|
authors_none.append(author_song.author.display_name)
|
||||||
|
authors_all = authors_words_music + authors_words + authors_music + authors_translation + authors_none
|
||||||
item.audit = [
|
item.audit = [
|
||||||
song.title, author_list, song.copyright, str(song.ccli_number)
|
song.title, authors_all, song.copyright, str(song.ccli_number)
|
||||||
]
|
]
|
||||||
item.raw_footer = []
|
item.raw_footer = []
|
||||||
item.raw_footer.append(song.title)
|
item.raw_footer.append(song.title)
|
||||||
item.raw_footer.append(create_separated_list(author_list))
|
if authors_none:
|
||||||
|
item.raw_footer.append("%s: %s" % (translate('OpenLP.Ui', 'Written by'),
|
||||||
|
create_separated_list(authors_none)))
|
||||||
|
if authors_words_music:
|
||||||
|
item.raw_footer.append("%s: %s" % (AuthorType.Types[AuthorType.WordsAndMusic],
|
||||||
|
create_separated_list(authors_words_music)))
|
||||||
|
if authors_words:
|
||||||
|
item.raw_footer.append("%s: %s" % (AuthorType.Types[AuthorType.Words],
|
||||||
|
create_separated_list(authors_words)))
|
||||||
|
if authors_music:
|
||||||
|
item.raw_footer.append("%s: %s" % (AuthorType.Types[AuthorType.Music],
|
||||||
|
create_separated_list(authors_music)))
|
||||||
|
if authors_translation:
|
||||||
|
item.raw_footer.append("%s: %s" % (AuthorType.Types[AuthorType.Translation],
|
||||||
|
create_separated_list(authors_translation)))
|
||||||
item.raw_footer.append(song.copyright)
|
item.raw_footer.append(song.copyright)
|
||||||
if Settings().value('core/ccli number'):
|
if Settings().value('core/ccli number'):
|
||||||
item.raw_footer.append(translate('SongsPlugin.MediaItem',
|
item.raw_footer.append(translate('SongsPlugin.MediaItem',
|
||||||
'CCLI License: ') + Settings().value('core/ccli number'))
|
'CCLI License: ') + Settings().value('core/ccli number'))
|
||||||
return author_list
|
return authors_all
|
||||||
|
|
||||||
def service_load(self, item):
|
def service_load(self, item):
|
||||||
"""
|
"""
|
||||||
|
@ -40,7 +40,7 @@ class SongStrings(object):
|
|||||||
# These strings should need a good reason to be retranslated elsewhere.
|
# These strings should need a good reason to be retranslated elsewhere.
|
||||||
Author = translate('OpenLP.Ui', 'Author', 'Singular')
|
Author = translate('OpenLP.Ui', 'Author', 'Singular')
|
||||||
Authors = translate('OpenLP.Ui', 'Authors', 'Plural')
|
Authors = translate('OpenLP.Ui', 'Authors', 'Plural')
|
||||||
AuthorUnknown = 'Author Unknown' # Used to populate the database.
|
AuthorUnknown = translate('OpenLP.Ui', 'Author Unknown') # Used to populate the database.
|
||||||
CopyrightSymbol = translate('OpenLP.Ui', '\xa9', 'Copyright symbol.')
|
CopyrightSymbol = translate('OpenLP.Ui', '\xa9', 'Copyright symbol.')
|
||||||
SongBook = translate('OpenLP.Ui', 'Song Book', 'Singular')
|
SongBook = translate('OpenLP.Ui', 'Song Book', 'Singular')
|
||||||
SongBooks = translate('OpenLP.Ui', 'Song Books', 'Plural')
|
SongBooks = translate('OpenLP.Ui', 'Song Books', 'Plural')
|
||||||
|
@ -32,7 +32,7 @@ backend for the Songs plugin
|
|||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from sqlalchemy import Column, types
|
from sqlalchemy import Column, ForeignKey, types
|
||||||
from sqlalchemy.exc import OperationalError
|
from sqlalchemy.exc import OperationalError
|
||||||
from sqlalchemy.sql.expression import func, false, null, text
|
from sqlalchemy.sql.expression import func, false, null, text
|
||||||
|
|
||||||
@ -97,3 +97,25 @@ def upgrade_3(session, metadata):
|
|||||||
op.add_column('songs', Column('temporary', types.Boolean(), server_default=false()))
|
op.add_column('songs', Column('temporary', types.Boolean(), server_default=false()))
|
||||||
except OperationalError:
|
except OperationalError:
|
||||||
log.info('Upgrade 3 has already been run')
|
log.info('Upgrade 3 has already been run')
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade_4(session, metadata):
|
||||||
|
"""
|
||||||
|
Version 4 upgrade.
|
||||||
|
|
||||||
|
This upgrade adds a column for author type to the authors_songs table
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Since SQLite doesn't support changing the primary key of a table, we need to recreate the table
|
||||||
|
# and copy the old values
|
||||||
|
op = get_upgrade_op(session)
|
||||||
|
op.create_table('authors_songs_tmp',
|
||||||
|
Column('author_id', types.Integer(), ForeignKey('authors.id'), primary_key=True),
|
||||||
|
Column('song_id', types.Integer(), ForeignKey('songs.id'), primary_key=True),
|
||||||
|
Column('author_type', types.String(), primary_key=True,
|
||||||
|
nullable=False, server_default=text('""')))
|
||||||
|
op.execute('INSERT INTO authors_songs_tmp SELECT author_id, song_id, "" FROM authors_songs')
|
||||||
|
op.drop_table('authors_songs')
|
||||||
|
op.rename_table('authors_songs_tmp', 'authors_songs')
|
||||||
|
except OperationalError:
|
||||||
|
log.info('Upgrade 4 has already been run')
|
||||||
|
@ -71,7 +71,7 @@ from lxml import etree, objectify
|
|||||||
from openlp.core.common import translate
|
from openlp.core.common import translate
|
||||||
from openlp.core.lib import FormattingTags
|
from openlp.core.lib import FormattingTags
|
||||||
from openlp.plugins.songs.lib import VerseType, clean_song
|
from openlp.plugins.songs.lib import VerseType, clean_song
|
||||||
from openlp.plugins.songs.lib.db import Author, Book, Song, Topic
|
from openlp.plugins.songs.lib.db import Author, AuthorSong, AuthorType, Book, Song, Topic
|
||||||
from openlp.core.utils import get_application_version
|
from openlp.core.utils import get_application_version
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
@ -166,7 +166,7 @@ class OpenLyrics(object):
|
|||||||
supported by the :class:`OpenLyrics` class:
|
supported by the :class:`OpenLyrics` class:
|
||||||
|
|
||||||
``<authors>``
|
``<authors>``
|
||||||
OpenLP does not support the attribute *type* and *lang*.
|
OpenLP does not support the attribute *lang*.
|
||||||
|
|
||||||
``<chord>``
|
``<chord>``
|
||||||
This property is not supported.
|
This property is not supported.
|
||||||
@ -269,10 +269,18 @@ class OpenLyrics(object):
|
|||||||
'verseOrder', properties, song.verse_order.lower())
|
'verseOrder', properties, song.verse_order.lower())
|
||||||
if song.ccli_number:
|
if song.ccli_number:
|
||||||
self._add_text_to_element('ccliNo', properties, song.ccli_number)
|
self._add_text_to_element('ccliNo', properties, song.ccli_number)
|
||||||
if song.authors:
|
if song.authors_songs:
|
||||||
authors = etree.SubElement(properties, 'authors')
|
authors = etree.SubElement(properties, 'authors')
|
||||||
for author in song.authors:
|
for author_song in song.authors_songs:
|
||||||
self._add_text_to_element('author', authors, author.display_name)
|
element = self._add_text_to_element('author', authors, author_song.author.display_name)
|
||||||
|
if author_song.author_type:
|
||||||
|
# Handle the special case 'words+music': Need to create two separate authors for that
|
||||||
|
if author_song.author_type == AuthorType.WordsAndMusic:
|
||||||
|
element.set('type', AuthorType.Words)
|
||||||
|
element = self._add_text_to_element('author', authors, author_song.author.display_name)
|
||||||
|
element.set('type', AuthorType.Music)
|
||||||
|
else:
|
||||||
|
element.set('type', author_song.author_type)
|
||||||
book = self.manager.get_object_filtered(Book, Book.id == song.song_book_id)
|
book = self.manager.get_object_filtered(Book, Book.id == song.song_book_id)
|
||||||
if book is not None:
|
if book is not None:
|
||||||
book = book.name
|
book = book.name
|
||||||
@ -501,16 +509,20 @@ class OpenLyrics(object):
|
|||||||
if hasattr(properties, 'authors'):
|
if hasattr(properties, 'authors'):
|
||||||
for author in properties.authors.author:
|
for author in properties.authors.author:
|
||||||
display_name = self._text(author)
|
display_name = self._text(author)
|
||||||
|
author_type = author.get('type', '')
|
||||||
if display_name:
|
if display_name:
|
||||||
authors.append(display_name)
|
authors.append((display_name, author_type))
|
||||||
for display_name in authors:
|
for (display_name, author_type) in authors:
|
||||||
author = self.manager.get_object_filtered(Author, Author.display_name == display_name)
|
author = self.manager.get_object_filtered(Author, Author.display_name == display_name)
|
||||||
if author is None:
|
if author is None:
|
||||||
# We need to create a new author, as the author does not exist.
|
# We need to create a new author, as the author does not exist.
|
||||||
author = Author.populate(display_name=display_name,
|
author = Author.populate(display_name=display_name,
|
||||||
last_name=display_name.split(' ')[-1],
|
last_name=display_name.split(' ')[-1],
|
||||||
first_name=' '.join(display_name.split(' ')[:-1]))
|
first_name=' '.join(display_name.split(' ')[:-1]))
|
||||||
song.authors.append(author)
|
author_song = AuthorSong()
|
||||||
|
author_song.author = author
|
||||||
|
author_song.author_type = author_type
|
||||||
|
song.authors_songs.append(author_song)
|
||||||
|
|
||||||
def _process_cclinumber(self, properties, song):
|
def _process_cclinumber(self, properties, song):
|
||||||
"""
|
"""
|
||||||
|
128
tests/functional/openlp_core_ui/test_media.py
Normal file
128
tests/functional/openlp_core_ui/test_media.py
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# OpenLP - Open Source Lyrics Projection #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Copyright (c) 2008-2014 Raoul Snyman #
|
||||||
|
# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan #
|
||||||
|
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
|
||||||
|
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
|
||||||
|
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
|
||||||
|
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||||
|
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
|
||||||
|
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# This program is free software; you can redistribute it and/or modify it #
|
||||||
|
# under the terms of the GNU General Public License as published by the Free #
|
||||||
|
# Software Foundation; version 2 of the License. #
|
||||||
|
# #
|
||||||
|
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||||
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||||
|
# more details. #
|
||||||
|
# #
|
||||||
|
# You should have received a copy of the GNU General Public License along #
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||||
|
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||||
|
###############################################################################
|
||||||
|
"""
|
||||||
|
Package to test the openlp.core.ui package.
|
||||||
|
"""
|
||||||
|
from PyQt4 import QtCore
|
||||||
|
from unittest import TestCase
|
||||||
|
|
||||||
|
from openlp.core.ui.media import get_media_players
|
||||||
|
|
||||||
|
from tests.functional import MagicMock, patch
|
||||||
|
from tests.helpers.testmixin import TestMixin
|
||||||
|
|
||||||
|
|
||||||
|
class TestMedia(TestCase, TestMixin):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_get_media_players_no_config(self):
|
||||||
|
"""
|
||||||
|
Test that when there's no config, get_media_players() returns an empty list of players (not a string)
|
||||||
|
"""
|
||||||
|
def value_results(key):
|
||||||
|
if key == 'media/players':
|
||||||
|
return ''
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# GIVEN: A mocked out Settings() object
|
||||||
|
with patch('openlp.core.ui.media.Settings.value') as mocked_value:
|
||||||
|
mocked_value.side_effect = value_results
|
||||||
|
|
||||||
|
# WHEN: get_media_players() is called
|
||||||
|
used_players, overridden_player = get_media_players()
|
||||||
|
|
||||||
|
# THEN: the used_players should be an empty list, and the overridden player should be an empty string
|
||||||
|
self.assertEqual([], used_players, 'Used players should be an empty list')
|
||||||
|
self.assertEqual('', overridden_player, 'Overridden player should be an empty string')
|
||||||
|
|
||||||
|
def test_get_media_players_no_players(self):
|
||||||
|
"""
|
||||||
|
Test that when there's no players but overridden player is set, get_media_players() returns 'auto'
|
||||||
|
"""
|
||||||
|
def value_results(key):
|
||||||
|
if key == 'media/override player':
|
||||||
|
return QtCore.Qt.Checked
|
||||||
|
else:
|
||||||
|
return ''
|
||||||
|
|
||||||
|
# GIVEN: A mocked out Settings() object
|
||||||
|
with patch('openlp.core.ui.media.Settings.value') as mocked_value:
|
||||||
|
mocked_value.side_effect = value_results
|
||||||
|
|
||||||
|
# WHEN: get_media_players() is called
|
||||||
|
used_players, overridden_player = get_media_players()
|
||||||
|
|
||||||
|
# THEN: the used_players should be an empty list, and the overridden player should be an empty string
|
||||||
|
self.assertEqual([], used_players, 'Used players should be an empty list')
|
||||||
|
self.assertEqual('auto', overridden_player, 'Overridden player should be "auto"')
|
||||||
|
|
||||||
|
def test_get_media_players_with_valid_list(self):
|
||||||
|
"""
|
||||||
|
Test that when get_media_players() is called the string list is interpreted correctly
|
||||||
|
"""
|
||||||
|
def value_results(key):
|
||||||
|
if key == 'media/players':
|
||||||
|
return '[vlc,webkit,phonon]'
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# GIVEN: A mocked out Settings() object
|
||||||
|
with patch('openlp.core.ui.media.Settings.value') as mocked_value:
|
||||||
|
mocked_value.side_effect = value_results
|
||||||
|
|
||||||
|
# WHEN: get_media_players() is called
|
||||||
|
used_players, overridden_player = get_media_players()
|
||||||
|
|
||||||
|
# THEN: the used_players should be an empty list, and the overridden player should be an empty string
|
||||||
|
self.assertEqual(['vlc', 'webkit', 'phonon'], used_players, 'Used players should be correct')
|
||||||
|
self.assertEqual('', overridden_player, 'Overridden player should be an empty string')
|
||||||
|
|
||||||
|
def test_get_media_players_with_overridden_player(self):
|
||||||
|
"""
|
||||||
|
Test that when get_media_players() is called the overridden player is correctly set
|
||||||
|
"""
|
||||||
|
def value_results(key):
|
||||||
|
if key == 'media/players':
|
||||||
|
return '[vlc,webkit,phonon]'
|
||||||
|
else:
|
||||||
|
return QtCore.Qt.Checked
|
||||||
|
|
||||||
|
# GIVEN: A mocked out Settings() object
|
||||||
|
with patch('openlp.core.ui.media.Settings.value') as mocked_value:
|
||||||
|
mocked_value.side_effect = value_results
|
||||||
|
|
||||||
|
# WHEN: get_media_players() is called
|
||||||
|
used_players, overridden_player = get_media_players()
|
||||||
|
|
||||||
|
# THEN: the used_players should be an empty list, and the overridden player should be an empty string
|
||||||
|
self.assertEqual(['vlc', 'webkit', 'phonon'], used_players, 'Used players should be correct')
|
||||||
|
self.assertEqual('vlc,webkit,phonon', overridden_player, 'Overridden player should be a string of players')
|
@ -10,6 +10,7 @@ from PyQt4 import QtCore, QtGui
|
|||||||
from openlp.core.common import Registry, Settings
|
from openlp.core.common import Registry, Settings
|
||||||
from openlp.core.lib import ServiceItem
|
from openlp.core.lib import ServiceItem
|
||||||
from openlp.plugins.songs.lib.mediaitem import SongMediaItem
|
from openlp.plugins.songs.lib.mediaitem import SongMediaItem
|
||||||
|
from openlp.plugins.songs.lib.db import AuthorType
|
||||||
from tests.functional import patch, MagicMock
|
from tests.functional import patch, MagicMock
|
||||||
from tests.helpers.testmixin import TestMixin
|
from tests.helpers.testmixin import TestMixin
|
||||||
|
|
||||||
@ -45,10 +46,12 @@ class TestMediaItem(TestCase, TestMixin):
|
|||||||
# GIVEN: A Song and a Service Item
|
# GIVEN: A Song and a Service Item
|
||||||
mock_song = MagicMock()
|
mock_song = MagicMock()
|
||||||
mock_song.title = 'My Song'
|
mock_song.title = 'My Song'
|
||||||
|
mock_song.authors_songs = []
|
||||||
mock_author = MagicMock()
|
mock_author = MagicMock()
|
||||||
mock_author.display_name = 'my author'
|
mock_author.display_name = 'my author'
|
||||||
mock_song.authors = []
|
mock_author_song = MagicMock()
|
||||||
mock_song.authors.append(mock_author)
|
mock_author_song.author = mock_author
|
||||||
|
mock_song.authors_songs.append(mock_author_song)
|
||||||
mock_song.copyright = 'My copyright'
|
mock_song.copyright = 'My copyright'
|
||||||
service_item = ServiceItem(None)
|
service_item = ServiceItem(None)
|
||||||
|
|
||||||
@ -56,7 +59,7 @@ class TestMediaItem(TestCase, TestMixin):
|
|||||||
author_list = self.media_item.generate_footer(service_item, mock_song)
|
author_list = self.media_item.generate_footer(service_item, mock_song)
|
||||||
|
|
||||||
# THEN: I get the following Array returned
|
# THEN: I get the following Array returned
|
||||||
self.assertEqual(service_item.raw_footer, ['My Song', 'my author', 'My copyright'],
|
self.assertEqual(service_item.raw_footer, ['My Song', 'Written by: my author', 'My copyright'],
|
||||||
'The array should be returned correctly with a song, one author and copyright')
|
'The array should be returned correctly with a song, one author and copyright')
|
||||||
self.assertEqual(author_list, ['my author'],
|
self.assertEqual(author_list, ['my author'],
|
||||||
'The author list should be returned correctly with one author')
|
'The author list should be returned correctly with one author')
|
||||||
@ -68,13 +71,25 @@ class TestMediaItem(TestCase, TestMixin):
|
|||||||
# GIVEN: A Song and a Service Item
|
# GIVEN: A Song and a Service Item
|
||||||
mock_song = MagicMock()
|
mock_song = MagicMock()
|
||||||
mock_song.title = 'My Song'
|
mock_song.title = 'My Song'
|
||||||
|
mock_song.authors_songs = []
|
||||||
mock_author = MagicMock()
|
mock_author = MagicMock()
|
||||||
mock_author.display_name = 'my author'
|
mock_author.display_name = 'my author'
|
||||||
mock_song.authors = []
|
mock_author_song = MagicMock()
|
||||||
mock_song.authors.append(mock_author)
|
mock_author_song.author = mock_author
|
||||||
|
mock_author_song.author_type = AuthorType.Music
|
||||||
|
mock_song.authors_songs.append(mock_author_song)
|
||||||
mock_author = MagicMock()
|
mock_author = MagicMock()
|
||||||
mock_author.display_name = 'another author'
|
mock_author.display_name = 'another author'
|
||||||
mock_song.authors.append(mock_author)
|
mock_author_song = MagicMock()
|
||||||
|
mock_author_song.author = mock_author
|
||||||
|
mock_author_song.author_type = AuthorType.Words
|
||||||
|
mock_song.authors_songs.append(mock_author_song)
|
||||||
|
mock_author = MagicMock()
|
||||||
|
mock_author.display_name = 'translator'
|
||||||
|
mock_author_song = MagicMock()
|
||||||
|
mock_author_song.author = mock_author
|
||||||
|
mock_author_song.author_type = AuthorType.Translation
|
||||||
|
mock_song.authors_songs.append(mock_author_song)
|
||||||
mock_song.copyright = 'My copyright'
|
mock_song.copyright = 'My copyright'
|
||||||
service_item = ServiceItem(None)
|
service_item = ServiceItem(None)
|
||||||
|
|
||||||
@ -82,22 +97,19 @@ class TestMediaItem(TestCase, TestMixin):
|
|||||||
author_list = self.media_item.generate_footer(service_item, mock_song)
|
author_list = self.media_item.generate_footer(service_item, mock_song)
|
||||||
|
|
||||||
# THEN: I get the following Array returned
|
# THEN: I get the following Array returned
|
||||||
self.assertEqual(service_item.raw_footer, ['My Song', 'my author and another author', 'My copyright'],
|
self.assertEqual(service_item.raw_footer, ['My Song', 'Words: another author', 'Music: my author',
|
||||||
|
'Translation: translator', 'My copyright'],
|
||||||
'The array should be returned correctly with a song, two authors and copyright')
|
'The array should be returned correctly with a song, two authors and copyright')
|
||||||
self.assertEqual(author_list, ['my author', 'another author'],
|
self.assertEqual(author_list, ['another author', 'my author', 'translator'],
|
||||||
'The author list should be returned correctly with two authors')
|
'The author list should be returned correctly with two authors')
|
||||||
|
|
||||||
def build_song_footer_base_ccli_test(self):
|
def build_song_footer_base_ccli_test(self):
|
||||||
"""
|
"""
|
||||||
Test build songs footer with basic song and two authors
|
Test build songs footer with basic song and a CCLI number
|
||||||
"""
|
"""
|
||||||
# GIVEN: A Song and a Service Item and a configured CCLI license
|
# GIVEN: A Song and a Service Item and a configured CCLI license
|
||||||
mock_song = MagicMock()
|
mock_song = MagicMock()
|
||||||
mock_song.title = 'My Song'
|
mock_song.title = 'My Song'
|
||||||
mock_author = MagicMock()
|
|
||||||
mock_author.display_name = 'my author'
|
|
||||||
mock_song.authors = []
|
|
||||||
mock_song.authors.append(mock_author)
|
|
||||||
mock_song.copyright = 'My copyright'
|
mock_song.copyright = 'My copyright'
|
||||||
service_item = ServiceItem(None)
|
service_item = ServiceItem(None)
|
||||||
Settings().setValue('core/ccli number', '1234')
|
Settings().setValue('core/ccli number', '1234')
|
||||||
@ -106,7 +118,7 @@ class TestMediaItem(TestCase, TestMixin):
|
|||||||
self.media_item.generate_footer(service_item, mock_song)
|
self.media_item.generate_footer(service_item, mock_song)
|
||||||
|
|
||||||
# THEN: I get the following Array returned
|
# THEN: I get the following Array returned
|
||||||
self.assertEqual(service_item.raw_footer, ['My Song', 'my author', 'My copyright', 'CCLI License: 1234'],
|
self.assertEqual(service_item.raw_footer, ['My Song', 'My copyright', 'CCLI License: 1234'],
|
||||||
'The array should be returned correctly with a song, an author, copyright and ccli')
|
'The array should be returned correctly with a song, an author, copyright and ccli')
|
||||||
|
|
||||||
# WHEN: I amend the CCLI value
|
# WHEN: I amend the CCLI value
|
||||||
@ -114,5 +126,5 @@ class TestMediaItem(TestCase, TestMixin):
|
|||||||
self.media_item.generate_footer(service_item, mock_song)
|
self.media_item.generate_footer(service_item, mock_song)
|
||||||
|
|
||||||
# THEN: I would get an amended footer string
|
# THEN: I would get an amended footer string
|
||||||
self.assertEqual(service_item.raw_footer, ['My Song', 'my author', 'My copyright', 'CCLI License: 4321'],
|
self.assertEqual(service_item.raw_footer, ['My Song', 'My copyright', 'CCLI License: 4321'],
|
||||||
'The array should be returned correctly with a song, an author, copyright and amended ccli')
|
'The array should be returned correctly with a song, an author, copyright and amended ccli')
|
||||||
|
70
tests/functional/test_init.py
Normal file
70
tests/functional/test_init.py
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# OpenLP - Open Source Lyrics Projection #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Copyright (c) 2008-2014 Raoul Snyman #
|
||||||
|
# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan #
|
||||||
|
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
|
||||||
|
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
|
||||||
|
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
|
||||||
|
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||||
|
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
|
||||||
|
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# This program is free software; you can redistribute it and/or modify it #
|
||||||
|
# under the terms of the GNU General Public License as published by the Free #
|
||||||
|
# Software Foundation; version 2 of the License. #
|
||||||
|
# #
|
||||||
|
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||||
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||||
|
# more details. #
|
||||||
|
# #
|
||||||
|
# You should have received a copy of the GNU General Public License along #
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||||
|
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||||
|
###############################################################################
|
||||||
|
"""
|
||||||
|
Package to test the openlp.core.__init__ package.
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
|
||||||
|
from unittest import TestCase
|
||||||
|
from unittest.mock import MagicMock, patch
|
||||||
|
from PyQt4 import QtCore
|
||||||
|
|
||||||
|
from openlp.core import OpenLP
|
||||||
|
from tests.helpers.testmixin import TestMixin
|
||||||
|
|
||||||
|
|
||||||
|
TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'resources'))
|
||||||
|
|
||||||
|
|
||||||
|
class TestInit(TestCase, TestMixin):
|
||||||
|
def setUp(self):
|
||||||
|
with patch('openlp.core.common.OpenLPMixin.__init__') as constructor:
|
||||||
|
constructor.return_value = None
|
||||||
|
self.openlp = OpenLP(list())
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
del self.openlp
|
||||||
|
|
||||||
|
def event_test(self):
|
||||||
|
"""
|
||||||
|
Test the reimplemented event method
|
||||||
|
"""
|
||||||
|
# GIVEN: A file path and a QEvent.
|
||||||
|
file_path = os.path.join(TEST_PATH, 'church.jpg')
|
||||||
|
mocked_file_method = MagicMock(return_value=file_path)
|
||||||
|
event = QtCore.QEvent(QtCore.QEvent.FileOpen)
|
||||||
|
event.file = mocked_file_method
|
||||||
|
|
||||||
|
# WHEN: Call the vent method.
|
||||||
|
result = self.openlp.event(event)
|
||||||
|
|
||||||
|
# THEN: The path should be inserted.
|
||||||
|
self.assertTrue(result, "The method should have returned True.")
|
||||||
|
mocked_file_method.assert_called_once_with()
|
||||||
|
self.assertEqual(self.openlp.args[0], file_path, "The path should be in args.")
|
Loading…
Reference in New Issue
Block a user