From 71b34578d71b6c112d57c91da02f7a17a07d599f Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Fri, 28 Mar 2014 17:06:16 +0100 Subject: [PATCH 01/33] Add support for the Author type property --- openlp/core/lib/db.py | 3 +- openlp/plugins/songs/forms/editsongdialog.py | 9 ++++- openlp/plugins/songs/forms/editsongform.py | 40 +++++++++++--------- openlp/plugins/songs/lib/__init__.py | 7 ---- openlp/plugins/songs/lib/db.py | 33 +++++++++++++++- openlp/plugins/songs/lib/mediaitem.py | 32 +++++++++++++--- openlp/plugins/songs/lib/upgrade.py | 11 +++++- 7 files changed, 100 insertions(+), 35 deletions(-) diff --git a/openlp/core/lib/db.py b/openlp/core/lib/db.py index c81cb5a3a..b4e027035 100644 --- a/openlp/core/lib/db.py +++ b/openlp/core/lib/db.py @@ -194,6 +194,7 @@ class Manager(object): db_ver, up_ver = upgrade_db(self.db_url, upgrade_mod) except (SQLAlchemyError, DBAPIError): log.exception('Error loading database: %s', self.db_url) + return if db_ver > up_ver: critical_error_message_box( translate('OpenLP.Manager', 'Database Error'), @@ -215,7 +216,7 @@ class Manager(object): Save an object to the database :param object_instance: The object to save - :param commit: Commit the session with this object + :param commit: Commit the session with this object """ for try_count in range(3): try: diff --git a/openlp/plugins/songs/forms/editsongdialog.py b/openlp/plugins/songs/forms/editsongdialog.py index f2ef5af06..7a491032b 100644 --- a/openlp/plugins/songs/forms/editsongdialog.py +++ b/openlp/plugins/songs/forms/editsongdialog.py @@ -122,6 +122,11 @@ class Ui_EditSongDialog(object): self.author_add_layout.setObjectName('author_add_layout') 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_types_combo_box = create_combo_box(self.authors_group_box, 'author_types_combo_box', editable=False) + # Need to give these boxes some min width, else they are too small + self.authors_combo_box.setMinimumWidth(150) + self.author_types_combo_box.setMinimumWidth(80) + self.author_add_layout.addWidget(self.author_types_combo_box) self.author_add_button = QtGui.QPushButton(self.authors_group_box) self.author_add_button.setObjectName('author_add_button') self.author_add_layout.addWidget(self.author_add_button) @@ -330,7 +335,7 @@ class Ui_EditSongDialog(object): translate('SongsPlugin.EditSongForm', 'Warning: 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. @@ -340,7 +345,7 @@ def create_combo_box(parent, name): combo_box = QtGui.QComboBox(parent) combo_box.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToMinimumContentsLength) 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.setObjectName(name) return combo_box diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index 8b1e3a897..2c651e983 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -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.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.db import Book, Song, Author, Topic, MediaFile +from openlp.plugins.songs.lib.db import Book, Song, Author, AuthorSong, Topic, MediaFile from openlp.plugins.songs.lib.ui import SongStrings from openlp.plugins.songs.lib.xml import SongXML 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) 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. """ - author_item = QtGui.QListWidgetItem(str(author.display_name)) - author_item.setData(QtCore.Qt.UserRole, author.id) + author_item = QtGui.QListWidgetItem(author.get_display_name(author_type)) + author_item.setData(QtCore.Qt.UserRole, (author.id, author_type)) self.authors_list_view.addItem(author_item) def _extract_verse_order(self, verse_order): @@ -302,6 +302,14 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): self.authors.append(author.display_name) 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(Author.Types[Author.TYPE_WORDS], Author.TYPE_WORDS) + self.author_types_combo_box.addItem(Author.Types[Author.TYPE_MUSIC], Author.TYPE_MUSIC) + self.author_types_combo_box.addItem(Author.Types[Author.TYPE_TRANSLATION], Author.TYPE_TRANSLATION) + def load_topics(self): """ Load the topics into the combobox. @@ -454,10 +462,8 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): self.tag_rows() # clear the results self.authors_list_view.clear() - for author in self.song.authors: - author_name = QtGui.QListWidgetItem(str(author.display_name)) - author_name.setData(QtCore.Qt.UserRole, author.id) - self.authors_list_view.addItem(author_name) + for author_song in self.song.authors_songs: + self._add_author_to_list(author_song.author, author_song.author_type) # clear the results self.topics_list_view.clear() for topic in self.song.topics: @@ -496,6 +502,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): """ item = int(self.authors_combo_box.currentIndex()) 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 # the QCompleter auto-completion class. See bug #812628. if text in self.authors: @@ -513,7 +520,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): author = Author.populate(first_name=text.rsplit(' ', 1)[0], last_name=text.rsplit(' ', 1)[1], display_name=text) self.manager.save_object(author) - self._add_author_to_list(author) + self._add_author_to_list(author, author_type) self.load_authors() self.authors_combo_box.setCurrentIndex(0) else: @@ -525,7 +532,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): critical_error_message_box( message=translate('SongsPlugin.EditSongForm', 'This author is already in the list.')) else: - self._add_author_to_list(author) + self._add_author_to_list(author, author_type) self.authors_combo_box.setCurrentIndex(0) else: QtGui.QMessageBox.warning( @@ -538,8 +545,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): """ Run a set of actions when an author in the list is selected (mainly enable the delete button). """ - if self.authors_list_view.count() > 1: - self.author_remove_button.setEnabled(True) + self.author_remove_button.setEnabled(True) def on_author_remove_button_clicked(self): """ @@ -906,13 +912,13 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): else: self.song.theme_name = None self._process_lyrics() - self.song.authors = [] + self.song.authors_songs = [] for row in range(self.authors_list_view.count()): item = self.authors_list_view.item(row) - author_id = (item.data(QtCore.Qt.UserRole)) - author = self.manager.get_object(Author, author_id) - if author is not None: - self.song.authors.append(author) + author_song = AuthorSong() + author_song.author_id = item.data(QtCore.Qt.UserRole)[0] + author_song.author_type = item.data(QtCore.Qt.UserRole)[1] + self.song.authors_songs.append(author_song) self.song.topics = [] for row in range(self.topics_list_view.count()): item = self.topics_list_view.item(row) diff --git a/openlp/plugins/songs/lib/__init__.py b/openlp/plugins/songs/lib/__init__.py index dc198d4b7..60e8788c8 100644 --- a/openlp/plugins/songs/lib/__init__.py +++ b/openlp/plugins/songs/lib/__init__.py @@ -389,13 +389,6 @@ def clean_song(manager, song): song.lyrics = str(song.lyrics, encoding='utf8') verses = SongXML().get_verses(song.lyrics) song.search_lyrics = ' '.join([clean_string(verse[1]) for verse in verses]) - # The song does not have any author, add one. - if not song.authors: - name = SongStrings.AuthorUnknown - author = manager.get_object_filtered(Author, Author.display_name == name) - if author is None: - author = Author.populate(display_name=name, last_name='', first_name='') - song.authors.append(author) if song.copyright: song.copyright = CONTROL_CHARS.sub('', song.copyright).strip() diff --git a/openlp/plugins/songs/lib/db.py b/openlp/plugins/songs/lib/db.py index c3965e2ed..5d360f4f1 100644 --- a/openlp/plugins/songs/lib/db.py +++ b/openlp/plugins/songs/lib/db.py @@ -39,12 +39,34 @@ from sqlalchemy.sql.expression import func from openlp.core.lib.db import BaseModel, init_db from openlp.core.utils import get_natural_key +from openlp.core.lib import translate class Author(BaseModel): """ Author model """ + #These types are defined by OpenLyrics: http://openlyrics.info/dataformat.html#authors + TYPE_WORDS = 'words' + TYPE_MUSIC = 'music' + TYPE_TRANSLATION = 'translation' + Types = { + TYPE_WORDS: translate('OpenLP.Ui', 'Words'), + TYPE_MUSIC: translate('OpenLP.Ui', 'Music'), + TYPE_TRANSLATION: translate('OpenLP.Ui', 'Translation') + } + def get_display_name(self, author_type=None): + if author_type: + return "%s: %s"%(self.Types[author_type], self.display_name) + 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 @@ -67,6 +89,7 @@ class Song(BaseModel): """ Song model """ + def __init__(self): self.sort_key = [] @@ -120,6 +143,7 @@ def init_schema(url): * author_id * song_id + * author_type **media_files Table** * id @@ -230,7 +254,8 @@ def init_schema(url): authors_songs_table = Table( 'authors_songs', metadata, 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) ) # Definition of the "songs_topics" table @@ -241,10 +266,14 @@ def init_schema(url): ) mapper(Author, authors_table) + mapper(AuthorSong, authors_songs_table, properties={ + 'author': relation(Author) + }) mapper(Book, song_books_table) mapper(MediaFile, media_files_table) mapper(Song, songs_table, properties={ - 'authors': relation(Author, backref='songs', secondary=authors_songs_table, lazy=False), + 'authors_songs': relation(AuthorSong, cascade="all, delete-orphan"), + 'authors': relation(Author, secondary=authors_songs_table, viewonly=True), 'book': relation(Book, backref='songs'), 'media_files': relation(MediaFile, backref='songs', order_by=media_files_table.c.weight), 'topics': relation(Topic, backref='songs', secondary=songs_topics_table) diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index ad981135f..ca0c42483 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -464,23 +464,45 @@ class SongMediaItem(MediaManagerItem): def generate_footer(self, item, song): """ 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 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_translation = [] + authors_none = [] + for author_song in song.authors_songs: + if author_song.author_type == Author.TYPE_WORDS: + authors_words.append(author_song.author.display_name) + elif author_song.author_type == Author.TYPE_MUSIC: + authors_music.append(author_song.author.display_name) + elif author_song.author_type == Author.TYPE_TRANSLATION: + authors_translation.append(author_song.author.display_name) + else: + authors_none.append(author_song.author.display_name) + authors_all = authors_words + authors_music + authors_translation + authors_none 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.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_words))) + if authors_words: + item.raw_footer.append("%s: %s"%(Author.Types[Author.TYPE_WORDS], create_separated_list(authors_words))) + if authors_music: + item.raw_footer.append("%s: %s"%(Author.Types[Author.TYPE_MUSIC], create_separated_list(authors_music))) + if authors_translation: + item.raw_footer.append("%s: %s"%(Author.Types[Author.TYPE_TRANSLATION], create_separated_list(authors_translation))) + if not authors_all: #No authors defined + item.raw_footer.append(SongStrings.AuthorUnknown) item.raw_footer.append(song.copyright) if Settings().value('core/ccli number'): item.raw_footer.append(translate('SongsPlugin.MediaItem', 'CCLI License: ') + Settings().value('core/ccli number')) - return author_list + return authors_all def service_load(self, item): """ diff --git a/openlp/plugins/songs/lib/upgrade.py b/openlp/plugins/songs/lib/upgrade.py index fdb0f17ef..15395fbb9 100644 --- a/openlp/plugins/songs/lib/upgrade.py +++ b/openlp/plugins/songs/lib/upgrade.py @@ -36,7 +36,7 @@ from sqlalchemy.sql.expression import func, false, null, text from openlp.core.lib.db import get_upgrade_op -__version__ = 3 +__version__ = 4 def upgrade_1(session, metadata): @@ -82,3 +82,12 @@ def upgrade_3(session, metadata): op.add_column('songs', Column('temporary', types.Boolean(create_constraint=False), server_default=false())) else: op.add_column('songs', Column('temporary', types.Boolean(), server_default=false())) + +def upgrade_4(session, metadata): + """ + Version 4 upgrade. + + This upgrade adds a column for author type to the authors table + """ + op = get_upgrade_op(session) + op.add_column('authors_songs', Column('author_type', types.String(), primary_key=True, server_default=null())) From 615442063e919730d40274a1886cc219c9d20183 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Sat, 29 Mar 2014 11:08:52 +0100 Subject: [PATCH 02/33] Fix display of Author type none --- openlp/plugins/songs/lib/mediaitem.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index ca0c42483..b71f27029 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -489,7 +489,7 @@ class SongMediaItem(MediaManagerItem): item.raw_footer = [] item.raw_footer.append(song.title) if authors_none: - item.raw_footer.append("%s: %s"%(translate('OpenLP.Ui', 'Written by'), create_separated_list(authors_words))) + item.raw_footer.append("%s: %s"%(translate('OpenLP.Ui', 'Written by'), create_separated_list(authors_none))) if authors_words: item.raw_footer.append("%s: %s"%(Author.Types[Author.TYPE_WORDS], create_separated_list(authors_words))) if authors_music: From 87051a094a2fc29a52ed0efad82b5708e3772816 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Sun, 30 Mar 2014 19:23:36 +0200 Subject: [PATCH 03/33] Put Author types in an own enum class --- openlp/plugins/songs/forms/editsongform.py | 8 ++++---- openlp/plugins/songs/lib/db.py | 24 +++++++++++++--------- openlp/plugins/songs/lib/mediaitem.py | 16 +++++++-------- 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index ca17b98dc..2915ea4e8 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -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.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.db import Book, Song, Author, AuthorSong, 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.xml import SongXML from openlp.plugins.songs.forms.editsongdialog import Ui_EditSongDialog @@ -306,9 +306,9 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): 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(Author.Types[Author.TYPE_WORDS], Author.TYPE_WORDS) - self.author_types_combo_box.addItem(Author.Types[Author.TYPE_MUSIC], Author.TYPE_MUSIC) - self.author_types_combo_box.addItem(Author.Types[Author.TYPE_TRANSLATION], Author.TYPE_TRANSLATION) + 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.Translation], AuthorType.Translation) def load_topics(self): """ diff --git a/openlp/plugins/songs/lib/db.py b/openlp/plugins/songs/lib/db.py index 5d360f4f1..0c1b3642c 100644 --- a/openlp/plugins/songs/lib/db.py +++ b/openlp/plugins/songs/lib/db.py @@ -46,18 +46,9 @@ class Author(BaseModel): """ Author model """ - #These types are defined by OpenLyrics: http://openlyrics.info/dataformat.html#authors - TYPE_WORDS = 'words' - TYPE_MUSIC = 'music' - TYPE_TRANSLATION = 'translation' - Types = { - TYPE_WORDS: translate('OpenLP.Ui', 'Words'), - TYPE_MUSIC: translate('OpenLP.Ui', 'Music'), - TYPE_TRANSLATION: translate('OpenLP.Ui', 'Translation') - } def get_display_name(self, author_type=None): if author_type: - return "%s: %s"%(self.Types[author_type], self.display_name) + return "%s: %s"%(AuthorType.Types[author_type], self.display_name) return self.display_name class AuthorSong(BaseModel): @@ -69,6 +60,19 @@ class AuthorSong(BaseModel): """ pass +class AuthorType(object): + """ + Enumeration for Author types. + They are defined by OpenLyrics: http://openlyrics.info/dataformat.html#authors + """ + Words = 'words' + Music = 'music' + Translation = 'translation' + Types = { + Words: translate('OpenLP.Ui', 'Words'), + Music: translate('OpenLP.Ui', 'Music'), + Translation: translate('OpenLP.Ui', 'Translation') + } class Book(BaseModel): """ diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index b71f27029..c40162474 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -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.songexportform import SongExportForm 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.xml import OpenLyrics, SongXML @@ -467,18 +467,18 @@ class SongMediaItem(MediaManagerItem): :param item: The service item to be amended :param song: The song to be used to generate the footer - :return List of all authors (only required for initial song generation) + :return: List of all authors (only required for initial song generation) """ authors_words = [] authors_music = [] authors_translation = [] authors_none = [] for author_song in song.authors_songs: - if author_song.author_type == Author.TYPE_WORDS: + if author_song.author_type == AuthorType.Words: authors_words.append(author_song.author.display_name) - elif author_song.author_type == Author.TYPE_MUSIC: + elif author_song.author_type == AuthorType.Music: authors_music.append(author_song.author.display_name) - elif author_song.author_type == Author.TYPE_TRANSLATION: + elif author_song.author_type == AuthorType.Translation: authors_translation.append(author_song.author.display_name) else: authors_none.append(author_song.author.display_name) @@ -491,11 +491,11 @@ class SongMediaItem(MediaManagerItem): if authors_none: item.raw_footer.append("%s: %s"%(translate('OpenLP.Ui', 'Written by'), create_separated_list(authors_none))) if authors_words: - item.raw_footer.append("%s: %s"%(Author.Types[Author.TYPE_WORDS], create_separated_list(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"%(Author.Types[Author.TYPE_MUSIC], create_separated_list(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"%(Author.Types[Author.TYPE_TRANSLATION], create_separated_list(authors_translation))) + item.raw_footer.append("%s: %s"%(AuthorType.Types[AuthorType.Translation], create_separated_list(authors_translation))) if not authors_all: #No authors defined item.raw_footer.append(SongStrings.AuthorUnknown) item.raw_footer.append(song.copyright) From c7358e4a9f46f91f27e276f3949204228016b344 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Sun, 30 Mar 2014 19:38:26 +0200 Subject: [PATCH 04/33] The author_type column is part of the primary key and thus can't be NULL --- openlp/plugins/songs/forms/editsongform.py | 6 ------ openlp/plugins/songs/lib/db.py | 4 ++-- openlp/plugins/songs/lib/mediaitem.py | 4 ++-- openlp/plugins/songs/lib/upgrade.py | 5 +++-- 4 files changed, 7 insertions(+), 12 deletions(-) diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index 2915ea4e8..f3a695f03 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -214,12 +214,6 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): critical_error_message_box( message=translate('SongsPlugin.EditSongForm', 'You need to type in at least one verse.')) return False - if self.authors_list_view.count() == 0: - self.song_tab_widget.setCurrentIndex(1) - self.authors_list_view.setFocus() - critical_error_message_box( - message=translate('SongsPlugin.EditSongForm', 'You need to have an author for this song.')) - return False if self.verse_order_edit.text(): result = self._validate_verse_list(self.verse_order_edit.text(), self.verse_list_widget.rowCount()) if not result: diff --git a/openlp/plugins/songs/lib/db.py b/openlp/plugins/songs/lib/db.py index 0c1b3642c..4e925e4b6 100644 --- a/openlp/plugins/songs/lib/db.py +++ b/openlp/plugins/songs/lib/db.py @@ -35,7 +35,7 @@ import re from sqlalchemy import Column, ForeignKey, Table, types 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.utils import get_natural_key @@ -259,7 +259,7 @@ def init_schema(url): 'authors_songs', metadata, 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) + Column('author_type', types.String(), primary_key=True, nullable=False, server_default=text('""')) ) # Definition of the "songs_topics" table diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index c40162474..ceb7848d6 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -234,8 +234,8 @@ class SongMediaItem(MediaManagerItem): if song.temporary: continue 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)) + song_detail = '%s (%s)' % (song.title, create_separated_list(author_list)) if author_list\ + else '%s'%song.title song_name = QtGui.QListWidgetItem(song_detail) song_name.setData(QtCore.Qt.UserRole, song.id) self.list_view.addItem(song_name) diff --git a/openlp/plugins/songs/lib/upgrade.py b/openlp/plugins/songs/lib/upgrade.py index 87f960f3c..4ec4ca1c4 100644 --- a/openlp/plugins/songs/lib/upgrade.py +++ b/openlp/plugins/songs/lib/upgrade.py @@ -102,7 +102,8 @@ def upgrade_4(session, metadata): """ Version 4 upgrade. - This upgrade adds a column for author type to the authors table + This upgrade adds a column for author type to the authors_songs table """ op = get_upgrade_op(session) - op.add_column('authors_songs', Column('author_type', types.String(), primary_key=True, server_default=null())) + op.add_column('authors_songs', Column('author_type', types.String(), primary_key=True, + nullable=False, server_default=text('""'))) From c98970d0e4d050e53bde9e53b3bf1d358c8750d2 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Sun, 30 Mar 2014 20:01:03 +0200 Subject: [PATCH 05/33] Fix mediaitem tests --- openlp/plugins/songs/lib/db.py | 1 - .../openlp_plugins/songs/test_mediaitem.py | 42 ++++++++++++------- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/openlp/plugins/songs/lib/db.py b/openlp/plugins/songs/lib/db.py index 4e925e4b6..204dac603 100644 --- a/openlp/plugins/songs/lib/db.py +++ b/openlp/plugins/songs/lib/db.py @@ -81,7 +81,6 @@ class Book(BaseModel): def __repr__(self): return '' % (str(self.id), self.name, self.publisher) - class MediaFile(BaseModel): """ MediaFile model diff --git a/tests/functional/openlp_plugins/songs/test_mediaitem.py b/tests/functional/openlp_plugins/songs/test_mediaitem.py index 2b5f02483..627663c62 100644 --- a/tests/functional/openlp_plugins/songs/test_mediaitem.py +++ b/tests/functional/openlp_plugins/songs/test_mediaitem.py @@ -10,6 +10,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.common import Registry, Settings from openlp.core.lib import ServiceItem from openlp.plugins.songs.lib.mediaitem import SongMediaItem +from openlp.plugins.songs.lib.db import AuthorType from tests.functional import patch, MagicMock from tests.helpers.testmixin import TestMixin @@ -45,10 +46,12 @@ class TestMediaItem(TestCase, TestMixin): # GIVEN: A Song and a Service Item mock_song = MagicMock() mock_song.title = 'My Song' + mock_song.authors_songs = [] mock_author = MagicMock() mock_author.display_name = 'my author' - mock_song.authors = [] - mock_song.authors.append(mock_author) + mock_author_song = MagicMock() + mock_author_song.author = mock_author + mock_song.authors_songs.append(mock_author_song) mock_song.copyright = 'My copyright' service_item = ServiceItem(None) @@ -56,7 +59,7 @@ class TestMediaItem(TestCase, TestMixin): author_list = self.media_item.generate_footer(service_item, mock_song) # 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') self.assertEqual(author_list, ['my 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 mock_song = MagicMock() mock_song.title = 'My Song' + mock_song.authors_songs = [] mock_author = MagicMock() mock_author.display_name = 'my author' - mock_song.authors = [] - mock_song.authors.append(mock_author) + mock_author_song = MagicMock() + 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.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' service_item = ServiceItem(None) @@ -82,22 +97,19 @@ class TestMediaItem(TestCase, TestMixin): author_list = self.media_item.generate_footer(service_item, mock_song) # 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') - 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') 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 mock_song = MagicMock() 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' service_item = ServiceItem(None) Settings().setValue('core/ccli number', '1234') @@ -106,7 +118,7 @@ class TestMediaItem(TestCase, TestMixin): self.media_item.generate_footer(service_item, mock_song) # 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', 'Author Unknown', 'My copyright', 'CCLI License: 1234'], 'The array should be returned correctly with a song, an author, copyright and ccli') # WHEN: I amend the CCLI value @@ -114,5 +126,5 @@ class TestMediaItem(TestCase, TestMixin): self.media_item.generate_footer(service_item, mock_song) # 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', 'Author Unknown', 'My copyright', 'CCLI License: 4321'], 'The array should be returned correctly with a song, an author, copyright and amended ccli') From ad9717ea42e78bb6f66dc6c3bd9f43fe82f98d91 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Sun, 30 Mar 2014 23:42:46 +0200 Subject: [PATCH 06/33] Add support for author types to OpenLyrics import and export --- openlp/core/ui/wizard.py | 2 +- openlp/plugins/songs/lib/db.py | 3 ++- openlp/plugins/songs/lib/xml.py | 22 ++++++++++++++-------- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/openlp/core/ui/wizard.py b/openlp/core/ui/wizard.py index 05951d14a..5815457b5 100644 --- a/openlp/core/ui/wizard.py +++ b/openlp/core/ui/wizard.py @@ -197,7 +197,7 @@ class OpenLPWizard(QtGui.QWizard, RegistryProperties): """ Run the wizard. """ - self.setDefaults() + self.set_defaults() return QtGui.QWizard.exec_(self) def reject(self): diff --git a/openlp/plugins/songs/lib/db.py b/openlp/plugins/songs/lib/db.py index 204dac603..f597fc2d2 100644 --- a/openlp/plugins/songs/lib/db.py +++ b/openlp/plugins/songs/lib/db.py @@ -275,8 +275,9 @@ def init_schema(url): mapper(Book, song_books_table) mapper(MediaFile, media_files_table) mapper(Song, songs_table, properties={ + # 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, viewonly=True), + 'authors': relation(Author, secondary=authors_songs_table), 'book': relation(Book, backref='songs'), 'media_files': relation(MediaFile, backref='songs', order_by=media_files_table.c.weight), 'topics': relation(Topic, backref='songs', secondary=songs_topics_table) diff --git a/openlp/plugins/songs/lib/xml.py b/openlp/plugins/songs/lib/xml.py index 667afebdd..7e41008bd 100644 --- a/openlp/plugins/songs/lib/xml.py +++ b/openlp/plugins/songs/lib/xml.py @@ -71,7 +71,7 @@ from lxml import etree, objectify from openlp.core.common import translate from openlp.core.lib import FormattingTags 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, Book, Song, Topic from openlp.core.utils import get_application_version log = logging.getLogger(__name__) @@ -166,7 +166,7 @@ class OpenLyrics(object): supported by the :class:`OpenLyrics` class: ```` - OpenLP does not support the attribute *type* and *lang*. + OpenLP does not support the attribute *lang*. ```` This property is not supported. @@ -269,10 +269,12 @@ class OpenLyrics(object): 'verseOrder', properties, song.verse_order.lower()) if 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') - for author in song.authors: - self._add_text_to_element('author', authors, author.display_name) + for author_song in song.authors_songs: + element = self._add_text_to_element('author', authors, author_song.author.display_name) + if author_song.author_type: + element.set('type', author_song.author_type) book = self.manager.get_object_filtered(Book, Book.id == song.song_book_id) if book is not None: book = book.name @@ -501,16 +503,20 @@ class OpenLyrics(object): if hasattr(properties, 'authors'): for author in properties.authors.author: display_name = self._text(author) + author_type = author.get('type', '') if display_name: - authors.append(display_name) - for display_name in authors: + authors.append((display_name, author_type)) + for (display_name, author_type) in authors: author = self.manager.get_object_filtered(Author, Author.display_name == display_name) if author is None: # We need to create a new author, as the author does not exist. author = Author.populate(display_name=display_name, last_name=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): """ From 88c641715caee5b3683ed71c4cf6eeeb4bee40f9 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Sun, 30 Mar 2014 23:45:52 +0200 Subject: [PATCH 07/33] Translate this string --- openlp/plugins/songs/lib/ui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/plugins/songs/lib/ui.py b/openlp/plugins/songs/lib/ui.py index 14f4777c9..4f60bec1f 100644 --- a/openlp/plugins/songs/lib/ui.py +++ b/openlp/plugins/songs/lib/ui.py @@ -40,7 +40,7 @@ class SongStrings(object): # These strings should need a good reason to be retranslated elsewhere. Author = translate('OpenLP.Ui', 'Author', 'Singular') Authors = translate('OpenLP.Ui', 'Authors', 'Plural') - AuthorUnknown = 'Author Unknown' # Used to populate the database. + AuthorUnknown = translate('OpenLP.Ui', 'Author Unknown') #Shown when a song has no author CopyrightSymbol = translate('OpenLP.Ui', '\xa9', 'Copyright symbol.') SongBook = translate('OpenLP.Ui', 'Song Book', 'Singular') SongBooks = translate('OpenLP.Ui', 'Song Books', 'Plural') From ad982e9fa31b0da6f2f54b4b01c3c332bb463c1f Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Sun, 30 Mar 2014 23:49:55 +0200 Subject: [PATCH 08/33] Add try/catch block to upgrade function until better solution is found --- openlp/plugins/songs/lib/upgrade.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/openlp/plugins/songs/lib/upgrade.py b/openlp/plugins/songs/lib/upgrade.py index 4ec4ca1c4..d0123feaa 100644 --- a/openlp/plugins/songs/lib/upgrade.py +++ b/openlp/plugins/songs/lib/upgrade.py @@ -104,6 +104,9 @@ def upgrade_4(session, metadata): This upgrade adds a column for author type to the authors_songs table """ - op = get_upgrade_op(session) - op.add_column('authors_songs', Column('author_type', types.String(), primary_key=True, - nullable=False, server_default=text('""'))) + try: + op = get_upgrade_op(session) + op.add_column('authors_songs', Column('author_type', types.String(), primary_key=True, + nullable=False, server_default=text('""'))) + except OperationalError: + log.info('Upgrade 4 has already been run') From 830e3d6624c717cf140e013206c65c096c874ccf Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Tue, 1 Apr 2014 23:07:49 +0200 Subject: [PATCH 09/33] PEP8 --- openlp/plugins/songs/lib/db.py | 6 +++++- openlp/plugins/songs/lib/mediaitem.py | 17 ++++++++++------- openlp/plugins/songs/lib/ui.py | 2 +- openlp/plugins/songs/lib/upgrade.py | 1 + 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/openlp/plugins/songs/lib/db.py b/openlp/plugins/songs/lib/db.py index f597fc2d2..59592a7e0 100644 --- a/openlp/plugins/songs/lib/db.py +++ b/openlp/plugins/songs/lib/db.py @@ -48,9 +48,10 @@ class Author(BaseModel): """ def get_display_name(self, author_type=None): if author_type: - return "%s: %s"%(AuthorType.Types[author_type], self.display_name) + return "%s: %s" % (AuthorType.Types[author_type], self.display_name) return self.display_name + class AuthorSong(BaseModel): """ Relationship between Authors and Songs (many to many). @@ -60,6 +61,7 @@ class AuthorSong(BaseModel): """ pass + class AuthorType(object): """ Enumeration for Author types. @@ -74,6 +76,7 @@ class AuthorType(object): Translation: translate('OpenLP.Ui', 'Translation') } + class Book(BaseModel): """ Book model @@ -81,6 +84,7 @@ class Book(BaseModel): def __repr__(self): return '' % (str(self.id), self.name, self.publisher) + class MediaFile(BaseModel): """ MediaFile model diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index ceb7848d6..71c5fea9c 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -234,8 +234,7 @@ class SongMediaItem(MediaManagerItem): if song.temporary: continue author_list = [author.display_name for author in song.authors] - song_detail = '%s (%s)' % (song.title, create_separated_list(author_list)) if author_list\ - else '%s'%song.title + song_detail = '%s (%s)' % (song.title, create_separated_list(author_list)) if author_list else song.title song_name = QtGui.QListWidgetItem(song_detail) song_name.setData(QtCore.Qt.UserRole, song.id) self.list_view.addItem(song_name) @@ -489,14 +488,18 @@ class SongMediaItem(MediaManagerItem): item.raw_footer = [] item.raw_footer.append(song.title) if authors_none: - item.raw_footer.append("%s: %s"%(translate('OpenLP.Ui', 'Written by'), create_separated_list(authors_none))) + item.raw_footer.append("%s: %s" % (translate('OpenLP.Ui', 'Written by'), + create_separated_list(authors_none))) if authors_words: - item.raw_footer.append("%s: %s"%(AuthorType.Types[AuthorType.Words], create_separated_list(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))) + 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))) - if not authors_all: #No authors defined + item.raw_footer.append("%s: %s" % (AuthorType.Types[AuthorType.Translation], + create_separated_list(authors_translation))) + if not authors_all: # No authors defined item.raw_footer.append(SongStrings.AuthorUnknown) item.raw_footer.append(song.copyright) if Settings().value('core/ccli number'): diff --git a/openlp/plugins/songs/lib/ui.py b/openlp/plugins/songs/lib/ui.py index 4f60bec1f..193b70608 100644 --- a/openlp/plugins/songs/lib/ui.py +++ b/openlp/plugins/songs/lib/ui.py @@ -40,7 +40,7 @@ class SongStrings(object): # These strings should need a good reason to be retranslated elsewhere. Author = translate('OpenLP.Ui', 'Author', 'Singular') Authors = translate('OpenLP.Ui', 'Authors', 'Plural') - AuthorUnknown = translate('OpenLP.Ui', 'Author Unknown') #Shown when a song has no author + AuthorUnknown = translate('OpenLP.Ui', 'Author Unknown') # Shown when a song has no author CopyrightSymbol = translate('OpenLP.Ui', '\xa9', 'Copyright symbol.') SongBook = translate('OpenLP.Ui', 'Song Book', 'Singular') SongBooks = translate('OpenLP.Ui', 'Song Books', 'Plural') diff --git a/openlp/plugins/songs/lib/upgrade.py b/openlp/plugins/songs/lib/upgrade.py index d0123feaa..96cd65051 100644 --- a/openlp/plugins/songs/lib/upgrade.py +++ b/openlp/plugins/songs/lib/upgrade.py @@ -98,6 +98,7 @@ def upgrade_3(session, metadata): except OperationalError: log.info('Upgrade 3 has already been run') + def upgrade_4(session, metadata): """ Version 4 upgrade. From 691247ffda9ac8a91ad62af6742d275087d39c33 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 2 Apr 2014 08:47:27 +0200 Subject: [PATCH 10/33] Don't allow a song to be saved without an author --- openlp/plugins/songs/forms/editsongform.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index f3a695f03..ebf967755 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -214,6 +214,12 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): critical_error_message_box( message=translate('SongsPlugin.EditSongForm', 'You need to type in at least one verse.')) return False + if self.authors_list_view.count() == 0: + self.song_tab_widget.setCurrentIndex(1) + self.authors_list_view.setFocus() + critical_error_message_box( + message=translate('SongsPlugin.EditSongForm', 'You need to have an author for this song.')) + return False if self.verse_order_edit.text(): result = self._validate_verse_list(self.verse_order_edit.text(), self.verse_list_widget.rowCount()) if not result: From 31b220c9726da648ecb3202b8d0c16c25fc9de6e Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 2 Apr 2014 08:49:03 +0200 Subject: [PATCH 11/33] Indentation --- openlp/plugins/songs/forms/editsongform.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index ebf967755..6417b4a73 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -217,8 +217,8 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): if self.authors_list_view.count() == 0: self.song_tab_widget.setCurrentIndex(1) self.authors_list_view.setFocus() - critical_error_message_box( - message=translate('SongsPlugin.EditSongForm', 'You need to have an author for this song.')) + critical_error_message_box(message=translate('SongsPlugin.EditSongForm', + 'You need to have an author for this song.')) return False if self.verse_order_edit.text(): result = self._validate_verse_list(self.verse_order_edit.text(), self.verse_list_widget.rowCount()) From dffee7fbc159e8ce776aec02ec1370af8f9bb45a Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 2 Apr 2014 09:03:53 +0200 Subject: [PATCH 12/33] Restore setting a default author --- openlp/plugins/songs/lib/__init__.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/openlp/plugins/songs/lib/__init__.py b/openlp/plugins/songs/lib/__init__.py index 60e8788c8..aa9fbc4c9 100644 --- a/openlp/plugins/songs/lib/__init__.py +++ b/openlp/plugins/songs/lib/__init__.py @@ -389,6 +389,13 @@ def clean_song(manager, song): song.lyrics = str(song.lyrics, encoding='utf8') verses = SongXML().get_verses(song.lyrics) song.search_lyrics = ' '.join([clean_string(verse[1]) for verse in verses]) + # The song does not have any author, add one. + if not song.authors and not song.authors_songs: # Need to check both relations + name = SongStrings.AuthorUnknown + author = manager.get_object_filtered(Author, Author.display_name == name) + if author is None: + author = Author.populate(display_name=name, last_name='', first_name='') + song.authors.append(author) if song.copyright: song.copyright = CONTROL_CHARS.sub('', song.copyright).strip() From adedb6eaebb1b20f3365ff3628818151fd037557 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 2 Apr 2014 09:04:12 +0200 Subject: [PATCH 13/33] Fix setDefaults -> set_defaults --- openlp/core/ui/firsttimeform.py | 4 ++-- openlp/core/ui/themeform.py | 4 ++-- openlp/plugins/bibles/forms/bibleimportform.py | 2 +- openlp/plugins/bibles/forms/bibleupgradeform.py | 2 +- openlp/plugins/songs/forms/duplicatesongremovalform.py | 2 +- openlp/plugins/songs/forms/songimportform.py | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/openlp/core/ui/firsttimeform.py b/openlp/core/ui/firsttimeform.py index aa89da6c0..fc6a61db6 100644 --- a/openlp/core/ui/firsttimeform.py +++ b/openlp/core/ui/firsttimeform.py @@ -110,10 +110,10 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard, RegistryProperties): """ Run the wizard. """ - self.setDefaults() + self.set_defaults() return QtGui.QWizard.exec_(self) - def setDefaults(self): + def set_defaults(self): """ Set up display at start of theme edit. """ diff --git a/openlp/core/ui/themeform.py b/openlp/core/ui/themeform.py index fbfc1035c..c8182232b 100644 --- a/openlp/core/ui/themeform.py +++ b/openlp/core/ui/themeform.py @@ -90,7 +90,7 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard, RegistryProperties): self.footer_font_combo_box.activated.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. """ @@ -261,7 +261,7 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard, RegistryProperties): log.debug('Editing theme %s' % self.theme.theme_name) self.temp_background_filename = '' self.update_theme_allowed = False - self.setDefaults() + self.set_defaults() self.update_theme_allowed = True self.theme_name_label.setVisible(not edit) self.theme_name_edit.setVisible(not edit) diff --git a/openlp/plugins/bibles/forms/bibleimportform.py b/openlp/plugins/bibles/forms/bibleimportform.py index ee5bee2d0..79b0bc699 100644 --- a/openlp/plugins/bibles/forms/bibleimportform.py +++ b/openlp/plugins/bibles/forms/bibleimportform.py @@ -465,7 +465,7 @@ class BibleImportForm(OpenLPWizard): self.license_details_page.registerField('license_copyright', self.copyright_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. """ diff --git a/openlp/plugins/bibles/forms/bibleupgradeform.py b/openlp/plugins/bibles/forms/bibleupgradeform.py index 9925b1ebc..0c004da8a 100644 --- a/openlp/plugins/bibles/forms/bibleupgradeform.py +++ b/openlp/plugins/bibles/forms/bibleupgradeform.py @@ -307,7 +307,7 @@ class BibleUpgradeForm(OpenLPWizard): if self.currentPage() == self.progress_page: return True - def setDefaults(self): + def set_defaults(self): """ Set default values for the wizard pages. """ diff --git a/openlp/plugins/songs/forms/duplicatesongremovalform.py b/openlp/plugins/songs/forms/duplicatesongremovalform.py index 22299cde5..8ca673448 100644 --- a/openlp/plugins/songs/forms/duplicatesongremovalform.py +++ b/openlp/plugins/songs/forms/duplicatesongremovalform.py @@ -244,7 +244,7 @@ class DuplicateSongRemovalForm(OpenLPWizard, RegistryProperties): self.break_search = True 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. """ diff --git a/openlp/plugins/songs/forms/songimportform.py b/openlp/plugins/songs/forms/songimportform.py index 27f0d9343..21569a034 100644 --- a/openlp/plugins/songs/forms/songimportform.py +++ b/openlp/plugins/songs/forms/songimportform.py @@ -304,7 +304,7 @@ class SongImportForm(OpenLPWizard, RegistryProperties): """ self.source_page.emit(QtCore.SIGNAL('completeChanged()')) - def setDefaults(self): + def set_defaults(self): """ Set default form values for the song import wizard. """ From d62cd37db4fe7347516967e59b328ff3cb289f55 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 2 Apr 2014 09:07:46 +0200 Subject: [PATCH 14/33] Restore comment --- openlp/plugins/songs/lib/ui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/plugins/songs/lib/ui.py b/openlp/plugins/songs/lib/ui.py index 193b70608..151b11b4b 100644 --- a/openlp/plugins/songs/lib/ui.py +++ b/openlp/plugins/songs/lib/ui.py @@ -40,7 +40,7 @@ class SongStrings(object): # These strings should need a good reason to be retranslated elsewhere. Author = translate('OpenLP.Ui', 'Author', 'Singular') Authors = translate('OpenLP.Ui', 'Authors', 'Plural') - AuthorUnknown = translate('OpenLP.Ui', 'Author Unknown') # Shown when a song has no author + AuthorUnknown = translate('OpenLP.Ui', 'Author Unknown') # Used to populate the database. CopyrightSymbol = translate('OpenLP.Ui', '\xa9', 'Copyright symbol.') SongBook = translate('OpenLP.Ui', 'Song Book', 'Singular') SongBooks = translate('OpenLP.Ui', 'Song Books', 'Plural') From cc635e9b9678b95ecfc3c3cdf42dd9e0150da2d5 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Tue, 8 Apr 2014 11:16:14 +0200 Subject: [PATCH 15/33] SQLite doesn't support changing a primary key --- openlp/plugins/songs/forms/editsongform.py | 2 +- openlp/plugins/songs/lib/upgrade.py | 13 ++++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index 6417b4a73..ba4298922 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -528,7 +528,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): elif item > 0: item_id = (self.authors_combo_box.itemData(item)) 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( message=translate('SongsPlugin.EditSongForm', 'This author is already in the list.')) else: diff --git a/openlp/plugins/songs/lib/upgrade.py b/openlp/plugins/songs/lib/upgrade.py index 96cd65051..59355bbec 100644 --- a/openlp/plugins/songs/lib/upgrade.py +++ b/openlp/plugins/songs/lib/upgrade.py @@ -32,7 +32,7 @@ backend for the Songs plugin """ import logging -from sqlalchemy import Column, types +from sqlalchemy import Column, ForeignKey, types from sqlalchemy.exc import OperationalError from sqlalchemy.sql.expression import func, false, null, text @@ -106,8 +106,15 @@ def upgrade_4(session, metadata): 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.add_column('authors_songs', Column('author_type', types.String(), primary_key=True, - nullable=False, server_default=text('""'))) + 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') From 7f8e76b8ddcb0d814698e2119f39845d9fd9e250 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Tue, 8 Apr 2014 11:21:56 +0200 Subject: [PATCH 16/33] Restore previous delete button behavior --- openlp/plugins/songs/forms/editsongform.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index ba4298922..c5ddb2a62 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -545,7 +545,8 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): """ Run a set of actions when an author in the list is selected (mainly enable the delete button). """ - self.author_remove_button.setEnabled(True) + if self.authors_list_view.count() > 1: + self.author_remove_button.setEnabled(True) def on_author_remove_button_clicked(self): """ From fb85b9858a2094198dcb31cfb54d9df1c7232059 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Tue, 8 Apr 2014 11:29:47 +0200 Subject: [PATCH 17/33] PEP8 --- openlp/plugins/songs/forms/editsongform.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index c5ddb2a62..d27f43343 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -302,7 +302,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): self.authors.append(author.display_name) set_case_insensitive_completer(self.authors, self.authors_combo_box) - #Types + # 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 @@ -689,7 +689,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): verse_index = VerseType.from_loose_input(verse_name) verse_tag = VerseType.tags[verse_index] # Later we need to handle v1a as well. - #regex = re.compile(r'(\d+\w.)') + # regex = re.compile(r'(\d+\w.)') regex = re.compile(r'\D*(\d+)\D*') match = regex.match(verse_num) if match: From 577c7321ffec12c4895d30415bfbcda1f22f8952 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Tue, 8 Apr 2014 20:52:05 +0200 Subject: [PATCH 18/33] Add new type for 'Words and Music' --- openlp/plugins/songs/forms/editsongform.py | 1 + openlp/plugins/songs/lib/db.py | 4 ++++ openlp/plugins/songs/lib/xml.py | 10 ++++++++-- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index d27f43343..4d6e1addb 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -308,6 +308,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): # 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): diff --git a/openlp/plugins/songs/lib/db.py b/openlp/plugins/songs/lib/db.py index 59592a7e0..fa53bccc8 100644 --- a/openlp/plugins/songs/lib/db.py +++ b/openlp/plugins/songs/lib/db.py @@ -66,13 +66,17 @@ 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') } diff --git a/openlp/plugins/songs/lib/xml.py b/openlp/plugins/songs/lib/xml.py index 7e41008bd..64c9aaf05 100644 --- a/openlp/plugins/songs/lib/xml.py +++ b/openlp/plugins/songs/lib/xml.py @@ -71,7 +71,7 @@ from lxml import etree, objectify from openlp.core.common import translate from openlp.core.lib import FormattingTags from openlp.plugins.songs.lib import VerseType, clean_song -from openlp.plugins.songs.lib.db import Author, AuthorSong, Book, Song, Topic +from openlp.plugins.songs.lib.db import Author, AuthorSong, AuthorType, Book, Song, Topic from openlp.core.utils import get_application_version log = logging.getLogger(__name__) @@ -274,7 +274,13 @@ class OpenLyrics(object): for author_song in song.authors_songs: element = self._add_text_to_element('author', authors, author_song.author.display_name) if author_song.author_type: - element.set('type', 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) if book is not None: book = book.name From efad4a55fae57ef56c7e8e9ce6b3e624218582ea Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Tue, 8 Apr 2014 20:57:01 +0200 Subject: [PATCH 19/33] Display 'Words and Music' type in the footer --- openlp/plugins/songs/lib/mediaitem.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index 71c5fea9c..af7157a63 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -470,6 +470,7 @@ class SongMediaItem(MediaManagerItem): """ authors_words = [] authors_music = [] + authors_words_music = [] authors_translation = [] authors_none = [] for author_song in song.authors_songs: @@ -477,11 +478,13 @@ class SongMediaItem(MediaManagerItem): 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 + authors_music + authors_translation + authors_none + authors_all = authors_words_music + authors_words + authors_music + authors_translation + authors_none item.audit = [ song.title, authors_all, song.copyright, str(song.ccli_number) ] @@ -490,6 +493,9 @@ class SongMediaItem(MediaManagerItem): 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))) @@ -499,8 +505,6 @@ class SongMediaItem(MediaManagerItem): if authors_translation: item.raw_footer.append("%s: %s" % (AuthorType.Types[AuthorType.Translation], create_separated_list(authors_translation))) - if not authors_all: # No authors defined - item.raw_footer.append(SongStrings.AuthorUnknown) item.raw_footer.append(song.copyright) if Settings().value('core/ccli number'): item.raw_footer.append(translate('SongsPlugin.MediaItem', From abf1671754bf008874bcaea1850a6b17eaf9bcee Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Tue, 8 Apr 2014 20:58:28 +0200 Subject: [PATCH 20/33] Ignore Komodo project and project directory --- .bzrignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.bzrignore b/.bzrignore index f149d97a7..716add8bb 100644 --- a/.bzrignore +++ b/.bzrignore @@ -5,6 +5,8 @@ *.ropeproject *.e4* .eric4project +.komodotools +*.komodoproject list openlp.org 2.0.e4* documentation/build/html From e0c638176278a8c2990325b6ea5eee0f11af4c04 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Tue, 8 Apr 2014 21:02:13 +0200 Subject: [PATCH 21/33] Fix test --- tests/functional/openlp_plugins/songs/test_mediaitem.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/functional/openlp_plugins/songs/test_mediaitem.py b/tests/functional/openlp_plugins/songs/test_mediaitem.py index 627663c62..1b372aa20 100644 --- a/tests/functional/openlp_plugins/songs/test_mediaitem.py +++ b/tests/functional/openlp_plugins/songs/test_mediaitem.py @@ -118,7 +118,7 @@ class TestMediaItem(TestCase, TestMixin): self.media_item.generate_footer(service_item, mock_song) # THEN: I get the following Array returned - self.assertEqual(service_item.raw_footer, ['My Song', 'Author Unknown', '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') # WHEN: I amend the CCLI value @@ -126,5 +126,5 @@ class TestMediaItem(TestCase, TestMixin): self.media_item.generate_footer(service_item, mock_song) # THEN: I would get an amended footer string - self.assertEqual(service_item.raw_footer, ['My Song', 'Author Unknown', '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') From d681f0a059b3c04423d4e06a919e6ad747c077bc Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Mon, 14 Apr 2014 19:09:47 +0100 Subject: [PATCH 22/33] Start to improve remote --- openlp/plugins/remotes/lib/httpserver.py | 56 ++++++++++++++++------- openlp/plugins/remotes/remoteplugin.py | 11 ++++- resources/images/network_auth.png | Bin 0 -> 608 bytes resources/images/network_server.png | Bin 0 -> 1133 bytes resources/images/network_ssl.png | Bin 0 -> 577 bytes resources/images/openlp-2.qrc | 5 ++ 6 files changed, 55 insertions(+), 17 deletions(-) create mode 100644 resources/images/network_auth.png create mode 100644 resources/images/network_server.png create mode 100644 resources/images/network_ssl.png diff --git a/openlp/plugins/remotes/lib/httpserver.py b/openlp/plugins/remotes/lib/httpserver.py index 22d0349f8..cc2d02ff3 100644 --- a/openlp/plugins/remotes/lib/httpserver.py +++ b/openlp/plugins/remotes/lib/httpserver.py @@ -38,9 +38,10 @@ import os import logging import time -from PyQt4 import QtCore +from PyQt4 import QtCore, QtGui -from openlp.core.common import AppLocation, Settings +from openlp.core.common import AppLocation, Settings, RegistryProperties +from openlp.core.lib import build_icon from openlp.plugins.remotes.lib import HttpRouter @@ -95,12 +96,13 @@ class HttpThread(QtCore.QThread): self.http_server.start_server() -class OpenLPServer(): +class OpenLPServer(RegistryProperties): def __init__(self): """ Initialise the http server, and start the server of the correct type http / https """ - log.debug('Initialise httpserver') + super(OpenLPServer, self).__init__() + log.debug('Initialise OpenLP') self.settings_section = 'remotes' self.http_thread = HttpThread(self) self.http_thread.start() @@ -112,25 +114,47 @@ class OpenLPServer(): address = Settings().value(self.settings_section + '/ip address') if Settings().value(self.settings_section + '/https enabled'): port = Settings().value(self.settings_section + '/https port') - self.httpd = HTTPSServer((address, port), CustomHandler) - log.debug('Started ssl httpd...') + self.start_server_instance(address, port, HTTPSServer) else: port = Settings().value(self.settings_section + '/port') - loop = 1 - while loop < 3: - try: - self.httpd = ThreadingHTTPServer((address, port), CustomHandler) - except OSError: - loop += 1 - time.sleep(0.1) - except: - log.error('Failed to start server ') - log.debug('Started non ssl httpd...') + self.start_server_instance(address, port, ThreadingHTTPServer) if hasattr(self, 'httpd') and self.httpd: self.httpd.serve_forever() + icon = QtGui.QImage(':/remote/network_server.png') + icon = icon.scaled(80, 80, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation) + overlay = QtGui.QImage(':/remote/network_ssl.png') + overlay = overlay.scaled(40, 40, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation) + painter = QtGui.QPainter(icon) + painter.drawImage(0, 0, overlay) + painter.end() + print("Hi") + self.default_theme_label.setText("hello") + self.default_theme_label.setIcon(build_icon(icon)) + self.default_theme_label.show() else: log.debug('Failed to start server') + def start_server_instance(self, address, port, server_class): + """ + Start the server + + :param address: The server address + :param port: The run port + :param server_class: the class to start + """ + loop = 1 + while loop < 4: + try: + self.httpd = server_class((address, port), CustomHandler) + log.debug("Server started for class %s %s %d" % (server_class, address, port)) + except OSError: + log.debug("failed to start http server thread state %d %s" % + (loop, self.http_thread.isRunning() is True)) + loop += 1 + time.sleep(0.1) + except: + log.error('Failed to start server ') + def stop_server(self): """ Stop the server diff --git a/openlp/plugins/remotes/remoteplugin.py b/openlp/plugins/remotes/remoteplugin.py index 393f08dd9..0e3825bdd 100644 --- a/openlp/plugins/remotes/remoteplugin.py +++ b/openlp/plugins/remotes/remoteplugin.py @@ -30,6 +30,8 @@ import logging import time +from PyQt4 import QtGui + from openlp.core.lib import Plugin, StringContent, translate, build_icon from openlp.plugins.remotes.lib import RemoteTab, OpenLPServer @@ -67,6 +69,13 @@ class RemotesPlugin(Plugin): log.debug('initialise') super(RemotesPlugin, self).initialise() self.server = OpenLPServer() + self.default_theme_label = QtGui.QToolButton(self.main_window.status_bar) + self.default_theme_label.setCheckable(False) + self.default_theme_label.setAutoRaise(True) + self.default_theme_label.setObjectName('default_theme_label') + self.main_window.status_bar.insertPermanentWidget(2, self.default_theme_label) + self.default_theme_label.hide() + self.server.default_theme_label = self.default_theme_label def finalise(self): """ @@ -108,5 +117,5 @@ class RemotesPlugin(Plugin): """ log.debug('remote config changed') self.finalise() - time.sleep(0.5) + time.sleep(1) self.initialise() diff --git a/resources/images/network_auth.png b/resources/images/network_auth.png new file mode 100644 index 0000000000000000000000000000000000000000..45e7a5c1788d214b1e63abdb2746cdfb938598ec GIT binary patch literal 608 zcmV-m0-ybfP)FV7f{fuWJai358$p+0R>_w(?=`@XQp(zNRTr6`JD zTAEkp{ksO;ZujtdCY#H=&xmo#D8?zHq~0av#q+hC!|52_5%i3mA6;Et$!7~`aon{F z*AN~dJVdyPjNBZNXe=V3x%;nOF2`t_Q?-rRa_Z7Xb7RP*xXG&5;V_^F)Ze>u1Ft$d+)n z_Y8$%;W1F%X&EP>0Z#l3bp8^Mc%UqV?hM_A55Oj{ z4t#=({S(CF@j2k-zi!DeJ$=2jlwK6ej77{h%yMrzC}SQ^2{7#TAP0O`e5peIjbzhV u86Urp0tWun*zuQ;F5q~}CxOPEyz4hAGR?5R1SdxT0000swx|LFf3SGE$k@yK*Te>U# z3R9R5P`Z&)XX9cjh!m!1y0BF;OpPYby!Z0nz2|oEUSeWpCMkJ01d{vCyZ3j_|D1C# zq0jN-ix+<3lf1}gGL%k#K*>Y`ni;>ndWFv(KQ_MKqGr=Em$ge%sr<=bKYNzwXI;@R zz*Vg_;xrvut6d4#bt#!lQYx9GqoX5ACK5sjNz7U(rKDy(((k%`17ydu$mMdFDHKqz z*E{o;lmGxC0+9d_VUW=`fYusXD=4iYqD_3QVUMOGfLmJ=2Y@jK0D%Y)B7DvD1Dxk) zBOnUBCj!L>02tE_Hz8?BOjQ4dX-9w%!k8EVfZGTpq(ruU8O>7oA;KUb7#tw-zCC9E zkO;%xmH@Fy5INTkAcVl{*MD^E2Frj!0N5asxu?{a84L6C;kE<_(Y6{kKp5;c!GH3? z05Ht(JP%3*!v_e001dl=R?CO)`=S1w$}mt`!*xA4jss?np%F2o)oLLK0tCUWhqrQs zK+L8mGDFP))(pPmdACI2Gad-Q>euk*#>VyaH7v`5jQW4iHilqK2*_~&q|<3kOiUn~ z%_5V@pwkw+$~2UKVK@c}{2;*f^%Yva53O}i1WW|L+JsUWjwc5o1ekSqcLInY1che+ znXp{rkRbGs!TL7I*ciC3i(2gpj^m)&Y{GF|IIcVV2(aPsP7r0e0bsPohf-pEd>s4x zzvAWoOQcdsj969-0L;*=JEf?Ug6}vOYc!yO00e?{}LeINh4eGAWZQ79DP zdLE|Vyn*Ao`25Ke%oi7MesK{`Cj>&hNf0s*AA+?8h%ol_DQ<3VVB0ntwvA@93D&Uy z071)#){K_#!w&+aMn{ofS%K%e;rXB$TIt?O8OE4<^hicO#{f(NSXf$G`k_=RnR2;o z0DJ+U7(Gh^!0gD|3yZfDHQ$*q;VX4OK5kitbBF%OxIfD@bD1UJGz`P2KjtGx4ypq zO^?n(pjxe-0C+z@tyZJ6v$IaWCn9*B2ivw$sZ`MA*uC9SN(s;NdM+hWO4RFhYmgie z_xx9-Qt1f5r|$wN7K_ifwzf9ztU&1r;P)(<+F=08q_00t&F1I%F#z!2z#$c(OoM@BmdoR*iyz z*z*Dgm?;aVIHxQOoC9YDX3HNE9{l7AL>!(vR2-fx9e4_&D+m7@K~?ZQ@I52tgDr%# zI?^7bJVX2?C;6!YJG+6uH;wH;lOVo2JV9(6~ytwJuv$Vf4-@_QLj?2 P00000NkvXXu0mjf+JX4) literal 0 HcmV?d00001 diff --git a/resources/images/openlp-2.qrc b/resources/images/openlp-2.qrc index 6af0e77a5..79036f08f 100644 --- a/resources/images/openlp-2.qrc +++ b/resources/images/openlp-2.qrc @@ -149,6 +149,11 @@ messagebox_info.png messagebox_warning.png + + network_server.png + network_ssl.png + network_auth.png + song_usage_active.png song_usage_inactive.png From 25687dfd4803b7c117d2c210b52f047bbc7c601b Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Fri, 18 Apr 2014 21:29:52 +0100 Subject: [PATCH 23/33] Complete icon and ssl change code --- openlp/plugins/remotes/lib/httprouter.py | 25 ++++++------ openlp/plugins/remotes/lib/httpserver.py | 50 +++++++++++++++--------- openlp/plugins/remotes/lib/remotetab.py | 30 ++++++++++++-- openlp/plugins/remotes/remoteplugin.py | 25 ++++++------ 4 files changed, 82 insertions(+), 48 deletions(-) diff --git a/openlp/plugins/remotes/lib/httprouter.py b/openlp/plugins/remotes/lib/httprouter.py index 5a10a14ae..125094a64 100644 --- a/openlp/plugins/remotes/lib/httprouter.py +++ b/openlp/plugins/remotes/lib/httprouter.py @@ -149,11 +149,11 @@ class HttpRouter(RegistryProperties): """ Initialise the router stack and any other variables. """ - authcode = "%s:%s" % (Settings().value('remotes/user id'), Settings().value('remotes/password')) + auth_code = "%s:%s" % (Settings().value('remotes/user id'), Settings().value('remotes/password')) try: - self.auth = base64.b64encode(authcode) + self.auth = base64.b64encode(auth_code) except TypeError: - self.auth = base64.b64encode(authcode.encode()).decode() + self.auth = base64.b64encode(auth_code.encode()).decode() self.routes = [ ('^/$', {'function': self.serve_file, 'secure': False}), ('^/(stage)$', {'function': self.serve_file, 'secure': False}), @@ -376,7 +376,6 @@ class HttpRouter(RegistryProperties): Examines the extension of the file and determines what the content_type should be, defaults to text/plain Returns the extension and the content_type """ - content_type = 'text/plain' ext = os.path.splitext(file_name)[1] content_type = FILE_TYPES.get(ext, 'text/plain') return ext, content_type @@ -439,7 +438,7 @@ class HttpRouter(RegistryProperties): if plugin.status == PluginStatus.Active: try: text = json.loads(self.request_data)['request']['text'] - except KeyError as ValueError: + except KeyError: return self.do_http_error() text = urllib.parse.unquote(text) self.alerts_manager.emit(QtCore.SIGNAL('alerts_text'), [text]) @@ -488,7 +487,7 @@ class HttpRouter(RegistryProperties): if self.request_data: try: data = json.loads(self.request_data)['request']['id'] - except KeyError as ValueError: + except KeyError: return self.do_http_error() log.info(data) # This slot expects an int within a list. @@ -547,7 +546,7 @@ class HttpRouter(RegistryProperties): """ try: text = json.loads(self.request_data)['request']['text'] - except KeyError as ValueError: + except KeyError: return self.do_http_error() text = urllib.parse.unquote(text) plugin = self.plugin_manager.get_plugin_by_name(plugin_name) @@ -563,12 +562,12 @@ class HttpRouter(RegistryProperties): Go live on an item of type ``plugin``. """ try: - id = json.loads(self.request_data)['request']['id'] - except KeyError as ValueError: + request_id = json.loads(self.request_data)['request']['id'] + except KeyError: return self.do_http_error() plugin = self.plugin_manager.get_plugin_by_name(plugin_name) if plugin.status == PluginStatus.Active and plugin.media_item: - plugin.media_item.emit(QtCore.SIGNAL('%s_go_live' % plugin_name), [id, True]) + plugin.media_item.emit(QtCore.SIGNAL('%s_go_live' % plugin_name), [request_id, True]) return self.do_http_success() def add_to_service(self, plugin_name): @@ -576,11 +575,11 @@ class HttpRouter(RegistryProperties): Add item of type ``plugin_name`` to the end of the service. """ try: - id = json.loads(self.request_data)['request']['id'] - except KeyError as ValueError: + request_id = json.loads(self.request_data)['request']['id'] + except KeyError: return self.do_http_error() plugin = self.plugin_manager.get_plugin_by_name(plugin_name) if plugin.status == PluginStatus.Active and plugin.media_item: - item_id = plugin.media_item.create_item_from_id(id) + item_id = plugin.media_item.create_item_from_id(request_id) plugin.media_item.emit(QtCore.SIGNAL('%s_add_to_service' % plugin_name), [item_id, True]) self.do_http_success() diff --git a/openlp/plugins/remotes/lib/httpserver.py b/openlp/plugins/remotes/lib/httpserver.py index cc2d02ff3..7b3d667cd 100644 --- a/openlp/plugins/remotes/lib/httpserver.py +++ b/openlp/plugins/remotes/lib/httpserver.py @@ -38,10 +38,9 @@ import os import logging import time -from PyQt4 import QtCore, QtGui +from PyQt4 import QtCore from openlp.core.common import AppLocation, Settings, RegistryProperties -from openlp.core.lib import build_icon from openlp.plugins.remotes.lib import HttpRouter @@ -72,7 +71,21 @@ class CustomHandler(BaseHTTPRequestHandler, HttpRouter): self.do_post_processor() -class ThreadingHTTPServer(ThreadingMixIn, HTTPServer): +class StoppableHttpServer(HTTPServer): + """ + Http server that reacts to self.stop flag + """ + + def serve_forever(self): + """ + Handle one request at a time until stopped. + """ + self.stop = False + while not self.stop: + self.handle_request() + + +class ThreadingHTTPServer(ThreadingMixIn, StoppableHttpServer): pass @@ -95,6 +108,10 @@ class HttpThread(QtCore.QThread): """ self.http_server.start_server() + def stop(self): + log.debug("stop called") + self.http_server.stop = True + class OpenLPServer(RegistryProperties): def __init__(self): @@ -112,25 +129,19 @@ class OpenLPServer(RegistryProperties): Start the correct server and save the handler """ address = Settings().value(self.settings_section + '/ip address') - if Settings().value(self.settings_section + '/https enabled'): + self.address = address + self.is_secure = Settings().value(self.settings_section + '/https enabled') + self.needs_authentication = Settings().value(self.settings_section + '/authentication enabled') + if self.is_secure: port = Settings().value(self.settings_section + '/https port') + self.port = port self.start_server_instance(address, port, HTTPSServer) else: port = Settings().value(self.settings_section + '/port') + self.port = port self.start_server_instance(address, port, ThreadingHTTPServer) if hasattr(self, 'httpd') and self.httpd: self.httpd.serve_forever() - icon = QtGui.QImage(':/remote/network_server.png') - icon = icon.scaled(80, 80, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation) - overlay = QtGui.QImage(':/remote/network_ssl.png') - overlay = overlay.scaled(40, 40, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation) - painter = QtGui.QPainter(icon) - painter.drawImage(0, 0, overlay) - painter.end() - print("Hi") - self.default_theme_label.setText("hello") - self.default_theme_label.setIcon(build_icon(icon)) - self.default_theme_label.show() else: log.debug('Failed to start server') @@ -149,7 +160,7 @@ class OpenLPServer(RegistryProperties): log.debug("Server started for class %s %s %d" % (server_class, address, port)) except OSError: log.debug("failed to start http server thread state %d %s" % - (loop, self.http_thread.isRunning() is True)) + (loop, self.http_thread.isRunning())) loop += 1 time.sleep(0.1) except: @@ -159,12 +170,13 @@ class OpenLPServer(RegistryProperties): """ Stop the server """ - self.http_thread.exit(0) + if self.http_thread.isRunning(): + self.http_thread.stop() self.httpd = None log.debug('Stopped the server.') -class HTTPSServer(HTTPServer): +class HTTPSServer(StoppableHttpServer): def __init__(self, address, handler): """ Initialise the secure handlers for the SSL server if required.s @@ -178,4 +190,4 @@ class HTTPSServer(HTTPServer): keyfile=os.path.join(local_data, 'remotes', 'openlp.key'), server_side=True) self.server_bind() - self.server_activate() + self.server_activate() \ No newline at end of file diff --git a/openlp/plugins/remotes/lib/remotetab.py b/openlp/plugins/remotes/lib/remotetab.py index d6b96cc1c..ba8766188 100644 --- a/openlp/plugins/remotes/lib/remotetab.py +++ b/openlp/plugins/remotes/lib/remotetab.py @@ -32,7 +32,7 @@ import os.path from PyQt4 import QtCore, QtGui, QtNetwork from openlp.core.common import AppLocation, Settings, translate -from openlp.core.lib import SettingsTab +from openlp.core.lib import SettingsTab, build_icon ZERO_URL = '0.0.0.0' @@ -234,6 +234,7 @@ class RemoteTab(SettingsTab): """ Load the configuration and update the server configuration if necessary """ + self.is_secure = Settings().value(self.settings_section + '/https enabled') self.port_spin_box.setValue(Settings().value(self.settings_section + '/port')) self.https_port_spin_box.setValue(Settings().value(self.settings_section + '/https port')) self.address_edit.setText(Settings().value(self.settings_section + '/ip address')) @@ -263,9 +264,7 @@ class RemoteTab(SettingsTab): Settings().value(self.settings_section + '/port') != self.port_spin_box.value() or \ Settings().value(self.settings_section + '/https port') != self.https_port_spin_box.value() or \ Settings().value(self.settings_section + '/https enabled') != \ - self.https_settings_group_box.isChecked() or \ - Settings().value(self.settings_section + '/authentication enabled') != \ - self.user_login_group_box.isChecked(): + self.https_settings_group_box.isChecked(): self.settings_form.register_post_process('remotes_config_updated') Settings().setValue(self.settings_section + '/port', self.port_spin_box.value()) Settings().setValue(self.settings_section + '/https port', self.https_port_spin_box.value()) @@ -275,6 +274,7 @@ class RemoteTab(SettingsTab): Settings().setValue(self.settings_section + '/authentication enabled', self.user_login_group_box.isChecked()) Settings().setValue(self.settings_section + '/user id', self.user_id.text()) Settings().setValue(self.settings_section + '/password', self.password.text()) + self.generate_icon() def on_twelve_hour_check_box_changed(self, check_state): """ @@ -290,3 +290,25 @@ class RemoteTab(SettingsTab): Invert the HTTP group box based on Https group settings """ self.http_settings_group_box.setEnabled(not self.https_settings_group_box.isChecked()) + + def generate_icon(self): + """ + Generate icon for main window + """ + self.remote_server_icon.hide() + icon = QtGui.QImage(':/remote/network_server.png') + icon = icon.scaled(80, 80, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation) + if self.is_secure: + overlay = QtGui.QImage(':/remote/network_ssl.png') + overlay = overlay.scaled(60, 60, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation) + painter = QtGui.QPainter(icon) + painter.drawImage(0, 0, overlay) + painter.end() + if Settings().value(self.settings_section + '/authentication enabled'): + overlay = QtGui.QImage(':/remote/network_auth.png') + overlay = overlay.scaled(60, 60, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation) + painter = QtGui.QPainter(icon) + painter.drawImage(20, 0, overlay) + painter.end() + self.remote_server_icon.setIcon(build_icon(icon)) + self.remote_server_icon.show() \ No newline at end of file diff --git a/openlp/plugins/remotes/remoteplugin.py b/openlp/plugins/remotes/remoteplugin.py index 069e3e5ec..2538991da 100644 --- a/openlp/plugins/remotes/remoteplugin.py +++ b/openlp/plugins/remotes/remoteplugin.py @@ -28,7 +28,6 @@ ############################################################################### import logging -import time from PyQt4 import QtGui @@ -69,13 +68,13 @@ class RemotesPlugin(Plugin): log.debug('initialise') super(RemotesPlugin, self).initialise() self.server = OpenLPServer() - self.default_theme_label = QtGui.QToolButton(self.main_window.status_bar) - self.default_theme_label.setCheckable(False) - self.default_theme_label.setAutoRaise(True) - self.default_theme_label.setObjectName('default_theme_label') - self.main_window.status_bar.insertPermanentWidget(2, self.default_theme_label) - self.default_theme_label.hide() - self.server.default_theme_label = self.default_theme_label + self.remote_server_icon = QtGui.QToolButton(self.main_window.status_bar) + self.remote_server_icon.setCheckable(False) + self.remote_server_icon.setAutoRaise(True) + self.remote_server_icon.setObjectName('remote_server_icon') + self.main_window.status_bar.insertPermanentWidget(2, self.remote_server_icon) + self.settings_tab.remote_server_icon = self.remote_server_icon + self.settings_tab.generate_icon() def finalise(self): """ @@ -113,9 +112,11 @@ class RemotesPlugin(Plugin): def config_update(self): """ - Called when Config is changed to restart the server on new address or port + Called when Config is changed to requests a restart with the server on new address or port """ log.debug('remote config changed') - self.finalise() - time.sleep(1) - self.initialise() + QtGui.QMessageBox.information(self.main_window, + translate('RemotePlugin', 'Server Config Change'), + translate('RemotePlugin', 'Server configuration changes will require a restart ' + 'to take effect.'), + QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok)) From e94643b5d17808f8463609baa1fa1cb049904b2c Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Fri, 18 Apr 2014 21:35:39 +0100 Subject: [PATCH 24/33] Fix invalid code --- openlp/plugins/remotes/lib/httpserver.py | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/openlp/plugins/remotes/lib/httpserver.py b/openlp/plugins/remotes/lib/httpserver.py index 7b3d667cd..9ed5c205a 100644 --- a/openlp/plugins/remotes/lib/httpserver.py +++ b/openlp/plugins/remotes/lib/httpserver.py @@ -71,21 +71,7 @@ class CustomHandler(BaseHTTPRequestHandler, HttpRouter): self.do_post_processor() -class StoppableHttpServer(HTTPServer): - """ - Http server that reacts to self.stop flag - """ - - def serve_forever(self): - """ - Handle one request at a time until stopped. - """ - self.stop = False - while not self.stop: - self.handle_request() - - -class ThreadingHTTPServer(ThreadingMixIn, StoppableHttpServer): +class ThreadingHTTPServer(ThreadingMixIn, HTTPServer): pass @@ -176,7 +162,7 @@ class OpenLPServer(RegistryProperties): log.debug('Stopped the server.') -class HTTPSServer(StoppableHttpServer): +class HTTPSServer(HTTPServer): def __init__(self, address, handler): """ Initialise the secure handlers for the SSL server if required.s From 5dc35112c33e81be6bd076596dac734f42d5772a Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sat, 19 Apr 2014 06:09:54 +0100 Subject: [PATCH 25/33] pep8 again --- openlp/core/ui/servicemanager.py | 3 ++- openlp/plugins/remotes/lib/httprouter.py | 1 + scripts/jenkins_script.py | 8 ++++---- scripts/translation_utils.py | 2 +- tests/functional/openlp_core_common/test_common.py | 4 ++-- tests/functional/openlp_core_lib/test_lib.py | 2 +- .../functional/openlp_core_ui/test_formattingtagsform.py | 4 ++-- tests/functional/openlp_plugins/bibles/test_http.py | 4 ++-- .../presentations/test_pptviewcontroller.py | 4 ++-- .../openlp_plugins/songs/test_foilpresenterimport.py | 2 +- 10 files changed, 18 insertions(+), 16 deletions(-) diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 9bc63eae6..1796ddc11 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -235,7 +235,8 @@ class Ui_ServiceManager(object): self.edit_action = create_widget_action(self.menu, text=translate('OpenLP.ServiceManager', '&Edit Item'), icon=':/general/general_edit.png', triggers=self.remote_edit) self.rename_action = create_widget_action(self.menu, text=translate('OpenLP.ServiceManager', '&Rename...'), - icon=':/general/general_edit.png', triggers=self.on_service_item_rename) + icon=':/general/general_edit.png', + triggers=self.on_service_item_rename) self.maintain_action = create_widget_action(self.menu, text=translate('OpenLP.ServiceManager', '&Reorder Item'), icon=':/general/general_edit.png', triggers=self.on_service_item_edit_form) diff --git a/openlp/plugins/remotes/lib/httprouter.py b/openlp/plugins/remotes/lib/httprouter.py index 125094a64..4241b34dc 100644 --- a/openlp/plugins/remotes/lib/httprouter.py +++ b/openlp/plugins/remotes/lib/httprouter.py @@ -452,6 +452,7 @@ class HttpRouter(RegistryProperties): """ Perform an action on the slide controller. """ + log.debug("controller_text var = %s" % var) current_item = self.live_controller.service_item data = [] if current_item: diff --git a/scripts/jenkins_script.py b/scripts/jenkins_script.py index 386ab69ef..cf438964d 100644 --- a/scripts/jenkins_script.py +++ b/scripts/jenkins_script.py @@ -115,14 +115,14 @@ class JenkinsTrigger(object): url = build.info['url'] print('[%s] %s' % (result_string, url)) # On failure open the browser. - #if result_string == "FAILURE": + # if result_string == "FAILURE": # url += 'console' # Popen(('xdg-open', url), stderr=PIPE) def get_repo_name(): """ - This returns the name of branch of the wokring directory. For example it returns *lp:~googol/openlp/render*. + This returns the name of branch of the working directory. For example it returns *lp:~googol/openlp/render*. """ # Run the bzr command. bzr = Popen(('bzr', 'info'), stdout=PIPE, stderr=PIPE) @@ -166,7 +166,7 @@ def main(): help='Disable output.') parser.add_option('-b', '--open-browser', dest='open_browser', action="store_true", default=False, help='Opens the jenkins page in your browser.') - #parser.add_option('-e', '--open-browser-on-error', dest='open_browser_on_error', action="store_true", + # parser.add_option('-e', '--open-browser-on-error', dest='open_browser_on_error', action="store_true", # default=False, help='Opens the jenkins page in your browser in case a test fails.') options, args = parser.parse_args(sys.argv) @@ -177,7 +177,7 @@ def main(): jenkins_trigger = JenkinsTrigger(token) try: jenkins_trigger.trigger_build() - except HTTPError as e: + except HTTPError: print("Wrong token.") return # Open the browser before printing the output. diff --git a/scripts/translation_utils.py b/scripts/translation_utils.py index 5aa320806..ad3edcaa3 100755 --- a/scripts/translation_utils.py +++ b/scripts/translation_utils.py @@ -96,7 +96,7 @@ class CommandStack(object): return len(self.data) def __getitem__(self, index): - if not index in self.data: + if index not in self.data: return None elif self.data[index].get('arguments'): return self.data[index]['command'], self.data[index]['arguments'] diff --git a/tests/functional/openlp_core_common/test_common.py b/tests/functional/openlp_core_common/test_common.py index 90b7d0520..ab2d11b3a 100644 --- a/tests/functional/openlp_core_common/test_common.py +++ b/tests/functional/openlp_core_common/test_common.py @@ -79,5 +79,5 @@ class TestCommonFunctions(TestCase): trace_error_handler(mocked_logger) # THEN: The mocked_logger.error() method should have been called with the correct parameters - mocked_logger.error.assert_called_with('OpenLP Error trace\n File openlp.fake at line 56 \n\t called trace_error_handler_test') - + mocked_logger.error.assert_called_with( + 'OpenLP Error trace\n File openlp.fake at line 56 \n\t called trace_error_handler_test') diff --git a/tests/functional/openlp_core_lib/test_lib.py b/tests/functional/openlp_core_lib/test_lib.py index bb3a17ebb..b4334a728 100644 --- a/tests/functional/openlp_core_lib/test_lib.py +++ b/tests/functional/openlp_core_lib/test_lib.py @@ -482,7 +482,7 @@ class TestLib(TestCase): # WHEN: we run the validate_thumb() function # THEN: we should have called a few functions, and the result should be True - #mocked_os.path.exists.assert_called_with(thumb_path) + # mocked_os.path.exists.assert_called_with(thumb_path) def validate_thumb_file_exists_and_older_test(self): """ diff --git a/tests/functional/openlp_core_ui/test_formattingtagsform.py b/tests/functional/openlp_core_ui/test_formattingtagsform.py index 05b5fed74..e71a75651 100644 --- a/tests/functional/openlp_core_ui/test_formattingtagsform.py +++ b/tests/functional/openlp_core_ui/test_formattingtagsform.py @@ -70,7 +70,7 @@ class TestFormattingTagForm(TestCase): form.save_button = MagicMock() # WHEN: on_text_edited is called with an arbitrary value - #form.on_text_edited('text') + # form.on_text_edited('text') # THEN: setEnabled and setDefault should have been called on save_push_button - #form.save_button.setEnabled.assert_called_with(True) + # form.save_button.setEnabled.assert_called_with(True) diff --git a/tests/functional/openlp_plugins/bibles/test_http.py b/tests/functional/openlp_plugins/bibles/test_http.py index b9bb8f11c..92af51619 100644 --- a/tests/functional/openlp_plugins/bibles/test_http.py +++ b/tests/functional/openlp_plugins/bibles/test_http.py @@ -35,7 +35,7 @@ from bs4 import BeautifulSoup from tests.functional import patch, MagicMock from openlp.plugins.bibles.lib.http import BSExtract -#TODO: Items left to test +# TODO: Items left to test # BGExtract # __init__ # _remove_elements @@ -68,7 +68,7 @@ class TestBSExtract(TestCase): """ Test the BSExtractClass """ - #TODO: Items left to test + # TODO: Items left to test # BSExtract # __init__ # get_bible_chapter diff --git a/tests/functional/openlp_plugins/presentations/test_pptviewcontroller.py b/tests/functional/openlp_plugins/presentations/test_pptviewcontroller.py index 8a8897cec..c3d0912c0 100644 --- a/tests/functional/openlp_plugins/presentations/test_pptviewcontroller.py +++ b/tests/functional/openlp_plugins/presentations/test_pptviewcontroller.py @@ -47,7 +47,7 @@ class TestPptviewController(TestCase, TestMixin): """ Test the PptviewController Class """ -#TODO: Items left to test +# TODO: Items left to test # PptviewController # start_process(self) # kill @@ -108,7 +108,7 @@ class TestPptviewDocument(TestCase): """ Test the PptviewDocument Class """ - #TODO: Items left to test + # TODO: Items left to test # PptviewDocument # __init__ # create_thumbnails diff --git a/tests/functional/openlp_plugins/songs/test_foilpresenterimport.py b/tests/functional/openlp_plugins/songs/test_foilpresenterimport.py index fbd339cf3..61206b9fa 100644 --- a/tests/functional/openlp_plugins/songs/test_foilpresenterimport.py +++ b/tests/functional/openlp_plugins/songs/test_foilpresenterimport.py @@ -44,7 +44,7 @@ class TestFoilPresenter(TestCase): """ Test the functions in the :mod:`foilpresenterimport` module. """ - #TODO: The following modules still need tests written for + # TODO: The following modules still need tests written for # xml_to_song # _child # _process_authors From af792941af9e6c57aec3964d95fb311672a8967f Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sat, 19 Apr 2014 06:13:46 +0100 Subject: [PATCH 26/33] pep8 again --- tests/functional/openlp_core_lib/test_file_dialog.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/functional/openlp_core_lib/test_file_dialog.py b/tests/functional/openlp_core_lib/test_file_dialog.py index b2c2178a9..ab7663a83 100644 --- a/tests/functional/openlp_core_lib/test_file_dialog.py +++ b/tests/functional/openlp_core_lib/test_file_dialog.py @@ -53,8 +53,8 @@ class TestFileDialog(TestCase): self.mocked_os.rest() self.mocked_qt_gui.reset() - # GIVEN: A List of known values as a return value from QFileDialog.getOpenFileNames and a list of valid - # file names. + # GIVEN: A List of known values as a return value from QFileDialog.getOpenFileNames and a list of valid file + # names. self.mocked_qt_gui.QFileDialog.getOpenFileNames.return_value = [ '/Valid File', '/url%20encoded%20file%20%231', '/non-existing'] self.mocked_os.path.exists.side_effect = lambda file_name: file_name in [ From 905a6267051e10fe8009158cce35a736d3d58613 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sat, 19 Apr 2014 06:26:49 +0100 Subject: [PATCH 27/33] fixes --- openlp/plugins/remotes/lib/httpserver.py | 2 +- openlp/plugins/remotes/lib/remotetab.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openlp/plugins/remotes/lib/httpserver.py b/openlp/plugins/remotes/lib/httpserver.py index 9ed5c205a..9a904090d 100644 --- a/openlp/plugins/remotes/lib/httpserver.py +++ b/openlp/plugins/remotes/lib/httpserver.py @@ -176,4 +176,4 @@ class HTTPSServer(HTTPServer): keyfile=os.path.join(local_data, 'remotes', 'openlp.key'), server_side=True) self.server_bind() - self.server_activate() \ No newline at end of file + self.server_activate() diff --git a/openlp/plugins/remotes/lib/remotetab.py b/openlp/plugins/remotes/lib/remotetab.py index ba8766188..42d46c581 100644 --- a/openlp/plugins/remotes/lib/remotetab.py +++ b/openlp/plugins/remotes/lib/remotetab.py @@ -311,4 +311,4 @@ class RemoteTab(SettingsTab): painter.drawImage(20, 0, overlay) painter.end() self.remote_server_icon.setIcon(build_icon(icon)) - self.remote_server_icon.show() \ No newline at end of file + self.remote_server_icon.show() From a2c99317c6a262e0474b527dfa351330baece378 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sat, 19 Apr 2014 06:42:45 +0100 Subject: [PATCH 28/33] fixes --- openlp/plugins/songs/lib/songbeamerimport.py | 2 +- tests/functional/openlp_core_lib/test_ui.py | 2 +- tests/functional/openlp_core_ui/test_maindisplay.py | 2 +- tests/functional/openlp_plugins/songs/test_ewimport.py | 10 +++++----- tests/functional/openlp_plugins/songs/test_lib.py | 1 - .../openlp_plugins/songs/test_songbeamerimport.py | 6 +++--- .../openlp_plugins/songs/test_songshowplusimport.py | 10 +++++----- tests/helpers/songfileimport.py | 10 +++++----- .../openlp_plugins/bibles/test_lib_parse_reference.py | 2 +- 9 files changed, 22 insertions(+), 23 deletions(-) diff --git a/openlp/plugins/songs/lib/songbeamerimport.py b/openlp/plugins/songs/lib/songbeamerimport.py index 5b86591e8..a0b166ded 100644 --- a/openlp/plugins/songs/lib/songbeamerimport.py +++ b/openlp/plugins/songs/lib/songbeamerimport.py @@ -137,7 +137,7 @@ class SongBeamerImport(SongImport): if line.startswith('#') and not read_verses: self.parseTags(line) elif line.startswith('--'): - # --- and -- allowed for page-breaks (difference in Songbeamer only in printout) + # --- and -- allowed for page-breaks (difference in Songbeamer only in printout) if self.current_verse: self.replace_html_tags() self.add_verse(self.current_verse, self.current_verse_type) diff --git a/tests/functional/openlp_core_lib/test_ui.py b/tests/functional/openlp_core_lib/test_ui.py index 91d59ab5a..025b1a638 100644 --- a/tests/functional/openlp_core_lib/test_ui.py +++ b/tests/functional/openlp_core_lib/test_ui.py @@ -162,7 +162,7 @@ class TestUi(TestCase): # WHEN: We create an action with some properties action = create_action(dialog, 'my_action', text='my text', icon=':/wizards/wizard_firsttime.bmp', - tooltip='my tooltip', statustip='my statustip') + tooltip='my tooltip', statustip='my statustip') # THEN: These properties should be set self.assertIsInstance(action, QtGui.QAction) diff --git a/tests/functional/openlp_core_ui/test_maindisplay.py b/tests/functional/openlp_core_ui/test_maindisplay.py index b1a4dc7f7..6d67a3b67 100644 --- a/tests/functional/openlp_core_ui/test_maindisplay.py +++ b/tests/functional/openlp_core_ui/test_maindisplay.py @@ -106,4 +106,4 @@ class TestMainDisplay(TestCase): self.assertEqual('QGraphicsView {}', main_display.styleSheet(), 'MainDisplay instance should not be transparent') self.assertFalse(main_display.testAttribute(QtCore.Qt.WA_TranslucentBackground), - 'MainDisplay hasnt translucent background') + 'MainDisplay hasnt translucent background') diff --git a/tests/functional/openlp_plugins/songs/test_ewimport.py b/tests/functional/openlp_plugins/songs/test_ewimport.py index c1b9db52d..981bc677d 100644 --- a/tests/functional/openlp_plugins/songs/test_ewimport.py +++ b/tests/functional/openlp_plugins/songs/test_ewimport.py @@ -141,7 +141,7 @@ class TestEasyWorshipSongImport(TestCase): self.assertIsNotNone(field_desc_entry, 'Import should not be none') self.assertEqual(field_desc_entry.name, name, 'FieldDescEntry.name should be the same as the name argument') self.assertEqual(field_desc_entry.field_type, field_type, - 'FieldDescEntry.type should be the same as the type argument') + 'FieldDescEntry.type should be the same as the type argument') self.assertEqual(field_desc_entry.size, size, 'FieldDescEntry.size should be the same as the size argument') def create_importer_test(self): @@ -231,8 +231,8 @@ class TestEasyWorshipSongImport(TestCase): # THEN: get_field should return the known results self.assertEqual(return_value, result, - 'get_field should return "%s" when called with "%s"' % - (result, TEST_FIELDS[field_index])) + 'get_field should return "%s" when called with "%s"' % + (result, TEST_FIELDS[field_index])) def get_memo_field_test(self): """ @@ -404,10 +404,10 @@ class TestEasyWorshipSongImport(TestCase): self.assertEqual(importer.copyright, song_copyright) if ccli_number: self.assertEqual(importer.ccli_number, ccli_number, 'ccli_number for %s should be %s' - % (title, ccli_number)) + % (title, ccli_number)) for verse_text, verse_tag in add_verse_calls: mocked_add_verse.assert_any_call(verse_text, verse_tag) if verse_order_list: self.assertEqual(importer.verse_order_list, verse_order_list, - 'verse_order_list for %s should be %s' % (title, verse_order_list)) + 'verse_order_list for %s should be %s' % (title, verse_order_list)) mocked_finish.assert_called_with() diff --git a/tests/functional/openlp_plugins/songs/test_lib.py b/tests/functional/openlp_plugins/songs/test_lib.py index 2ab808bc9..b67c1a4be 100644 --- a/tests/functional/openlp_plugins/songs/test_lib.py +++ b/tests/functional/openlp_plugins/songs/test_lib.py @@ -129,7 +129,6 @@ class TestLib(TestCase): # THEN: The result should be a tuple of songs.. assert result == (self.song1, self.song2), 'The result should be the tuble of songs' - def songs_probably_equal_different_song_test(self): """ Test the songs_probably_equal function with two different songs. diff --git a/tests/functional/openlp_plugins/songs/test_songbeamerimport.py b/tests/functional/openlp_plugins/songs/test_songbeamerimport.py index f08cedec5..284f972ef 100644 --- a/tests/functional/openlp_plugins/songs/test_songbeamerimport.py +++ b/tests/functional/openlp_plugins/songs/test_songbeamerimport.py @@ -91,7 +91,7 @@ class TestSongBeamerImport(TestCase): # THEN: do_import should return none and the progress bar maximum should not be set. self.assertIsNone(importer.do_import(), 'do_import should return None when import_source is not a list') self.assertEqual(mocked_import_wizard.progress_bar.setMaximum.called, False, - 'setMaxium on import_wizard.progress_bar should not have been called') + 'setMaxium on import_wizard.progress_bar should not have been called') def valid_import_source_test(self): """ @@ -149,10 +149,10 @@ class TestSongBeamerImport(TestCase): mocked_add_verse.assert_any_call(verse_text, verse_tag) if song_book_name: self.assertEqual(importer.song_book_name, song_book_name, 'song_book_name for %s should be "%s"' % - (song_file, song_book_name)) + (song_file, song_book_name)) if song_number: self.assertEqual(importer.song_number, song_number, 'song_number for %s should be %s' % - (song_file, song_number)) + (song_file, song_number)) mocked_finish.assert_called_with() def check_verse_marks_test(self): diff --git a/tests/functional/openlp_plugins/songs/test_songshowplusimport.py b/tests/functional/openlp_plugins/songs/test_songshowplusimport.py index f2839c332..63e5beb8a 100644 --- a/tests/functional/openlp_plugins/songs/test_songshowplusimport.py +++ b/tests/functional/openlp_plugins/songs/test_songshowplusimport.py @@ -96,7 +96,7 @@ class TestSongShowPlusImport(TestCase): # THEN: do_import should return none and the progress bar maximum should not be set. self.assertIsNone(importer.do_import(), 'do_import should return None when import_source is not a list') self.assertEqual(mocked_import_wizard.progress_bar.setMaximum.called, False, - 'setMaximum on import_wizard.progress_bar should not have been called') + 'setMaximum on import_wizard.progress_bar should not have been called') def valid_import_source_test(self): """ @@ -144,8 +144,8 @@ class TestSongShowPlusImport(TestCase): # THEN: The returned value should should correlate with the input arguments for original_tag, openlp_tag in test_values: self.assertEqual(importer.to_openlp_verse_tag(original_tag), openlp_tag, - 'SongShowPlusImport.to_openlp_verse_tag should return "%s" when called with "%s"' % - (openlp_tag, original_tag)) + 'SongShowPlusImport.to_openlp_verse_tag should return "%s" when called with "%s"' % + (openlp_tag, original_tag)) def to_openlp_verse_tag_verse_order_test(self): """ @@ -173,5 +173,5 @@ class TestSongShowPlusImport(TestCase): # THEN: The returned value should should correlate with the input arguments for original_tag, openlp_tag in test_values: self.assertEqual(importer.to_openlp_verse_tag(original_tag, ignore_unique=True), openlp_tag, - 'SongShowPlusImport.to_openlp_verse_tag should return "%s" when called with "%s"' % - (openlp_tag, original_tag)) + 'SongShowPlusImport.to_openlp_verse_tag should return "%s" when called with "%s"' % + (openlp_tag, original_tag)) diff --git a/tests/helpers/songfileimport.py b/tests/helpers/songfileimport.py index 49a09528c..16935ffca 100644 --- a/tests/helpers/songfileimport.py +++ b/tests/helpers/songfileimport.py @@ -117,23 +117,23 @@ class SongImportTestHelper(TestCase): self.mocked_add_copyright.assert_called_with(song_copyright) if ccli_number: self.assertEqual(importer.ccli_number, ccli_number, 'ccli_number for %s should be %s' % - (source_file_name, ccli_number)) + (source_file_name, ccli_number)) for verse_text, verse_tag in add_verse_calls: self.mocked_add_verse.assert_any_call(verse_text, verse_tag) if topics: self.assertEqual(importer.topics, topics, 'topics for %s should be %s' % (source_file_name, topics)) if comments: self.assertEqual(importer.comments, comments, 'comments for %s should be "%s"' % - (source_file_name, comments)) + (source_file_name, comments)) if song_book_name: self.assertEqual(importer.song_book_name, song_book_name, 'song_book_name for %s should be "%s"' % - (source_file_name, song_book_name)) + (source_file_name, song_book_name)) if song_number: self.assertEqual(importer.song_number, song_number, 'song_number for %s should be %s' % - (source_file_name, song_number)) + (source_file_name, song_number)) if verse_order_list: self.assertEqual(importer.verse_order_list, [], 'verse_order_list for %s should be %s' % - (source_file_name, verse_order_list)) + (source_file_name, verse_order_list)) self.mocked_finish.assert_called_with() def _get_data(self, data, key): diff --git a/tests/interfaces/openlp_plugins/bibles/test_lib_parse_reference.py b/tests/interfaces/openlp_plugins/bibles/test_lib_parse_reference.py index 84f80e7ed..6883f9eb5 100644 --- a/tests/interfaces/openlp_plugins/bibles/test_lib_parse_reference.py +++ b/tests/interfaces/openlp_plugins/bibles/test_lib_parse_reference.py @@ -104,4 +104,4 @@ class TestBibleManager(TestCase, TestMixin): results = parse_reference('1 Timothy 1:1-2:1', self.manager.db_cache['tests'], MagicMock(), 54) # THEN a verse array should be returned self.assertEqual([(54, 1, 1, -1), (54, 2, 1, 1)], results, "The bible verses should matches the expected " - "results") + "results") From ce55fd1d57b61be6e36aac7e8374a0b141ee5f12 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sun, 20 Apr 2014 21:16:08 +0100 Subject: [PATCH 29/33] fixes --- openlp/core/ui/pluginform.py | 1 - openlp/plugins/remotes/lib/remotetab.py | 2 +- openlp/plugins/remotes/remoteplugin.py | 20 ++++++++++++++------ 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/openlp/core/ui/pluginform.py b/openlp/core/ui/pluginform.py index 91b98b97a..78bdee4a5 100644 --- a/openlp/core/ui/pluginform.py +++ b/openlp/core/ui/pluginform.py @@ -30,7 +30,6 @@ The actual plugin view form """ import logging -import os from PyQt4 import QtGui diff --git a/openlp/plugins/remotes/lib/remotetab.py b/openlp/plugins/remotes/lib/remotetab.py index 42d46c581..4db25cfc2 100644 --- a/openlp/plugins/remotes/lib/remotetab.py +++ b/openlp/plugins/remotes/lib/remotetab.py @@ -310,5 +310,5 @@ class RemoteTab(SettingsTab): painter = QtGui.QPainter(icon) painter.drawImage(20, 0, overlay) painter.end() - self.remote_server_icon.setIcon(build_icon(icon)) + self.remote_server_icon.setPixmap(QtGui.QPixmap.fromImage(icon)) self.remote_server_icon.show() diff --git a/openlp/plugins/remotes/remoteplugin.py b/openlp/plugins/remotes/remoteplugin.py index 2538991da..582192df4 100644 --- a/openlp/plugins/remotes/remoteplugin.py +++ b/openlp/plugins/remotes/remoteplugin.py @@ -68,12 +68,20 @@ class RemotesPlugin(Plugin): log.debug('initialise') super(RemotesPlugin, self).initialise() self.server = OpenLPServer() - self.remote_server_icon = QtGui.QToolButton(self.main_window.status_bar) - self.remote_server_icon.setCheckable(False) - self.remote_server_icon.setAutoRaise(True) - self.remote_server_icon.setObjectName('remote_server_icon') - self.main_window.status_bar.insertPermanentWidget(2, self.remote_server_icon) - self.settings_tab.remote_server_icon = self.remote_server_icon + if not hasattr(self, 'remote_server_icon'): + self.remote_server_icon = QtGui.QLabel(self.main_window.status_bar) + size_policy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) + size_policy.setHorizontalStretch(0) + size_policy.setVerticalStretch(0) + size_policy.setHeightForWidth(self.remote_server_icon.sizePolicy().hasHeightForWidth()) + self.remote_server_icon.setSizePolicy(size_policy) + self.remote_server_icon.setFrameShadow(QtGui.QFrame.Plain) + self.remote_server_icon.setLineWidth(1) + self.remote_server_icon.setScaledContents(True) + self.remote_server_icon.setFixedSize(20, 20) + self.remote_server_icon.setObjectName('remote_server_icon') + self.main_window.status_bar.insertPermanentWidget(2, self.remote_server_icon) + self.settings_tab.remote_server_icon = self.remote_server_icon self.settings_tab.generate_icon() def finalise(self): From ac44396a405b57d7650b135ebe21d27b51995852 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Mon, 21 Apr 2014 11:58:09 +0200 Subject: [PATCH 30/33] 2 row layout for author add widgets --- openlp/plugins/songs/forms/editsongdialog.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/openlp/plugins/songs/forms/editsongdialog.py b/openlp/plugins/songs/forms/editsongdialog.py index 7a491032b..7935b892a 100644 --- a/openlp/plugins/songs/forms/editsongdialog.py +++ b/openlp/plugins/songs/forms/editsongdialog.py @@ -118,18 +118,21 @@ class Ui_EditSongDialog(object): self.authors_group_box.setObjectName('authors_group_box') self.authors_layout = QtGui.QVBoxLayout(self.authors_group_box) 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_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.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) # Need to give these boxes some min width, else they are too small self.authors_combo_box.setMinimumWidth(150) self.author_types_combo_box.setMinimumWidth(80) - self.author_add_layout.addWidget(self.author_types_combo_box) + self.author_type_layout.addWidget(self.author_types_combo_box) self.author_add_button = QtGui.QPushButton(self.authors_group_box) 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_list_view = QtGui.QListWidget(self.authors_group_box) self.authors_list_view.setAlternatingRowColors(True) From 70a5795cd249f2621e1c3b67b91ec0a8f9ce2ede Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Mon, 21 Apr 2014 11:58:53 +0200 Subject: [PATCH 31/33] Remove min width --- openlp/plugins/songs/forms/editsongdialog.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/openlp/plugins/songs/forms/editsongdialog.py b/openlp/plugins/songs/forms/editsongdialog.py index 7935b892a..d0fb51a2d 100644 --- a/openlp/plugins/songs/forms/editsongdialog.py +++ b/openlp/plugins/songs/forms/editsongdialog.py @@ -125,9 +125,6 @@ class Ui_EditSongDialog(object): 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_types_combo_box = create_combo_box(self.authors_group_box, 'author_types_combo_box', editable=False) - # Need to give these boxes some min width, else they are too small - self.authors_combo_box.setMinimumWidth(150) - self.author_types_combo_box.setMinimumWidth(80) self.author_type_layout.addWidget(self.author_types_combo_box) self.author_add_button = QtGui.QPushButton(self.authors_group_box) self.author_add_button.setObjectName('author_add_button') From cc0af8845ec8bc80e73d974706e6fb41477fabfb Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Mon, 21 Apr 2014 12:06:17 +0200 Subject: [PATCH 32/33] PEP8 --- openlp/plugins/songs/lib/upgrade.py | 3 ++- tests/functional/openlp_plugins/songs/test_mediaitem.py | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/openlp/plugins/songs/lib/upgrade.py b/openlp/plugins/songs/lib/upgrade.py index 59355bbec..580ae767d 100644 --- a/openlp/plugins/songs/lib/upgrade.py +++ b/openlp/plugins/songs/lib/upgrade.py @@ -112,7 +112,8 @@ def upgrade_4(session, metadata): 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('""'))) + 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') diff --git a/tests/functional/openlp_plugins/songs/test_mediaitem.py b/tests/functional/openlp_plugins/songs/test_mediaitem.py index 1b372aa20..308881c2e 100644 --- a/tests/functional/openlp_plugins/songs/test_mediaitem.py +++ b/tests/functional/openlp_plugins/songs/test_mediaitem.py @@ -76,19 +76,19 @@ class TestMediaItem(TestCase, TestMixin): mock_author.display_name = 'my author' mock_author_song = MagicMock() mock_author_song.author = mock_author - mock_author_song.author_type=AuthorType.Music + mock_author_song.author_type = AuthorType.Music mock_song.authors_songs.append(mock_author_song) mock_author = MagicMock() mock_author.display_name = 'another author' mock_author_song = MagicMock() mock_author_song.author = mock_author - mock_author_song.author_type=AuthorType.Words + 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_author_song.author_type = AuthorType.Translation mock_song.authors_songs.append(mock_author_song) mock_song.copyright = 'My copyright' service_item = ServiceItem(None) From 6eccf3eadf3d0048319dcf70f2fd407a27ee383c Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Mon, 21 Apr 2014 16:40:54 +0200 Subject: [PATCH 33/33] change display of list view --- openlp/plugins/songs/lib/db.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/plugins/songs/lib/db.py b/openlp/plugins/songs/lib/db.py index fa53bccc8..91649c951 100644 --- a/openlp/plugins/songs/lib/db.py +++ b/openlp/plugins/songs/lib/db.py @@ -48,7 +48,7 @@ class Author(BaseModel): """ def get_display_name(self, author_type=None): if author_type: - return "%s: %s" % (AuthorType.Types[author_type], self.display_name) + return "%s (%s)" % (self.display_name, AuthorType.Types[author_type]) return self.display_name