forked from openlp/openlp
Documentation fixups and \nstart to add OpenlLyrics export for songs
This commit is contained in:
parent
771c86783e
commit
90c631ce87
@ -5,18 +5,30 @@ 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``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.
|
||||
|
||||
.. 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**.
|
||||
|
||||
.. image:: pics/songusagereport.png
|
||||
@ -27,11 +39,3 @@ The details extracted are:
|
||||
- Song Title
|
||||
- Song Copyright
|
||||
- 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
|
||||
|
@ -420,7 +420,7 @@ class MediaManagerItem(QtGui.QWidget):
|
||||
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')
|
||||
|
||||
@ -482,7 +482,7 @@ class MediaManagerItem(QtGui.QWidget):
|
||||
# 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,
|
||||
@ -490,7 +490,7 @@ class MediaManagerItem(QtGui.QWidget):
|
||||
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)
|
||||
@ -525,7 +525,7 @@ class MediaManagerItem(QtGui.QWidget):
|
||||
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
|
||||
"""
|
||||
@ -534,7 +534,7 @@ class MediaManagerItem(QtGui.QWidget):
|
||||
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
|
||||
|
@ -101,6 +101,7 @@ class ServiceItem(object):
|
||||
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 @@ class ServiceItem(object):
|
||||
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 @@ class ServiceItem(object):
|
||||
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)
|
||||
|
@ -651,7 +651,7 @@ class BibleMediaItem(MediaManagerItem):
|
||||
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.
|
||||
|
@ -144,7 +144,7 @@ class CustomMediaItem(MediaManagerItem):
|
||||
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
|
||||
|
@ -154,7 +154,7 @@ class ImageMediaItem(MediaManagerItem):
|
||||
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(
|
||||
|
@ -116,7 +116,7 @@ class MediaMediaItem(MediaManagerItem):
|
||||
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:
|
||||
|
@ -38,7 +38,7 @@ log = logging.getLogger(__name__)
|
||||
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 @@ class PresentationMediaItem(MediaManagerItem):
|
||||
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 @@ class PresentationMediaItem(MediaManagerItem):
|
||||
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 @@ class PresentationMediaItem(MediaManagerItem):
|
||||
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 @@ class PresentationMediaItem(MediaManagerItem):
|
||||
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.
|
||||
|
@ -92,7 +92,6 @@ class VerseType(object):
|
||||
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
|
||||
|
@ -32,7 +32,7 @@ from openlp.core.lib import MediaManagerItem, BaseListWithDnD, Receiver, \
|
||||
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 @@ class SongMediaItem(MediaManagerItem):
|
||||
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 @@ class SongMediaItem(MediaManagerItem):
|
||||
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 @@ class SongMediaItem(MediaManagerItem):
|
||||
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 @@ class SongMediaItem(MediaManagerItem):
|
||||
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 @@ class SongMediaItem(MediaManagerItem):
|
||||
]
|
||||
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 @@ class SongMediaItem(MediaManagerItem):
|
||||
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))
|
||||
|
@ -97,7 +97,6 @@ class SongXMLBuilder(object):
|
||||
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 @@ class LyricsXML(object):
|
||||
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)
|
||||
|
Loading…
Reference in New Issue
Block a user