openlp-core team mailing list archive
-
openlp-core team
-
Mailing list archive
-
Message #04566
[Merge] lp:~trb143/openlp/bugs into lp:openlp
Tim Bentley has proposed merging lp:~trb143/openlp/bugs into lp:openlp.
Requested reviews:
OpenLP Core (openlp-core)
Documentation of Song Usage.
Remove theme for Images.
Sort out silly ness which requires a restart
Add preview when returning from blank screen.
Start on the OpenLyrics Import / Export for service items.
--
https://code.launchpad.net/~trb143/openlp/bugs/+merge/42014
Your team OpenLP Core is requested to review the proposed merge of lp:~trb143/openlp/bugs into lp:openlp.
=== modified file 'documentation/manual/source/index.rst'
--- documentation/manual/source/index.rst 2010-10-15 20:09:57 +0000
+++ documentation/manual/source/index.rst 2010-11-27 15:29:52 +0000
@@ -14,6 +14,7 @@
introduction
glossary
dualmonitors
+ songusage
Indices and tables
==================
=== added file 'documentation/manual/source/pics/songusage.png'
Binary files documentation/manual/source/pics/songusage.png 1970-01-01 00:00:00 +0000 and documentation/manual/source/pics/songusage.png 2010-11-27 15:29:52 +0000 differ
=== added file 'documentation/manual/source/pics/songusagedelete.png'
Binary files documentation/manual/source/pics/songusagedelete.png 1970-01-01 00:00:00 +0000 and documentation/manual/source/pics/songusagedelete.png 2010-11-27 15:29:52 +0000 differ
=== added file 'documentation/manual/source/pics/songusagereport.png'
Binary files documentation/manual/source/pics/songusagereport.png 1970-01-01 00:00:00 +0000 and documentation/manual/source/pics/songusagereport.png 2010-11-27 15:29:52 +0000 differ
=== added file 'documentation/manual/source/songusage.rst'
--- documentation/manual/source/songusage.rst 1970-01-01 00:00:00 +0000
+++ documentation/manual/source/songusage.rst 2010-11-27 15:29:52 +0000
@@ -0,0 +1,41 @@
+==========
+Song Usage
+==========
+
+The Songusage plugin records the usage of Songs when they are used in a **Live**
+situation. If the plugin is active all songs sent to the Live Service Manager
+are recorded by this plugin. Once the plugin has been activated by the plugin
+menu it can be turned on and off by use of the :guilabel:`F4` key or the
+SongUsage menus.
+
+The image below shows the menu items and how to access them.
+
+.. image:: pics/songusage.png
+
+Delete Tracking Data
+^^^^^^^^^^^^^^^^^^^^
+This option allows the removal of stored data use is no longer required.
+Select the date you wish to remove data up to and press :guilabel:`Ok`.
+
+This option is not reversable.
+
+.. image:: pics/songusagedelete.png
+
+Extract Tracking Data
+^^^^^^^^^^^^^^^^^^^^^
+This option allows reports to be generated between any two dates.
+
+The system automatically defaults to dates between the 1st September last year
+and 31st August this year. The data is written to a file in the selected
+directory.
+
+The file name is **usage_detail_fromdate_todate.txt**.
+
+.. image:: pics/songusagereport.png
+
+The details extracted are:
+ - Date item used
+ - Time item used
+ - Song Title
+ - Song Copyright
+ - Song CCLI.
=== modified file 'openlp/core/lib/mediamanageritem.py'
--- openlp/core/lib/mediamanageritem.py 2010-11-20 18:14:43 +0000
+++ openlp/core/lib/mediamanageritem.py 2010-11-27 15:29:52 +0000
@@ -320,15 +320,9 @@
translate('OpenLP.MediaManagerItem',
'&Add to selected Service Item'),
self.onAddEditClick))
- if QtCore.QSettings().value(u'advanced/double click live',
- QtCore.QVariant(False)).toBool():
- QtCore.QObject.connect(self.listView,
- QtCore.SIGNAL(u'doubleClicked(QModelIndex)'),
- self.onLiveClick)
- else:
- QtCore.QObject.connect(self.listView,
- QtCore.SIGNAL(u'doubleClicked(QModelIndex)'),
- self.onPreviewClick)
+ QtCore.QObject.connect(self.listView,
+ QtCore.SIGNAL(u'doubleClicked(QModelIndex)'),
+ self.onClickPressed)
def initialise(self):
"""
@@ -426,10 +420,20 @@
raise NotImplementedError(u'MediaManagerItem.onDeleteClick needs to '
u'be defined by the plugin')
- def generateSlideData(self, service_item, item=None):
+ def generateSlideData(self, service_item, item=None, xmlVersion=False):
raise NotImplementedError(u'MediaManagerItem.generateSlideData needs '
u'to be defined by the plugin')
+ def onClickPressed(self):
+ """
+ Allows the list click action to be determined dynamically
+ """
+ if QtCore.QSettings().value(u'advanced/double click live',
+ QtCore.QVariant(False)).toBool():
+ self.onLiveClick()
+ else:
+ self.onPreviewClick()
+
def onPreviewClick(self):
"""
Preview an item by building a service item then adding that service
@@ -478,7 +482,7 @@
# service items?
if self.singleServiceItem or self.remoteTriggered:
log.debug(self.plugin.name + u' Add requested')
- service_item = self.buildServiceItem()
+ service_item = self.buildServiceItem(None, True)
if service_item:
service_item.from_plugin = False
self.parent.serviceManager.addServiceItem(service_item,
@@ -486,7 +490,7 @@
else:
items = self.listView.selectedIndexes()
for item in items:
- service_item = self.buildServiceItem(item)
+ service_item = self.buildServiceItem(item, True)
if service_item:
service_item.from_plugin = False
self.parent.serviceManager.addServiceItem(service_item)
@@ -521,7 +525,7 @@
unicode(translate('OpenLP.MediaManagerItem',
'You must select a %s service item.')) % self.title)
- def buildServiceItem(self, item=None):
+ def buildServiceItem(self, item=None, xmlVersion=False):
"""
Common method for generating a service item
"""
@@ -530,7 +534,7 @@
service_item.add_icon(self.serviceItemIconName)
else:
service_item.add_icon(self.parent.icon_path)
- if self.generateSlideData(service_item, item):
+ if self.generateSlideData(service_item, item, xmlVersion):
return service_item
else:
return None
=== modified file 'openlp/core/lib/serviceitem.py'
--- openlp/core/lib/serviceitem.py 2010-11-20 18:14:43 +0000
+++ openlp/core/lib/serviceitem.py 2010-11-27 15:29:52 +0000
@@ -101,6 +101,7 @@
self.search_string = u''
self.data_string = u''
self.edit_id = None
+ self.xml_version = None
self._new_item()
def _new_item(self):
@@ -252,7 +253,8 @@
u'from_plugin': self.from_plugin,
u'capabilities': self.capabilities,
u'search': self.search_string,
- u'data': self.data_string
+ u'data': self.data_string,
+ u'xmlVersion': self.xml_version
}
service_data = []
if self.service_item_type == ServiceItemType.Text:
@@ -294,6 +296,8 @@
if u'search' in header:
self.search_string = header[u'search']
self.data_string = header[u'data']
+ if u'xmlVersion' in header:
+ self.xml_version = header[u'xmlVersion']
if self.service_item_type == ServiceItemType.Text:
for slide in serviceitem[u'serviceitem'][u'data']:
self._raw_frames.append(slide)
=== modified file 'openlp/core/ui/advancedtab.py'
--- openlp/core/ui/advancedtab.py 2010-11-20 16:36:54 +0000
+++ openlp/core/ui/advancedtab.py 2010-11-27 15:29:52 +0000
@@ -146,7 +146,7 @@
self.mediaPluginCheckBox.setText(translate('OpenLP.AdvancedTab',
'Remember active media manager tab on startup'))
self.doubleClickLiveCheckBox.setText(translate('OpenLP.AdvancedTab',
- 'Double-click to send items straight to live (requires restart)'))
+ 'Double-click to send items straight to live'))
self.expandServiceItemCheckBox.setText(translate('OpenLP.AdvancedTab',
'Expand new service items on creation'))
# self.sharedDirGroupBox.setTitle(
=== modified file 'openlp/core/ui/maindisplay.py'
--- openlp/core/ui/maindisplay.py 2010-11-20 16:36:54 +0000
+++ openlp/core/ui/maindisplay.py 2010-11-27 15:29:52 +0000
@@ -459,9 +459,9 @@
if self.phononActive:
self.webView.setVisible(False)
self.videoPlay()
+ self.hide_mode = None
# Trigger actions when display is active again
Receiver.send_message(u'maindisplay_active')
- self.hide_mode = None
class AudioPlayer(QtCore.QObject):
"""
=== modified file 'openlp/core/ui/slidecontroller.py'
--- openlp/core/ui/slidecontroller.py 2010-11-20 18:14:43 +0000
+++ openlp/core/ui/slidecontroller.py 2010-11-27 15:29:52 +0000
@@ -331,10 +331,8 @@
QtCore.QObject.connect(self.PreviewListWidget,
QtCore.SIGNAL(u'clicked(QModelIndex)'), self.onSlideSelected)
if not self.isLive:
- if QtCore.QSettings().value(u'advanced/double click live',
- QtCore.QVariant(False)).toBool():
- QtCore.QObject.connect(self.PreviewListWidget,
- QtCore.SIGNAL(u'doubleClicked(QModelIndex)'), self.onGoLive)
+ QtCore.QObject.connect(self.PreviewListWidget,
+ QtCore.SIGNAL(u'doubleClicked(QModelIndex)'), self.onGoLiveClick)
if isLive:
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'slidecontroller_live_spin_delay'),
@@ -391,6 +389,8 @@
if self.isLive:
QtCore.QObject.connect(self.volumeSlider,
QtCore.SIGNAL(u'sliderReleased()'), self.mediaVolume)
+ QtCore.QObject.connect(Receiver.get_receiver(),
+ QtCore.SIGNAL(u'maindisplay_active'), self.updatePreview)
def screenSizeChanged(self):
"""
@@ -823,16 +823,15 @@
row)
def updatePreview(self):
+ log.debug(u'updatePreview %s ' %self.screens.current[u'primary'])
if not self.screens.current[u'primary']:
# Grab now, but try again in a couple of seconds if slide change
# is slow
QtCore.QTimer.singleShot(0.5, self.grabMainDisplay)
QtCore.QTimer.singleShot(2.5, self.grabMainDisplay)
else:
- label = self.PreviewListWidget.cellWidget(
- self.PreviewListWidget.currentRow(), 1)
- if label:
- self.SlidePreview.setPixmap(label.pixmap())
+ self.SlidePreview.setPixmap(
+ QtGui.QPixmap.fromImage(self.display.preview()))
def grabMainDisplay(self):
winid = QtGui.QApplication.desktop().winId()
@@ -944,6 +943,14 @@
Receiver.send_message(u'%s_edit' % self.serviceItem.name.lower(),
u'P:%s' % self.serviceItem.edit_id)
+ def onGoLiveClick(self):
+ """
+ triggered by clicking the Preview slide items
+ """
+ if QtCore.QSettings().value(u'advanced/double click live',
+ QtCore.QVariant(False)).toBool():
+ self.onGoLive()
+
def onGoLive(self):
"""
If preview copy slide item to live
=== modified file 'openlp/plugins/bibles/lib/mediaitem.py'
--- openlp/plugins/bibles/lib/mediaitem.py 2010-10-21 15:31:22 +0000
+++ openlp/plugins/bibles/lib/mediaitem.py 2010-11-27 15:29:52 +0000
@@ -651,7 +651,7 @@
obj = obj.toPyObject()
return unicode(obj)
- def generateSlideData(self, service_item, item=None):
+ def generateSlideData(self, service_item, item=None, xmlVersion=False):
"""
Generates and formats the slides for the service item as well as the
service item's title.
=== modified file 'openlp/plugins/custom/lib/mediaitem.py'
--- openlp/plugins/custom/lib/mediaitem.py 2010-11-20 18:14:43 +0000
+++ openlp/plugins/custom/lib/mediaitem.py 2010-11-27 15:29:52 +0000
@@ -144,7 +144,7 @@
for row in row_list:
self.listView.takeItem(row)
- def generateSlideData(self, service_item, item=None):
+ def generateSlideData(self, service_item, item=None, xmlVersion=False):
raw_slides = []
raw_footer = []
slide = None
=== modified file 'openlp/plugins/images/lib/mediaitem.py'
--- openlp/plugins/images/lib/mediaitem.py 2010-10-23 17:37:10 +0000
+++ openlp/plugins/images/lib/mediaitem.py 2010-11-27 15:29:52 +0000
@@ -154,7 +154,7 @@
item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(file))
self.listView.addItem(item_name)
- def generateSlideData(self, service_item, item=None):
+ def generateSlideData(self, service_item, item=None, xmlVersion=False):
items = self.listView.selectedIndexes()
if items:
service_item.title = unicode(
@@ -163,6 +163,8 @@
service_item.add_capability(ItemCapabilities.AllowsPreview)
service_item.add_capability(ItemCapabilities.AllowsLoop)
service_item.add_capability(ItemCapabilities.AllowsAdditions)
+ # force a nonexistent theme
+ service_item.theme = -1
for item in items:
bitem = self.listView.item(item.row())
filename = unicode(bitem.data(QtCore.Qt.UserRole).toString())
=== modified file 'openlp/plugins/media/lib/mediaitem.py'
--- openlp/plugins/media/lib/mediaitem.py 2010-11-21 20:45:22 +0000
+++ openlp/plugins/media/lib/mediaitem.py 2010-11-27 15:29:52 +0000
@@ -116,7 +116,7 @@
self.parent.liveController.display.video(filename, 0, True)
self.resetButton.setVisible(True)
- def generateSlideData(self, service_item, item=None):
+ def generateSlideData(self, service_item, item=None, xmlVersion=False):
if item is None:
item = self.listView.currentItem()
if item is None:
=== modified file 'openlp/plugins/presentations/lib/mediaitem.py'
--- openlp/plugins/presentations/lib/mediaitem.py 2010-09-27 18:15:55 +0000
+++ openlp/plugins/presentations/lib/mediaitem.py 2010-11-27 15:29:52 +0000
@@ -38,7 +38,7 @@
class PresentationListView(BaseListWithDnD):
"""
Class for the list of Presentations
-
+
We have to explicitly create separate classes for each plugin
in order for DnD to the Service manager to work correctly.
"""
@@ -67,7 +67,7 @@
self.message_listener = MessageListener(self)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'mediaitem_presentation_rebuild'), self.rebuild)
-
+
def retranslateUi(self):
"""
The name of the plugin media displayed in UI
@@ -159,7 +159,7 @@
if self.DisplayTypeComboBox.count() > 1:
self.DisplayTypeComboBox.insertItem(0, self.Automatic)
self.DisplayTypeComboBox.setCurrentIndex(0)
- if QtCore.QSettings().value(self.settingsSection + u'/override app',
+ if QtCore.QSettings().value(self.settingsSection + u'/override app',
QtCore.QVariant(QtCore.Qt.Unchecked)) == QtCore.Qt.Checked:
self.PresentationWidget.show()
else:
@@ -238,7 +238,7 @@
SettingsManager.set_list(self.settingsSection,
self.settingsSection, self.getFileList())
- def generateSlideData(self, service_item, item=None):
+ def generateSlideData(self, service_item, item=None, xmlVersion=False):
"""
Load the relevant information for displaying the presentation
in the slidecontroller. In the case of powerpoints, an image
@@ -277,7 +277,7 @@
def findControllerByType(self, filename):
"""
Determine the default application controller to use for the selected
- file type. This is used if "Automatic" is set as the preferred
+ file type. This is used if "Automatic" is set as the preferred
controller. Find the first (alphabetic) enabled controller which
"supports" the extension. If none found, then look for a controller
which "alsosupports" it instead.
=== modified file 'openlp/plugins/songs/lib/__init__.py'
--- openlp/plugins/songs/lib/__init__.py 2010-09-14 18:18:47 +0000
+++ openlp/plugins/songs/lib/__init__.py 2010-11-27 15:29:52 +0000
@@ -92,7 +92,6 @@
unicode(VerseType.to_string(VerseType.Other)).lower():
return VerseType.Other
-
-from xml import LyricsXML, SongXMLBuilder, SongXMLParser
+from xml import LyricsXML, SongXMLBuilder, SongXMLParser, OpenLyricsParser
from songstab import SongsTab
from mediaitem import SongMediaItem
=== modified file 'openlp/plugins/songs/lib/mediaitem.py'
--- openlp/plugins/songs/lib/mediaitem.py 2010-11-24 17:40:28 +0000
+++ openlp/plugins/songs/lib/mediaitem.py 2010-11-27 15:29:52 +0000
@@ -32,7 +32,7 @@
ItemCapabilities, translate, check_item_selected
from openlp.plugins.songs.forms import EditSongForm, SongMaintenanceForm, \
SongImportForm
-from openlp.plugins.songs.lib import SongXMLParser
+from openlp.plugins.songs.lib import SongXMLParser, OpenLyricsParser
from openlp.plugins.songs.lib.db import Author, Song
log = logging.getLogger(__name__)
@@ -54,7 +54,6 @@
MediaManagerItem.__init__(self, parent, self, icon)
self.edit_song_form = EditSongForm(self, self.parent.manager)
self.singleServiceItem = False
- #self.edit_song_form = EditSongForm(self.parent.manager, self)
self.song_maintenance_form = SongMaintenanceForm(
self.parent.manager, self)
# Holds information about whether the edit is remotly triggered and
@@ -141,7 +140,7 @@
self.updateServiceOnEdit = QtCore.QSettings().value(
self.settingsSection + u'/update service on edit',
QtCore.QVariant(u'False')).toBool()
- self.AddSongFromServide = QtCore.QSettings().value(
+ self.addSongFromService = QtCore.QSettings().value(
self.settingsSection + u'/add song from service',
QtCore.QVariant(u'True')).toBool()
@@ -328,7 +327,7 @@
self.parent.manager.delete_object(Song, item_id)
self.onSearchTextButtonClick()
- def generateSlideData(self, service_item, item=None):
+ def generateSlideData(self, service_item, item=None, xmlVersion=False):
log.debug(u'generateSlideData (%s:%s)' % (service_item, item))
raw_footer = []
author_list = u''
@@ -355,7 +354,7 @@
if song.lyrics.startswith(u'<?xml version='):
songXML = SongXMLParser(song.lyrics)
verseList = songXML.get_verses()
- #no verse list or only 1 space (in error)
+ # no verse list or only 1 space (in error)
if not song.verse_order or not song.verse_order.strip():
for verse in verseList:
verseTag = u'%s:%s' % (
@@ -397,6 +396,7 @@
]
service_item.data_string = {u'title':song.search_title,
u'authors':author_list}
+ service_item.xml_version = OpenLyricsParser().songToXml(song)
return True
def serviceLoad(self, item):
@@ -411,16 +411,26 @@
Song.search_title.asc())
author_list = item.data_string[u'authors'].split(u', ')
editId = 0
- uuid = 0
+ uuid = item._uuid
if search_results:
for song in search_results:
count = 0
for author in song.authors:
if author.display_name in author_list:
count += 1
+ # All Authors the same
if count == len(author_list):
editId = song.id
- uuid = item._uuid
+ else:
+ # Authors different
+ if self.addSongFromService:
+ editId = OpenLyricsParser(). \
+ xmlToSong(item.xml_version)
+ else:
+ # Title does not match
+ if self.addSongFromService:
+ editId = OpenLyricsParser().xmlToSong(item.xml_version)
+ # Update service with correct song id
if editId != 0:
Receiver.send_message(u'service_item_update',
u'%s:%s' %(editId, uuid))
=== modified file 'openlp/plugins/songs/lib/xml.py'
--- openlp/plugins/songs/lib/xml.py 2010-09-14 18:18:47 +0000
+++ openlp/plugins/songs/lib/xml.py 2010-11-27 15:29:52 +0000
@@ -97,7 +97,6 @@
return etree.tostring(self.song_xml, encoding=u'UTF-8',
xml_declaration=True)
-
class SongXMLParser(object):
"""
A class to read in and parse a song's XML.
@@ -239,3 +238,71 @@
song_output = u'<?xml version="1.0" encoding="UTF-8"?>' + \
u'<song version="1.0">%s</song>' % lyrics_output
return song_output
+
+class OpenLyricsParser(object):
+ """
+ This class represents the converter for Song to/from OpenLyrics XML.
+ """
+ def songToXml(self, song):
+ """
+ Convert the song to OpenLyrics Format
+ """
+ songXML = SongXMLParser(song.lyrics)
+ verseList = songXML.get_verses()
+ song_xml = objectify.fromstring(
+ u'<song version="0.7" createdIn="OpenLP 2.0"/>')
+ properties = etree.SubElement(song_xml, u'properties')
+ titles = etree.SubElement(properties, u'titles')
+ self.add_text_to_element(u'title', titles, song.title)
+ if song.alternate_title:
+ self.add_text_to_element(u'title', titles, song.alternate_title)
+ if song.theme_name:
+ themes = etree.SubElement(properties, u'themes')
+ self.add_text_to_element(u'theme', themes, song.theme_name)
+ self.add_text_to_element(u'copyright', properties, song.copyright)
+ self.add_text_to_element(u'verseOrder', properties, song.verse_order)
+ if song.ccli_number:
+ self.add_text_to_element(u'ccliNo', properties, song.ccli_number)
+ authors = etree.SubElement(properties, u'authors')
+ for author in song.authors:
+ self.add_text_to_element(u'author', authors, author.display_name)
+ lyrics = etree.SubElement(song_xml, u'lyrics')
+ for verse in verseList:
+ verseTag = u'%s%s' % (
+ verse[0][u'type'][0].lower(), verse[0][u'label'])
+ element = self.add_text_to_element(u'verse', lyrics, None, verseTag)
+ element = self.add_text_to_element(u'lines', element)
+ for line in unicode(verse[1]).split(u'\n'):
+ self.add_text_to_element(u'line', element, line)
+ #print self.dump_xml(song_xml)
+ return u'' #self.extract_xml(song_xml)
+
+ def add_text_to_element(self, tag, parent, text=None, label=None):
+ if label:
+ element = etree.Element(tag, name = unicode(label))
+ else:
+ element = etree.Element(tag)
+ if text:
+ element.text = unicode(text)
+ parent.append(element)
+ return element
+
+ def xmlToSong(self, xml):
+ """
+ Create a Song from OpenLyrics format xml
+ """
+ return 0
+
+ def dump_xml(self, xml):
+ """
+ Debugging aid to dump XML so that we can see what we have.
+ """
+ return etree.tostring(xml, encoding=u'UTF-8',
+ xml_declaration=True, pretty_print=True)
+
+ def extract_xml(self, xml):
+ """
+ Extract our newly created XML song.
+ """
+ return etree.tostring(xml, encoding=u'UTF-8',
+ xml_declaration=True)