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**
|
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
|
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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.
|
||||||
|
@ -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
|
||||||
|
@ -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(
|
||||||
|
@ -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:
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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))
|
||||||
|
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user