openlp-core team mailing list archive
-
openlp-core team
-
Mailing list archive
-
Message #23560
[Merge] lp:~sam92/openlp/bug-1316979 into lp:openlp
Samuel Mehrbrodt has proposed merging lp:~sam92/openlp/bug-1316979 into lp:openlp.
Requested reviews:
Raoul Snyman (raoul-snyman)
Related bugs:
Bug #1316979 in OpenLP: "Crash when trying to delete a duplicated song"
https://bugs.launchpad.net/openlp/+bug/1316979
For more details, see:
https://code.launchpad.net/~sam92/openlp/bug-1316979/+merge/219314
Fix bug 1316979 by changing the 'authors' relation to readonly.
The main change is in line 133, the rest is just removing the usage of that relationship.
lp:~sam92/openlp/bug-1316979 (revision 2386)
[SUCCESS] http://ci.openlp.org/job/Branch-01-Pull/448/
[SUCCESS] http://ci.openlp.org/job/Branch-02-Functional-Tests/404/
[SUCCESS] http://ci.openlp.org/job/Branch-03-Interface-Tests/349/
[SUCCESS] http://ci.openlp.org/job/Branch-04-Windows_Tests/310/
[SUCCESS] http://ci.openlp.org/job/Branch-05a-Code_Analysis/213/
[SUCCESS] http://ci.openlp.org/job/Branch-05b-Test_Coverage/87/
--
https://code.launchpad.net/~sam92/openlp/bug-1316979/+merge/219314
Your team OpenLP Core is subscribed to branch lp:openlp.
=== modified file 'openlp/core/lib/ui.py'
--- openlp/core/lib/ui.py 2014-04-08 14:08:07 +0000
+++ openlp/core/lib/ui.py 2014-05-13 09:15:44 +0000
@@ -295,7 +295,7 @@
Sets a case insensitive text completer for a widget.
:param cache: The list of items to use as suggestions.
- :param widget: A widget to set the completer (QComboBox or QTextEdit instance)
+ :param widget: A widget to set the completer (QComboBox or QLineEdit instance)
"""
completer = QtGui.QCompleter(cache)
completer.setCaseSensitivity(QtCore.Qt.CaseInsensitive)
=== modified file 'openlp/plugins/songs/forms/editsongform.py'
--- openlp/plugins/songs/forms/editsongform.py 2014-05-03 13:58:31 +0000
+++ openlp/plugins/songs/forms/editsongform.py 2014-05-13 09:15:44 +0000
@@ -42,7 +42,7 @@
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, AuthorType, Topic, MediaFile
+from openlp.plugins.songs.lib.db import Book, Song, Author, 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
@@ -966,10 +966,8 @@
self.song.authors_songs = []
for row in range(self.authors_list_view.count()):
item = self.authors_list_view.item(row)
- 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.add_author(self.manager.get_object(Author, item.data(QtCore.Qt.UserRole)[0]),
+ item.data(QtCore.Qt.UserRole)[1])
self.song.topics = []
for row in range(self.topics_list_view.count()):
item = self.topics_list_view.item(row)
=== modified file 'openlp/plugins/songs/forms/songmaintenanceform.py'
--- openlp/plugins/songs/forms/songmaintenanceform.py 2014-03-21 21:38:08 +0000
+++ openlp/plugins/songs/forms/songmaintenanceform.py 2014-05-13 09:15:44 +0000
@@ -400,7 +400,7 @@
"""
Merges two authors into one author.
- :param old_author: The object, which was edited, that will be deleted
+ :param old_author: The object, which was edited, that will be deleted
"""
# Find the duplicate.
existing_author = self.manager.get_object_filtered(
@@ -415,11 +415,9 @@
# Find the songs, which have the old_author as author.
songs = self.manager.get_all_objects(Song, Song.authors.contains(old_author))
for song in songs:
- # We check if the song has already existing_author as author. If
- # that is not the case we add it.
- if existing_author not in song.authors:
- song.authors.append(existing_author)
- song.authors.remove(old_author)
+ for author_song in song.authors_songs:
+ song.add_author(existing_author, author_song.author_type)
+ song.remove_author(old_author, author_song.author_type)
self.manager.save_object(song)
self.manager.delete_object(Author, old_author.id)
=== modified file 'openlp/plugins/songs/lib/__init__.py'
--- openlp/plugins/songs/lib/__init__.py 2014-05-07 20:29:53 +0000
+++ openlp/plugins/songs/lib/__init__.py 2014-05-13 09:15:44 +0000
@@ -390,12 +390,12 @@
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
+ if not song.authors_songs:
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)
+ song.add_author(author)
if song.copyright:
song.copyright = CONTROL_CHARS.sub('', song.copyright).strip()
=== modified file 'openlp/plugins/songs/lib/db.py'
--- openlp/plugins/songs/lib/db.py 2014-04-21 14:40:54 +0000
+++ openlp/plugins/songs/lib/db.py 2014-05-13 09:15:44 +0000
@@ -114,6 +114,33 @@
"""
self.sort_key = get_natural_key(self.title)
+ def add_author(self, author, author_type=None):
+ """
+ Add an author to the song if it not yet exists
+
+ :param author: Author object
+ :param author_type: AuthorType constant or None
+ """
+ for author_song in self.authors_songs:
+ if author_song.author == author and author_song.author_type == author_type:
+ return
+ new_author_song = AuthorSong()
+ new_author_song.author = author
+ new_author_song.author_type = author_type
+ self.authors_songs.append(new_author_song)
+
+ def remove_author(self, author, author_type=None):
+ """
+ Remove an existing author from the song
+
+ :param author: Author object
+ :param author_type: AuthorType constant or None
+ """
+ for author_song in self.authors_songs:
+ if author_song.author == author and author_song.author_type == author_type:
+ self.authors_songs.remove(author_song)
+ return
+
class Topic(BaseModel):
"""
@@ -283,9 +310,10 @@
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.
+ # Use the authors_songs relation when you need access to the 'author_type' attribute
+ # or when creating new relations
'authors_songs': relation(AuthorSong, cascade="all, delete-orphan"),
- 'authors': relation(Author, secondary=authors_songs_table),
+ '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)
=== modified file 'openlp/plugins/songs/lib/foilpresenterimport.py'
--- openlp/plugins/songs/lib/foilpresenterimport.py 2014-03-21 21:38:08 +0000
+++ openlp/plugins/songs/lib/foilpresenterimport.py 2014-05-13 09:15:44 +0000
@@ -343,7 +343,7 @@
author = Author.populate(display_name=display_name, last_name=display_name.split(' ')[-1],
first_name=' '.join(display_name.split(' ')[:-1]))
self.manager.save_object(author)
- song.authors.append(author)
+ song.add_author(author)
def _process_cclinumber(self, foilpresenterfolie, song):
"""
=== modified file 'openlp/plugins/songs/lib/olpimport.py'
--- openlp/plugins/songs/lib/olpimport.py 2014-03-21 21:38:08 +0000
+++ openlp/plugins/songs/lib/olpimport.py 2014-05-13 09:15:44 +0000
@@ -187,7 +187,7 @@
first_name=author.first_name,
last_name=author.last_name,
display_name=author.display_name)
- new_song.authors.append(existing_author)
+ new_song.add_author(existing_author)
if song.book:
existing_song_book = self.manager.get_object_filtered(Book, Book.name == song.book.name)
if existing_song_book is None:
=== modified file 'openlp/plugins/songs/lib/songimport.py'
--- openlp/plugins/songs/lib/songimport.py 2014-03-21 21:38:08 +0000
+++ openlp/plugins/songs/lib/songimport.py 2014-05-13 09:15:44 +0000
@@ -325,7 +325,7 @@
author = Author.populate(display_name=author_text,
last_name=author_text.split(' ')[-1],
first_name=' '.join(author_text.split(' ')[:-1]))
- song.authors.append(author)
+ song.add_author(author)
if self.song_book_name:
song_book = self.manager.get_object_filtered(Book, Book.name == self.song_book_name)
if song_book is None:
=== modified file 'openlp/plugins/songs/lib/songselect.py'
--- openlp/plugins/songs/lib/songselect.py 2014-05-02 06:42:17 +0000
+++ openlp/plugins/songs/lib/songselect.py 2014-05-13 09:15:44 +0000
@@ -196,13 +196,13 @@
db_song.lyrics = song_xml.extract_xml()
clean_song(self.db_manager, db_song)
self.db_manager.save_object(db_song)
- db_song.authors = []
+ db_song.authors_songs = []
for author_name in song['authors']:
author = self.db_manager.get_object_filtered(Author, Author.display_name == author_name)
if not author:
author = Author.populate(first_name=author_name.rsplit(' ', 1)[0],
last_name=author_name.rsplit(' ', 1)[1],
display_name=author_name)
- db_song.authors.append(author)
+ db_song.add_author(author)
self.db_manager.save_object(db_song)
return db_song
=== modified file 'openlp/plugins/songs/lib/xml.py'
--- openlp/plugins/songs/lib/xml.py 2014-05-03 12:56:58 +0000
+++ openlp/plugins/songs/lib/xml.py 2014-05-13 09:15:44 +0000
@@ -71,7 +71,7 @@
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, AuthorType, Book, Song, Topic
+from openlp.plugins.songs.lib.db import Author, AuthorType, Book, Song, Topic
from openlp.core.utils import get_application_version
log = logging.getLogger(__name__)
@@ -519,10 +519,7 @@
author = Author.populate(display_name=display_name,
last_name=display_name.split(' ')[-1],
first_name=' '.join(display_name.split(' ')[:-1]))
- author_song = AuthorSong()
- author_song.author = author
- author_song.author_type = author_type
- song.authors_songs.append(author_song)
+ song.add_author(author, author_type)
def _process_cclinumber(self, properties, song):
"""
=== modified file 'tests/functional/openlp_core_lib/test_ui.py'
--- tests/functional/openlp_core_lib/test_ui.py 2014-05-07 23:52:51 +0000
+++ tests/functional/openlp_core_lib/test_ui.py 2014-05-13 09:15:44 +0000
@@ -154,6 +154,22 @@
self.assertEqual('my tooltip', action.toolTip())
self.assertEqual('my statustip', action.statusTip())
+ def test_set_case_insensitive_completer(self):
+ """
+ Test setting a case insensitive completer on a widget
+ """
+ # GIVEN: A QComboBox and a list of completion items
+ line_edit = QtGui.QLineEdit()
+ suggestions = ['one', 'Two', 'THRee', 'FOUR']
+
+ # WHEN: We call the function
+ set_case_insensitive_completer(suggestions, line_edit)
+
+ # THEN: The Combobox should have a completer which is case insensitive
+ completer = line_edit.completer()
+ self.assertIsInstance(completer, QtGui.QCompleter)
+ self.assertEqual(completer.caseSensitivity(), QtCore.Qt.CaseInsensitive)
+
def test_create_valign_selection_widgets(self):
"""
Test creating a combo box for valign selection
=== added file 'tests/functional/openlp_plugins/songs/test_db.py'
--- tests/functional/openlp_plugins/songs/test_db.py 1970-01-01 00:00:00 +0000
+++ tests/functional/openlp_plugins/songs/test_db.py 2014-05-13 09:15:44 +0000
@@ -0,0 +1,114 @@
+# -*- 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 #
+###############################################################################
+"""
+This module contains tests for the db submodule of the Songs plugin.
+"""
+from unittest import TestCase
+
+from openlp.plugins.songs.lib.db import Song, Author, AuthorType
+
+
+class TestDB(TestCase):
+ """
+ Test the functions in the :mod:`db` module.
+ """
+
+ def test_add_author(self):
+ """
+ Test adding an author to a song
+ """
+ # GIVEN: A song and an author
+ song = Song()
+ song.authors_songs = []
+ author = Author()
+ author.first_name = "Max"
+ author.last_name = "Mustermann"
+
+ # WHEN: We add an author to the song
+ song.add_author(author)
+
+ # THEN: The author should have been added with author_type=None
+ self.assertEqual(1, len(song.authors_songs))
+ self.assertEqual("Max", song.authors_songs[0].author.first_name)
+ self.assertEqual("Mustermann", song.authors_songs[0].author.last_name)
+ self.assertIsNone(song.authors_songs[0].author_type)
+
+ def test_add_author_with_type(self):
+ """
+ Test adding an author with a type specified to a song
+ """
+ # GIVEN: A song and an author
+ song = Song()
+ song.authors_songs = []
+ author = Author()
+ author.first_name = "Max"
+ author.last_name = "Mustermann"
+
+ # WHEN: We add an author to the song
+ song.add_author(author, AuthorType.Words)
+
+ # THEN: The author should have been added with author_type=None
+ self.assertEqual(1, len(song.authors_songs))
+ self.assertEqual("Max", song.authors_songs[0].author.first_name)
+ self.assertEqual("Mustermann", song.authors_songs[0].author.last_name)
+ self.assertEqual(AuthorType.Words, song.authors_songs[0].author_type)
+
+ def test_remove_author(self):
+ """
+ Test removing an author from a song
+ """
+ # GIVEN: A song with an author
+ song = Song()
+ song.authors_songs = []
+ author = Author()
+ song.add_author(author)
+
+ # WHEN: We remove the author
+ song.remove_author(author)
+
+ # THEN: It should have been removed
+ self.assertEqual(0, len(song.authors_songs))
+
+ def test_remove_author_with_type(self):
+ """
+ Test removing an author with a type specified from a song
+ """
+ # GIVEN: A song with two authors
+ song = Song()
+ song.authors_songs = []
+ author = Author()
+ song.add_author(author)
+ song.add_author(author, AuthorType.Translation)
+
+ # WHEN: We remove the author with a certain type
+ song.remove_author(author, AuthorType.Translation)
+
+ # THEN: It should have been removed and the other author should still be there
+ self.assertEqual(1, len(song.authors_songs))
+ self.assertEqual(None, song.authors_songs[0].author_type)
=== modified file 'tests/functional/openlp_plugins/songs/test_songselect.py'
--- tests/functional/openlp_plugins/songs/test_songselect.py 2014-04-02 19:35:09 +0000
+++ tests/functional/openlp_plugins/songs/test_songselect.py 2014-05-13 09:15:44 +0000
@@ -344,7 +344,7 @@
mocked_db_manager.get_object_filtered.assert_called_with(MockedAuthor, False)
MockedAuthor.populate.assert_called_with(first_name='Public', last_name='Domain',
display_name='Public Domain')
- self.assertEqual(1, len(result.authors), 'There should only be one author')
+ self.assertEqual(1, len(result.authors_songs), 'There should only be one author')
def save_song_existing_author_test(self):
"""
@@ -379,4 +379,4 @@
'The save_object() method should have been called twice')
mocked_db_manager.get_object_filtered.assert_called_with(MockedAuthor, False)
self.assertEqual(0, MockedAuthor.populate.call_count, 'A new author should not have been instantiated')
- self.assertEqual(1, len(result.authors), 'There should only be one author')
+ self.assertEqual(1, len(result.authors_songs), 'There should only be one author')