From 0ee684d3a87cb63d92cbce15f02f7b9177cfaf81 Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Wed, 26 Mar 2014 20:49:12 +0100 Subject: [PATCH 01/82] Added support for ews import --- openlp/plugins/songs/lib/ewimport.py | 233 +++++++++++++++++++++------ openlp/plugins/songs/lib/importer.py | 5 +- 2 files changed, 185 insertions(+), 53 deletions(-) diff --git a/openlp/plugins/songs/lib/ewimport.py b/openlp/plugins/songs/lib/ewimport.py index 9e3ae566e..9117aaadc 100644 --- a/openlp/plugins/songs/lib/ewimport.py +++ b/openlp/plugins/songs/lib/ewimport.py @@ -34,13 +34,13 @@ EasyWorship song databases into the current installation database. import os import struct import re +import zlib from openlp.core.lib import translate from openlp.plugins.songs.lib import VerseType from openlp.plugins.songs.lib import retrieve_windows_encoding, strip_rtf from .songimport import SongImport -RTF_STRIPPING_REGEX = re.compile(r'\{\\tx[^}]*\}') # regex: at least two newlines, can have spaces between them SLIDE_BREAK_REGEX = re.compile(r'\n *?\n[\n ]*') NUMBER_REGEX = re.compile(r'[0-9]+') @@ -77,7 +77,91 @@ class EasyWorshipSongImport(SongImport): def do_import(self): """ - Import the songs + Determines the type of file to import and calls the appropiate method + + :return: + """ + if self.import_source.lower().endswith('ews'): + self.import_ews() + else: + self.import_db() + + def import_ews(self): + """ + Import the songs from service file + The full spec of the ews files can be found here: + https://github.com/meinders/lithium-ews/blob/master/docs/ews%20file%20format.md + + :return: + """ + # Open ews file if it exists + if not os.path.isfile(self.import_source): + return + # Make sure there is room for at least a header and one entry + if os.path.getsize(self.import_source) < 892: + return + # Take a stab at how text is encoded + self.encoding = 'cp1252' + self.ews_file = open(self.import_source, 'rb') + # Get file version + type, = struct.unpack('<38s', self.ews_file.read(38)) + version = type.decode()[-3:] + # Set fileposition based on filetype/version + file_pos = 0 + if version == ' 5': + file_pos = 56 + elif version == ' 3': + file_pos = 48 + elif version == '1.6': + file_pos = 40 + else: + return + entry_count = self.get_i32(file_pos) + entry_length = self.get_i16(file_pos+4) + file_pos += 6 + self.import_wizard.progress_bar.setMaximum(entry_count) + # Loop over songs + for x in range(1, entry_count): + # Load entry metadata + self.set_defaults() + self.title = self.get_string(file_pos, 50) + resource = self.get_string(file_pos + 51, 255) + authors = self.get_string(file_pos + 307, 50) + copyright = self.get_string(file_pos + 358, 100) + admin = self.get_string(file_pos + 459, 50) + cont_ptr = self.get_i32(file_pos + 800) + cont_type = self.get_i32(file_pos + 820) + notes = self.get_string(file_pos + 1155, 160) + self.ccli_number = self.get_string(file_pos + 1410, 10) + # Only handle content type 1 (songs) + if cont_type != 1: + file_pos += entry_length + continue + # Load song content + content_length = self.get_i32(cont_ptr) + deflated_content = self.get_bytes(cont_ptr + 4, content_length - 10) + deflated_length = self.get_i32(cont_ptr + 4 + content_length - 6) + inflated_content = zlib.decompress(deflated_content, 15, deflated_length) + if copyright: + self.copyright = copyright + if admin: + if copyright: + self.copyright += ', ' + self.copyright += translate('SongsPlugin.EasyWorshipSongImport', + 'Administered by %s') % admin + # Set the SongImport object members. + self.set_song_import_object(authors, inflated_content) + if self.stop_import_flag: + break + if not self.finish(): + self.log_error(self.import_source) + # Set file_pos for next entry + file_pos += entry_length + self.ews_file.close() + + def import_db(self): + """ + Import the songs from the database :return: """ @@ -176,7 +260,6 @@ class EasyWorshipSongImport(SongImport): ccli = self.get_field(fi_ccli) authors = self.get_field(fi_author) words = self.get_field(fi_words) - # Set the SongImport object members. if copy: self.copyright = copy.decode() if admin: @@ -187,55 +270,11 @@ class EasyWorshipSongImport(SongImport): if ccli: self.ccli_number = ccli.decode() if authors: - # Split up the authors - author_list = authors.split(b'/') - if len(author_list) < 2: - author_list = authors.split(b';') - if len(author_list) < 2: - author_list = authors.split(b',') - for author_name in author_list: - self.add_author(author_name.decode().strip()) - if words: - # Format the lyrics - result = strip_rtf(words.decode(), self.encoding) - if result is None: - return - words, self.encoding = result - verse_type = VerseType.tags[VerseType.Verse] - for verse in SLIDE_BREAK_REGEX.split(words): - verse = verse.strip() - if not verse: - continue - verse_split = verse.split('\n', 1) - first_line_is_tag = False - # EW tags: verse, chorus, pre-chorus, bridge, tag, - # intro, ending, slide - for tag in VerseType.tags + ['tag', 'slide']: - tag = tag.lower() - ew_tag = verse_split[0].strip().lower() - if ew_tag.startswith(tag): - verse_type = tag[0] - if tag == 'tag' or tag == 'slide': - verse_type = VerseType.tags[VerseType.Other] - first_line_is_tag = True - number_found = False - # check if tag is followed by number and/or note - if len(ew_tag) > len(tag): - match = NUMBER_REGEX.search(ew_tag) - if match: - number = match.group() - verse_type += number - number_found = True - match = NOTE_REGEX.search(ew_tag) - if match: - self.comments += ew_tag + '\n' - if not number_found: - verse_type += '1' - break - self.add_verse(verse_split[-1].strip() if first_line_is_tag else verse, verse_type) - if len(self.comments) > 5: - self.comments += str(translate('SongsPlugin.EasyWorshipSongImport', - '\n[above are Song Tags with notes imported from EasyWorship]')) + authors = authors.decode() + else: + authors = '' + # Set the SongImport object members. + self.set_song_import_object(authors, words) if self.stop_import_flag: break if not self.finish(): @@ -243,6 +282,61 @@ class EasyWorshipSongImport(SongImport): db_file.close() self.memo_file.close() + def set_song_import_object(self, authors, words): + """ + Set the SongImport object members. + """ + if authors: + # Split up the authors + author_list = authors.split('/') + if len(author_list) < 2: + author_list = authors.split(';') + if len(author_list) < 2: + author_list = authors.split(',') + for author_name in author_list: + self.add_author(author_name.strip()) + if words: + # Format the lyrics + result = strip_rtf(words.decode(), self.encoding) + if result is None: + return + words, self.encoding = result + verse_type = VerseType.tags[VerseType.Verse] + for verse in SLIDE_BREAK_REGEX.split(words): + verse = verse.strip() + if not verse: + continue + verse_split = verse.split('\n', 1) + first_line_is_tag = False + # EW tags: verse, chorus, pre-chorus, bridge, tag, + # intro, ending, slide + for tag in VerseType.tags + ['tag', 'slide']: + tag = tag.lower() + ew_tag = verse_split[0].strip().lower() + if ew_tag.startswith(tag): + verse_type = tag[0] + if tag == 'tag' or tag == 'slide': + verse_type = VerseType.tags[VerseType.Other] + first_line_is_tag = True + number_found = False + # check if tag is followed by number and/or note + if len(ew_tag) > len(tag): + match = NUMBER_REGEX.search(ew_tag) + if match: + number = match.group() + verse_type += number + number_found = True + match = NOTE_REGEX.search(ew_tag) + if match: + self.comments += ew_tag + '\n' + if not number_found: + verse_type += '1' + break + self.add_verse(verse_split[-1].strip() if first_line_is_tag else verse, verse_type) + if len(self.comments) > 5: + self.comments += str(translate('SongsPlugin.EasyWorshipSongImport', + '\n[above are Song Tags with notes imported from EasyWorship]')) + def find_field(self, field_name): """ Find a field in the descriptions @@ -323,3 +417,38 @@ class EasyWorshipSongImport(SongImport): return self.memo_file.read(blob_size) else: return 0 + + def get_bytes(self, pos, length): + """ + Get bytes from ews_file + """ + self.ews_file.seek(pos) + return self.ews_file.read(length) + + def get_string(self, pos, length): + """ + Get string from ews_file + """ + bytes = self.get_bytes(pos, length) + mask = '<' + str(length) + 's' + byte_str, = struct.unpack(mask, bytes) + return byte_str.decode('unicode-escape').replace('\0', '').strip() + + def get_i16(self, pos): + """ + Get short int from ews_file + """ + + bytes = self.get_bytes(pos, 2) + mask = ' Date: Fri, 28 Mar 2014 17:06:16 +0100 Subject: [PATCH 02/82] 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 03/82] 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 04/82] 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 05/82] 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 06/82] 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 07/82] 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 08/82] 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 09/82] 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 10/82] 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 11/82] 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 12/82] 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 13/82] 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 14/82] 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 15/82] 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 5c405050f1c9a0af8dd427862a8efabff66a243e Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Fri, 4 Apr 2014 19:57:03 +0200 Subject: [PATCH 16/82] Added test for ews import. --- openlp/plugins/songs/lib/ewimport.py | 10 ++- .../openlp_plugins/songs/test_ewimport.py | 59 +++++++++++++++++- tests/resources/easyworshipsongs/test1.ews | Bin 0 -> 2722 bytes 3 files changed, 66 insertions(+), 3 deletions(-) create mode 100644 tests/resources/easyworshipsongs/test1.ews diff --git a/openlp/plugins/songs/lib/ewimport.py b/openlp/plugins/songs/lib/ewimport.py index 9117aaadc..32f3a3b7e 100644 --- a/openlp/plugins/songs/lib/ewimport.py +++ b/openlp/plugins/songs/lib/ewimport.py @@ -91,17 +91,24 @@ class EasyWorshipSongImport(SongImport): Import the songs from service file The full spec of the ews files can be found here: https://github.com/meinders/lithium-ews/blob/master/docs/ews%20file%20format.md + or here: http://wiki.openlp.org/Development:EasyWorship_EWS_Format :return: """ # Open ews file if it exists if not os.path.isfile(self.import_source): + log.debug('Given ews file does not exists.') return # Make sure there is room for at least a header and one entry if os.path.getsize(self.import_source) < 892: + log.debug('Given ews file is to small to contain valid data.') return # Take a stab at how text is encoded self.encoding = 'cp1252' + self.encoding = retrieve_windows_encoding(self.encoding) + if not self.encoding: + log.debug('No encoding set.') + return self.ews_file = open(self.import_source, 'rb') # Get file version type, = struct.unpack('<38s', self.ews_file.read(38)) @@ -115,13 +122,14 @@ class EasyWorshipSongImport(SongImport): elif version == '1.6': file_pos = 40 else: + log.debug('Given ews file is of unknown version.') return entry_count = self.get_i32(file_pos) entry_length = self.get_i16(file_pos+4) file_pos += 6 self.import_wizard.progress_bar.setMaximum(entry_count) # Loop over songs - for x in range(1, entry_count): + for i in range(entry_count): # Load entry metadata self.set_defaults() self.title = self.get_string(file_pos, 50) diff --git a/tests/functional/openlp_plugins/songs/test_ewimport.py b/tests/functional/openlp_plugins/songs/test_ewimport.py index a22519bec..9a09e0b34 100644 --- a/tests/functional/openlp_plugins/songs/test_ewimport.py +++ b/tests/functional/openlp_plugins/songs/test_ewimport.py @@ -69,6 +69,21 @@ SONG_TEST_DATA = [ 'Just to bow and receive a new blessing,\nIn the beautiful garden of prayer.', 'v3')], 'verse_order_list': []}] +EWS_SONG_TEST_DATA =\ + { 'title' : 'Vi pløjed og vi så\'de', + 'authors' : ['Matthias Claudius'], + 'verses' : + [('Vi pløjed og vi så\'de\nvor sæd i sorten jord,\nså bad vi ham os hjælpe,\nsom højt i Himlen bor,\n' + 'og han lod snefald hegne\nmod frosten barsk og hård,\nhan lod det tø og regne\nog varme mildt i vår.', + 'v1'), + ('Alle gode gaver\nde kommer ovenned,\nså tak da Gud, ja, pris dog Gud\nfor al hans kærlighed!', 'c1'), + ('Han er jo den, hvis vilje\nopholder alle ting,\nhan klæder markens lilje\nog runder himlens ring,\n' + 'ham lyder vind og vove,\nham rører ravnes nød,\nhvi skulle ej hans småbørn\nda og få dagligt brød?', 'v2'), + ('Ja, tak, du kære Fader,\nså mild, så rig, så rund,\nfor korn i hæs og lader,\nfor godt i allen stund!\n' + 'Vi kan jo intet give,\nsom nogen ting er værd,\nmen tag vort stakkels hjerte,\nså ringe som det er!', + 'v3')], + } + class EasyWorshipSongImportLogger(EasyWorshipSongImport): """ @@ -349,9 +364,9 @@ class TestEasyWorshipSongImport(TestCase): self.assertIsNone(importer.do_import(), 'do_import should return None when db_size is less than 0x800') mocked_retrieve_windows_encoding.assert_call(encoding) - def file_import_test(self): + def db_file_import_test(self): """ - Test the actual import of real song files and check that the imported data is correct. + Test the actual import of real song database files and check that the imported data is correct. """ # GIVEN: Test files with a mocked out SongImport class, a mocked out "manager", a mocked out "import_wizard", @@ -402,3 +417,43 @@ class TestEasyWorshipSongImport(TestCase): self.assertEquals(importer.verse_order_list, verse_order_list, 'verse_order_list for %s should be %s' % (title, verse_order_list)) mocked_finish.assert_called_with() + + def ews_file_import_test(self): + """ + Test the actual import of song from ews file and check that the imported data is correct. + """ + + # GIVEN: Test files with a mocked out SongImport class, a mocked out "manager", a mocked out "import_wizard", + # and mocked out "author", "add_copyright", "add_verse", "finish" methods. + with patch('openlp.plugins.songs.lib.ewimport.SongImport'), \ + patch('openlp.plugins.songs.lib.ewimport.retrieve_windows_encoding') as mocked_retrieve_windows_encoding: + mocked_retrieve_windows_encoding.return_value = 'cp1252' + mocked_manager = MagicMock() + mocked_import_wizard = MagicMock() + mocked_add_author = MagicMock() + mocked_add_verse = MagicMock() + mocked_finish = MagicMock() + mocked_title = MagicMock() + mocked_finish.return_value = True + importer = EasyWorshipSongImportLogger(mocked_manager) + importer.import_wizard = mocked_import_wizard + importer.stop_import_flag = False + importer.add_author = mocked_add_author + importer.add_verse = mocked_add_verse + importer.title = mocked_title + importer.finish = mocked_finish + importer.topics = [] + + # WHEN: Importing ews file + importer.import_source = os.path.join(TEST_PATH, 'test1.ews') + + # THEN: do_import should return none, the song data should be as expected, and finish should have been + # called. + title = EWS_SONG_TEST_DATA['title'] + self.assertIsNone(importer.do_import(), 'do_import should return None when it has completed') + self.assertIn(title, importer._title_assignment_list, 'title for should be "%s"' % title) + mocked_add_author.assert_any_call(EWS_SONG_TEST_DATA['authors'][0]) + for verse_text, verse_tag in EWS_SONG_TEST_DATA['verses']: + mocked_add_verse.assert_any_call(verse_text, verse_tag) + mocked_finish.assert_called_with() + diff --git a/tests/resources/easyworshipsongs/test1.ews b/tests/resources/easyworshipsongs/test1.ews new file mode 100644 index 0000000000000000000000000000000000000000..2cb9676f140623ced292334ae7d9bb050e7f23e3 GIT binary patch literal 2722 zcmZ=wEUpaCFDlN+EKmqe&PYuu%}G^o%LEc(sYS(^`FRRJV9FrH00trsFq#p_kYEqX zR4B;#k(HXFke{wlmZ?zuR6QjXqGXiB9s<6JB_$b|iNy-eIfO%$DnG6P`GPv10}XG|;e0qyR;6%46ynmLeek|Ns9V9uBbZ zU;+YWW*Bc+dYueUxuoVPxFqHkXQwKVp_^1KqxqY-5c6PWV5qqDcKX@A%LW3+-+Syi z{3eqzbOq<_rP_QPij0pKEq*?_GWkevGW%`b1zPXV_1unqD;{qs#QtqU*&{3a|Hktq z{l6bNzDwW|o36;UuaoQ-uQ_$~X55u?$08(+rY+ibtE1|&m%M@4hhy29alV)1a^@7y zOXKX%(v<4Yn7+u!I%eOag+*I;Sy#*npYnQlb9?2N8)uK-{Gs@@$c%YC@8!3(hs~$$ zKDpPl;^f|?-zV+flfTirYyFYfDegRyFFU(GE!%VZPj%pAi6;&JyaT7DpKiV@w%YXO z-Y>nH*IqY2UB4z`j%@wf8@IaClQuA#rf- z6b=e(YMNjFCqvBPaKe$yz4sO|T|clwu_HKUuGSoW*}48xidkD0OLdBEJGc61O^Oif z)Q~kBrrPdccsuR70rRthSpM9q{~l5HFYZmacIqdOO6A0Fay`KUI@c~~WNh3h`R@&9 zXtnr_eoM!@nv;Oob*vW*~w>~fA0J|zvi~pes0MI z^J5Aoeiz$iE}mx>Tv%M&FL$ToT2h^w%gx3~AL^EiZJ5d}v4?NLiQgth^J9Y_c^z&P zT=4kGCpUB5Rg>#C*0$dYJI7wL;D_4ueA%D(^%lsUbX{zAd(H>BsC1D{(=|*MZSzh0 zSE_c^Qz>ZrCB>h2Q?!2z}wsHpRhn-s02KOZ_E=5ign$u(<{9@;wili#bD4v9$ zcB`j-Jb3kOMR-9q$Ab&S%R(|$XHVQc>Hbc?$mJ={oPrCzeqDXtzUo`R)wIy9;-4Ai zPwGEe&bs36$6SGXo~O>|y#G9Luf?r6^KiAPlU?V1w|=hsmFdo=IqZJwlQm~76ghM! zp+I$;fYM5vldqbsPsVCxNUxO7`ZD8oSk(0i26oCnM5Hc Date: Fri, 4 Apr 2014 22:28:55 +0200 Subject: [PATCH 17/82] Some cleanup and comments --- openlp/plugins/songs/lib/ewimport.py | 52 +++++++++++++++++-- .../openlp_plugins/songs/test_ewimport.py | 1 - 2 files changed, 48 insertions(+), 5 deletions(-) diff --git a/openlp/plugins/songs/lib/ewimport.py b/openlp/plugins/songs/lib/ewimport.py index e273223e7..a4a8b6944 100644 --- a/openlp/plugins/songs/lib/ewimport.py +++ b/openlp/plugins/songs/lib/ewimport.py @@ -110,6 +110,15 @@ class EasyWorshipSongImport(SongImport): log.debug('No encoding set.') return self.ews_file = open(self.import_source, 'rb') + # EWS header, version '1.6'/' 3'/' 5': + # Offset Field Data type Length Details + # -------------------------------------------------------------------------------------------------- + # 0 Filetype string 38 Specifies the file type and version. + # "EasyWorship Schedule File Version 1.6" or + # "EasyWorship Schedule File Version 3" or + # "EasyWorship Schedule File Version 5" + # 40/48/56 Entry count int32le 4 Number of items in the schedule + # 44/52/60 Entry length int16le 2 Length of schedule entries: 0x0718 = 1816 # Get file version type, = struct.unpack('<38s', self.ews_file.read(38)) version = type.decode()[-3:] @@ -130,22 +139,39 @@ class EasyWorshipSongImport(SongImport): self.import_wizard.progress_bar.setMaximum(entry_count) # Loop over songs for i in range(entry_count): - # Load entry metadata + # Load EWS entry metadata: + # Offset Field Data type Length Details + # ------------------------------------------------------------------------------------------------ + # 0 Title cstring 50 + # 307 Author cstring 50 + # 358 Copyright cstring 100 + # 459 Administrator cstring 50 + # 800 Content pointer int32le 4 Position of the content for this entry. + # 820 Content type int32le 4 0x01 = Song, 0x02 = Scripture, 0x03 = Presentation, + # 0x04 = Video, 0x05 = Live video, 0x07 = Image, + # 0x08 = Audio, 0x09 = Web + # 1410 Song number cstring 10 self.set_defaults() - self.title = self.get_string(file_pos, 50) - resource = self.get_string(file_pos + 51, 255) + self.title = self.get_string(file_pos + 0, 50) authors = self.get_string(file_pos + 307, 50) copyright = self.get_string(file_pos + 358, 100) admin = self.get_string(file_pos + 459, 50) cont_ptr = self.get_i32(file_pos + 800) cont_type = self.get_i32(file_pos + 820) - notes = self.get_string(file_pos + 1155, 160) self.ccli_number = self.get_string(file_pos + 1410, 10) # Only handle content type 1 (songs) if cont_type != 1: file_pos += entry_length continue # Load song content + # Offset Field Data type Length Details + # ------------------------------------------------------------------------------------------------ + # 0 Length int32le 4 Length (L) of the content, including the compressed content + # and the following fields (14 bytes total). + # 4 Content string L-14 Content compressed with deflate. + # Checksum int32be 4 Alder-32 checksum. + # (unknown) 4 0x51 0x4b 0x03 0x04 + # Content length int32le 4 Length of content after decompression content_length = self.get_i32(cont_ptr) deflated_content = self.get_bytes(cont_ptr + 4, content_length - 10) deflated_length = self.get_i32(cont_ptr + 4 + content_length - 6) @@ -293,6 +319,10 @@ class EasyWorshipSongImport(SongImport): def set_song_import_object(self, authors, words): """ Set the SongImport object members. + + :param authors: String with authons + :param words: Bytes with rtf-encoding + :return: """ if authors: # Split up the authors @@ -429,6 +459,10 @@ class EasyWorshipSongImport(SongImport): def get_bytes(self, pos, length): """ Get bytes from ews_file + + :param pos: Position to read from + :param length: Bytes to read + :return: Bytes read """ self.ews_file.seek(pos) return self.ews_file.read(length) @@ -436,6 +470,10 @@ class EasyWorshipSongImport(SongImport): def get_string(self, pos, length): """ Get string from ews_file + + :param pos: Position to read from + :param length: Characters to read + :return: String read """ bytes = self.get_bytes(pos, length) mask = '<' + str(length) + 's' @@ -445,6 +483,9 @@ class EasyWorshipSongImport(SongImport): def get_i16(self, pos): """ Get short int from ews_file + + :param pos: Position to read from + :return: Short integer read """ bytes = self.get_bytes(pos, 2) @@ -455,6 +496,9 @@ class EasyWorshipSongImport(SongImport): def get_i32(self, pos): """ Get long int from ews_file + + :param pos: Position to read from + :return: Long integer read """ bytes = self.get_bytes(pos, 4) mask = ' Date: Sat, 5 Apr 2014 09:22:24 +0200 Subject: [PATCH 18/82] PEP8 fixes --- openlp/plugins/songs/lib/ewimport.py | 14 +++++++------- .../openlp_plugins/songs/test_ewimport.py | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/openlp/plugins/songs/lib/ewimport.py b/openlp/plugins/songs/lib/ewimport.py index a4a8b6944..e36021d04 100644 --- a/openlp/plugins/songs/lib/ewimport.py +++ b/openlp/plugins/songs/lib/ewimport.py @@ -143,7 +143,7 @@ class EasyWorshipSongImport(SongImport): # Offset Field Data type Length Details # ------------------------------------------------------------------------------------------------ # 0 Title cstring 50 - # 307 Author cstring 50 + # 307 Author cstring 50 # 358 Copyright cstring 100 # 459 Administrator cstring 50 # 800 Content pointer int32le 4 Position of the content for this entry. @@ -166,7 +166,7 @@ class EasyWorshipSongImport(SongImport): # Load song content # Offset Field Data type Length Details # ------------------------------------------------------------------------------------------------ - # 0 Length int32le 4 Length (L) of the content, including the compressed content + # 0 Length int32le 4 Length (L) of content, including the compressed content # and the following fields (14 bytes total). # 4 Content string L-14 Content compressed with deflate. # Checksum int32be 4 Alder-32 checksum. @@ -319,7 +319,7 @@ class EasyWorshipSongImport(SongImport): def set_song_import_object(self, authors, words): """ Set the SongImport object members. - + :param authors: String with authons :param words: Bytes with rtf-encoding :return: @@ -459,7 +459,7 @@ class EasyWorshipSongImport(SongImport): def get_bytes(self, pos, length): """ Get bytes from ews_file - + :param pos: Position to read from :param length: Bytes to read :return: Bytes read @@ -470,7 +470,7 @@ class EasyWorshipSongImport(SongImport): def get_string(self, pos, length): """ Get string from ews_file - + :param pos: Position to read from :param length: Characters to read :return: String read @@ -483,7 +483,7 @@ class EasyWorshipSongImport(SongImport): def get_i16(self, pos): """ Get short int from ews_file - + :param pos: Position to read from :return: Short integer read """ @@ -496,7 +496,7 @@ class EasyWorshipSongImport(SongImport): def get_i32(self, pos): """ Get long int from ews_file - + :param pos: Position to read from :return: Long integer read """ diff --git a/tests/functional/openlp_plugins/songs/test_ewimport.py b/tests/functional/openlp_plugins/songs/test_ewimport.py index 11e5d4f95..1e84134bd 100644 --- a/tests/functional/openlp_plugins/songs/test_ewimport.py +++ b/tests/functional/openlp_plugins/songs/test_ewimport.py @@ -70,19 +70,18 @@ SONG_TEST_DATA = [ 'verse_order_list': []}] EWS_SONG_TEST_DATA =\ - { 'title' : 'Vi pløjed og vi så\'de', - 'authors' : ['Matthias Claudius'], - 'verses' : + {'title': 'Vi pløjed og vi så\'de', + 'authors': ['Matthias Claudius'], + 'verses': [('Vi pløjed og vi så\'de\nvor sæd i sorten jord,\nså bad vi ham os hjælpe,\nsom højt i Himlen bor,\n' - 'og han lod snefald hegne\nmod frosten barsk og hård,\nhan lod det tø og regne\nog varme mildt i vår.', + 'og han lod snefald hegne\nmod frosten barsk og hård,\nhan lod det tø og regne\nog varme mildt i vår.', 'v1'), ('Alle gode gaver\nde kommer ovenned,\nså tak da Gud, ja, pris dog Gud\nfor al hans kærlighed!', 'c1'), ('Han er jo den, hvis vilje\nopholder alle ting,\nhan klæder markens lilje\nog runder himlens ring,\n' 'ham lyder vind og vove,\nham rører ravnes nød,\nhvi skulle ej hans småbørn\nda og få dagligt brød?', 'v2'), ('Ja, tak, du kære Fader,\nså mild, så rig, så rund,\nfor korn i hæs og lader,\nfor godt i allen stund!\n' 'Vi kan jo intet give,\nsom nogen ting er værd,\nmen tag vort stakkels hjerte,\nså ringe som det er!', - 'v3')], - } + 'v3')]} class EasyWorshipSongImportLogger(EasyWorshipSongImport): @@ -435,7 +434,8 @@ class TestEasyWorshipSongImport(TestCase): # GIVEN: Test files with a mocked out SongImport class, a mocked out "manager", a mocked out "import_wizard", # and mocked out "author", "add_copyright", "add_verse", "finish" methods. with patch('openlp.plugins.songs.lib.ewimport.SongImport'), \ - patch('openlp.plugins.songs.lib.ewimport.retrieve_windows_encoding') as mocked_retrieve_windows_encoding: + patch('openlp.plugins.songs.lib.ewimport.retrieve_windows_encoding') \ + as mocked_retrieve_windows_encoding: mocked_retrieve_windows_encoding.return_value = 'cp1252' mocked_manager = MagicMock() mocked_import_wizard = MagicMock() From cc635e9b9678b95ecfc3c3cdf42dd9e0150da2d5 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Tue, 8 Apr 2014 11:16:14 +0200 Subject: [PATCH 19/82] 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 20/82] 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 21/82] 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 22/82] 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 23/82] 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 24/82] 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 25/82] 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 26/82] 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 27/82] 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 28/82] 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 29/82] 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 30/82] 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 31/82] 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 32/82] 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 51c14bdf5131e907a8109ae48abc52c0f084c9f4 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sun, 20 Apr 2014 15:24:18 +0200 Subject: [PATCH 33/82] fixed bug 1296104 Fixes: https://launchpad.net/bugs/1296104 --- openlp/core/lib/renderer.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/openlp/core/lib/renderer.py b/openlp/core/lib/renderer.py index 233af3784..e24381558 100644 --- a/openlp/core/lib/renderer.py +++ b/openlp/core/lib/renderer.py @@ -248,6 +248,9 @@ class Renderer(OpenLPMixin, RegistryMixin, RegistryProperties): elif item.is_capable(ItemCapabilities.CanSoftBreak): pages = [] if '[---]' in text: + # Remove two or more option slide breaks next to each other (causing infinite loop). + while u'\n[---]\n[---]\n' in text: + text = text.replace(u'\n[---]\n[---]\n', u'\n[---]\n') while True: slides = text.split('\n[---]\n', 2) # If there are (at least) two occurrences of [---] we use the first two slides (and neglect the last From 85c217910297482658b0d92cc9635f4cbd8c350d Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sun, 20 Apr 2014 16:03:07 +0200 Subject: [PATCH 34/82] added test --- .../openlp_core_ui/test_shortcutlistform.py | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 tests/interfaces/openlp_core_ui/test_shortcutlistform.py diff --git a/tests/interfaces/openlp_core_ui/test_shortcutlistform.py b/tests/interfaces/openlp_core_ui/test_shortcutlistform.py new file mode 100644 index 000000000..27b48838d --- /dev/null +++ b/tests/interfaces/openlp_core_ui/test_shortcutlistform.py @@ -0,0 +1,79 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2014 Raoul Snyman # +# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan # +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # +# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, # +# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, # +# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, # +# Frode Woldsund, Martin Zibricky, Patrick Zimmermann # +# --------------------------------------------------------------------------- # +# This program is free software; you can redistribute it and/or modify it # +# under the terms of the GNU General Public License as published by the Free # +# Software Foundation; version 2 of the License. # +# # +# This program is distributed in the hope that it will be useful, but WITHOUT # +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # +# more details. # +# # +# You should have received a copy of the GNU General Public License along # +# with this program; if not, write to the Free Software Foundation, Inc., 59 # +# Temple Place, Suite 330, Boston, MA 02111-1307 USA # +############################################################################### +""" +Package to test the openlp.core.ui.shortcutform package. +""" +from unittest import TestCase + +from PyQt4 import QtCore, QtGui, QtTest + +from openlp.core.common import Registry +from openlp.core.ui.shortcutlistform import ShortcutListForm +from tests.interfaces import patch +from tests.helpers.testmixin import TestMixin + + +class TestShortcutform(TestCase, TestMixin): + + def setUp(self): + """ + Create the UI + """ + Registry.create() + self.get_application() + self.main_window = QtGui.QMainWindow() + Registry().register('main_window', self.main_window) + self.form = ShortcutListForm() + + def tearDown(self): + """ + Delete all the C++ objects at the end so that we don't have a segfault + """ + del self.form + del self.main_window + + def adjust_button_test(self): + """ + Test the _adjust_button() method + """ + # GIVEN: A button. + button = QtGui.QPushButton() + checked= True + enabled = True + text = "new!" + + # WHEN: Call the method. + with patch('PyQt4.QtGui.QPushButton.setChecked') as mocked_check_method: + self.form._adjust_button(button, checked, enabled, text) + + + # THEN: The button should be changed. + self.assertEqual(button.text(), text, "The text should match.") + mocked_check_method.assert_called_once_with(True) + self.assertEqual(button.isEnabled(), enabled, "The button should be disabled.") \ No newline at end of file From 57cd04a9570628d7dfe840fd2d052a66f8be59ed Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sun, 20 Apr 2014 16:10:37 +0200 Subject: [PATCH 35/82] Fixes: https://launchpad.net/bugs/1296104 From 79d3e95c32dd265d8e41f9b14cb55515e03c6b78 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sun, 20 Apr 2014 16:13:09 +0200 Subject: [PATCH 36/82] pep8 fixes --- tests/interfaces/openlp_core_ui/test_shortcutlistform.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/interfaces/openlp_core_ui/test_shortcutlistform.py b/tests/interfaces/openlp_core_ui/test_shortcutlistform.py index 27b48838d..472bce03f 100644 --- a/tests/interfaces/openlp_core_ui/test_shortcutlistform.py +++ b/tests/interfaces/openlp_core_ui/test_shortcutlistform.py @@ -64,7 +64,7 @@ class TestShortcutform(TestCase, TestMixin): """ # GIVEN: A button. button = QtGui.QPushButton() - checked= True + checked = True enabled = True text = "new!" @@ -72,7 +72,6 @@ class TestShortcutform(TestCase, TestMixin): with patch('PyQt4.QtGui.QPushButton.setChecked') as mocked_check_method: self.form._adjust_button(button, checked, enabled, text) - # THEN: The button should be changed. self.assertEqual(button.text(), text, "The text should match.") mocked_check_method.assert_called_once_with(True) From df800850e1cee6a43955e35a7886d2d0950dc26e Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sun, 20 Apr 2014 18:00:24 +0200 Subject: [PATCH 37/82] fixed bug 1240037 (Error occured when moving directory) Fixes: https://launchpad.net/bugs/1240037 --- openlp/core/ui/mainwindow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index 81e822c16..e17b1a976 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -1334,7 +1334,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow, RegistryProperties): if self.copy_data: log.info('Copying data to new path') try: - self.showStatusMessage( + self.show_status_message( translate('OpenLP.MainWindow', 'Copying OpenLP data to new data directory location - %s ' '- Please wait for copy to finish').replace('%s', self.new_data_path)) dir_util.copy_tree(old_data_path, self.new_data_path) From cf798e73d592f43285eb9b1654dc808ee5f71ebf Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sun, 20 Apr 2014 18:02:50 +0200 Subject: [PATCH 38/82] removed not needed code --- openlp/core/ui/mainwindow.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index e17b1a976..9c193b079 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -1364,8 +1364,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow, RegistryProperties): args = [] for a in self.arguments: args.extend([a]) - for arg in args: - filename = arg + for filename in args: if not isinstance(filename, str): filename = str(filename, sys.getfilesystemencoding()) if filename.endswith(('.osz', '.oszl')): From 54ed6aeea02a6a5076049d8e6c1334b52b71e94e Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Sun, 20 Apr 2014 21:03:35 +0200 Subject: [PATCH 39/82] Made the EWS import a seperate point. Also some small updates to comments. --- openlp/plugins/songs/lib/ewimport.py | 10 +----- openlp/plugins/songs/lib/importer.py | 46 ++++++++++++++++------------ 2 files changed, 27 insertions(+), 29 deletions(-) diff --git a/openlp/plugins/songs/lib/ewimport.py b/openlp/plugins/songs/lib/ewimport.py index 1bbe5fed4..faa4122c8 100644 --- a/openlp/plugins/songs/lib/ewimport.py +++ b/openlp/plugins/songs/lib/ewimport.py @@ -78,8 +78,6 @@ class EasyWorshipSongImport(SongImport): def do_import(self): """ Determines the type of file to import and calls the appropiate method - - :return: """ if self.import_source.lower().endswith('ews'): self.import_ews() @@ -92,8 +90,6 @@ class EasyWorshipSongImport(SongImport): The full spec of the ews files can be found here: https://github.com/meinders/lithium-ews/blob/master/docs/ews%20file%20format.md or here: http://wiki.openlp.org/Development:EasyWorship_EWS_Format - - :return: """ # Open ews file if it exists if not os.path.isfile(self.import_source): @@ -196,8 +192,6 @@ class EasyWorshipSongImport(SongImport): def import_db(self): """ Import the songs from the database - - :return: """ # Open the DB and MB files if they exist import_source_mb = self.import_source.replace('.DB', '.MB') @@ -322,7 +316,6 @@ class EasyWorshipSongImport(SongImport): :param authors: String with authons :param words: Bytes with rtf-encoding - :return: """ if authors: # Split up the authors @@ -380,7 +373,6 @@ class EasyWorshipSongImport(SongImport): Find a field in the descriptions :param field_name: field to find - :return: """ return [i for i, x in enumerate(self.field_descriptions) if x.name == field_name][0] @@ -417,7 +409,7 @@ class EasyWorshipSongImport(SongImport): Extract the field :param field_desc_index: Field index value - :return: + :return: The field value """ field = self.fields[field_desc_index] field_desc = self.field_descriptions[field_desc_index] diff --git a/openlp/plugins/songs/lib/importer.py b/openlp/plugins/songs/lib/importer.py index 0f04075d5..ed32a43ee 100644 --- a/openlp/plugins/songs/lib/importer.py +++ b/openlp/plugins/songs/lib/importer.py @@ -153,19 +153,20 @@ class SongFormat(object): CCLI = 3 DreamBeam = 4 EasySlides = 5 - EasyWorship = 6 - FoilPresenter = 7 - MediaShout = 8 - OpenSong = 9 - PowerSong = 10 - SongBeamer = 11 - SongPro = 12 - SongShowPlus = 13 - SongsOfFellowship = 14 - SundayPlus = 15 - WordsOfWorship = 16 - WorshipCenterPro = 17 - ZionWorx = 18 + EasyWorshipDB = 6 + EasyWorshipService = 7 + FoilPresenter = 8 + MediaShout = 9 + OpenSong = 10 + PowerSong = 11 + SongBeamer = 12 + SongPro = 13 + SongShowPlus = 14 + SongsOfFellowship = 15 + SundayPlus = 16 + WordsOfWorship = 17 + WorshipCenterPro = 18 + ZionWorx = 19 # Set optional attribute defaults __defaults__ = { @@ -224,15 +225,19 @@ class SongFormat(object): 'selectMode': SongFormatSelect.SingleFile, 'filter': '%s (*.xml)' % translate('SongsPlugin.ImportWizardForm', 'EasySlides XML File') }, - EasyWorship: { + EasyWorshipDB: { 'class': EasyWorshipSongImport, - 'name': 'EasyWorship', + 'name': 'EasyWorship Song Database', 'prefix': 'ew', 'selectMode': SongFormatSelect.SingleFile, - 'filter': '%s (*.db);; %s (*.ews)' % (translate('SongsPlugin.ImportWizardForm', - 'EasyWorship Song Database'), - translate('SongsPlugin.ImportWizardForm', - 'EasyWorship Service File')) + 'filter': '%s (*.db)' % translate('SongsPlugin.ImportWizardForm', 'EasyWorship Song Database') + }, + EasyWorshipService: { + 'class': EasyWorshipSongImport, + 'name': 'EasyWorship Service File', + 'prefix': 'ew', + 'selectMode': SongFormatSelect.SingleFile, + 'filter': '%s (*.ews)' % translate('SongsPlugin.ImportWizardForm', 'EasyWorship Service File') }, FoilPresenter: { 'class': FoilPresenterImport, @@ -344,7 +349,8 @@ class SongFormat(object): SongFormat.CCLI, SongFormat.DreamBeam, SongFormat.EasySlides, - SongFormat.EasyWorship, + SongFormat.EasyWorshipDB, + SongFormat.EasyWorshipService, SongFormat.FoilPresenter, SongFormat.MediaShout, SongFormat.OpenSong, From ce55fd1d57b61be6e36aac7e8374a0b141ee5f12 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sun, 20 Apr 2014 21:16:08 +0100 Subject: [PATCH 40/82] 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 41/82] 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 42/82] 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 43/82] 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 44/82] 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 From 6c08a80bfda00504312ec54f82cf51a320fbb17e Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Tue, 22 Apr 2014 12:29:15 +0200 Subject: [PATCH 45/82] added test; refactor --- openlp/core/ui/shortcutlistform.py | 20 +++--- openlp/core/utils/actions.py | 13 ++-- .../openlp_core_utils/test_actions.py | 62 +++++++++++++++++++ 3 files changed, 79 insertions(+), 16 deletions(-) diff --git a/openlp/core/ui/shortcutlistform.py b/openlp/core/ui/shortcutlistform.py index 4b64c3b54..dbfbbd439 100644 --- a/openlp/core/ui/shortcutlistform.py +++ b/openlp/core/ui/shortcutlistform.py @@ -244,10 +244,10 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog, RegistryProperties) self.primary_push_button.setChecked(False) self.alternate_push_button.setChecked(False) else: - if action.defaultShortcuts: - primary_label_text = action.defaultShortcuts[0].toString() - if len(action.defaultShortcuts) == 2: - alternate_label_text = action.defaultShortcuts[1].toString() + if action.default_shortcuts: + primary_label_text = action.default_shortcuts[0].toString() + if len(action.default_shortcuts) == 2: + alternate_label_text = action.default_shortcuts[1].toString() shortcuts = self._action_shortcuts(action) # We do not want to loose pending changes, that is why we have to keep the text when, this function has not # been triggered by a signal. @@ -292,7 +292,7 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog, RegistryProperties) self._adjust_button(self.alternate_push_button, False, text='') for category in self.action_list.categories: for action in category.actions: - self.changed_actions[action] = action.defaultShortcuts + self.changed_actions[action] = action.default_shortcuts self.refresh_shortcut_list() def on_default_radio_button_clicked(self, toggled): @@ -306,7 +306,7 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog, RegistryProperties) if action is None: return temp_shortcuts = self._action_shortcuts(action) - self.changed_actions[action] = action.defaultShortcuts + self.changed_actions[action] = action.default_shortcuts self.refresh_shortcut_list() primary_button_text = '' alternate_button_text = '' @@ -357,8 +357,8 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog, RegistryProperties) return shortcuts = self._action_shortcuts(action) new_shortcuts = [] - if action.defaultShortcuts: - new_shortcuts.append(action.defaultShortcuts[0]) + if action.default_shortcuts: + new_shortcuts.append(action.default_shortcuts[0]) # We have to check if the primary default shortcut is available. But we only have to check, if the action # has a default primary shortcut (an "empty" shortcut is always valid and if the action does not have a # default primary shortcut, then the alternative shortcut (not the default one) will become primary @@ -383,8 +383,8 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog, RegistryProperties) new_shortcuts = [] if shortcuts: new_shortcuts.append(shortcuts[0]) - if len(action.defaultShortcuts) == 2: - new_shortcuts.append(action.defaultShortcuts[1]) + if len(action.default_shortcuts) == 2: + new_shortcuts.append(action.default_shortcuts[1]) if len(new_shortcuts) == 2: if not self._validiate_shortcut(action, new_shortcuts[1]): return diff --git a/openlp/core/utils/actions.py b/openlp/core/utils/actions.py index 29f2d279b..b4efc561f 100644 --- a/openlp/core/utils/actions.py +++ b/openlp/core/utils/actions.py @@ -69,6 +69,7 @@ class CategoryActionList(object): """ Implement the __getitem__() method to make this class a dictionary type """ + assert False for weight, action in self.actions: if action.text() == key: return action @@ -78,7 +79,10 @@ class CategoryActionList(object): """ Implement the __contains__() method to make this class a dictionary type """ - return item in self + for weight, action in self.actions: + if action.text() == item: + return True + return False def __len__(self): """ @@ -107,10 +111,7 @@ class CategoryActionList(object): """ Implement the has_key() method to make this class a dictionary type """ - for weight, action in self.actions: - if action.text() == key: - return True - return False + return key in self def append(self, name): """ @@ -270,7 +271,7 @@ class ActionList(object): settings = Settings() settings.beginGroup('shortcuts') # Get the default shortcut from the config. - action.defaultShortcuts = settings.get_default_value(action.objectName()) + action.default_shortcuts = settings.get_default_value(action.objectName()) if weight is None: self.categories[category].actions.append(action) else: diff --git a/tests/functional/openlp_core_utils/test_actions.py b/tests/functional/openlp_core_utils/test_actions.py index 2868f8555..566b020fe 100644 --- a/tests/functional/openlp_core_utils/test_actions.py +++ b/tests/functional/openlp_core_utils/test_actions.py @@ -32,9 +32,11 @@ Package to test the openlp.core.utils.actions package. from unittest import TestCase from PyQt4 import QtGui, QtCore +from mock import MagicMock from openlp.core.common import Settings from openlp.core.utils import ActionList +from openlp.core.utils.actions import CategoryActionList from tests.helpers.testmixin import TestMixin @@ -149,3 +151,63 @@ class TestActionList(TestCase, TestMixin): # THEN: Both action should keep their shortcuts. assert len(action3.shortcuts()) == 2, 'The action should have two shortcut assigned.' assert len(action_with_same_shortcuts3.shortcuts()) == 2, 'The action should have two shortcuts assigned.' + + +class TestCategoryActionList(TestCase): + def setUp(self): + """ + """ + self.added_action = MagicMock() + self.added_action.text = MagicMock('first') + self.not_added_action = MagicMock('second') + self.not_added_action.text = MagicMock() + self.list = CategoryActionList() + self.list.add(self.added_action, 10) + + def tearDown(self): + """ + + """ + del self.list + + def len_test(self): + """ + Test the __len__ method + """ + # GIVEN: The list. + + # WHEN: Check the length + length = len(self.list) + + # THEN: + self.assertEqual(length, 1, "The length should be 1.") + + # GIVEN: A list with an item. + self.list.append(self.not_added_action) + + # WHEN: Check the length. + length = len(self.list) + + # THEN: + self.assertEqual(length, 2, "The length should be 2.") + + def remove_test(self): + """ + Test the remove() method + """ + # GIVEN: The list + + # WHEN: Delete an item from the list. + self.list.remove(self.added_action) + + # THEN: Now the element should not be in the list anymore. + self.assertFalse(self.added_action in self.list) + + def contains_test(self): + """ + Test the __contains__() method + """ + # GIVEN: The list. + # WHEN: Do nothing. + # THEN: A not added item should not be in the list. + self.assertFalse(self.not_added_action in self.list) \ No newline at end of file From 1ae7c32adb435428193f05a39980d403712f9d2a Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Tue, 22 Apr 2014 12:32:02 +0200 Subject: [PATCH 46/82] fixed import --- tests/functional/openlp_core_utils/test_actions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/openlp_core_utils/test_actions.py b/tests/functional/openlp_core_utils/test_actions.py index 566b020fe..9ee0f5c6a 100644 --- a/tests/functional/openlp_core_utils/test_actions.py +++ b/tests/functional/openlp_core_utils/test_actions.py @@ -32,11 +32,11 @@ Package to test the openlp.core.utils.actions package. from unittest import TestCase from PyQt4 import QtGui, QtCore -from mock import MagicMock from openlp.core.common import Settings from openlp.core.utils import ActionList from openlp.core.utils.actions import CategoryActionList +from tests.functional import MagicMock from tests.helpers.testmixin import TestMixin From 5a3aecf997040b0705fd7319f8d8fb39a3455eaa Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Tue, 22 Apr 2014 17:06:21 +0200 Subject: [PATCH 47/82] fixed bugs in class --- openlp/core/utils/actions.py | 15 +- .../openlp_core_utils/test_actions.py | 147 +++++++++++------- 2 files changed, 94 insertions(+), 68 deletions(-) diff --git a/openlp/core/utils/actions.py b/openlp/core/utils/actions.py index b4efc561f..98eaced5b 100644 --- a/openlp/core/utils/actions.py +++ b/openlp/core/utils/actions.py @@ -69,18 +69,17 @@ class CategoryActionList(object): """ Implement the __getitem__() method to make this class a dictionary type """ - assert False - for weight, action in self.actions: - if action.text() == key: - return action - raise KeyError('Action "%s" does not exist.' % key) + try: + return self.actions[key] + except: + raise KeyError('Action "%s" does not exist.' % key) def __contains__(self, item): """ Implement the __contains__() method to make this class a dictionary type """ for weight, action in self.actions: - if action.text() == item: + if action == item: return True return False @@ -113,14 +112,14 @@ class CategoryActionList(object): """ return key in self - def append(self, name): + def append(self, action): """ Append an action """ weight = 0 if self.actions: weight = self.actions[-1][0] + 1 - self.add(name, weight) + self.add(action, weight) def add(self, action, weight=0): """ diff --git a/tests/functional/openlp_core_utils/test_actions.py b/tests/functional/openlp_core_utils/test_actions.py index 9ee0f5c6a..d79617037 100644 --- a/tests/functional/openlp_core_utils/test_actions.py +++ b/tests/functional/openlp_core_utils/test_actions.py @@ -36,10 +36,96 @@ from PyQt4 import QtGui, QtCore from openlp.core.common import Settings from openlp.core.utils import ActionList from openlp.core.utils.actions import CategoryActionList -from tests.functional import MagicMock +from tests.functional import MagicMock, patch from tests.helpers.testmixin import TestMixin +class TestCategoryActionList(TestCase): + def setUp(self): + """ + Create an instance and a few example actions. + """ + self.action1 = MagicMock() + self.action1.text.return_value = 'first' + self.action2 = MagicMock() + self.action2.text.return_value = 'second' + self.list = CategoryActionList() + + def tearDown(self): + """ + Clean up + """ + del self.list + + def contains_test(self): + """ + Test the __contains__() method + """ + # GIVEN: The list. + # WHEN: Add an action + self.list.append(self.action1) + # THEN: + self.assertTrue(self.action1 in self.list) + self.assertFalse(self.action2 in self.list) + + def len_test(self): + """ + Test the __len__ method + """ + # GIVEN: The list. + # WHEN: + # THEN: Check the length. + self.assertEqual(len(self.list), 0, "The length should be 0.") + + # GIVEN: The list. + # WHEN: Append an action. + self.list.append(self.action1) + + # THEN: Check the length. + self.assertEqual(len(self.list), 1, "The length should be 1.") + + def append_test(self): + """ + Test the append() method + """ + # GIVEN: The list. + # WHEN: Append an action. + self.list.append(self.action1) + self.list.append(self.action2) + + # THEN: Check if the actions are in the list and check if they have the correct weights. + self.assertTrue(self.action1 in self.list) + self.assertTrue(self.action2 in self.list) + self.assertEqual(self.list[0], (0, self.action1)) + self.assertEqual(self.list[1], (1, self.action2)) + + def add_test(self): + """ + Test the add() method + """ + # GIVEN: The list. + # WHEN: Append actions. + self.list.add(self.action1, 42) + self.list.add(self.action2, 99) + + # THEN: Check if they were added and have the specified weights. + self.assertTrue(self.action1 in self.list) + self.assertTrue(self.action2 in self.list) + self.assertEqual(self.list[0], (42, self.action1)) + self.assertEqual(self.list[1], (99, self.action2)) + + def remove_test(self): + """ + Test the remove() method + """ + # GIVEN: The list + # WHEN: Delete an item from the list. + self.list.remove(self.action1) + + # THEN: Now the element should not be in the list anymore. + self.assertFalse(self.action1 in self.list) + + class TestActionList(TestCase, TestMixin): """ Test the ActionList class @@ -152,62 +238,3 @@ class TestActionList(TestCase, TestMixin): assert len(action3.shortcuts()) == 2, 'The action should have two shortcut assigned.' assert len(action_with_same_shortcuts3.shortcuts()) == 2, 'The action should have two shortcuts assigned.' - -class TestCategoryActionList(TestCase): - def setUp(self): - """ - """ - self.added_action = MagicMock() - self.added_action.text = MagicMock('first') - self.not_added_action = MagicMock('second') - self.not_added_action.text = MagicMock() - self.list = CategoryActionList() - self.list.add(self.added_action, 10) - - def tearDown(self): - """ - - """ - del self.list - - def len_test(self): - """ - Test the __len__ method - """ - # GIVEN: The list. - - # WHEN: Check the length - length = len(self.list) - - # THEN: - self.assertEqual(length, 1, "The length should be 1.") - - # GIVEN: A list with an item. - self.list.append(self.not_added_action) - - # WHEN: Check the length. - length = len(self.list) - - # THEN: - self.assertEqual(length, 2, "The length should be 2.") - - def remove_test(self): - """ - Test the remove() method - """ - # GIVEN: The list - - # WHEN: Delete an item from the list. - self.list.remove(self.added_action) - - # THEN: Now the element should not be in the list anymore. - self.assertFalse(self.added_action in self.list) - - def contains_test(self): - """ - Test the __contains__() method - """ - # GIVEN: The list. - # WHEN: Do nothing. - # THEN: A not added item should not be in the list. - self.assertFalse(self.not_added_action in self.list) \ No newline at end of file From 1cd7d73d10b60ef5a0b058bcd5cbbc69a7e7d6bc Mon Sep 17 00:00:00 2001 From: Stefan Strasser Date: Fri, 25 Apr 2014 22:04:43 +0200 Subject: [PATCH 48/82] fixed bug #1312629 Error on entering invalid character in verse-order Fixes: https://launchpad.net/bugs/1312629 --- openlp/plugins/songs/lib/__init__.py | 6 ++-- .../openlp_plugins/songs/test_lib.py | 32 +++++++++++++++++-- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/openlp/plugins/songs/lib/__init__.py b/openlp/plugins/songs/lib/__init__.py index dc198d4b7..343bc710b 100644 --- a/openlp/plugins/songs/lib/__init__.py +++ b/openlp/plugins/songs/lib/__init__.py @@ -206,14 +206,14 @@ class VerseType(object): Return the VerseType for a given tag :param verse_tag: The string to return a VerseType for - :param default: Default return value if no matching tag is found + :param default: Default return value if no matching tag is found (a valid VerseType or None) :return: A VerseType of the tag """ verse_tag = verse_tag[0].lower() for num, tag in enumerate(VerseType.tags): if verse_tag == tag: return num - if len(VerseType.names) > default: + if default in range(0,len(VerseType.names)) or default is None: return default else: return VerseType.Other @@ -231,7 +231,7 @@ class VerseType(object): for num, tag in enumerate(VerseType.translated_tags): if verse_tag == tag: return num - if len(VerseType.names) > default: + if default in range(0,len(VerseType.names)) or default is None: return default else: return VerseType.Other diff --git a/tests/functional/openlp_plugins/songs/test_lib.py b/tests/functional/openlp_plugins/songs/test_lib.py index b67c1a4be..b06a18a7c 100644 --- a/tests/functional/openlp_plugins/songs/test_lib.py +++ b/tests/functional/openlp_plugins/songs/test_lib.py @@ -445,9 +445,9 @@ class TestVerseType(TestCase): # THEN: The result should be VerseType.Chorus self.assertEqual(result, VerseType.Chorus, 'The result should be VerseType.Chorus, but was "%s"' % result) - def from_tag_with_invalid_default_test(self): + def from_tag_with_invalid_intdefault_test(self): """ - Test that the from_tag() method returns a sane default when passed an invalid tag and an invalid default. + Test that the from_tag() method returns a sane default when passed an invalid tag and an invalid int default. """ # GIVEN: A mocked out translate() function that just returns what it was given with patch('openlp.plugins.songs.lib.translate') as mocked_translate: @@ -458,3 +458,31 @@ class TestVerseType(TestCase): # THEN: The result should be VerseType.Other self.assertEqual(result, VerseType.Other, 'The result should be VerseType.Other, but was "%s"' % result) + + def from_tag_with_invalid_default_test(self): + """ + Test that the from_tag() method returns a sane default when passed an invalid tag and an invalid default. + """ + # GIVEN: A mocked out translate() function that just returns what it was given + with patch('openlp.plugins.songs.lib.translate') as mocked_translate: + mocked_translate.side_effect = lambda x, y: y + + # WHEN: We run the from_tag() method with an invalid verse type, we get the specified default back + result = VerseType.from_tag('@', 'asdf') + + # THEN: The result should be VerseType.Other + self.assertEqual(result, VerseType.Other, 'The result should be VerseType.Other, but was "%s"' % result) + + def from_tag_with_none_default_test(self): + """ + Test that the from_tag() method returns a sane default when passed an invalid tag and None as default. + """ + # GIVEN: A mocked out translate() function that just returns what it was given + with patch('openlp.plugins.songs.lib.translate') as mocked_translate: + mocked_translate.side_effect = lambda x, y: y + + # WHEN: We run the from_tag() method with an invalid verse type, we get the specified default back + result = VerseType.from_tag('m', None) + + # THEN: The result should be VerseType.Other + self.assertIsNone(result, 'The result should be None, but was "%s"' % result) From c2d1589dcb2787891faed11d2b77f8be08a62762 Mon Sep 17 00:00:00 2001 From: Stefan Strasser Date: Sat, 26 Apr 2014 07:07:08 +0200 Subject: [PATCH 49/82] fixed a pep-issue in songs-lib and corrected a comment in a test --- openlp/plugins/songs/lib/__init__.py | 4 ++-- tests/functional/openlp_plugins/songs/test_lib.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/openlp/plugins/songs/lib/__init__.py b/openlp/plugins/songs/lib/__init__.py index 343bc710b..a0a8e94bd 100644 --- a/openlp/plugins/songs/lib/__init__.py +++ b/openlp/plugins/songs/lib/__init__.py @@ -213,7 +213,7 @@ class VerseType(object): for num, tag in enumerate(VerseType.tags): if verse_tag == tag: return num - if default in range(0,len(VerseType.names)) or default is None: + if default in range(0, len(VerseType.names)) or default is None: return default else: return VerseType.Other @@ -231,7 +231,7 @@ class VerseType(object): for num, tag in enumerate(VerseType.translated_tags): if verse_tag == tag: return num - if default in range(0,len(VerseType.names)) or default is None: + if default in range(0, len(VerseType.names)) or default is None: return default else: return VerseType.Other diff --git a/tests/functional/openlp_plugins/songs/test_lib.py b/tests/functional/openlp_plugins/songs/test_lib.py index b06a18a7c..140126f26 100644 --- a/tests/functional/openlp_plugins/songs/test_lib.py +++ b/tests/functional/openlp_plugins/songs/test_lib.py @@ -484,5 +484,5 @@ class TestVerseType(TestCase): # WHEN: We run the from_tag() method with an invalid verse type, we get the specified default back result = VerseType.from_tag('m', None) - # THEN: The result should be VerseType.Other + # THEN: The result should be None self.assertIsNone(result, 'The result should be None, but was "%s"' % result) From aa899bfde0868e8a241c358398b12110b68890d2 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sat, 26 Apr 2014 10:53:36 +0200 Subject: [PATCH 50/82] fixes --- openlp/core/utils/actions.py | 2 +- .../openlp_core_utils/test_actions.py | 44 ++++++++++++++----- 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/openlp/core/utils/actions.py b/openlp/core/utils/actions.py index 98eaced5b..66da3f256 100644 --- a/openlp/core/utils/actions.py +++ b/openlp/core/utils/actions.py @@ -70,7 +70,7 @@ class CategoryActionList(object): Implement the __getitem__() method to make this class a dictionary type """ try: - return self.actions[key] + return self.actions[key][1] except: raise KeyError('Action "%s" does not exist.' % key) diff --git a/tests/functional/openlp_core_utils/test_actions.py b/tests/functional/openlp_core_utils/test_actions.py index d79617037..8cc6df5da 100644 --- a/tests/functional/openlp_core_utils/test_actions.py +++ b/tests/functional/openlp_core_utils/test_actions.py @@ -36,7 +36,7 @@ from PyQt4 import QtGui, QtCore from openlp.core.common import Settings from openlp.core.utils import ActionList from openlp.core.utils.actions import CategoryActionList -from tests.functional import MagicMock, patch +from tests.functional import MagicMock from tests.helpers.testmixin import TestMixin @@ -57,6 +57,23 @@ class TestCategoryActionList(TestCase): """ del self.list + def get_test(self): + """ + Test the __getitem__() method + """ + # GIVEN: The list. + self.list.append(self.action1) + + # WHEN: Add an action. + returned_action = self.list[0] + print(returned_action) + + # THEN: Check if the correct action was returned. + self.assertEqual(self.action1, returned_action) + + # THEN: Test if an exception is raised when trying to access a non existing item. + self.assertRaises(KeyError, self.list.__getitem__, 1) + def contains_test(self): """ Test the __contains__() method @@ -64,7 +81,8 @@ class TestCategoryActionList(TestCase): # GIVEN: The list. # WHEN: Add an action self.list.append(self.action1) - # THEN: + + # THEN: The actions should (not) be in the list. self.assertTrue(self.action1 in self.list) self.assertFalse(self.action2 in self.list) @@ -73,7 +91,7 @@ class TestCategoryActionList(TestCase): Test the __len__ method """ # GIVEN: The list. - # WHEN: + # WHEN: Do nothing. # THEN: Check the length. self.assertEqual(len(self.list), 0, "The length should be 0.") @@ -96,23 +114,27 @@ class TestCategoryActionList(TestCase): # THEN: Check if the actions are in the list and check if they have the correct weights. self.assertTrue(self.action1 in self.list) self.assertTrue(self.action2 in self.list) - self.assertEqual(self.list[0], (0, self.action1)) - self.assertEqual(self.list[1], (1, self.action2)) + self.assertEqual(self.list[0], self.action1) + self.assertEqual(self.list[1], self.action2) def add_test(self): """ Test the add() method """ - # GIVEN: The list. - # WHEN: Append actions. - self.list.add(self.action1, 42) - self.list.add(self.action2, 99) + # GIVEN: The list and weights. + action1_weight = 42 + action2_weight = 41 + + # WHEN: Add actions and their weights. + self.list.add(self.action1, action1_weight) + self.list.add(self.action2, action2_weight) # THEN: Check if they were added and have the specified weights. self.assertTrue(self.action1 in self.list) self.assertTrue(self.action2 in self.list) - self.assertEqual(self.list[0], (42, self.action1)) - self.assertEqual(self.list[1], (99, self.action2)) + # Now check if action1 is second and action2 is first (due to their weights). + self.assertEqual(self.list[0], self.action2) + self.assertEqual(self.list[1], self.action1) def remove_test(self): """ From 6bdadff334e6ac67571f2c80d8a4c86261928414 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sat, 26 Apr 2014 11:01:12 +0200 Subject: [PATCH 51/82] raise exception when item not in list --- openlp/core/utils/actions.py | 16 ++++------------ .../functional/openlp_core_utils/test_actions.py | 7 ++++++- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/openlp/core/utils/actions.py b/openlp/core/utils/actions.py index 66da3f256..d37a17ffe 100644 --- a/openlp/core/utils/actions.py +++ b/openlp/core/utils/actions.py @@ -69,17 +69,14 @@ class CategoryActionList(object): """ Implement the __getitem__() method to make this class a dictionary type """ - try: - return self.actions[key][1] - except: - raise KeyError('Action "%s" does not exist.' % key) + return self.actions[key][1] - def __contains__(self, item): + def __contains__(self, key): """ Implement the __contains__() method to make this class a dictionary type """ for weight, action in self.actions: - if action == item: + if action == key: return True return False @@ -106,12 +103,6 @@ class CategoryActionList(object): self.index += 1 return self.actions[self.index - 1][1] - def has_key(self, key): - """ - Implement the has_key() method to make this class a dictionary type - """ - return key in self - def append(self, action): """ Append an action @@ -136,6 +127,7 @@ class CategoryActionList(object): if action[1] == remove_action: self.actions.remove(action) return + raise ValueError('Action "%s" does not exist.' % remove_action) class CategoryList(object): diff --git a/tests/functional/openlp_core_utils/test_actions.py b/tests/functional/openlp_core_utils/test_actions.py index 8cc6df5da..890bde2ba 100644 --- a/tests/functional/openlp_core_utils/test_actions.py +++ b/tests/functional/openlp_core_utils/test_actions.py @@ -72,7 +72,7 @@ class TestCategoryActionList(TestCase): self.assertEqual(self.action1, returned_action) # THEN: Test if an exception is raised when trying to access a non existing item. - self.assertRaises(KeyError, self.list.__getitem__, 1) + self.assertRaises(IndexError, self.list.__getitem__, 1) def contains_test(self): """ @@ -141,12 +141,17 @@ class TestCategoryActionList(TestCase): Test the remove() method """ # GIVEN: The list + self.list.append(self.action1) + # WHEN: Delete an item from the list. self.list.remove(self.action1) # THEN: Now the element should not be in the list anymore. self.assertFalse(self.action1 in self.list) + # THEN: Check if an exception is raised when trying to remove a not present action. + self.assertRaises(ValueError, self.list.remove, self.action2) + class TestActionList(TestCase, TestMixin): """ From 533710fc5b389aefe804559eca1e6e95b8fec83f Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sat, 26 Apr 2014 11:02:04 +0200 Subject: [PATCH 52/82] blank line --- tests/functional/openlp_core_utils/test_actions.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/functional/openlp_core_utils/test_actions.py b/tests/functional/openlp_core_utils/test_actions.py index 890bde2ba..1d815a94b 100644 --- a/tests/functional/openlp_core_utils/test_actions.py +++ b/tests/functional/openlp_core_utils/test_actions.py @@ -264,4 +264,3 @@ class TestActionList(TestCase, TestMixin): # THEN: Both action should keep their shortcuts. assert len(action3.shortcuts()) == 2, 'The action should have two shortcut assigned.' assert len(action_with_same_shortcuts3.shortcuts()) == 2, 'The action should have two shortcuts assigned.' - From 53c584ede4a4ccddd5f226a5150c571caadc0d62 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sat, 26 Apr 2014 11:26:20 +0200 Subject: [PATCH 53/82] removed print --- tests/functional/openlp_core_utils/test_actions.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/functional/openlp_core_utils/test_actions.py b/tests/functional/openlp_core_utils/test_actions.py index 1d815a94b..c608ce0b1 100644 --- a/tests/functional/openlp_core_utils/test_actions.py +++ b/tests/functional/openlp_core_utils/test_actions.py @@ -66,7 +66,6 @@ class TestCategoryActionList(TestCase): # WHEN: Add an action. returned_action = self.list[0] - print(returned_action) # THEN: Check if the correct action was returned. self.assertEqual(self.action1, returned_action) From 66c1f7c5da14b69e3069ec7f436d0348ba354150 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sat, 26 Apr 2014 11:35:50 +0200 Subject: [PATCH 54/82] renamed variable and parameter --- openlp/core/utils/actions.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/openlp/core/utils/actions.py b/openlp/core/utils/actions.py index d37a17ffe..3d679ac02 100644 --- a/openlp/core/utils/actions.py +++ b/openlp/core/utils/actions.py @@ -119,15 +119,15 @@ class CategoryActionList(object): self.actions.append((weight, action)) self.actions.sort(key=lambda act: act[0]) - def remove(self, remove_action): + def remove(self, action): """ Remove an action """ - for action in self.actions: - if action[1] == remove_action: - self.actions.remove(action) + for item in self.actions: + if item[1] == action: + self.actions.remove(item) return - raise ValueError('Action "%s" does not exist.' % remove_action) + raise ValueError('Action "%s" does not exist.' % action) class CategoryList(object): From 911864a4424acb62fc3e7b2384b7a2f59c1e396a Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sat, 26 Apr 2014 19:31:59 +0200 Subject: [PATCH 55/82] removed useless code --- openlp/core/utils/actions.py | 6 ----- .../openlp_core_utils/test_actions.py | 24 ++++--------------- 2 files changed, 4 insertions(+), 26 deletions(-) diff --git a/openlp/core/utils/actions.py b/openlp/core/utils/actions.py index 3d679ac02..256d36a1e 100644 --- a/openlp/core/utils/actions.py +++ b/openlp/core/utils/actions.py @@ -65,12 +65,6 @@ class CategoryActionList(object): self.index = 0 self.actions = [] - def __getitem__(self, key): - """ - Implement the __getitem__() method to make this class a dictionary type - """ - return self.actions[key][1] - def __contains__(self, key): """ Implement the __contains__() method to make this class a dictionary type diff --git a/tests/functional/openlp_core_utils/test_actions.py b/tests/functional/openlp_core_utils/test_actions.py index c608ce0b1..4958c4677 100644 --- a/tests/functional/openlp_core_utils/test_actions.py +++ b/tests/functional/openlp_core_utils/test_actions.py @@ -57,22 +57,6 @@ class TestCategoryActionList(TestCase): """ del self.list - def get_test(self): - """ - Test the __getitem__() method - """ - # GIVEN: The list. - self.list.append(self.action1) - - # WHEN: Add an action. - returned_action = self.list[0] - - # THEN: Check if the correct action was returned. - self.assertEqual(self.action1, returned_action) - - # THEN: Test if an exception is raised when trying to access a non existing item. - self.assertRaises(IndexError, self.list.__getitem__, 1) - def contains_test(self): """ Test the __contains__() method @@ -113,8 +97,8 @@ class TestCategoryActionList(TestCase): # THEN: Check if the actions are in the list and check if they have the correct weights. self.assertTrue(self.action1 in self.list) self.assertTrue(self.action2 in self.list) - self.assertEqual(self.list[0], self.action1) - self.assertEqual(self.list[1], self.action2) + self.assertEqual(self.list.actions[0], (0, self.action1)) + self.assertEqual(self.list.actions[1], (1, self.action2)) def add_test(self): """ @@ -132,8 +116,8 @@ class TestCategoryActionList(TestCase): self.assertTrue(self.action1 in self.list) self.assertTrue(self.action2 in self.list) # Now check if action1 is second and action2 is first (due to their weights). - self.assertEqual(self.list[0], self.action2) - self.assertEqual(self.list[1], self.action1) + self.assertEqual(self.list.actions[0], (41, self.action2)) + self.assertEqual(self.list.actions[1], (42, self.action1)) def remove_test(self): """ From 3f1e7f1912ce54c475f4e18353177e1e046a9160 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sat, 26 Apr 2014 19:36:47 +0200 Subject: [PATCH 56/82] fixes --- openlp/core/utils/actions.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/openlp/core/utils/actions.py b/openlp/core/utils/actions.py index 256d36a1e..924a5a2f9 100644 --- a/openlp/core/utils/actions.py +++ b/openlp/core/utils/actions.py @@ -170,9 +170,9 @@ class CategoryList(object): self.index += 1 return self.categories[self.index - 1] - def has_key(self, key): + def __contains__(self, key): """ - Implement the has_key() method to make this class like a dictionary + Implement the __contains__() method to make this class like a dictionary """ for category in self.categories: if category.name == key: @@ -186,10 +186,7 @@ class CategoryList(object): weight = 0 if self.categories: weight = self.categories[-1].weight + 1 - if actions: - self.add(name, weight, actions) - else: - self.add(name, weight) + self.add(name, weight, actions) def add(self, name, weight=0, actions=None): """ From 88a3ac62160bac2a23bb87f48ef07768b671e6ca Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sat, 26 Apr 2014 19:40:26 +0200 Subject: [PATCH 57/82] match list behaviour --- openlp/core/utils/actions.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openlp/core/utils/actions.py b/openlp/core/utils/actions.py index 924a5a2f9..d81e16b2e 100644 --- a/openlp/core/utils/actions.py +++ b/openlp/core/utils/actions.py @@ -209,6 +209,8 @@ class CategoryList(object): for category in self.categories: if category.name == name: self.categories.remove(category) + return + raise ValueError('Category "%s" does not exist.' % name) class ActionList(object): From 48fe8c5e37b8846e502347f74ac834b8993af821 Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Sat, 26 Apr 2014 21:47:11 +0200 Subject: [PATCH 58/82] Restructured the tests a bit. --- tests/functional/openlp_plugins/songs/test_ewimport.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/functional/openlp_plugins/songs/test_ewimport.py b/tests/functional/openlp_plugins/songs/test_ewimport.py index b58fcb249..244775bd5 100644 --- a/tests/functional/openlp_plugins/songs/test_ewimport.py +++ b/tests/functional/openlp_plugins/songs/test_ewimport.py @@ -400,10 +400,11 @@ class TestEasyWorshipSongImport(TestCase): # WHEN: Importing each file importer.import_source = os.path.join(TEST_PATH, 'Songs.DB') + import_result = importer.do_import() # THEN: do_import should return none, the song data should be as expected, and finish should have been # called. - self.assertIsNone(importer.do_import(), 'do_import should return None when it has completed') + self.assertIsNone(import_result, 'do_import should return None when it has completed') for song_data in SONG_TEST_DATA: title = song_data['title'] author_calls = song_data['authors'] @@ -455,11 +456,12 @@ class TestEasyWorshipSongImport(TestCase): # WHEN: Importing ews file importer.import_source = os.path.join(TEST_PATH, 'test1.ews') + import_result = importer.do_import() # THEN: do_import should return none, the song data should be as expected, and finish should have been # called. title = EWS_SONG_TEST_DATA['title'] - self.assertIsNone(importer.do_import(), 'do_import should return None when it has completed') + self.assertIsNone(import_result, 'do_import should return None when it has completed') self.assertIn(title, importer._title_assignment_list, 'title for should be "%s"' % title) mocked_add_author.assert_any_call(EWS_SONG_TEST_DATA['authors'][0]) for verse_text, verse_tag in EWS_SONG_TEST_DATA['verses']: From 282ed7e1a6440ee4dba4804859be51e52d87fa85 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Tue, 29 Apr 2014 13:04:19 +0200 Subject: [PATCH 59/82] fixed u' --- openlp/core/lib/renderer.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/openlp/core/lib/renderer.py b/openlp/core/lib/renderer.py index e24381558..71a1f6058 100644 --- a/openlp/core/lib/renderer.py +++ b/openlp/core/lib/renderer.py @@ -249,8 +249,8 @@ class Renderer(OpenLPMixin, RegistryMixin, RegistryProperties): pages = [] if '[---]' in text: # Remove two or more option slide breaks next to each other (causing infinite loop). - while u'\n[---]\n[---]\n' in text: - text = text.replace(u'\n[---]\n[---]\n', u'\n[---]\n') + while '\n[---]\n[---]\n' in text: + text = text.replace('\n[---]\n[---]\n', '\n[---]\n') while True: slides = text.split('\n[---]\n', 2) # If there are (at least) two occurrences of [---] we use the first two slides (and neglect the last @@ -395,7 +395,7 @@ class Renderer(OpenLPMixin, RegistryMixin, RegistryProperties): off when displayed. :param lines: The text to be fitted on the slide split into lines. - :param line_end: The text added after each line. Either ``u' '`` or ``u'
``. + :param line_end: The text added after each line. Either ``' '`` or ``'
``. """ formatted = [] previous_html = '' @@ -419,7 +419,7 @@ class Renderer(OpenLPMixin, RegistryMixin, RegistryProperties): processed word by word. This is sometimes need for **bible** verses. :param lines: The text to be fitted on the slide split into lines. - :param line_end: The text added after each line. Either ``u' '`` or ``u'
``. This is needed for **bibles**. + :param line_end: The text added after each line. Either ``' '`` or ``'
``. This is needed for **bibles**. """ formatted = [] previous_html = '' @@ -456,7 +456,7 @@ class Renderer(OpenLPMixin, RegistryMixin, RegistryProperties): """ Tests the given text for not closed formatting tags and returns a tuple consisting of three unicode strings:: - (u'{st}{r}Text text text{/r}{/st}', u'{st}{r}', u'') + ('{st}{r}Text text text{/r}{/st}', '{st}{r}', '') The first unicode string is the text, with correct closing tags. The second unicode string are OpenLP's opening formatting tags and the third unicode string the html opening formatting tags. @@ -503,8 +503,8 @@ class Renderer(OpenLPMixin, RegistryMixin, RegistryProperties): The text contains html. :param raw_list: The elements which do not fit on a slide and needs to be processed using the binary chop. The elements can contain formatting tags. - :param separator: The separator for the elements. For lines this is ``u'
'`` and for words this is ``u' '``. - :param line_end: The text added after each "element line". Either ``u' '`` or ``u'
``. This is needed for + :param separator: The separator for the elements. For lines this is ``'
'`` and for words this is ``' '``. + :param line_end: The text added after each "element line". Either ``' '`` or ``'
``. This is needed for bibles. """ smallest_index = 0 From 3f75a913e2f33505a60562a71f5dbdd49d5750a1 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Thu, 1 May 2014 18:57:45 +0200 Subject: [PATCH 60/82] removed not working tests --- openlp/core/common/historycombobox.py | 2 +- .../interfaces/openlp_core_common/__init__.py | 28 +++++++++++++++ .../test_historycombobox.py} | 35 +++++++++++-------- .../openlp_core_lib/test_searchedit.py | 7 ---- .../openlp_core_ui/test_filerenamedialog.py | 2 +- 5 files changed, 50 insertions(+), 24 deletions(-) create mode 100644 tests/interfaces/openlp_core_common/__init__.py rename tests/interfaces/{openlp_core_ui/test_splashscreen.py => openlp_core_common/test_historycombobox.py} (74%) diff --git a/openlp/core/common/historycombobox.py b/openlp/core/common/historycombobox.py index b6d06ff9f..aadc0ea39 100644 --- a/openlp/core/common/historycombobox.py +++ b/openlp/core/common/historycombobox.py @@ -53,7 +53,7 @@ class HistoryComboBox(QtGui.QComboBox): self.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Preferred) self.setInsertPolicy(QtGui.QComboBox.InsertAtTop) - def keyPressEvent(self, event): + def keyPressEvlent(self, event): """ Override the inherited keyPressEvent method to emit the ``returnPressed`` signal and to save the current text to the dropdown list. diff --git a/tests/interfaces/openlp_core_common/__init__.py b/tests/interfaces/openlp_core_common/__init__.py new file mode 100644 index 000000000..6b241e7fc --- /dev/null +++ b/tests/interfaces/openlp_core_common/__init__.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2014 Raoul Snyman # +# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan # +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # +# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, # +# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, # +# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, # +# Frode Woldsund, Martin Zibricky, Patrick Zimmermann # +# --------------------------------------------------------------------------- # +# This program is free software; you can redistribute it and/or modify it # +# under the terms of the GNU General Public License as published by the Free # +# Software Foundation; version 2 of the License. # +# # +# This program is distributed in the hope that it will be useful, but WITHOUT # +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # +# more details. # +# # +# You should have received a copy of the GNU General Public License along # +# with this program; if not, write to the Free Software Foundation, Inc., 59 # +# Temple Place, Suite 330, Boston, MA 02111-1307 USA # +############################################################################### diff --git a/tests/interfaces/openlp_core_ui/test_splashscreen.py b/tests/interfaces/openlp_core_common/test_historycombobox.py similarity index 74% rename from tests/interfaces/openlp_core_ui/test_splashscreen.py rename to tests/interfaces/openlp_core_common/test_historycombobox.py index 35c15f9ec..00a63f15f 100644 --- a/tests/interfaces/openlp_core_ui/test_splashscreen.py +++ b/tests/interfaces/openlp_core_common/test_historycombobox.py @@ -27,34 +27,39 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### """ -Test the openlp.core.ui.splashscreen class. +Module to test the :mod:`~openlp.core.common.historycombobox` module. """ + from unittest import TestCase -from PyQt4 import QtGui +from PyQt4 import QtCore, QtGui, QtTest -from openlp.core.ui import SplashScreen +from openlp.core.common import Registry +from openlp.core.common import HistoryComboBox from tests.helpers.testmixin import TestMixin +from tests.interfaces import MagicMock, patch -class TestSplashScreen(TestCase, TestMixin): +class TestHistoryComboBox(TestCase, TestMixin): def setUp(self): + Registry.create() self.get_application() self.main_window = QtGui.QMainWindow() + Registry().register('main_window', self.main_window) + self.combo = HistoryComboBox(self.main_window) def tearDown(self): - """ - Delete all the C++ objects at the end so that we don't have a segfault - """ - del self.app - del self.main_window + del self.combo - def setupUi_test(self): + def getItems_test(self): """ - Test if the setupUi method.... + Test the getItems() method """ - # GIVEN: A splash screen instance. - splash = SplashScreen() + # GIVEN: The combo. - # THEN: Check if the splash has a setupUi method. - assert hasattr(splash, 'setupUi'), 'The Splash Screen should have a setupUi() method.' + # WHEN: Add two items. + self.combo.addItem('test1') + self.combo.addItem('test2') + + # THEN: The list of items should contain both strings. + self.assertEqual(self.combo.getItems(), ['test1', 'test2']) \ No newline at end of file diff --git a/tests/interfaces/openlp_core_lib/test_searchedit.py b/tests/interfaces/openlp_core_lib/test_searchedit.py index 22bf6fae3..f2cf18988 100644 --- a/tests/interfaces/openlp_core_lib/test_searchedit.py +++ b/tests/interfaces/openlp_core_lib/test_searchedit.py @@ -30,7 +30,6 @@ Module to test the EditCustomForm. """ from unittest import TestCase -from unittest.mock import MagicMock from PyQt4 import QtCore, QtGui, QtTest @@ -127,9 +126,3 @@ class TestSearchEdit(TestCase, TestMixin): # THEN: The search edit text should be cleared and the button be hidden. assert not self.search_edit.text(), "The search edit should not have any text." assert self.search_edit.clear_button.isHidden(), "The clear button should be hidden." - - def resize_event_test(self): - """ - Just check if the resizeEvent() method is re-implemented. - """ - assert hasattr(self.search_edit, "resizeEvent"), "The search edit should re-implement the resizeEvent method." diff --git a/tests/interfaces/openlp_core_ui/test_filerenamedialog.py b/tests/interfaces/openlp_core_ui/test_filerenamedialog.py index 905f167e9..7d14c7d9c 100644 --- a/tests/interfaces/openlp_core_ui/test_filerenamedialog.py +++ b/tests/interfaces/openlp_core_ui/test_filerenamedialog.py @@ -89,7 +89,7 @@ class TestStartFileRenameForm(TestCase, TestMixin): Test that the file_name_edit setFocus has called with True when executed """ # GIVEN: A mocked QDialog.exec_() method and mocked file_name_edit.setFocus() method. - with patch('PyQt4.QtGui.QDialog.exec_') as mocked_exec: + with patch('PyQt4.QtGui.QDialog.exec_'): mocked_set_focus = MagicMock() self.form.file_name_edit.setFocus = mocked_set_focus From 53d830b737952a8d4b997bf7914faff8157f9eb0 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Thu, 1 May 2014 19:06:47 +0200 Subject: [PATCH 61/82] removed old code --- openlp/plugins/songs/forms/editverseform.py | 2 -- openlp/plugins/songs/lib/mediaitem.py | 12 ++---------- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/openlp/plugins/songs/forms/editverseform.py b/openlp/plugins/songs/forms/editverseform.py index 79a69a015..bc2532b0d 100644 --- a/openlp/plugins/songs/forms/editverseform.py +++ b/openlp/plugins/songs/forms/editverseform.py @@ -122,8 +122,6 @@ class EditVerseForm(QtGui.QDialog, Ui_EditVerseDialog): text = text[:position + 4] match = VERSE_REGEX.match(text) if match: - # TODO: Not used, remove? - # verse_tag = match.group(1) try: verse_num = int(match.group(2)) + 1 except ValueError: diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index 32730ce3c..288711b06 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -518,16 +518,8 @@ class SongMediaItem(MediaManagerItem): log.debug('service_load') if self.plugin.status != PluginStatus.Active or not item.data_string: return - if item.data_string['title'].find('@') == -1: - # FIXME: This file seems to be an old one (prior to 1.9.5), which means, that the search title - # (data_string[u'title']) is probably wrong. We add "@" to search title and hope that we do not add any - # duplicate. This should work for songs without alternate title. - temp = (re.compile(r'\W+', re.UNICODE).sub(' ', item.data_string['title'].strip()) + '@').strip().lower() - search_results = \ - self.plugin.manager.get_all_objects(Song, Song.search_title == temp, Song.search_title.asc()) - else: - search_results = self.plugin.manager.get_all_objects( - Song, Song.search_title == item.data_string['title'], Song.search_title.asc()) + search_results = self.plugin.manager.get_all_objects( + Song, Song.search_title == item.data_string['title'], Song.search_title.asc()) edit_id = 0 add_song = True if search_results: From 8fb086134e1297a3db6ff27cd698669e6c026027 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Thu, 1 May 2014 19:49:43 +0200 Subject: [PATCH 62/82] dead code, not used variables, docs, unused imports --- openlp/core/lib/__init__.py | 3 +-- openlp/core/lib/screen.py | 9 ++++----- openlp/core/ui/aboutform.py | 2 +- openlp/core/ui/exceptiondialog.py | 2 +- openlp/core/ui/firsttimeform.py | 13 +++++-------- openlp/core/ui/media/mediaplayer.py | 2 -- openlp/core/ui/media/phononplayer.py | 2 -- openlp/plugins/bibles/lib/manager.py | 12 ++++++------ openlp/plugins/bibles/lib/mediaitem.py | 1 - openlp/plugins/custom/forms/editcustomform.py | 1 - openlp/plugins/images/lib/db.py | 2 +- openlp/plugins/media/lib/mediaitem.py | 1 - openlp/plugins/songs/forms/songselectform.py | 2 -- 13 files changed, 19 insertions(+), 33 deletions(-) diff --git a/openlp/core/lib/__init__.py b/openlp/core/lib/__init__.py index 9561baff4..a7cba33a0 100644 --- a/openlp/core/lib/__init__.py +++ b/openlp/core/lib/__init__.py @@ -296,8 +296,7 @@ def create_separated_list(string_list): :param string_list: List of unicode strings """ - if LooseVersion(Qt.PYQT_VERSION_STR) >= LooseVersion('4.9') and \ - LooseVersion(Qt.qVersion()) >= LooseVersion('4.8'): + if LooseVersion(Qt.PYQT_VERSION_STR) >= LooseVersion('4.9') and LooseVersion(Qt.qVersion()) >= LooseVersion('4.8'): return QtCore.QLocale().createSeparatedList(string_list) if not string_list: return '' diff --git a/openlp/core/lib/screen.py b/openlp/core/lib/screen.py index 17ead5346..8f03d9ab2 100644 --- a/openlp/core/lib/screen.py +++ b/openlp/core/lib/screen.py @@ -63,8 +63,7 @@ class ScreenList(object): """ Initialise the screen list. - ``desktop`` - A ``QDesktopWidget`` object. + :param desktop: A QDesktopWidget object. """ screen_list = cls() screen_list.desktop = desktop @@ -153,9 +152,9 @@ class ScreenList(object): :param screen: A dict with the screen properties:: { - u'primary': True, - u'number': 0, - u'size': PyQt4.QtCore.QRect(0, 0, 1024, 768) + 'primary': True, + 'number': 0, + 'size': PyQt4.QtCore.QRect(0, 0, 1024, 768) } """ log.info('Screen %d found with resolution %s' % (screen['number'], screen['size'])) diff --git a/openlp/core/ui/aboutform.py b/openlp/core/ui/aboutform.py index e971bbec4..3825312bd 100644 --- a/openlp/core/ui/aboutform.py +++ b/openlp/core/ui/aboutform.py @@ -30,7 +30,7 @@ The About dialog. """ -from PyQt4 import QtCore, QtGui +from PyQt4 import QtGui from .aboutdialog import Ui_AboutDialog from openlp.core.lib import translate diff --git a/openlp/core/ui/exceptiondialog.py b/openlp/core/ui/exceptiondialog.py index 212fee4cd..b8b3941cd 100644 --- a/openlp/core/ui/exceptiondialog.py +++ b/openlp/core/ui/exceptiondialog.py @@ -30,7 +30,7 @@ The GUI widgets of the exception dialog. """ -from PyQt4 import QtCore, QtGui +from PyQt4 import QtGui from openlp.core.lib import translate from openlp.core.lib.ui import create_button, create_button_box diff --git a/openlp/core/ui/firsttimeform.py b/openlp/core/ui/firsttimeform.py index b9b5f5997..a98d7861a 100644 --- a/openlp/core/ui/firsttimeform.py +++ b/openlp/core/ui/firsttimeform.py @@ -170,6 +170,7 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard, RegistryProperties): elif self.currentId() == FirstTimePage.Progress: return -1 elif self.currentId() == FirstTimePage.NoInternet: + print(1111) return FirstTimePage.Progress elif self.currentId() == FirstTimePage.Themes: self.application.set_busy_cursor() @@ -199,8 +200,8 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard, RegistryProperties): self.no_internet_label.setText(self.no_internet_text + self.cancelWizardText) elif page_id == FirstTimePage.Defaults: self.theme_combo_box.clear() - for iter in range(self.themes_list_widget.count()): - item = self.themes_list_widget.item(iter) + for index in range(self.themes_list_widget.count()): + item = self.themes_list_widget.item(index) if item.checkState() == QtCore.Qt.Checked: self.theme_combo_box.addItem(item.text()) if self.has_run_wizard: @@ -292,13 +293,9 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard, RegistryProperties): """ themes = self.config.get('themes', 'files') themes = themes.split(',') - for theme in themes: - filename = self.config.get('theme_%s' % theme, 'filename') + for index, theme in enumerate(themes): screenshot = self.config.get('theme_%s' % theme, 'screenshot') - for index in range(self.themes_list_widget.count()): - item = self.themes_list_widget.item(index) - if item.data(QtCore.Qt.UserRole) == filename: - break + item = self.themes_list_widget.item(index) item.setIcon(build_icon(os.path.join(gettempdir(), 'openlp', screenshot))) def _get_file_size(self, url): diff --git a/openlp/core/ui/media/mediaplayer.py b/openlp/core/ui/media/mediaplayer.py index 3246d58c4..22ea7ecfc 100644 --- a/openlp/core/ui/media/mediaplayer.py +++ b/openlp/core/ui/media/mediaplayer.py @@ -29,8 +29,6 @@ """ The :mod:`~openlp.core.ui.media.mediaplayer` module contains the MediaPlayer class. """ -import os - from openlp.core.common import RegistryProperties from openlp.core.ui.media import MediaState diff --git a/openlp/core/ui/media/phononplayer.py b/openlp/core/ui/media/phononplayer.py index 5e94dbd0e..b343755a0 100644 --- a/openlp/core/ui/media/phononplayer.py +++ b/openlp/core/ui/media/phononplayer.py @@ -33,10 +33,8 @@ import logging import mimetypes from datetime import datetime -from PyQt4 import QtGui from PyQt4.phonon import Phonon -from openlp.core.common import Settings from openlp.core.lib import translate from openlp.core.ui.media import MediaState diff --git a/openlp/plugins/bibles/lib/manager.py b/openlp/plugins/bibles/lib/manager.py index 8ad446ab4..c57fc117e 100644 --- a/openlp/plugins/bibles/lib/manager.py +++ b/openlp/plugins/bibles/lib/manager.py @@ -54,19 +54,19 @@ class BibleFormat(object): WebDownload = 3 @staticmethod - def get_class(format): + def get_class(bible_format): """ Return the appropriate implementation class. - :param format: The Bible format. + :param bible_format: The Bible format. """ - if format == BibleFormat.OSIS: + if bible_format == BibleFormat.OSIS: return OSISBible - elif format == BibleFormat.CSV: + elif bible_format == BibleFormat.CSV: return CSVBible - elif format == BibleFormat.OpenSong: + elif bible_format == BibleFormat.OpenSong: return OpenSongBible - elif format == BibleFormat.WebDownload: + elif bible_format == BibleFormat.WebDownload: return HTTPBible else: return None diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index a2cb1b594..c13448a4a 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -837,7 +837,6 @@ class BibleMediaItem(MediaManagerItem): # If there are no more items we check whether we have to add bible_text. if bible_text: raw_slides.append(bible_text.lstrip()) - bible_text = '' # Service Item: Capabilities if self.settings.layout_style == LayoutStyle.Continuous and not second_bible: # Split the line but do not replace line breaks in renderer. diff --git a/openlp/plugins/custom/forms/editcustomform.py b/openlp/plugins/custom/forms/editcustomform.py index 010f7d1bc..9c7a080aa 100644 --- a/openlp/plugins/custom/forms/editcustomform.py +++ b/openlp/plugins/custom/forms/editcustomform.py @@ -197,7 +197,6 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog): self.slide_list_view.clear() self.slide_list_view.addItems(slides) else: - old_slides = [] old_row = self.slide_list_view.currentRow() # Create a list with all (old/unedited) slides. old_slides = [self.slide_list_view.item(row).text() for row in range(self.slide_list_view.count())] diff --git a/openlp/plugins/images/lib/db.py b/openlp/plugins/images/lib/db.py index 68ca3d11d..896f93b17 100644 --- a/openlp/plugins/images/lib/db.py +++ b/openlp/plugins/images/lib/db.py @@ -31,7 +31,7 @@ The :mod:`db` module provides the database and schema that is the backend for th """ from sqlalchemy import Column, ForeignKey, Table, types -from sqlalchemy.orm import mapper, relation, reconstructor +from sqlalchemy.orm import mapper from openlp.core.lib.db import BaseModel, init_db diff --git a/openlp/plugins/media/lib/mediaitem.py b/openlp/plugins/media/lib/mediaitem.py index 87d8e3311..92426334a 100644 --- a/openlp/plugins/media/lib/mediaitem.py +++ b/openlp/plugins/media/lib/mediaitem.py @@ -314,7 +314,6 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties): def get_list(self, type=MediaType.Audio): media = Settings().value(self.settings_section + '/media files') media.sort(key=lambda filename: get_locale_key(os.path.split(str(filename))[1])) - extension = [] if type == MediaType.Audio: extension = self.media_controller.audio_extensions_list else: diff --git a/openlp/plugins/songs/forms/songselectform.py b/openlp/plugins/songs/forms/songselectform.py index d3ff5ab52..f9f658c5b 100755 --- a/openlp/plugins/songs/forms/songselectform.py +++ b/openlp/plugins/songs/forms/songselectform.py @@ -319,8 +319,6 @@ class SongSelectForm(QtGui.QDialog, Ui_SongSelectDialog): def on_search_finished(self): """ Slot which is called when the search is completed. - - :param songs: """ self.application.process_events() self.search_progress_bar.setVisible(False) From 40a0d612bd838a2d318c776f8cabe2dcfd93dd48 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Thu, 1 May 2014 20:02:20 +0200 Subject: [PATCH 63/82] moved code --- openlp/core/ui/slidecontroller.py | 76 ++++++++++++++++--------------- 1 file changed, 39 insertions(+), 37 deletions(-) diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index bf92e9d76..ab5b231a4 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -45,6 +45,13 @@ from openlp.core.lib.ui import create_action from openlp.core.utils.actions import ActionList, CategoryOrder from openlp.core.ui.listpreviewwidget import ListPreviewWidget +try: + from openlp.plugins.songs.lib import VerseType + SONGS_PLUGIN_AVAILABLE = True +except ImportError: + SONGS_PLUGIN_AVAILABLE = False + + # Threshold which has to be trespassed to toggle. HIDE_MENU_THRESHOLD = 27 AUDIO_TIME_LABEL_STYLESHEET = 'background-color: palette(background); ' \ @@ -400,11 +407,6 @@ class SlideController(DisplayController, RegistryProperties): For example to jump to "V3" you have to press "V" and afterwards but within a time frame of 350ms you have to press "3". """ - try: - from openlp.plugins.songs.lib import VerseType - SONGS_PLUGIN_AVAILABLE = True - except ImportError: - SONGS_PLUGIN_AVAILABLE = False sender_name = self.sender().objectName() verse_type = sender_name[15:] if sender_name[:15] == 'shortcutAction_' else '' if SONGS_PLUGIN_AVAILABLE: @@ -495,38 +497,6 @@ class SlideController(DisplayController, RegistryProperties): self.on_theme_display(False) self.on_hide_display(False) - def service_previous(self, field=None): - """ - Live event to select the previous service item from the service manager. - """ - self.keypress_queue.append(ServiceItemAction.Previous) - self._process_queue() - - def service_next(self, field=None): - """ - Live event to select the next service item from the service manager. - """ - self.keypress_queue.append(ServiceItemAction.Next) - self._process_queue() - - def _process_queue(self): - """ - Process the service item request queue. The key presses can arrive - faster than the processing so implement a FIFO queue. - """ - if self.keypress_queue: - while len(self.keypress_queue) and not self.keypress_loop: - self.keypress_loop = True - keypress_command = self.keypress_queue.popleft() - if keypress_command == ServiceItemAction.Previous: - self.service_manager.previous_item() - elif keypress_command == ServiceItemAction.PreviousLastSlide: - # Go to the last slide of the previous item - self.service_manager.previous_item(last_slide=True) - else: - self.service_manager.next_item() - self.keypress_loop = False - def screen_size_changed(self): """ Settings dialog has changed the screen size of adjust output and screen previews. @@ -1382,3 +1352,35 @@ class LiveController(RegistryMixin, OpenLPMixin, SlideController): process the bootstrap post setup request """ self.post_set_up() + + def service_previous(self, field=None): + """ + Live event to select the previous service item from the service manager. + """ + self.keypress_queue.append(ServiceItemAction.Previous) + self._process_queue() + + def service_next(self, field=None): + """ + Live event to select the next service item from the service manager. + """ + self.keypress_queue.append(ServiceItemAction.Next) + self._process_queue() + + def _process_queue(self): + """ + Process the service item request queue. The key presses can arrive faster than the processing so implement a + FIFO queue. + """ + if self.keypress_queue: + while len(self.keypress_queue) and not self.keypress_loop: + self.keypress_loop = True + keypress_command = self.keypress_queue.popleft() + if keypress_command == ServiceItemAction.Previous: + self.service_manager.previous_item() + elif keypress_command == ServiceItemAction.PreviousLastSlide: + # Go to the last slide of the previous item + self.service_manager.previous_item(last_slide=True) + else: + self.service_manager.next_item() + self.keypress_loop = False From e0c623fd80b66b6deb4d1eb8cf2c3434a32d8346 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Fri, 2 May 2014 08:32:16 +0200 Subject: [PATCH 64/82] From 4231692ae11de4c60b40a116047d50888be56507 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Fri, 2 May 2014 08:34:57 +0200 Subject: [PATCH 65/82] removed print --- openlp/core/common/historycombobox.py | 2 +- openlp/core/ui/firsttimeform.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/openlp/core/common/historycombobox.py b/openlp/core/common/historycombobox.py index aadc0ea39..b6d06ff9f 100644 --- a/openlp/core/common/historycombobox.py +++ b/openlp/core/common/historycombobox.py @@ -53,7 +53,7 @@ class HistoryComboBox(QtGui.QComboBox): self.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Preferred) self.setInsertPolicy(QtGui.QComboBox.InsertAtTop) - def keyPressEvlent(self, event): + def keyPressEvent(self, event): """ Override the inherited keyPressEvent method to emit the ``returnPressed`` signal and to save the current text to the dropdown list. diff --git a/openlp/core/ui/firsttimeform.py b/openlp/core/ui/firsttimeform.py index a98d7861a..531c4b49f 100644 --- a/openlp/core/ui/firsttimeform.py +++ b/openlp/core/ui/firsttimeform.py @@ -170,7 +170,6 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard, RegistryProperties): elif self.currentId() == FirstTimePage.Progress: return -1 elif self.currentId() == FirstTimePage.NoInternet: - print(1111) return FirstTimePage.Progress elif self.currentId() == FirstTimePage.Themes: self.application.set_busy_cursor() From deff310f530ea02868125ce3890c7cba1b7d8f0a Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Fri, 2 May 2014 08:42:17 +0200 Subject: [PATCH 66/82] removed u' --- openlp/core/common/applocation.py | 2 +- openlp/core/common/settings.py | 3 +-- openlp/core/lib/plugin.py | 2 +- openlp/core/lib/screen.py | 2 +- openlp/core/ui/printserviceform.py | 2 +- openlp/core/ui/themestab.py | 2 +- openlp/core/ui/wizard.py | 2 +- openlp/core/utils/__init__.py | 2 +- openlp/plugins/alerts/forms/__init__.py | 2 +- openlp/plugins/bibles/forms/__init__.py | 2 +- openlp/plugins/bibles/lib/__init__.py | 4 ++-- openlp/plugins/bibles/lib/biblestab.py | 2 +- openlp/plugins/bibles/lib/db.py | 13 ++++++------- openlp/plugins/bibles/lib/http.py | 2 +- openlp/plugins/bibles/lib/mediaitem.py | 2 +- openlp/plugins/images/forms/__init__.py | 2 +- .../presentations/lib/presentationcontroller.py | 2 +- openlp/plugins/songs/forms/__init__.py | 2 +- openlp/plugins/songs/forms/songimportform.py | 8 ++++---- openlp/plugins/songs/lib/__init__.py | 2 +- openlp/plugins/songs/lib/easyslidesimport.py | 2 +- openlp/plugins/songs/lib/songbeamerimport.py | 2 +- openlp/plugins/songs/lib/songselect.py | 4 ++-- openlp/plugins/songs/lib/xml.py | 2 +- tests/functional/openlp_core_lib/test_lib.py | 2 +- tests/functional/openlp_core_utils/test_utils.py | 8 ++++---- 26 files changed, 39 insertions(+), 41 deletions(-) diff --git a/openlp/core/common/applocation.py b/openlp/core/common/applocation.py index 1fce25000..2bc4027b6 100644 --- a/openlp/core/common/applocation.py +++ b/openlp/core/common/applocation.py @@ -110,7 +110,7 @@ class AppLocation(object): :param extension: Defaults to *None*. The extension to search for. For example:: - u'.png' + '.png' """ path = AppLocation.get_data_path() if section: diff --git a/openlp/core/common/settings.py b/openlp/core/common/settings.py index 590452f73..3b7b31ca1 100644 --- a/openlp/core/common/settings.py +++ b/openlp/core/common/settings.py @@ -68,8 +68,7 @@ class Settings(QtCore.QSettings): ``__obsolete_settings__`` Each entry is structured in the following way:: - (u'general/enable slide loop', u'advanced/slide limits', - [(SlideLimits.Wrap, True), (SlideLimits.End, False)]) + ('general/enable slide loop', 'advanced/slide limits', [(SlideLimits.Wrap, True), (SlideLimits.End, False)]) The first entry is the *old key*; it will be removed. diff --git a/openlp/core/lib/plugin.py b/openlp/core/lib/plugin.py index 1f459524c..8cd13088d 100644 --- a/openlp/core/lib/plugin.py +++ b/openlp/core/lib/plugin.py @@ -129,7 +129,7 @@ class Plugin(QtCore.QObject, RegistryProperties): class MyPlugin(Plugin): def __init__(self): - super(MyPlugin, self).__init__('MyPlugin', version=u'0.1') + super(MyPlugin, self).__init__('MyPlugin', version='0.1') :param name: Defaults to *None*. The name of the plugin. :param default_settings: A dict containing the plugin's settings. The value to each key is the default value diff --git a/openlp/core/lib/screen.py b/openlp/core/lib/screen.py index 8f03d9ab2..d05200f2f 100644 --- a/openlp/core/lib/screen.py +++ b/openlp/core/lib/screen.py @@ -135,7 +135,7 @@ class ScreenList(object): Returns a list with the screens. This should only be used to display available screens to the user:: - [u'Screen 1 (primary)', u'Screen 2'] + ['Screen 1 (primary)', 'Screen 2'] """ screen_list = [] for screen in self.screen_list: diff --git a/openlp/core/ui/printserviceform.py b/openlp/core/ui/printserviceform.py index 489eefa78..41caaab52 100644 --- a/openlp/core/ui/printserviceform.py +++ b/openlp/core/ui/printserviceform.py @@ -242,7 +242,7 @@ class PrintServiceForm(QtGui.QDialog, Ui_PrintServiceDialog, RegistryProperties) Creates a html element. If ``text`` is given, the element's text will set and if a ``parent`` is given, the element is appended. - :param tag: The html tag, e. g. ``u'span'``. Defaults to ``None``. + :param tag: The html tag, e. g. ``'span'``. Defaults to ``None``. :param text: The text for the tag. Defaults to ``None``. :param parent: The parent element. Defaults to ``None``. :param classId: Value for the class attribute diff --git a/openlp/core/ui/themestab.py b/openlp/core/ui/themestab.py index 1f54f984b..0478f0ed0 100644 --- a/openlp/core/ui/themestab.py +++ b/openlp/core/ui/themestab.py @@ -190,7 +190,7 @@ class ThemesTab(SettingsTab): :param theme_list: The list of available themes:: - [u'Bible Theme', u'Song Theme'] + ['Bible Theme', 'Song Theme'] """ # Reload as may have been triggered by the ThemeManager. self.global_theme = Settings().value(self.settings_section + '/global theme') diff --git a/openlp/core/ui/wizard.py b/openlp/core/ui/wizard.py index 5815457b5..5996296b5 100644 --- a/openlp/core/ui/wizard.py +++ b/openlp/core/ui/wizard.py @@ -279,7 +279,7 @@ class OpenLPWizard(QtGui.QWizard, RegistryProperties): :param filters: The file extension filters. It should contain the file description as well as the file extension. For example:: - u'OpenLP 2.0 Databases (*.sqlite)' + 'OpenLP 2.0 Databases (*.sqlite)' """ if filters: filters += ';;' diff --git a/openlp/core/utils/__init__.py b/openlp/core/utils/__init__.py index a5b5f356a..add663132 100644 --- a/openlp/core/utils/__init__.py +++ b/openlp/core/utils/__init__.py @@ -113,7 +113,7 @@ def get_application_version(): """ Returns the application version of the running instance of OpenLP:: - {u'full': u'1.9.4-bzr1249', u'version': u'1.9.4', u'build': u'bzr1249'} + {'full': '1.9.4-bzr1249', 'version': '1.9.4', 'build': 'bzr1249'} """ global APPLICATION_VERSION if APPLICATION_VERSION: diff --git a/openlp/plugins/alerts/forms/__init__.py b/openlp/plugins/alerts/forms/__init__.py index 55d44372e..fbe3c7170 100644 --- a/openlp/plugins/alerts/forms/__init__.py +++ b/openlp/plugins/alerts/forms/__init__.py @@ -32,7 +32,7 @@ other class holds all the functional code, like slots and loading and saving. The first class, commonly known as the **Dialog** class, is typically named ``Ui_Dialog``. It is a slightly modified version of the class that the ``pyuic4`` command produces from Qt4's .ui file. Typical modifications will be -converting most strings from "" to u'' and using OpenLP's ``translate()`` function for translating strings. +converting most strings from "" to '' and using OpenLP's ``translate()`` function for translating strings. The second class, commonly known as the **Form** class, is typically named ``Form``. This class is the one which is instantiated and used. It uses dual inheritance to inherit from (usually) QtGui.QDialog and the Ui class mentioned diff --git a/openlp/plugins/bibles/forms/__init__.py b/openlp/plugins/bibles/forms/__init__.py index 974d6d7a5..d6c77a9a3 100644 --- a/openlp/plugins/bibles/forms/__init__.py +++ b/openlp/plugins/bibles/forms/__init__.py @@ -33,7 +33,7 @@ other class holds all the functional code, like slots and loading and saving. The first class, commonly known as the **Dialog** class, is typically named ``Ui_Dialog``. It is a slightly modified version of the class that the ``pyuic4`` command produces from Qt4's .ui file. Typical modifications will be -converting most strings from "" to u'' and using OpenLP's ``translate()`` function for translating strings. +converting most strings from "" to '' and using OpenLP's ``translate()`` function for translating strings. The second class, commonly known as the **Form** class, is typically named ``Form``. This class is the one which is instantiated and used. It uses dual inheritance to inherit from (usually) QtGui.QDialog and the Ui class mentioned diff --git a/openlp/plugins/bibles/lib/__init__.py b/openlp/plugins/bibles/lib/__init__.py index 50a0e2a63..d67319797 100644 --- a/openlp/plugins/bibles/lib/__init__.py +++ b/openlp/plugins/bibles/lib/__init__.py @@ -262,7 +262,7 @@ def parse_reference(reference, bible, language_selection, book_ref_id=False): For example:: - [(u'John', 3, 16, 18), (u'John', 4, 1, 1)] + [('John', 3, 16, 18), ('John', 4, 1, 1)] **Reference string details:** @@ -311,7 +311,7 @@ def parse_reference(reference, bible, language_selection, book_ref_id=False): ``(?P[0-9]+)`` The ``to_verse`` reference is equivalent to group 2. - The full reference is matched against get_reference_match(u'full'). This regular expression looks like this: + The full reference is matched against get_reference_match('full'). This regular expression looks like this: ``^\s*(?!\s)(?P[\d]*[^\d]+)(?Dialog``. It is a slightly modified version of the class that the ``pyuic4`` command produces from Qt4's .ui file. Typical modifications will be -converting most strings from "" to u'' and using OpenLP's ``translate()`` function for translating strings. +converting most strings from "" to '' and using OpenLP's ``translate()`` function for translating strings. The second class, commonly known as the **Form** class, is typically named ``Form``. This class is the one which is instantiated and used. It uses dual inheritance to inherit from (usually) QtGui.QDialog and the Ui class mentioned diff --git a/openlp/plugins/presentations/lib/presentationcontroller.py b/openlp/plugins/presentations/lib/presentationcontroller.py index 6c8d7fb8c..3060bcdb0 100644 --- a/openlp/plugins/presentations/lib/presentationcontroller.py +++ b/openlp/plugins/presentations/lib/presentationcontroller.py @@ -354,7 +354,7 @@ class PresentationController(object): class MyPresentationController(PresentationController): def __init__(self, plugin): PresentationController.__init( - self, plugin, u'My Presenter App') + self, plugin, 'My Presenter App') :param plugin: Defaults to *None*. The presentationplugin object :param name: Name of the application, to appear in the application diff --git a/openlp/plugins/songs/forms/__init__.py b/openlp/plugins/songs/forms/__init__.py index 3094a0eda..eb29c12bf 100644 --- a/openlp/plugins/songs/forms/__init__.py +++ b/openlp/plugins/songs/forms/__init__.py @@ -34,7 +34,7 @@ code, like slots and loading and saving. The first class, commonly known as the **Dialog** class, is typically named ``Ui_Dialog``. It is a slightly modified version of the class that the ``pyuic4`` command produces from Qt4's .ui file. Typical modifications will be -converting most strings from "" to u'' and using OpenLP's ``translate()`` +converting most strings from "" to '' and using OpenLP's ``translate()`` function for translating strings. The second class, commonly known as the **Form** class, is typically named diff --git a/openlp/plugins/songs/forms/songimportform.py b/openlp/plugins/songs/forms/songimportform.py index 21569a034..2a05f06cd 100644 --- a/openlp/plugins/songs/forms/songimportform.py +++ b/openlp/plugins/songs/forms/songimportform.py @@ -231,11 +231,11 @@ class SongImportForm(OpenLPWizard, RegistryProperties): """ Opens a QFileDialog and writes the filenames to the given listbox. - :param title: The title of the dialog (unicode). + :param title: The title of the dialog (str). :param listbox: A listbox (QListWidget). - :param filters: The file extension filters. It should contain the file descriptions - as well as the file extensions. For example:: - u'SongBeamer Files (*.sng)' + :param filters: The file extension filters. It should contain the file descriptions as well as the file + extensions. For example:: + 'SongBeamer Files (*.sng)' """ if filters: filters += ';;' diff --git a/openlp/plugins/songs/lib/__init__.py b/openlp/plugins/songs/lib/__init__.py index aa9fbc4c9..95868ffa7 100644 --- a/openlp/plugins/songs/lib/__init__.py +++ b/openlp/plugins/songs/lib/__init__.py @@ -434,7 +434,7 @@ def strip_rtf(text, default_encoding=None): # Current font is the font tag we last met. font = '' # Character encoding is defined inside fonttable. - # font_table could contain eg u'0': u'cp1252' + # font_table could contain eg '0': u'cp1252' font_table = {'': ''} # Stack of things to keep track of when entering/leaving groups. stack = [] diff --git a/openlp/plugins/songs/lib/easyslidesimport.py b/openlp/plugins/songs/lib/easyslidesimport.py index e28e7bf97..ca9a9b755 100644 --- a/openlp/plugins/songs/lib/easyslidesimport.py +++ b/openlp/plugins/songs/lib/easyslidesimport.py @@ -292,7 +292,7 @@ class EasySlidesImport(SongImport): return True def _extract_region(self, line): - # this was true already: line[0:7] == u'[region': + # this was true already: line[0:7] == '[region': """ Extract the region from text diff --git a/openlp/plugins/songs/lib/songbeamerimport.py b/openlp/plugins/songs/lib/songbeamerimport.py index a0b166ded..9c5e74c06 100644 --- a/openlp/plugins/songs/lib/songbeamerimport.py +++ b/openlp/plugins/songs/lib/songbeamerimport.py @@ -171,7 +171,7 @@ class SongBeamerImport(SongImport): :param line: The line in the file. It should consist of a tag and a value for this tag (unicode):: - u'#Title=Nearer my God to Thee' + '#Title=Nearer my God to Thee' """ tag_val = line.split('=', 1) if len(tag_val) == 1: diff --git a/openlp/plugins/songs/lib/songselect.py b/openlp/plugins/songs/lib/songselect.py index a18b08c8a..232cdb9cf 100644 --- a/openlp/plugins/songs/lib/songselect.py +++ b/openlp/plugins/songs/lib/songselect.py @@ -146,14 +146,14 @@ class SongSelectImport(object): try: song_page = BeautifulSoup(self.opener.open(song['link']).read(), 'lxml') except (TypeError, HTTPError) as e: - log.exception(u'Could not get song from SongSelect, %s', e) + log.exception('Could not get song from SongSelect, %s', e) return None if callback: callback() try: lyrics_page = BeautifulSoup(self.opener.open(song['link'] + '/lyrics').read(), 'lxml') except (TypeError, HTTPError): - log.exception(u'Could not get lyrics from SongSelect') + log.exception('Could not get lyrics from SongSelect') return None if callback: callback() diff --git a/openlp/plugins/songs/lib/xml.py b/openlp/plugins/songs/lib/xml.py index 68641a1d5..b856cb53f 100644 --- a/openlp/plugins/songs/lib/xml.py +++ b/openlp/plugins/songs/lib/xml.py @@ -344,7 +344,7 @@ class OpenLyrics(object): """ Tests the given text for not closed formatting tags and returns a tuple consisting of two unicode strings:: - (u'{st}{r}', u'{/r}{/st}') + ('{st}{r}', '{/r}{/st}') The first unicode string are the start tags (for the next slide). The second unicode string are the end tags. diff --git a/tests/functional/openlp_core_lib/test_lib.py b/tests/functional/openlp_core_lib/test_lib.py index b4334a728..2e06e47fe 100644 --- a/tests/functional/openlp_core_lib/test_lib.py +++ b/tests/functional/openlp_core_lib/test_lib.py @@ -305,7 +305,7 @@ class TestLib(TestCase): # WHEN: We convert an image to a byte array result = image_to_byte(mocked_image) - # THEN: We should receive a value of u'base64mock' + # THEN: We should receive a value of 'base64mock' MockedQtCore.QByteArray.assert_called_with() MockedQtCore.QBuffer.assert_called_with(mocked_byte_array) mocked_buffer.open.assert_called_with('writeonly') diff --git a/tests/functional/openlp_core_utils/test_utils.py b/tests/functional/openlp_core_utils/test_utils.py index a97d757ea..075ecb14f 100644 --- a/tests/functional/openlp_core_utils/test_utils.py +++ b/tests/functional/openlp_core_utils/test_utils.py @@ -250,7 +250,7 @@ class TestUtils(TestCase): # THEN: The user agent is a Linux (or ChromeOS) user agent result = 'Linux' in user_agent or 'CrOS' in user_agent - self.assertTrue(result, u'The user agent should be a valid Linux user agent') + self.assertTrue(result, 'The user agent should be a valid Linux user agent') def get_user_agent_windows_test(self): """ @@ -265,7 +265,7 @@ class TestUtils(TestCase): user_agent = _get_user_agent() # THEN: The user agent is a Linux (or ChromeOS) user agent - self.assertIn('Windows', user_agent, u'The user agent should be a valid Windows user agent') + self.assertIn('Windows', user_agent, 'The user agent should be a valid Windows user agent') def get_user_agent_macos_test(self): """ @@ -280,7 +280,7 @@ class TestUtils(TestCase): user_agent = _get_user_agent() # THEN: The user agent is a Linux (or ChromeOS) user agent - self.assertIn('Mac OS X', user_agent, u'The user agent should be a valid OS X user agent') + self.assertIn('Mac OS X', user_agent, 'The user agent should be a valid OS X user agent') def get_user_agent_default_test(self): """ @@ -295,7 +295,7 @@ class TestUtils(TestCase): user_agent = _get_user_agent() # THEN: The user agent is a Linux (or ChromeOS) user agent - self.assertIn('NetBSD', user_agent, u'The user agent should be the default user agent') + self.assertIn('NetBSD', user_agent, 'The user agent should be the default user agent') def get_web_page_no_url_test(self): """ From 87587ab8e8c86d7649b288d02e6c84271fe75b04 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Fri, 2 May 2014 08:50:54 +0200 Subject: [PATCH 67/82] short lines --- openlp/plugins/bibles/lib/mediaitem.py | 43 ++++++++++---------------- 1 file changed, 16 insertions(+), 27 deletions(-) diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index 5d9c598ed..dd14c6409 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -360,8 +360,7 @@ class BibleMediaItem(MediaManagerItem): combo boxes on the 'Advanced Search' Tab. This is not of any importance of the 'Quick Search' Tab. :param bible: The bible to initialise (unicode). - :param last_book_id: The "book reference id" of the book which is chosen at the moment. - (int) + :param last_book_id: The "book reference id" of the book which is chosen at the moment. (int) """ log.debug('initialise_advanced_bible %s, %s', bible, last_book_id) book_data = self.plugin.manager.get_books(bible) @@ -421,9 +420,8 @@ class BibleMediaItem(MediaManagerItem): def update_auto_completer(self): """ - This updates the bible book completion list for the search field. The - completion depends on the bible. It is only updated when we are doing a - reference search, otherwise the auto completion list is removed. + This updates the bible book completion list for the search field. The completion depends on the bible. It is + only updated when we are doing a reference search, otherwise the auto completion list is removed. """ log.debug('update_auto_completer') # Save the current search type to the configuration. @@ -593,8 +591,7 @@ class BibleMediaItem(MediaManagerItem): :param range_from: The first number of the range (int). :param range_to: The last number of the range (int). :param combo: The combo box itself (QComboBox). - :param restore: If True, then the combo's currentText will be restored after - adjusting (if possible). + :param restore: If True, then the combo's currentText will be restored after adjusting (if possible). """ log.debug('adjust_combo_box %s, %s, %s', combo, range_from, range_to) if restore: @@ -640,8 +637,8 @@ class BibleMediaItem(MediaManagerItem): def on_quick_search_button(self): """ - Does a quick search and saves the search results. Quick search can - either be "Reference Search" or "Text Search". + Does a quick search and saves the search results. Quick search can either be "Reference Search" or + "Text Search". """ log.debug('Quick Search Button clicked') self.quickSearchButton.setEnabled(False) @@ -696,8 +693,7 @@ class BibleMediaItem(MediaManagerItem): def display_results(self, bible, second_bible=''): """ - Displays the search results in the media manager. All data needed for - further action is saved for/in each row. + Displays the search results in the media manager. All data needed for further action is saved for/in each row. """ items = self.build_display_results(bible, second_bible, self.search_results) for bible_verse in items: @@ -708,8 +704,7 @@ class BibleMediaItem(MediaManagerItem): def build_display_results(self, bible, second_bible, search_results): """ - Displays the search results in the media manager. All data needed for - further action is saved for/in each row. + Displays the search results in the media manager. All data needed for further action is saved for/in each row. """ verse_separator = get_reference_separator('sep_v_display') version = self.plugin.manager.get_meta_data(bible, 'name').value @@ -858,9 +853,8 @@ class BibleMediaItem(MediaManagerItem): def format_title(self, start_bitem, old_bitem): """ - This method is called, when we have to change the title, because - we are at the end of a verse range. E. g. if we want to add - Genesis 1:1-6 as well as Daniel 2:14. + This method is called, when we have to change the title, because we are at the end of a verse range. E. g. if we + want to add Genesis 1:1-6 as well as Daniel 2:14. :param start_bitem: The first item of a range. :param old_bitem: The last item of a range. @@ -890,10 +884,8 @@ class BibleMediaItem(MediaManagerItem): def check_title(self, bitem, old_bitem): """ - This method checks if we are at the end of an verse range. If that is - the case, we return True, otherwise False. E. g. if we added - - Genesis 1:1-6, but the next verse is Daniel 2:14, we return True. + This method checks if we are at the end of an verse range. If that is the case, we return True, otherwise False. + E. g. if we added Genesis 1:1-6, but the next verse is Daniel 2:14, we return True. :param bitem: The item we are dealing with at the moment. :param old_bitem: The item we were previously dealing with. @@ -917,18 +909,15 @@ class BibleMediaItem(MediaManagerItem): return True elif old_chapter + 1 == chapter and (verse != 1 or old_verse != self.plugin.manager.get_verse_count(old_bible, old_book, old_chapter)): - # We are in the following chapter, but the last verse was not the - # last verse of the chapter or the current verse is not the - # first one of the chapter. + # We are in the following chapter, but the last verse was not the last verse of the chapter or the current + # verse is not the first one of the chapter. return True return False def format_verse(self, old_chapter, chapter, verse): """ - Formats and returns the text, each verse starts with, for the given - chapter and verse. The text is either surrounded by round, square, - - curly brackets or no brackets at all. For example:: + Formats and returns the text, each verse starts with, for the given chapter and verse. The text is either + surrounded by round, square, curly brackets or no brackets at all. For example:: '{su}1:1{/su}' From f4646f955b39462b03ed5b84797a346010e6fa52 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Fri, 2 May 2014 20:02:25 +0200 Subject: [PATCH 68/82] From 50fb727bae0032aeaea618c8ad67397176020d2b Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Fri, 2 May 2014 20:11:45 +0200 Subject: [PATCH 69/82] From 8b76dcc216526619c82616193887c7a0dbc6d425 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Sat, 3 May 2014 14:56:58 +0200 Subject: [PATCH 70/82] Fix bug 1310523 --- openlp/plugins/songs/lib/xml.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openlp/plugins/songs/lib/xml.py b/openlp/plugins/songs/lib/xml.py index b856cb53f..87e5da21e 100644 --- a/openlp/plugins/songs/lib/xml.py +++ b/openlp/plugins/songs/lib/xml.py @@ -310,9 +310,9 @@ class OpenLyrics(object): verse_tag = verse[0]['type'][0].lower() verse_number = verse[0]['label'] verse_def = verse_tag + verse_number - verse_tags.append(verse_def) # Create the letter from the number of duplicates - verse[0]['suffix'] = chr(96 + verse_tags.count(verse_def)) + verse[0][u'suffix'] = chr(97 + (verse_tags.count(verse_def) % 26)) + verse_tags.append(verse_def) # If the verse tag is a duplicate use the suffix letter for verse in verse_list: verse_tag = verse[0]['type'][0].lower() From 938f59416d2b89d20e41730d3ea8f1b6f14f913b Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Sat, 3 May 2014 15:46:48 +0200 Subject: [PATCH 71/82] Fix bug 1199639 Fixes: https://launchpad.net/bugs/1199639 --- openlp/plugins/songs/forms/editsongform.py | 36 ++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index 1814655ea..81a66e3f7 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -107,6 +107,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): self.audio_list_widget.setAlternatingRowColors(True) self.find_verse_split = re.compile('---\[\]---\n', re.UNICODE) self.whitespace = re.compile(r'\W+', re.UNICODE) + self.find_tags = re.compile(u'\{/?\w+\}', re.UNICODE) def _load_objects(self, cls, combo, cache): """ @@ -234,8 +235,43 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): self.manager.save_object(book) else: return False + # Validate tags (lp#1199639) + misplaced_tags = [] + for i in range(self.verse_list_widget.rowCount()): + item = self.verse_list_widget.item(i, 0) + tags = self.find_tags.findall(item.text()) + if not self._validate_tags(tags): + field = item.data(QtCore.Qt.UserRole) + misplaced_tags.append('%s %s' % (VerseType.translated_name(field[0]), field[1:])) + if misplaced_tags: + critical_error_message_box( + message=translate('SongsPlugin.EditSongForm', + 'There are misplaced formatting tags in the following verses:\n\n%s\n\n' + 'Please correct these tags before continuing.' % ', '.join(misplaced_tags))) + return False return True + def _validate_tags(self, _tags): + """ + Validates a list of tags + Deletes the first affiliated tag pair which is located side by side in the list + and call itself recursively with the shortened tag list. + If there is any misplaced tag in the list, either the length of the tag list is not even, + or the function won't find any tag pairs side by side. + If there is no misplaced tag, the length of the list will be zero on any recursive run. + + :return: True if the function can't find any mismatched tags. Else False. + """ + if len(_tags) == 0: + return True + if len(_tags) % 2 != 0: + return False + for i in range(len(_tags)-1): + if _tags[i+1] == "{/" + _tags[i][1:]: + del _tags[i:i+2] + return self._validate_tags(_tags) + return False + def _process_lyrics(self): """ Process the lyric data entered by the user into the OpenLP XML format. From a7bdb9dfa0962b96c411859037707cedb4d13e1f Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Sat, 3 May 2014 15:56:32 +0200 Subject: [PATCH 72/82] Fix bug 1310523 Fixes: https://launchpad.net/bugs/1310523 --- openlp/plugins/songs/forms/editsongform.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index 81a66e3f7..992cb533c 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -237,11 +237,13 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): return False # Validate tags (lp#1199639) misplaced_tags = [] + verse_tags = [] for i in range(self.verse_list_widget.rowCount()): item = self.verse_list_widget.item(i, 0) tags = self.find_tags.findall(item.text()) + field = item.data(QtCore.Qt.UserRole) + verse_tags.append(field) if not self._validate_tags(tags): - field = item.data(QtCore.Qt.UserRole) misplaced_tags.append('%s %s' % (VerseType.translated_name(field[0]), field[1:])) if misplaced_tags: critical_error_message_box( @@ -249,6 +251,17 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): 'There are misplaced formatting tags in the following verses:\n\n%s\n\n' 'Please correct these tags before continuing.' % ', '.join(misplaced_tags))) return False + for tag in verse_tags: + if verse_tags.count(tag) > 26: + # lp#1310523: OpenLyrics allows only a-z variants of one verse: + # http://openlyrics.info/dataformat.html#verse-name + critical_error_message_box(message=translate( + 'SongsPlugin.EditSongForm', 'You have %(count)s verses named %(name)s %(number)s. ' + 'You can have at most 26 verses with the same name' % + {'count': verse_tags.count(tag), + 'name': VerseType.translated_name(tag[0]), + 'number': tag[1:]})) + return False return True def _validate_tags(self, _tags): From afaf9480c317c8336a1a722a0813e515b047f810 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Sat, 3 May 2014 15:58:31 +0200 Subject: [PATCH 73/82] Rename variable --- openlp/plugins/songs/forms/editsongform.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index 992cb533c..ae47a110f 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -264,7 +264,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): return False return True - def _validate_tags(self, _tags): + def _validate_tags(self, tags): """ Validates a list of tags Deletes the first affiliated tag pair which is located side by side in the list @@ -273,16 +273,17 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): or the function won't find any tag pairs side by side. If there is no misplaced tag, the length of the list will be zero on any recursive run. + :param tags: A list of tags :return: True if the function can't find any mismatched tags. Else False. """ - if len(_tags) == 0: + if len(tags) == 0: return True - if len(_tags) % 2 != 0: + if len(tags) % 2 != 0: return False - for i in range(len(_tags)-1): - if _tags[i+1] == "{/" + _tags[i][1:]: - del _tags[i:i+2] - return self._validate_tags(_tags) + for i in range(len(tags)-1): + if tags[i+1] == "{/" + tags[i][1:]: + del tags[i:i+2] + return self._validate_tags(tags) return False def _process_lyrics(self): From 635ab98f7301dedd8c5c820490e8b610a55cad34 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Sat, 3 May 2014 16:19:29 +0200 Subject: [PATCH 74/82] Fix bug 1216234 Fixes: https://launchpad.net/bugs/1216234 --- openlp/plugins/bibles/lib/mediaitem.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index dd14c6409..76f91071a 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -60,7 +60,6 @@ class BibleMediaItem(MediaManagerItem): log.info('Bible Media Item loaded') def __init__(self, parent, plugin): - self.icon_path = 'songs/song' self.lock_icon = build_icon(':/bibles/bibles_search_lock.png') self.unlock_icon = build_icon(':/bibles/bibles_search_unlock.png') MediaManagerItem.__init__(self, parent, plugin) @@ -172,6 +171,7 @@ class BibleMediaItem(MediaManagerItem): self.page_layout.addWidget(tab) tab.setVisible(False) lock_button.toggled.connect(self.on_lock_button_toggled) + second_combo_box.currentIndexChanged.connect(self.on_second_bible_combobox_index_changed) setattr(self, prefix + 'VersionLabel', version_label) setattr(self, prefix + 'VersionComboBox', version_combo_box) setattr(self, prefix + 'SecondLabel', second_label) @@ -263,11 +263,15 @@ class BibleMediaItem(MediaManagerItem): def config_update(self): log.debug('config_update') if Settings().value(self.settings_section + '/second bibles'): + self.quickSecondLabel.setVisible(True) + self.quickSecondComboBox.setVisible(True) self.advancedSecondLabel.setVisible(True) self.advancedSecondComboBox.setVisible(True) self.quickSecondLabel.setVisible(True) self.quickSecondComboBox.setVisible(True) else: + self.quickSecondLabel.setVisible(False) + self.quickSecondComboBox.setVisible(False) self.advancedSecondLabel.setVisible(False) self.advancedSecondComboBox.setVisible(False) self.quickSecondLabel.setVisible(False) @@ -459,6 +463,17 @@ class BibleMediaItem(MediaManagerItem): books.sort(key=get_locale_key) set_case_insensitive_completer(books, self.quick_search_edit) + def on_second_bible_combobox_index_changed(self, selection): + """ + Activate the style combobox only when no second bible is selected + """ + if selection == 0: + self.quickStyleComboBox.setEnabled(True) + self.advancedStyleComboBox.setEnabled(True) + else: + self.quickStyleComboBox.setEnabled(False) + self.advancedStyleComboBox.setEnabled(False) + def on_import_click(self): if not hasattr(self, 'import_wizard'): self.import_wizard = BibleImportForm(self, self.plugin.manager, self.plugin) From a6a08bcfdd1b10cbfc0febb29766aaa5e05afe6a Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Sat, 3 May 2014 17:01:43 +0200 Subject: [PATCH 75/82] Fix bug 1157938 Fixes: https://launchpad.net/bugs/1157938 --- .bzrignore | 1 + openlp/core/ui/aboutdialog.py | 2 +- openlp/core/ui/exceptiondialog.py | 3 ++- openlp/core/ui/filerenamedialog.py | 3 ++- openlp/core/ui/firsttimelanguagedialog.py | 2 ++ openlp/core/ui/firsttimewizard.py | 2 ++ openlp/core/ui/formattingtagdialog.py | 1 + openlp/core/ui/mainwindow.py | 2 +- openlp/core/ui/plugindialog.py | 2 ++ openlp/core/ui/printservicedialog.py | 1 + openlp/core/ui/serviceitemeditdialog.py | 2 ++ openlp/core/ui/settingsdialog.py | 2 +- openlp/core/ui/shortcutlistdialog.py | 1 + openlp/core/ui/starttimedialog.py | 2 ++ openlp/core/ui/themelayoutdialog.py | 2 ++ openlp/core/ui/themewizard.py | 1 + openlp/core/ui/wizard.py | 1 + openlp/plugins/alerts/forms/alertdialog.py | 2 +- openlp/plugins/bibles/forms/booknamedialog.py | 2 ++ openlp/plugins/bibles/forms/editbibledialog.py | 2 +- openlp/plugins/bibles/forms/languagedialog.py | 2 ++ openlp/plugins/custom/forms/editcustomdialog.py | 2 +- openlp/plugins/custom/forms/editcustomslidedialog.py | 3 ++- openlp/plugins/songs/forms/authorsdialog.py | 2 +- openlp/plugins/songs/forms/editsongdialog.py | 2 +- openlp/plugins/songs/forms/editversedialog.py | 1 + openlp/plugins/songs/forms/mediafilesdialog.py | 2 +- openlp/plugins/songs/forms/songbookdialog.py | 3 ++- openlp/plugins/songs/forms/songmaintenancedialog.py | 1 + openlp/plugins/songs/forms/topicsdialog.py | 3 ++- openlp/plugins/songusage/forms/songusagedeletedialog.py | 2 ++ openlp/plugins/songusage/forms/songusagedetaildialog.py | 1 + 32 files changed, 46 insertions(+), 14 deletions(-) diff --git a/.bzrignore b/.bzrignore index 6377150e0..a0c3f0b4f 100644 --- a/.bzrignore +++ b/.bzrignore @@ -32,3 +32,4 @@ tests.kdev4 *.orig __pycache__ *.dll +.directory diff --git a/openlp/core/ui/aboutdialog.py b/openlp/core/ui/aboutdialog.py index 276a073bb..251e0657c 100644 --- a/openlp/core/ui/aboutdialog.py +++ b/openlp/core/ui/aboutdialog.py @@ -44,7 +44,7 @@ class Ui_AboutDialog(object): Set up the UI for the dialog. """ about_dialog.setObjectName('about_dialog') - about_dialog.setWindowIcon(build_icon(':/icon/openlp-logo-16x16.png')) + about_dialog.setWindowIcon(build_icon(':/icon/openlp-logo.svg')) self.about_dialog_layout = QtGui.QVBoxLayout(about_dialog) self.about_dialog_layout.setObjectName('about_dialog_layout') self.logo_label = QtGui.QLabel(about_dialog) diff --git a/openlp/core/ui/exceptiondialog.py b/openlp/core/ui/exceptiondialog.py index b8b3941cd..329ed7797 100644 --- a/openlp/core/ui/exceptiondialog.py +++ b/openlp/core/ui/exceptiondialog.py @@ -32,7 +32,7 @@ The GUI widgets of the exception dialog. from PyQt4 import QtGui -from openlp.core.lib import translate +from openlp.core.lib import translate, build_icon from openlp.core.lib.ui import create_button, create_button_box @@ -45,6 +45,7 @@ class Ui_ExceptionDialog(object): Set up the UI. """ exception_dialog.setObjectName('exception_dialog') + exception_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) self.exception_layout = QtGui.QVBoxLayout(exception_dialog) self.exception_layout.setObjectName('exception_layout') self.message_layout = QtGui.QHBoxLayout() diff --git a/openlp/core/ui/filerenamedialog.py b/openlp/core/ui/filerenamedialog.py index 0900b82c3..4a316aece 100644 --- a/openlp/core/ui/filerenamedialog.py +++ b/openlp/core/ui/filerenamedialog.py @@ -31,7 +31,7 @@ The UI widgets for the rename dialog """ from PyQt4 import QtCore, QtGui -from openlp.core.lib import translate +from openlp.core.lib import translate, build_icon from openlp.core.lib.ui import create_button_box @@ -44,6 +44,7 @@ class Ui_FileRenameDialog(object): Set up the UI """ file_rename_dialog.setObjectName('file_rename_dialog') + file_rename_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) file_rename_dialog.resize(300, 10) self.dialog_layout = QtGui.QGridLayout(file_rename_dialog) self.dialog_layout.setObjectName('dialog_layout') diff --git a/openlp/core/ui/firsttimelanguagedialog.py b/openlp/core/ui/firsttimelanguagedialog.py index 10b341c2c..b4263e498 100644 --- a/openlp/core/ui/firsttimelanguagedialog.py +++ b/openlp/core/ui/firsttimelanguagedialog.py @@ -32,6 +32,7 @@ The UI widgets of the language selection dialog. from PyQt4 import QtGui from openlp.core.common import translate +from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box @@ -44,6 +45,7 @@ class Ui_FirstTimeLanguageDialog(object): Set up the UI. """ language_dialog.setObjectName('language_dialog') + language_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) language_dialog.resize(300, 50) self.dialog_layout = QtGui.QVBoxLayout(language_dialog) self.dialog_layout.setContentsMargins(8, 8, 8, 8) diff --git a/openlp/core/ui/firsttimewizard.py b/openlp/core/ui/firsttimewizard.py index 1a270f931..35623ded2 100644 --- a/openlp/core/ui/firsttimewizard.py +++ b/openlp/core/ui/firsttimewizard.py @@ -34,6 +34,7 @@ from PyQt4 import QtCore, QtGui import sys from openlp.core.common import translate +from openlp.core.lib import build_icon from openlp.core.lib.ui import add_welcome_page @@ -60,6 +61,7 @@ class Ui_FirstTimeWizard(object): Set up the UI. """ first_time_wizard.setObjectName('first_time_wizard') + first_time_wizard.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) first_time_wizard.resize(550, 386) first_time_wizard.setModal(True) first_time_wizard.setWizardStyle(QtGui.QWizard.ModernStyle) diff --git a/openlp/core/ui/formattingtagdialog.py b/openlp/core/ui/formattingtagdialog.py index 387bca0a7..569405a05 100644 --- a/openlp/core/ui/formattingtagdialog.py +++ b/openlp/core/ui/formattingtagdialog.py @@ -45,6 +45,7 @@ class Ui_FormattingTagDialog(object): Set up the UI """ formatting_tag_dialog.setObjectName('formatting_tag_dialog') + formatting_tag_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) formatting_tag_dialog.resize(725, 548) self.list_data_grid_layout = QtGui.QVBoxLayout(formatting_tag_dialog) self.list_data_grid_layout.setMargin(8) diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index 9c193b079..5b5aa0781 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -89,7 +89,7 @@ class Ui_MainWindow(object): Set up the user interface """ main_window.setObjectName('MainWindow') - main_window.setWindowIcon(build_icon(':/icon/openlp-logo-64x64.png')) + main_window.setWindowIcon(build_icon(':/icon/openlp-logo.svg')) main_window.setDockNestingEnabled(True) # Set up the main container, which contains all the other form widgets. self.main_content = QtGui.QWidget(main_window) diff --git a/openlp/core/ui/plugindialog.py b/openlp/core/ui/plugindialog.py index f4d2d991b..40311677e 100644 --- a/openlp/core/ui/plugindialog.py +++ b/openlp/core/ui/plugindialog.py @@ -32,6 +32,7 @@ The UI widgets of the plugin view dialog from PyQt4 import QtCore, QtGui from openlp.core.common import UiStrings, translate +from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box @@ -44,6 +45,7 @@ class Ui_PluginViewDialog(object): Set up the UI """ pluginViewDialog.setObjectName('pluginViewDialog') + pluginViewDialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) pluginViewDialog.setWindowModality(QtCore.Qt.ApplicationModal) self.plugin_layout = QtGui.QVBoxLayout(pluginViewDialog) self.plugin_layout.setObjectName('plugin_layout') diff --git a/openlp/core/ui/printservicedialog.py b/openlp/core/ui/printservicedialog.py index f59dcb044..0873a0b4a 100644 --- a/openlp/core/ui/printservicedialog.py +++ b/openlp/core/ui/printservicedialog.py @@ -56,6 +56,7 @@ class Ui_PrintServiceDialog(object): Set up the UI """ print_service_dialog.setObjectName('print_service_dialog') + print_service_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) print_service_dialog.resize(664, 594) self.main_layout = QtGui.QVBoxLayout(print_service_dialog) self.main_layout.setSpacing(0) diff --git a/openlp/core/ui/serviceitemeditdialog.py b/openlp/core/ui/serviceitemeditdialog.py index 76139c875..1fa19eb31 100644 --- a/openlp/core/ui/serviceitemeditdialog.py +++ b/openlp/core/ui/serviceitemeditdialog.py @@ -32,6 +32,7 @@ The UI widgets for the service item edit dialog from PyQt4 import QtGui from openlp.core.common import translate +from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box, create_button @@ -44,6 +45,7 @@ class Ui_ServiceItemEditDialog(object): Set up the UI """ serviceItemEditDialog.setObjectName('serviceItemEditDialog') + serviceItemEditDialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) self.dialog_layout = QtGui.QGridLayout(serviceItemEditDialog) self.dialog_layout.setContentsMargins(8, 8, 8, 8) self.dialog_layout.setSpacing(8) diff --git a/openlp/core/ui/settingsdialog.py b/openlp/core/ui/settingsdialog.py index f625680d6..fbac6a155 100644 --- a/openlp/core/ui/settingsdialog.py +++ b/openlp/core/ui/settingsdialog.py @@ -45,8 +45,8 @@ class Ui_SettingsDialog(object): Set up the UI """ settings_dialog.setObjectName('settings_dialog') + settings_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) settings_dialog.resize(800, 500) - settings_dialog.setWindowIcon(build_icon(':/system/system_settings.png')) self.dialog_layout = QtGui.QGridLayout(settings_dialog) self.dialog_layout.setObjectName('dialog_layout') self.dialog_layout.setMargin(8) diff --git a/openlp/core/ui/shortcutlistdialog.py b/openlp/core/ui/shortcutlistdialog.py index 54433da24..ef2dc4056 100644 --- a/openlp/core/ui/shortcutlistdialog.py +++ b/openlp/core/ui/shortcutlistdialog.py @@ -66,6 +66,7 @@ class Ui_ShortcutListDialog(object): Set up the UI """ shortcutListDialog.setObjectName('shortcutListDialog') + shortcutListDialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) shortcutListDialog.resize(500, 438) self.shortcut_list_layout = QtGui.QVBoxLayout(shortcutListDialog) self.shortcut_list_layout.setObjectName('shortcut_list_layout') diff --git a/openlp/core/ui/starttimedialog.py b/openlp/core/ui/starttimedialog.py index 54e0629a4..cfd507bb2 100644 --- a/openlp/core/ui/starttimedialog.py +++ b/openlp/core/ui/starttimedialog.py @@ -32,6 +32,7 @@ The UI widgets for the time dialog from PyQt4 import QtCore, QtGui from openlp.core.common import UiStrings, translate +from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box @@ -44,6 +45,7 @@ class Ui_StartTimeDialog(object): Set up the UI """ StartTimeDialog.setObjectName('StartTimeDialog') + StartTimeDialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) StartTimeDialog.resize(350, 10) self.dialog_layout = QtGui.QGridLayout(StartTimeDialog) self.dialog_layout.setObjectName('dialog_layout') diff --git a/openlp/core/ui/themelayoutdialog.py b/openlp/core/ui/themelayoutdialog.py index 023d6259c..d3fbeed2d 100644 --- a/openlp/core/ui/themelayoutdialog.py +++ b/openlp/core/ui/themelayoutdialog.py @@ -32,6 +32,7 @@ The layout of the theme from PyQt4 import QtGui from openlp.core.common import translate +from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box @@ -44,6 +45,7 @@ class Ui_ThemeLayoutDialog(object): Set up the UI """ themeLayoutDialog.setObjectName('themeLayoutDialogDialog') + themeLayoutDialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) self.preview_layout = QtGui.QVBoxLayout(themeLayoutDialog) self.preview_layout.setObjectName('preview_layout') self.preview_area = QtGui.QWidget(themeLayoutDialog) diff --git a/openlp/core/ui/themewizard.py b/openlp/core/ui/themewizard.py index 77ccb0663..bda52c807 100644 --- a/openlp/core/ui/themewizard.py +++ b/openlp/core/ui/themewizard.py @@ -46,6 +46,7 @@ class Ui_ThemeWizard(object): Set up the UI """ themeWizard.setObjectName('OpenLP.ThemeWizard') + themeWizard.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) themeWizard.setModal(True) themeWizard.setWizardStyle(QtGui.QWizard.ModernStyle) themeWizard.setOptions(QtGui.QWizard.IndependentPages | diff --git a/openlp/core/ui/wizard.py b/openlp/core/ui/wizard.py index 5996296b5..908981dc6 100644 --- a/openlp/core/ui/wizard.py +++ b/openlp/core/ui/wizard.py @@ -118,6 +118,7 @@ class OpenLPWizard(QtGui.QWizard, RegistryProperties): """ Set up the wizard UI. """ + self.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) self.setModal(True) self.setWizardStyle(QtGui.QWizard.ModernStyle) self.setOptions(QtGui.QWizard.IndependentPages | diff --git a/openlp/plugins/alerts/forms/alertdialog.py b/openlp/plugins/alerts/forms/alertdialog.py index e4fd29a39..47e61365a 100644 --- a/openlp/plugins/alerts/forms/alertdialog.py +++ b/openlp/plugins/alerts/forms/alertdialog.py @@ -46,7 +46,7 @@ class Ui_AlertDialog(object): """ alert_dialog.setObjectName('alert_dialog') alert_dialog.resize(400, 300) - alert_dialog.setWindowIcon(build_icon(':/icon/openlp-logo-16x16.png')) + alert_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) self.alert_dialog_layout = QtGui.QGridLayout(alert_dialog) self.alert_dialog_layout.setObjectName('alert_dialog_layout') self.alert_text_layout = QtGui.QFormLayout() diff --git a/openlp/plugins/bibles/forms/booknamedialog.py b/openlp/plugins/bibles/forms/booknamedialog.py index 5903391c3..120caeff0 100644 --- a/openlp/plugins/bibles/forms/booknamedialog.py +++ b/openlp/plugins/bibles/forms/booknamedialog.py @@ -30,12 +30,14 @@ from PyQt4 import QtCore, QtGui from openlp.core.common import translate +from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box class Ui_BookNameDialog(object): def setupUi(self, book_name_dialog): book_name_dialog.setObjectName('book_name_dialog') + book_name_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) book_name_dialog.resize(400, 271) self.book_name_layout = QtGui.QVBoxLayout(book_name_dialog) self.book_name_layout.setSpacing(8) diff --git a/openlp/plugins/bibles/forms/editbibledialog.py b/openlp/plugins/bibles/forms/editbibledialog.py index 1fbaa2f1e..e9e39a053 100644 --- a/openlp/plugins/bibles/forms/editbibledialog.py +++ b/openlp/plugins/bibles/forms/editbibledialog.py @@ -39,8 +39,8 @@ from openlp.plugins.bibles.lib.db import BiblesResourcesDB class Ui_EditBibleDialog(object): def setupUi(self, edit_bible_dialog): edit_bible_dialog.setObjectName('edit_bible_dialog') + edit_bible_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) edit_bible_dialog.resize(520, 400) - edit_bible_dialog.setWindowIcon(build_icon(':/icon/openlp-logo-16x16.png')) edit_bible_dialog.setModal(True) self.dialog_layout = QtGui.QVBoxLayout(edit_bible_dialog) self.dialog_layout.setSpacing(8) diff --git a/openlp/plugins/bibles/forms/languagedialog.py b/openlp/plugins/bibles/forms/languagedialog.py index 10382ea13..ab40503d2 100644 --- a/openlp/plugins/bibles/forms/languagedialog.py +++ b/openlp/plugins/bibles/forms/languagedialog.py @@ -30,12 +30,14 @@ from PyQt4 import QtGui from openlp.core.common import translate +from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box class Ui_LanguageDialog(object): def setupUi(self, language_dialog): language_dialog.setObjectName('language_dialog') + language_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) language_dialog.resize(400, 165) self.language_layout = QtGui.QVBoxLayout(language_dialog) self.language_layout.setSpacing(8) diff --git a/openlp/plugins/custom/forms/editcustomdialog.py b/openlp/plugins/custom/forms/editcustomdialog.py index 22a33d921..5ed92f95e 100644 --- a/openlp/plugins/custom/forms/editcustomdialog.py +++ b/openlp/plugins/custom/forms/editcustomdialog.py @@ -41,8 +41,8 @@ class Ui_CustomEditDialog(object): :param custom_edit_dialog: The Dialog """ custom_edit_dialog.setObjectName('custom_edit_dialog') + custom_edit_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) custom_edit_dialog.resize(450, 350) - custom_edit_dialog.setWindowIcon(build_icon(':/icon/openlp-logo-16x16.png')) self.dialog_layout = QtGui.QVBoxLayout(custom_edit_dialog) self.dialog_layout.setObjectName('dialog_layout') self.title_layout = QtGui.QHBoxLayout() diff --git a/openlp/plugins/custom/forms/editcustomslidedialog.py b/openlp/plugins/custom/forms/editcustomslidedialog.py index 3b74566fc..c59cc4cd1 100644 --- a/openlp/plugins/custom/forms/editcustomslidedialog.py +++ b/openlp/plugins/custom/forms/editcustomslidedialog.py @@ -30,13 +30,14 @@ from PyQt4 import QtGui from openlp.core.common import UiStrings, translate -from openlp.core.lib import SpellTextEdit +from openlp.core.lib import SpellTextEdit, build_icon from openlp.core.lib.ui import create_button, create_button_box class Ui_CustomSlideEditDialog(object): def setupUi(self, custom_slide_edit_dialog): custom_slide_edit_dialog.setObjectName('custom_slide_edit_dialog') + custom_slide_edit_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) custom_slide_edit_dialog.resize(350, 300) self.dialog_layout = QtGui.QVBoxLayout(custom_slide_edit_dialog) self.slide_text_edit = SpellTextEdit(self) diff --git a/openlp/plugins/songs/forms/authorsdialog.py b/openlp/plugins/songs/forms/authorsdialog.py index 65fb169b0..c5a93894b 100644 --- a/openlp/plugins/songs/forms/authorsdialog.py +++ b/openlp/plugins/songs/forms/authorsdialog.py @@ -43,8 +43,8 @@ class Ui_AuthorsDialog(object): Set up the UI for the dialog. """ authors_dialog.setObjectName('authors_dialog') + authorsDialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) authors_dialog.resize(300, 10) - authors_dialog.setWindowIcon(build_icon(':/icon/openlp-logo-16x16.png')) authors_dialog.setModal(True) self.dialog_layout = QtGui.QVBoxLayout(authors_dialog) self.dialog_layout.setObjectName('dialog_layout') diff --git a/openlp/plugins/songs/forms/editsongdialog.py b/openlp/plugins/songs/forms/editsongdialog.py index d0fb51a2d..a9ca71946 100644 --- a/openlp/plugins/songs/forms/editsongdialog.py +++ b/openlp/plugins/songs/forms/editsongdialog.py @@ -43,8 +43,8 @@ class Ui_EditSongDialog(object): """ def setupUi(self, edit_song_dialog): edit_song_dialog.setObjectName('edit_song_dialog') + edit_song_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) edit_song_dialog.resize(650, 400) - edit_song_dialog.setWindowIcon(build_icon(':/icon/openlp-logo-16x16.png')) edit_song_dialog.setModal(True) self.dialog_layout = QtGui.QVBoxLayout(edit_song_dialog) self.dialog_layout.setSpacing(8) diff --git a/openlp/plugins/songs/forms/editversedialog.py b/openlp/plugins/songs/forms/editversedialog.py index f1901b203..1dc3d9182 100644 --- a/openlp/plugins/songs/forms/editversedialog.py +++ b/openlp/plugins/songs/forms/editversedialog.py @@ -37,6 +37,7 @@ from openlp.plugins.songs.lib import VerseType class Ui_EditVerseDialog(object): def setupUi(self, edit_verse_dialog): edit_verse_dialog.setObjectName('edit_verse_dialog') + edit_verse_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) edit_verse_dialog.resize(400, 400) edit_verse_dialog.setModal(True) self.dialog_layout = QtGui.QVBoxLayout(edit_verse_dialog) diff --git a/openlp/plugins/songs/forms/mediafilesdialog.py b/openlp/plugins/songs/forms/mediafilesdialog.py index 3a1db39ed..495e08883 100644 --- a/openlp/plugins/songs/forms/mediafilesdialog.py +++ b/openlp/plugins/songs/forms/mediafilesdialog.py @@ -42,10 +42,10 @@ class Ui_MediaFilesDialog(object): Set up the user interface. """ media_files_dialog.setObjectName('media_files_dialog') + media_files_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) media_files_dialog.setWindowModality(QtCore.Qt.ApplicationModal) media_files_dialog.resize(400, 300) media_files_dialog.setModal(True) - media_files_dialog.setWindowIcon(build_icon(':/icon/openlp-logo-16x16.png')) self.files_vertical_layout = QtGui.QVBoxLayout(media_files_dialog) self.files_vertical_layout.setSpacing(8) self.files_vertical_layout.setMargin(8) diff --git a/openlp/plugins/songs/forms/songbookdialog.py b/openlp/plugins/songs/forms/songbookdialog.py index 8cacef1a2..1ed79a4eb 100644 --- a/openlp/plugins/songs/forms/songbookdialog.py +++ b/openlp/plugins/songs/forms/songbookdialog.py @@ -29,7 +29,7 @@ from PyQt4 import QtGui -from openlp.core.lib import translate +from openlp.core.lib import translate, build_icon from openlp.core.lib.ui import create_button_box @@ -42,6 +42,7 @@ class Ui_SongBookDialog(object): Set up the user interface. """ song_book_dialog.setObjectName('song_book_dialog') + song_book_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) song_book_dialog.resize(300, 10) self.dialog_layout = QtGui.QVBoxLayout(song_book_dialog) self.dialog_layout.setObjectName('dialog_layout') diff --git a/openlp/plugins/songs/forms/songmaintenancedialog.py b/openlp/plugins/songs/forms/songmaintenancedialog.py index 84e3535d3..893ae9c1c 100644 --- a/openlp/plugins/songs/forms/songmaintenancedialog.py +++ b/openlp/plugins/songs/forms/songmaintenancedialog.py @@ -44,6 +44,7 @@ class Ui_SongMaintenanceDialog(object): Set up the user interface for the song maintenance dialog """ song_maintenance_dialog.setObjectName('song_maintenance_dialog') + song_maintenance_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) song_maintenance_dialog.setWindowModality(QtCore.Qt.ApplicationModal) song_maintenance_dialog.resize(10, 350) self.dialog_layout = QtGui.QGridLayout(song_maintenance_dialog) diff --git a/openlp/plugins/songs/forms/topicsdialog.py b/openlp/plugins/songs/forms/topicsdialog.py index eb6229bf6..ffa7da333 100644 --- a/openlp/plugins/songs/forms/topicsdialog.py +++ b/openlp/plugins/songs/forms/topicsdialog.py @@ -29,7 +29,7 @@ from PyQt4 import QtGui -from openlp.core.lib import translate +from openlp.core.lib import translate, build_icon from openlp.core.lib.ui import create_button_box @@ -42,6 +42,7 @@ class Ui_TopicsDialog(object): Set up the user interface for the topics dialog. """ topics_dialog.setObjectName('topics_dialog') + topics_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) topics_dialog.resize(300, 10) self.dialog_layout = QtGui.QVBoxLayout(topics_dialog) self.dialog_layout.setObjectName('dialog_layout') diff --git a/openlp/plugins/songusage/forms/songusagedeletedialog.py b/openlp/plugins/songusage/forms/songusagedeletedialog.py index 01597a790..190292678 100644 --- a/openlp/plugins/songusage/forms/songusagedeletedialog.py +++ b/openlp/plugins/songusage/forms/songusagedeletedialog.py @@ -30,6 +30,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.common import translate +from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box @@ -44,6 +45,7 @@ class Ui_SongUsageDeleteDialog(object): :param song_usage_delete_dialog: """ song_usage_delete_dialog.setObjectName('song_usage_delete_dialog') + song_usage_delete_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) song_usage_delete_dialog.resize(291, 243) self.vertical_layout = QtGui.QVBoxLayout(song_usage_delete_dialog) self.vertical_layout.setSpacing(8) diff --git a/openlp/plugins/songusage/forms/songusagedetaildialog.py b/openlp/plugins/songusage/forms/songusagedetaildialog.py index ede5075a0..9431c7894 100644 --- a/openlp/plugins/songusage/forms/songusagedetaildialog.py +++ b/openlp/plugins/songusage/forms/songusagedetaildialog.py @@ -45,6 +45,7 @@ class Ui_SongUsageDetailDialog(object): :param song_usage_detail_dialog: """ song_usage_detail_dialog.setObjectName('song_usage_detail_dialog') + song_usage_detail_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) song_usage_detail_dialog.resize(609, 413) self.vertical_layout = QtGui.QVBoxLayout(song_usage_detail_dialog) self.vertical_layout.setSpacing(8) From b02fc541a348610513d477a32d559e6392d016fb Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Sat, 3 May 2014 17:22:10 +0200 Subject: [PATCH 76/82] Reorganize tests --- tests/functional/openlp_core_lib/test_ui.py | 64 ++++++++++----------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/tests/functional/openlp_core_lib/test_ui.py b/tests/functional/openlp_core_lib/test_ui.py index 025b1a638..1002550d2 100644 --- a/tests/functional/openlp_core_lib/test_ui.py +++ b/tests/functional/openlp_core_lib/test_ui.py @@ -82,6 +82,21 @@ class TestUi(TestCase): self.assertEqual(1, len(btnbox.buttons())) self.assertEqual(QtGui.QDialogButtonBox.HelpRole, btnbox.buttonRole(btnbox.buttons()[0])) + def test_create_horizontal_adjusting_combo_box(self): + """ + Test creating a horizontal adjusting combo box + """ + # GIVEN: A dialog + dialog = QtGui.QDialog() + + # WHEN: We create the combobox + combo = create_horizontal_adjusting_combo_box(dialog, 'combo1') + + # THEN: We should get a ComboBox + self.assertIsInstance(combo, QtGui.QComboBox) + self.assertEqual('combo1', combo.objectName()) + self.assertEqual(QtGui.QComboBox.AdjustToMinimumContentsLength, combo.sizeAdjustPolicy()) + def test_create_button(self): """ Test creating a button @@ -114,38 +129,6 @@ class TestUi(TestCase): self.assertEqual('my_btn', btn.objectName()) self.assertTrue(btn.isEnabled()) - def test_create_valign_selection_widgets(self): - """ - Test creating a combo box for valign selection - """ - # GIVEN: A dialog - dialog = QtGui.QDialog() - - # WHEN: We create the widgets - label, combo = create_valign_selection_widgets(dialog) - - # THEN: We should get a label and a combobox. - self.assertEqual(translate('OpenLP.Ui', '&Vertical Align:'), label.text()) - self.assertIsInstance(combo, QtGui.QComboBox) - self.assertEqual(combo, label.buddy()) - for text in [UiStrings().Top, UiStrings().Middle, UiStrings().Bottom]: - self.assertTrue(combo.findText(text) >= 0) - - def test_create_horizontal_adjusting_combo_box(self): - """ - Test creating a horizontal adjusting combo box - """ - # GIVEN: A dialog - dialog = QtGui.QDialog() - - # WHEN: We create the combobox - combo = create_horizontal_adjusting_combo_box(dialog, 'combo1') - - # THEN: We should get a ComboBox - self.assertIsInstance(combo, QtGui.QComboBox) - self.assertEqual('combo1', combo.objectName()) - self.assertEqual(QtGui.QComboBox.AdjustToMinimumContentsLength, combo.sizeAdjustPolicy()) - def test_create_action(self): """ Test creating an action @@ -170,3 +153,20 @@ class TestUi(TestCase): self.assertIsInstance(action.icon(), QtGui.QIcon) self.assertEqual('my tooltip', action.toolTip()) self.assertEqual('my statustip', action.statusTip()) + + def test_create_valign_selection_widgets(self): + """ + Test creating a combo box for valign selection + """ + # GIVEN: A dialog + dialog = QtGui.QDialog() + + # WHEN: We create the widgets + label, combo = create_valign_selection_widgets(dialog) + + # THEN: We should get a label and a combobox. + self.assertEqual(translate('OpenLP.Ui', '&Vertical Align:'), label.text()) + self.assertIsInstance(combo, QtGui.QComboBox) + self.assertEqual(combo, label.buddy()) + for text in [UiStrings().Top, UiStrings().Middle, UiStrings().Bottom]: + self.assertTrue(combo.findText(text) >= 0) From f60379b542a285eb0e4789bfe18e9c2e078c1d54 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Sat, 3 May 2014 17:22:20 +0200 Subject: [PATCH 77/82] Add new test --- tests/functional/openlp_core_lib/test_ui.py | 27 +++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tests/functional/openlp_core_lib/test_ui.py b/tests/functional/openlp_core_lib/test_ui.py index 1002550d2..f1b8ee17a 100644 --- a/tests/functional/openlp_core_lib/test_ui.py +++ b/tests/functional/openlp_core_lib/test_ui.py @@ -170,3 +170,30 @@ class TestUi(TestCase): self.assertEqual(combo, label.buddy()) for text in [UiStrings().Top, UiStrings().Middle, UiStrings().Bottom]: self.assertTrue(combo.findText(text) >= 0) + + def test_find_and_set_in_combo_box(self): + """ + Test finding a string in a combo box and setting it as the selected item if present + """ + # GIVEN: A ComboBox + combo = QtGui.QComboBox() + combo.addItems(['One', 'Two', 'Three']) + combo.setCurrentIndex(1) + + # WHEN: We call the method with a non-existing value and set_missing=False + find_and_set_in_combo_box(combo, 'Four', set_missing=False) + + # THEN: The index should not have changed + self.assertEqual(1, combo.currentIndex()) + + # WHEN: We call the method with a non-existing value + find_and_set_in_combo_box(combo, 'Four') + + # THEN: The index should have been reset + self.assertEqual(0, combo.currentIndex()) + + # WHEN: We call the method with the default behavior + find_and_set_in_combo_box(combo, 'Three') + + # THEN: The index should have changed + self.assertEqual(2, combo.currentIndex()) From 7a1741258abc6a0ee44782c5bfd800b43ade8053 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Sat, 3 May 2014 17:25:45 +0200 Subject: [PATCH 78/82] Typo --- openlp/plugins/songs/forms/authorsdialog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/plugins/songs/forms/authorsdialog.py b/openlp/plugins/songs/forms/authorsdialog.py index c5a93894b..7bca76ca6 100644 --- a/openlp/plugins/songs/forms/authorsdialog.py +++ b/openlp/plugins/songs/forms/authorsdialog.py @@ -43,7 +43,7 @@ class Ui_AuthorsDialog(object): Set up the UI for the dialog. """ authors_dialog.setObjectName('authors_dialog') - authorsDialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) + authors_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg')) authors_dialog.resize(300, 10) authors_dialog.setModal(True) self.dialog_layout = QtGui.QVBoxLayout(authors_dialog) From 4a75849708df5561610b5a7fe40c70fec8ba8de4 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Sat, 3 May 2014 17:27:44 +0200 Subject: [PATCH 79/82] Fix readme links --- README.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.txt b/README.txt index b937e1d5f..e3e7cc579 100644 --- a/README.txt +++ b/README.txt @@ -5,7 +5,7 @@ You're probably reading this because you've just downloaded the source code for OpenLP 2.0. If you are looking for the installer file, please go to the download page on the web site:: - http://openlp.org/en/download.html + http://openlp.org/download If you're looking for how to contribute to OpenLP, then please look at the OpenLP wiki:: @@ -13,4 +13,3 @@ OpenLP wiki:: http://wiki.openlp.org/ Thanks for downloading OpenLP 2.0! - From d4edd91d5676a397f2c70f1d0dffb651554f309a Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Sat, 3 May 2014 17:28:31 +0200 Subject: [PATCH 80/82] This is no longer 2.0 --- README.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.txt b/README.txt index e3e7cc579..04294b1b8 100644 --- a/README.txt +++ b/README.txt @@ -1,8 +1,8 @@ -OpenLP 2.0 -========== +OpenLP +====== You're probably reading this because you've just downloaded the source code for -OpenLP 2.0. If you are looking for the installer file, please go to the download +OpenLP. If you are looking for the installer file, please go to the download page on the web site:: http://openlp.org/download @@ -12,4 +12,4 @@ OpenLP wiki:: http://wiki.openlp.org/ -Thanks for downloading OpenLP 2.0! +Thanks for downloading OpenLP! From 37653bfd134398a66ad3e24c57df1c29ae99fddf Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Sat, 3 May 2014 17:34:13 +0200 Subject: [PATCH 81/82] Add missing tag to test --- tests/utils/test_bzr_tags.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/utils/test_bzr_tags.py b/tests/utils/test_bzr_tags.py index 95017e94f..393f4ce25 100644 --- a/tests/utils/test_bzr_tags.py +++ b/tests/utils/test_bzr_tags.py @@ -53,6 +53,7 @@ TAGS = [ ['2.0.1', '?'], ['2.0.2', '?'], ['2.0.3', '?'], + ['2.0.4', '?'], ['2.1.0', '2119'] ] From 9b396e7699fb52480ecc99ed5597716eac338466 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Mon, 5 May 2014 18:20:54 +0200 Subject: [PATCH 82/82] Fix bug 1211913 Fixes: https://launchpad.net/bugs/1211913 --- openlp/plugins/songs/lib/sundayplusimport.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/openlp/plugins/songs/lib/sundayplusimport.py b/openlp/plugins/songs/lib/sundayplusimport.py index f22f8b058..5c5f73047 100644 --- a/openlp/plugins/songs/lib/sundayplusimport.py +++ b/openlp/plugins/songs/lib/sundayplusimport.py @@ -68,7 +68,7 @@ class SundayPlusImport(SongImport): for filename in self.import_source: if self.stop_import_flag: return - song_file = open(filename) + song_file = open(filename, 'rb') self.do_import_file(song_file) song_file.close() @@ -103,7 +103,7 @@ class SundayPlusImport(SongImport): # Now we are looking for the name. if data[i:i + 1] == '#': name_end = data.find(':', i + 1) - name = data[i + 1:name_end] + name = data[i + 1:name_end].upper() i = name_end + 1 while data[i:i + 1] == ' ': i += 1 @@ -129,13 +129,13 @@ class SundayPlusImport(SongImport): value = data[i:end] # If we are in the main group. if not cell: - if name == 'title': + if name == 'TITLE': self.title = self.decode(self.unescape(value)) - elif name == 'Author': + elif name == 'AUTHOR': author = self.decode(self.unescape(value)) if len(author): self.add_author(author) - elif name == 'Copyright': + elif name == 'COPYRIGHT': self.copyright = self.decode(self.unescape(value)) elif name[0:4] == 'CELL': self.parse(value, cell=name[4:]) @@ -147,12 +147,12 @@ class SundayPlusImport(SongImport): verse_type = VerseType.tags[VerseType.from_loose_input(value[0])] if len(value) >= 2 and value[-1] in ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']: verse_type = "%s%s" % (verse_type, value[-1]) - elif name == 'Hotkey': - # Hotkey always appears after MARKER_NAME, so it + elif name == 'HOTKEY': + # HOTKEY always appears after MARKER_NAME, so it # effectively overrides MARKER_NAME, if present. if len(value) and value in list(HOTKEY_TO_VERSE_TYPE.keys()): verse_type = HOTKEY_TO_VERSE_TYPE[value] - if name == 'rtf': + if name == 'RTF': value = self.unescape(value) result = strip_rtf(value, self.encoding) if result is None: