forked from openlp/openlp
Fix bug 1316979 by changing the 'authors' relation to readonly.
bzr-revno: 2384
This commit is contained in:
commit
50c2c9bc0e
@ -295,7 +295,7 @@ def set_case_insensitive_completer(cache, widget):
|
||||
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)
|
||||
|
@ -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, 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 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties):
|
||||
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)
|
||||
|
@ -415,11 +415,9 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog, RegistryPrope
|
||||
# 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)
|
||||
|
||||
|
@ -390,12 +390,12 @@ def clean_song(manager, song):
|
||||
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()
|
||||
|
||||
|
@ -114,6 +114,33 @@ class Song(BaseModel):
|
||||
"""
|
||||
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 @@ 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.
|
||||
# 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)
|
||||
|
@ -343,7 +343,7 @@ class FoilPresenter(object):
|
||||
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):
|
||||
"""
|
||||
|
@ -187,7 +187,7 @@ class OpenLPSongImport(SongImport):
|
||||
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:
|
||||
|
@ -325,7 +325,7 @@ class SongImport(QtCore.QObject):
|
||||
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:
|
||||
|
@ -196,13 +196,13 @@ class SongSelectImport(object):
|
||||
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
|
||||
|
@ -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, 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 @@ class OpenLyrics(object):
|
||||
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):
|
||||
"""
|
||||
|
114
tests/functional/openlp_plugins/songs/test_db.py
Normal file
114
tests/functional/openlp_plugins/songs/test_db.py
Normal file
@ -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)
|
@ -344,7 +344,7 @@ class TestSongSelect(TestCase):
|
||||
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 @@ class TestSongSelect(TestCase):
|
||||
'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')
|
||||
|
Loading…
Reference in New Issue
Block a user