Documentation fixups and \nstart to add OpenlLyrics export for songs

This commit is contained in:
Tim Bentley 2010-11-27 15:25:00 +00:00
parent 771c86783e
commit 90c631ce87
11 changed files with 123 additions and 39 deletions

View File

@ -5,18 +5,30 @@ Song Usage
The Songusage plugin records the usage of Songs when they are used in a **Live** 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 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 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``F4`` key or the SongUsage menus. 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. The image below shows the menu items and how to access them.
.. image:: pics/songusage.png .. 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.
Generating reports
------------------
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 a selectable directory.
The file name is **usage_detail_fromdate_todate.txt**. The file name is **usage_detail_fromdate_todate.txt**.
.. image:: pics/songusagereport.png .. image:: pics/songusagereport.png
@ -27,11 +39,3 @@ The details extracted are:
- Song Title - Song Title
- Song Copyright - Song Copyright
- Song CCLI. - Song CCLI.
Removing 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``Ok``. This option is
not reversable.
.. image:: pics/songusagedelete.png

View File

@ -420,7 +420,7 @@ class MediaManagerItem(QtGui.QWidget):
raise NotImplementedError(u'MediaManagerItem.onDeleteClick needs to ' raise NotImplementedError(u'MediaManagerItem.onDeleteClick needs to '
u'be defined by the plugin') 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 ' raise NotImplementedError(u'MediaManagerItem.generateSlideData needs '
u'to be defined by the plugin') u'to be defined by the plugin')
@ -482,7 +482,7 @@ class MediaManagerItem(QtGui.QWidget):
# service items? # service items?
if self.singleServiceItem or self.remoteTriggered: if self.singleServiceItem or self.remoteTriggered:
log.debug(self.plugin.name + u' Add requested') log.debug(self.plugin.name + u' Add requested')
service_item = self.buildServiceItem() service_item = self.buildServiceItem(None, True)
if service_item: if service_item:
service_item.from_plugin = False service_item.from_plugin = False
self.parent.serviceManager.addServiceItem(service_item, self.parent.serviceManager.addServiceItem(service_item,
@ -490,7 +490,7 @@ class MediaManagerItem(QtGui.QWidget):
else: else:
items = self.listView.selectedIndexes() items = self.listView.selectedIndexes()
for item in items: for item in items:
service_item = self.buildServiceItem(item) service_item = self.buildServiceItem(item, True)
if service_item: if service_item:
service_item.from_plugin = False service_item.from_plugin = False
self.parent.serviceManager.addServiceItem(service_item) self.parent.serviceManager.addServiceItem(service_item)
@ -525,7 +525,7 @@ class MediaManagerItem(QtGui.QWidget):
unicode(translate('OpenLP.MediaManagerItem', unicode(translate('OpenLP.MediaManagerItem',
'You must select a %s service item.')) % self.title) '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 Common method for generating a service item
""" """
@ -534,7 +534,7 @@ class MediaManagerItem(QtGui.QWidget):
service_item.add_icon(self.serviceItemIconName) service_item.add_icon(self.serviceItemIconName)
else: else:
service_item.add_icon(self.parent.icon_path) service_item.add_icon(self.parent.icon_path)
if self.generateSlideData(service_item, item): if self.generateSlideData(service_item, item, xmlVersion):
return service_item return service_item
else: else:
return None return None

View File

@ -101,6 +101,7 @@ class ServiceItem(object):
self.search_string = u'' self.search_string = u''
self.data_string = u'' self.data_string = u''
self.edit_id = None self.edit_id = None
self.xml_version = None
self._new_item() self._new_item()
def _new_item(self): def _new_item(self):
@ -252,7 +253,8 @@ class ServiceItem(object):
u'from_plugin': self.from_plugin, u'from_plugin': self.from_plugin,
u'capabilities': self.capabilities, u'capabilities': self.capabilities,
u'search': self.search_string, u'search': self.search_string,
u'data': self.data_string u'data': self.data_string,
u'xmlVersion': self.xml_version
} }
service_data = [] service_data = []
if self.service_item_type == ServiceItemType.Text: if self.service_item_type == ServiceItemType.Text:
@ -294,6 +296,8 @@ class ServiceItem(object):
if u'search' in header: if u'search' in header:
self.search_string = header[u'search'] self.search_string = header[u'search']
self.data_string = header[u'data'] self.data_string = header[u'data']
if u'xmlVersion' in header:
self.xml_version = header[u'xmlVersion']
if self.service_item_type == ServiceItemType.Text: if self.service_item_type == ServiceItemType.Text:
for slide in serviceitem[u'serviceitem'][u'data']: for slide in serviceitem[u'serviceitem'][u'data']:
self._raw_frames.append(slide) self._raw_frames.append(slide)

View File

@ -651,7 +651,7 @@ class BibleMediaItem(MediaManagerItem):
obj = obj.toPyObject() obj = obj.toPyObject()
return unicode(obj) 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 Generates and formats the slides for the service item as well as the
service item's title. service item's title.

View File

@ -144,7 +144,7 @@ class CustomMediaItem(MediaManagerItem):
for row in row_list: for row in row_list:
self.listView.takeItem(row) self.listView.takeItem(row)
def generateSlideData(self, service_item, item=None): def generateSlideData(self, service_item, item=None, xmlVersion=False):
raw_slides = [] raw_slides = []
raw_footer = [] raw_footer = []
slide = None slide = None

View File

@ -154,7 +154,7 @@ class ImageMediaItem(MediaManagerItem):
item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(file)) item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(file))
self.listView.addItem(item_name) 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() items = self.listView.selectedIndexes()
if items: if items:
service_item.title = unicode( service_item.title = unicode(

View File

@ -116,7 +116,7 @@ class MediaMediaItem(MediaManagerItem):
self.parent.liveController.display.video(filename, 0, True) self.parent.liveController.display.video(filename, 0, True)
self.resetButton.setVisible(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: if item is None:
item = self.listView.currentItem() item = self.listView.currentItem()
if item is None: if item is None:

View File

@ -38,7 +38,7 @@ log = logging.getLogger(__name__)
class PresentationListView(BaseListWithDnD): class PresentationListView(BaseListWithDnD):
""" """
Class for the list of Presentations Class for the list of Presentations
We have to explicitly create separate classes for each plugin We have to explicitly create separate classes for each plugin
in order for DnD to the Service manager to work correctly. in order for DnD to the Service manager to work correctly.
""" """
@ -67,7 +67,7 @@ class PresentationMediaItem(MediaManagerItem):
self.message_listener = MessageListener(self) self.message_listener = MessageListener(self)
QtCore.QObject.connect(Receiver.get_receiver(), QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'mediaitem_presentation_rebuild'), self.rebuild) QtCore.SIGNAL(u'mediaitem_presentation_rebuild'), self.rebuild)
def retranslateUi(self): def retranslateUi(self):
""" """
The name of the plugin media displayed in UI The name of the plugin media displayed in UI
@ -159,7 +159,7 @@ class PresentationMediaItem(MediaManagerItem):
if self.DisplayTypeComboBox.count() > 1: if self.DisplayTypeComboBox.count() > 1:
self.DisplayTypeComboBox.insertItem(0, self.Automatic) self.DisplayTypeComboBox.insertItem(0, self.Automatic)
self.DisplayTypeComboBox.setCurrentIndex(0) 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: QtCore.QVariant(QtCore.Qt.Unchecked)) == QtCore.Qt.Checked:
self.PresentationWidget.show() self.PresentationWidget.show()
else: else:
@ -238,7 +238,7 @@ class PresentationMediaItem(MediaManagerItem):
SettingsManager.set_list(self.settingsSection, SettingsManager.set_list(self.settingsSection,
self.settingsSection, self.getFileList()) 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 Load the relevant information for displaying the presentation
in the slidecontroller. In the case of powerpoints, an image in the slidecontroller. In the case of powerpoints, an image
@ -277,7 +277,7 @@ class PresentationMediaItem(MediaManagerItem):
def findControllerByType(self, filename): def findControllerByType(self, filename):
""" """
Determine the default application controller to use for the selected 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 controller. Find the first (alphabetic) enabled controller which
"supports" the extension. If none found, then look for a controller "supports" the extension. If none found, then look for a controller
which "alsosupports" it instead. which "alsosupports" it instead.

View File

@ -92,7 +92,6 @@ class VerseType(object):
unicode(VerseType.to_string(VerseType.Other)).lower(): unicode(VerseType.to_string(VerseType.Other)).lower():
return VerseType.Other return VerseType.Other
from xml import LyricsXML, SongXMLBuilder, SongXMLParser, OpenLyricsParser
from xml import LyricsXML, SongXMLBuilder, SongXMLParser
from songstab import SongsTab from songstab import SongsTab
from mediaitem import SongMediaItem from mediaitem import SongMediaItem

View File

@ -32,7 +32,7 @@ from openlp.core.lib import MediaManagerItem, BaseListWithDnD, Receiver, \
ItemCapabilities, translate, check_item_selected ItemCapabilities, translate, check_item_selected
from openlp.plugins.songs.forms import EditSongForm, SongMaintenanceForm, \ from openlp.plugins.songs.forms import EditSongForm, SongMaintenanceForm, \
SongImportForm 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 from openlp.plugins.songs.lib.db import Author, Song
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -54,7 +54,6 @@ class SongMediaItem(MediaManagerItem):
MediaManagerItem.__init__(self, parent, self, icon) MediaManagerItem.__init__(self, parent, self, icon)
self.edit_song_form = EditSongForm(self, self.parent.manager) self.edit_song_form = EditSongForm(self, self.parent.manager)
self.singleServiceItem = False self.singleServiceItem = False
#self.edit_song_form = EditSongForm(self.parent.manager, self)
self.song_maintenance_form = SongMaintenanceForm( self.song_maintenance_form = SongMaintenanceForm(
self.parent.manager, self) self.parent.manager, self)
# Holds information about whether the edit is remotly triggered and # Holds information about whether the edit is remotly triggered and
@ -141,7 +140,7 @@ class SongMediaItem(MediaManagerItem):
self.updateServiceOnEdit = QtCore.QSettings().value( self.updateServiceOnEdit = QtCore.QSettings().value(
self.settingsSection + u'/update service on edit', self.settingsSection + u'/update service on edit',
QtCore.QVariant(u'False')).toBool() QtCore.QVariant(u'False')).toBool()
self.AddSongFromServide = QtCore.QSettings().value( self.addSongFromService = QtCore.QSettings().value(
self.settingsSection + u'/add song from service', self.settingsSection + u'/add song from service',
QtCore.QVariant(u'True')).toBool() QtCore.QVariant(u'True')).toBool()
@ -328,7 +327,7 @@ class SongMediaItem(MediaManagerItem):
self.parent.manager.delete_object(Song, item_id) self.parent.manager.delete_object(Song, item_id)
self.onSearchTextButtonClick() 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)) log.debug(u'generateSlideData (%s:%s)' % (service_item, item))
raw_footer = [] raw_footer = []
author_list = u'' author_list = u''
@ -355,7 +354,7 @@ class SongMediaItem(MediaManagerItem):
if song.lyrics.startswith(u'<?xml version='): if song.lyrics.startswith(u'<?xml version='):
songXML = SongXMLParser(song.lyrics) songXML = SongXMLParser(song.lyrics)
verseList = songXML.get_verses() 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(): if not song.verse_order or not song.verse_order.strip():
for verse in verseList: for verse in verseList:
verseTag = u'%s:%s' % ( verseTag = u'%s:%s' % (
@ -397,6 +396,7 @@ class SongMediaItem(MediaManagerItem):
] ]
service_item.data_string = {u'title':song.search_title, service_item.data_string = {u'title':song.search_title,
u'authors':author_list} u'authors':author_list}
service_item.xml_version = OpenLyricsParser().songToXml(song)
return True return True
def serviceLoad(self, item): def serviceLoad(self, item):
@ -411,16 +411,26 @@ class SongMediaItem(MediaManagerItem):
Song.search_title.asc()) Song.search_title.asc())
author_list = item.data_string[u'authors'].split(u', ') author_list = item.data_string[u'authors'].split(u', ')
editId = 0 editId = 0
uuid = 0 uuid = item._uuid
if search_results: if search_results:
for song in search_results: for song in search_results:
count = 0 count = 0
for author in song.authors: for author in song.authors:
if author.display_name in author_list: if author.display_name in author_list:
count += 1 count += 1
# All Authors the same
if count == len(author_list): if count == len(author_list):
editId = song.id 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: if editId != 0:
Receiver.send_message(u'service_item_update', Receiver.send_message(u'service_item_update',
u'%s:%s' %(editId, uuid)) u'%s:%s' %(editId, uuid))

View File

@ -97,7 +97,6 @@ class SongXMLBuilder(object):
return etree.tostring(self.song_xml, encoding=u'UTF-8', return etree.tostring(self.song_xml, encoding=u'UTF-8',
xml_declaration=True) xml_declaration=True)
class SongXMLParser(object): class SongXMLParser(object):
""" """
A class to read in and parse a song's XML. A class to read in and parse a song's XML.
@ -239,3 +238,71 @@ class LyricsXML(object):
song_output = u'<?xml version="1.0" encoding="UTF-8"?>' + \ song_output = u'<?xml version="1.0" encoding="UTF-8"?>' + \
u'<song version="1.0">%s</song>' % lyrics_output u'<song version="1.0">%s</song>' % lyrics_output
return song_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)