This commit is contained in:
Arjan Schrijver 2013-05-19 13:32:31 +02:00
commit 24cefc7249
11 changed files with 184 additions and 51 deletions

View File

@ -102,7 +102,6 @@ class MediaManagerItem(QtGui.QWidget):
self.setupUi()
self.retranslateUi()
self.auto_select_id = -1
Registry().register_function(u'%s_service_load' % self.plugin.name, self.service_load)
# Need to use event as called across threads and UI is updated
QtCore.QObject.connect(self, QtCore.SIGNAL(u'%s_go_live' % self.plugin.name), self.go_live_remote)
QtCore.QObject.connect(self, QtCore.SIGNAL(u'%s_add_to_service' % self.plugin.name), self.add_to_service_remote)
@ -585,12 +584,15 @@ class MediaManagerItem(QtGui.QWidget):
else:
return None
def service_load(self, message):
def service_load(self, item):
"""
Method to add processing when a service has been loaded and individual service items need to be processed by the
plugins.
``item``
The item to be processed and returned.
"""
pass
return item
def check_search_result(self):
"""

View File

@ -715,13 +715,10 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
else:
service_item.set_from_service(item, self.servicePath)
service_item.validate_item(self.suffixes)
self.load_item_unique_identifier = 0
if service_item.is_capable(ItemCapabilities.OnLoadUpdate):
Registry().execute(u'%s_service_load' % service_item.name.lower(), service_item)
# if the item has been processed
if service_item.unique_identifier == self.load_item_unique_identifier:
service_item.edit_id = int(self.load_item_edit_id)
service_item.temporary_edit = self.load_item_temporary
new_item = Registry().get(service_item.name).service_load(service_item)
if new_item:
service_item = new_item
self.add_service_item(service_item, repaint=False)
delete_file(p_file)
self.main_window.add_recent_file(file_name)
@ -1260,14 +1257,6 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
self.repaint_service_list(-1, -1)
self.application.set_normal_cursor()
def service_item_update(self, edit_id, unique_identifier, temporary=False):
"""
Triggered from plugins to update service items. Save the values as they will be used as part of the service load
"""
self.load_item_unique_identifier = unique_identifier
self.load_item_edit_id = int(edit_id)
self.load_item_temporary = str_to_bool(temporary)
def replace_service_item(self, newItem):
"""
Using the service item passed replace the one with the same edit id if found.

View File

@ -40,6 +40,7 @@ from openlp.plugins.custom.lib.db import CustomSlide
log = logging.getLogger(__name__)
class CustomSearch(object):
"""
An enumeration for custom search methods.
@ -214,7 +215,6 @@ class CustomMediaItem(MediaManagerItem):
Settings().setValue(u'%s/last search type' % self.settings_section, self.search_text_edit.current_search_type())
# Reload the list considering the new search type.
search_keywords = self.search_text_edit.displayText()
search_results = []
search_type = self.search_text_edit.current_search_type()
if search_type == CustomSearch.Titles:
log.debug(u'Titles Search')
@ -252,7 +252,8 @@ class CustomMediaItem(MediaManagerItem):
and_(CustomSlide.title == item.title, CustomSlide.theme_name == item.theme,
CustomSlide.credits == item.raw_footer[0][len(item.title) + 1:]))
if custom:
self.service_manager.service_item_update(custom.id, item.unique_identifier)
item.edit_id = custom.id
return item
else:
if self.add_custom_from_service:
self.create_from_service_item(item)
@ -281,8 +282,6 @@ class CustomMediaItem(MediaManagerItem):
custom.text = unicode(custom_xml.extract_xml(), u'utf-8')
self.plugin.manager.save_object(custom)
self.on_search_text_button_clicked()
if item.name.lower() == u'custom':
Registry().execute(u'service_item_update', u'%s:%s:%s' % (custom.id, item.unique_identifier, False))
def on_clear_text_button_click(self):
"""

View File

@ -147,7 +147,7 @@ window.OpenLP = {
},
pollServer: function () {
$.getJSON(
"/stage/api/poll",
"/stage/poll",
function (data, status) {
var prevItem = OpenLP.currentItem;
OpenLP.currentSlide = data.results.slide;

View File

@ -26,7 +26,7 @@
window.OpenLP = {
loadService: function (event) {
$.getJSON(
"/stage/api/service/list",
"/stage/service/list",
function (data, status) {
OpenLP.nextSong = "";
$("#notes").html("");
@ -46,7 +46,7 @@ window.OpenLP = {
},
loadSlides: function (event) {
$.getJSON(
"/stage/api/controller/live/text",
"/stage/controller/live/text",
function (data, status) {
OpenLP.currentSlides = data.results.slides;
OpenLP.currentSlide = 0;
@ -137,7 +137,7 @@ window.OpenLP = {
},
pollServer: function () {
$.getJSON(
"/stage/api/poll",
"/stage/poll",
function (data, status) {
OpenLP.updateClock(data);
if (OpenLP.currentItem != data.results.item ||

View File

@ -267,11 +267,11 @@ class HttpRouter(object):
(u'^/(stage)$', self.serve_file),
(r'^/files/(.*)$', self.serve_file),
(r'^/api/poll$', self.poll),
(r'^/stage/api/poll$', self.poll),
(r'^/stage/poll$', self.poll),
(r'^/api/controller/(live|preview)/(.*)$', self.controller),
(r'^/stage/api/controller/(live|preview)/(.*)$', self.controller),
(r'^/stage/controller/(live|preview)/(.*)$', self.controller),
(r'^/api/service/(.*)$', self.service),
(r'^/stage/api/service/(.*)$', self.service),
(r'^/stage/service/(.*)$', self.service),
(r'^/api/display/(hide|show|blank|theme|desktop)$', self.display),
(r'^/api/alert$', self.alert),
(r'^/api/plugin/(search)$', self.plugin_info),

View File

@ -52,3 +52,4 @@ This allows OpenLP to use ``self.object`` for all the GUI elements while keeping
them separate from the functionality, so that it is easier to recreate the GUI
from the .ui files later if necessary.
"""
from editsongform import EditSongForm

View File

@ -72,10 +72,7 @@ class SongMediaItem(MediaManagerItem):
def __init__(self, parent, plugin):
self.icon_path = u'songs/song'
MediaManagerItem.__init__(self, parent, plugin)
self.edit_song_form = EditSongForm(self, self.main_window, self.plugin.manager)
self.openLyrics = OpenLyrics(self.plugin.manager)
self.single_service_item = False
self.song_maintenance_form = SongMaintenanceForm(self.plugin.manager, self)
# Holds information about whether the edit is remotely triggered and which Song is required.
self.remote_song = -1
self.edit_item = None
@ -132,6 +129,12 @@ class SongMediaItem(MediaManagerItem):
'Maintain the lists of authors, topics and books.'))
def initialise(self):
"""
Initialise variables when they cannot be initialised in the constructor.
"""
self.songMaintenanceForm = SongMaintenanceForm(self.plugin.manager, self)
self.editSongForm = EditSongForm(self, self.main_window, self.plugin.manager)
self.openLyrics = OpenLyrics(self.plugin.manager)
self.search_text_edit.set_search_types([
(SongSearch.Entire, u':/songs/song_search_all.png',
translate('SongsPlugin.MediaItem', 'Entire Song'),
@ -157,7 +160,6 @@ class SongMediaItem(MediaManagerItem):
Settings().setValue(u'%s/last search type' % self.settings_section, self.search_text_edit.current_search_type())
# Reload the list considering the new search type.
search_keywords = unicode(self.search_text_edit.displayText())
search_results = []
search_type = self.search_text_edit.current_search_type()
if search_type == SongSearch.Entire:
log.debug(u'Entire Song Search')
@ -457,14 +459,7 @@ class SongMediaItem(MediaManagerItem):
for slide in verses:
service_item.add_from_text(unicode(slide))
service_item.title = song.title
author_list = [unicode(author.display_name) for author in song.authors]
service_item.raw_footer.append(song.title)
service_item.raw_footer.append(create_separated_list(author_list))
service_item.raw_footer.append(song.copyright)
if Settings().value(u'core/ccli number'):
service_item.raw_footer.append(translate('SongsPlugin.MediaItem', 'CCLI License: ') +
Settings().value(u'core/ccli number'))
service_item.audit = [song.title, author_list, song.copyright, unicode(song.ccli_number)]
author_list = self.generate_footer(service_item, song)
service_item.data_string = {u'title': song.search_title, u'authors': u', '.join(author_list)}
service_item.xml_version = self.openLyrics.song_to_xml(song)
# Add the audio file to the service item.
@ -473,6 +468,30 @@ class SongMediaItem(MediaManagerItem):
service_item.background_audio = [m.file_name for m in song.media_files]
return True
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.
``item``
The service item to be amended
``song``
The song to be used to generate the footer
"""
author_list = [unicode(author.display_name) for author in song.authors]
item.audit = [
song.title, author_list, song.copyright, unicode(song.ccli_number)
]
item.raw_footer = []
item.raw_footer.append(song.title)
item.raw_footer.append(create_separated_list(author_list))
item.raw_footer.append(song.copyright)
if Settings().value(u'core/ccli number'):
item.raw_footer.append(translate('SongsPlugin.MediaItem', 'CCLI License: ') +
Settings().value(u'core/ccli number'))
return author_list
def service_load(self, item):
"""
Triggered by a song being loaded by the service manager.
@ -490,9 +509,8 @@ class SongMediaItem(MediaManagerItem):
else:
search_results = self.plugin.manager.get_all_objects(Song,
Song.search_title == item.data_string[u'title'], Song.search_title.asc())
editId = 0
edit_id = 0
add_song = True
temporary = False
if search_results:
for song in search_results:
author_list = item.data_string[u'authors']
@ -505,7 +523,7 @@ class SongMediaItem(MediaManagerItem):
break
if same_authors and author_list.strip(u', ') == u'':
add_song = False
editId = song.id
edit_id = song.id
break
# If there's any backing tracks, copy them over.
if item.background_audio:
@ -523,11 +541,11 @@ class SongMediaItem(MediaManagerItem):
# If there's any backing tracks, copy them over.
if item.background_audio:
self._update_background_audio(song, item)
editId = song.id
temporary = True
# Update service with correct song id.
if editId:
self.service_manager.service_item_update(editId, item.unique_identifier, temporary)
edit_id = song.id
# Update service with correct song id and return it to caller.
item.edit_id = edit_id
self.generate_footer(item, song)
return item
def search(self, string, showError):
"""

View File

@ -235,8 +235,7 @@ class SongsPlugin(Plugin):
u'delete': translate('SongsPlugin', 'Delete the selected song.'),
u'preview': translate('SongsPlugin', 'Preview the selected song.'),
u'live': translate('SongsPlugin', 'Send the selected song live.'),
u'service': translate('SongsPlugin',
'Add the selected song to the service.')
u'service': translate('SongsPlugin', 'Add the selected song to the service.')
}
self.set_plugin_ui_text_strings(tooltips)

View File

@ -1,7 +1,7 @@
"""
Package to test the openlp.core.lib.screenlist package.
"""
import copy
from unittest import TestCase
from mock import MagicMock

View File

@ -0,0 +1,125 @@
"""
This module contains tests for the lib submodule of the Songs plugin.
"""
import os
from tempfile import mkstemp
from unittest import TestCase
from mock import patch, MagicMock
from PyQt4 import QtGui
from openlp.core.lib import Registry, ServiceItem, Settings
from openlp.plugins.songs.lib.mediaitem import SongMediaItem
class TestMediaItem(TestCase):
"""
Test the functions in the :mod:`lib` module.
"""
def setUp(self):
"""
Set up the components need for all tests.
"""
Registry.create()
Registry().register(u'service_list', MagicMock())
Registry().register(u'main_window', MagicMock())
with patch('openlp.core.lib.mediamanageritem.MediaManagerItem.__init__'), \
patch('openlp.plugins.songs.forms.editsongform.EditSongForm.__init__'):
self.media_item = SongMediaItem(MagicMock(), MagicMock())
fd, self.ini_file = mkstemp(u'.ini')
Settings().set_filename(self.ini_file)
self.application = QtGui.QApplication.instance()
def tearDown(self):
"""
Delete all the C++ objects at the end so that we don't have a segfault
"""
del self.application
# Not all tests use settings!
try:
os.unlink(self.ini_file)
os.unlink(Settings().fileName())
except Exception:
pass
def build_song_footer_one_author_test(self):
"""
Test build songs footer with basic song and one author
"""
# GIVEN: A Song and a Service Item
mock_song = MagicMock()
mock_song.title = u'My Song'
mock_author = MagicMock()
mock_author.display_name = u'my author'
mock_song.authors = []
mock_song.authors.append(mock_author)
mock_song.copyright = u'My copyright'
service_item = ServiceItem(None)
# WHEN: I generate the Footer with default settings
author_list = self.media_item.generate_footer(service_item, mock_song)
# THEN: I get the following Array returned
self.assertEqual(service_item.raw_footer, [u'My Song', u'my author', u'My copyright'],
u'The array should be returned correctly with a song, one author and copyright')
self.assertEqual(author_list, [u'my author'],
u'The author list should be returned correctly with one author')
def build_song_footer_two_authors_test(self):
"""
Test build songs footer with basic song and two authors
"""
# GIVEN: A Song and a Service Item
mock_song = MagicMock()
mock_song.title = u'My Song'
mock_author = MagicMock()
mock_author.display_name = u'my author'
mock_song.authors = []
mock_song.authors.append(mock_author)
mock_author = MagicMock()
mock_author.display_name = u'another author'
mock_song.authors.append(mock_author)
mock_song.copyright = u'My copyright'
service_item = ServiceItem(None)
# WHEN: I generate the Footer with default settings
author_list = self.media_item.generate_footer(service_item, mock_song)
# THEN: I get the following Array returned
self.assertEqual(service_item.raw_footer, [u'My Song', u'my author and another author', u'My copyright'],
u'The array should be returned correctly with a song, two authors and copyright')
self.assertEqual(author_list, [u'my author', u'another author'],
u'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
"""
# GIVEN: A Song and a Service Item and a configured CCLI license
mock_song = MagicMock()
mock_song.title = u'My Song'
mock_author = MagicMock()
mock_author.display_name = u'my author'
mock_song.authors = []
mock_song.authors.append(mock_author)
mock_song.copyright = u'My copyright'
service_item = ServiceItem(None)
Settings().setValue(u'core/ccli number', u'1234')
# WHEN: I generate the Footer with default settings
self.media_item.generate_footer(service_item, mock_song)
# THEN: I get the following Array returned
self.assertEqual(service_item.raw_footer, [u'My Song', u'my author', u'My copyright', u'CCLI License: 1234'],
u'The array should be returned correctly with a song, an author, copyright and ccli')
# WHEN: I amend the CCLI value
Settings().setValue(u'core/ccli number', u'4321')
self.media_item.generate_footer(service_item, mock_song)
# THEN: I would get an amended footer string
self.assertEqual(service_item.raw_footer, [u'My Song', u'my author', u'My copyright', u'CCLI License: 4321'],
u'The array should be returned correctly with a song, an author, copyright and amended ccli')