Start XML refactoring

bzr-revno: 914
This commit is contained in:
Jon Tibble 2010-07-04 22:32:30 +01:00
commit a3f562e356
15 changed files with 234 additions and 222 deletions

View File

@ -60,18 +60,6 @@
.. autoclass:: openlp.core.lib.settingstab.SettingsTab
:members:
:mod:`SongXMLBuilder`
---------------------
.. autoclass:: openlp.core.lib.songxmlhandler.SongXMLBuilder
:members:
:mod:`SongXMLParser`
--------------------
.. autoclass:: openlp.core.lib.songxmlhandler.SongXMLParser
:members:
:mod:`ThemeXML`
---------------
@ -83,10 +71,3 @@
.. autoclass:: openlp.core.lib.toolbar.OpenLPToolbar
:members:
:mod:`XmlRootClass`
-------------------
.. autoclass:: openlp.core.lib.xmlrootclass.XmlRootClass
:members:

View File

@ -202,28 +202,17 @@ def check_item_selected(list_widget, message):
return False
return True
class ThemeLevel(object):
"""
Provides an enumeration for the level a theme applies to
"""
Global = 1
Service = 2
Song = 3
from eventreceiver import Receiver
from settingsmanager import SettingsManager
from plugin import PluginStatus, Plugin
from pluginmanager import PluginManager
from settingstab import SettingsTab
from xmlrootclass import XmlRootClass
from serviceitem import ServiceItem
from serviceitem import ServiceItemType
from serviceitem import ItemCapabilities
from toolbar import OpenLPToolbar
from dockwidget import OpenLPDockWidget
from songxmlhandler import SongXMLBuilder, SongXMLParser
from themexmlhandler import ThemeXML
from theme import ThemeLevel, ThemeXML
from renderer import Renderer
from rendermanager import RenderManager
from mediamanageritem import MediaManagerItem

View File

@ -79,6 +79,14 @@ BLANK_THEME_XML = \
</theme>
'''
class ThemeLevel(object):
"""
Provides an enumeration for the level a theme applies to
"""
Global = 1
Service = 2
Song = 3
class ThemeXML(object):
"""
A class to encapsulate the Theme XML.
@ -313,7 +321,6 @@ class ThemeXML(object):
element.appendChild(value)
background.appendChild(element)
def child_element(self, element, tag, value):
"""
Generic child element creator.
@ -414,4 +421,3 @@ class ThemeXML(object):
if key[0:1] != u'_':
theme_strings.append(u'%30s: %s' % (key, getattr(self, key)))
return u'\n'.join(theme_strings)

View File

@ -1,104 +0,0 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2010 Raoul Snyman #
# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael #
# Gorven, Scott Guerrieri, Christian Richter, Maikel Stuivenberg, Martin #
# Thompson, Jon Tibble, Carsten Tinggaard #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
import os
import sys
from xml.etree.ElementTree import ElementTree, XML
sys.path.append(os.path.abspath(os.path.join(u'.', u'..', u'..')))
class XmlRootClass(object):
"""
Root class for themes, songs etc
This class provides interface for parsing xml files into object attributes.
If you overload this class and provide a function called `post_tag_hook`,
it will be called thusly for each `tag, value` pair::
(element.tag, val) = self.post_tag_hook(element.tag, val)
"""
def _set_from_xml(self, xml, root_tag):
"""
Set song properties from given xml content.
``xml``
Formatted xml tags and values.
``root_tag``
The root tag of the xml.
"""
root = ElementTree(element=XML(xml))
xml_iter = root.getiterator()
for element in xml_iter:
if element.tag != root_tag:
text = element.text
if text is None:
val = text
elif isinstance(text, basestring):
# Strings need special handling to sort the colours out
if text[0] == u'$':
# This might be a hex number, let's try to convert it.
try:
val = int(text[1:], 16)
except ValueError:
pass
else:
# Let's just see if it's a integer.
try:
val = int(text)
except ValueError:
# Ok, it seems to be a string.
val = text
if hasattr(self, u'post_tag_hook'):
(element.tag, val) = \
self.post_tag_hook(element.tag, val)
setattr(self, element.tag, val)
def __str__(self):
"""
Return string with all public attributes
The string is formatted with one attribute per line
If the string is split on newline then the length of the
list is equal to the number of attributes
"""
attributes = []
for attrib in dir(self):
if not attrib.startswith(u'_'):
attributes.append(
u'%30s : %s' % (attrib, getattr(self, attrib)))
return u'\n'.join(attributes)
def _get_as_string(self):
"""
Return one string with all public attributes
"""
result = u''
for attrib in dir(self):
if not attrib.startswith(u'_'):
result += u'_%s_' % getattr(self, attrib)
return result

View File

@ -222,4 +222,3 @@ class Theme(object):
if key[0:1] != u'_':
theme_strings.append(u'%30s : %s' % (key, getattr(self, key)))
return u'\n'.join(theme_strings)

View File

@ -28,7 +28,8 @@ import logging
from PyQt4 import QtCore, QtGui
from editcustomdialog import Ui_customEditDialog
from openlp.core.lib import SongXMLBuilder, SongXMLParser, Receiver, translate
from openlp.core.lib import Receiver, translate
from openlp.plugins.custom.lib import CustomXMLBuilder, CustomXMLParser
from openlp.plugins.custom.lib.db import CustomSlide
log = logging.getLogger(__name__)
@ -119,8 +120,8 @@ class EditCustomForm(QtGui.QDialog, Ui_customEditDialog):
self.customSlide = self.custommanager.get_object(CustomSlide, id)
self.TitleEdit.setText(self.customSlide.title)
self.CreditEdit.setText(self.customSlide.credits)
songXML = SongXMLParser(self.customSlide.text)
verseList = songXML.get_verses()
customXML = CustomXMLParser(self.customSlide.text)
verseList = customXML.get_verses()
for verse in verseList:
self.VerseListView.addItem(verse[1])
theme = self.customSlide.theme_name
@ -152,7 +153,7 @@ class EditCustomForm(QtGui.QDialog, Ui_customEditDialog):
translate('CustomPlugin.EditCustomForm', 'Error'), message,
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok))
return False
sxml = SongXMLBuilder()
sxml = CustomXMLBuilder()
sxml.new_document()
sxml.add_lyrics_to_song()
count = 1

View File

@ -23,5 +23,6 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
from customxmlhandler import CustomXMLBuilder, CustomXMLParser
from mediaitem import CustomMediaItem
from customtab import CustomTab

View File

@ -23,7 +23,8 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
The :mod:`songxmlhandler` module provides the XML functionality for songs
The :mod:`customxmlhandler` module provides the XML functionality for custom
slides
The basic XML is of the format::
@ -45,26 +46,26 @@ from xml.parsers.expat import ExpatError
log = logging.getLogger(__name__)
class SongXMLBuilder(object):
class CustomXMLBuilder(object):
"""
This class builds the XML used to describe songs.
"""
log.info(u'SongXMLBuilder Loaded')
log.info(u'CustomXMLBuilder Loaded')
def __init__(self):
"""
Set up the song builder.
"""
# Create the minidom document
self.song_xml = Document()
self.custom_xml = Document()
def new_document(self):
"""
Create a new song XML document.
"""
# Create the <song> base element
self.song = self.song_xml.createElement(u'song')
self.song_xml.appendChild(self.song)
self.song = self.custom_xml.createElement(u'song')
self.custom_xml.appendChild(self.song)
self.song.setAttribute(u'version', u'1.0')
def add_lyrics_to_song(self):
@ -73,7 +74,7 @@ class SongXMLBuilder(object):
song.
"""
# Create the main <lyrics> element
self.lyrics = self.song_xml.createElement(u'lyrics')
self.lyrics = self.custom_xml.createElement(u'lyrics')
self.lyrics.setAttribute(u'language', u'en')
self.song.appendChild(self.lyrics)
@ -92,32 +93,32 @@ class SongXMLBuilder(object):
The actual text of the verse to be stored.
"""
#log.debug(u'add_verse_to_lyrics %s, %s\n%s' % (type, number, content))
verse = self.song_xml.createElement(u'verse')
verse = self.custom_xml.createElement(u'verse')
verse.setAttribute(u'type', type)
verse.setAttribute(u'label', number)
self.lyrics.appendChild(verse)
# add data as a CDATA section to protect the XML from special chars
cds = self.song_xml.createCDATASection(content)
cds = self.custom_xml.createCDATASection(content)
verse.appendChild(cds)
def dump_xml(self):
"""
Debugging aid to dump XML so that we can see what we have.
"""
return self.song_xml.toprettyxml(indent=u' ')
return self.custom_xml.toprettyxml(indent=u' ')
def extract_xml(self):
"""
Extract our newly created XML song.
"""
return self.song_xml.toxml(u'utf-8')
return self.custom_xml.toxml(u'utf-8')
class SongXMLParser(object):
class CustomXMLParser(object):
"""
A class to read in and parse a song's XML.
"""
log.info(u'SongXMLParser Loaded')
log.info(u'CustomXMLParser Loaded')
def __init__(self, xml):
"""
@ -126,9 +127,9 @@ class SongXMLParser(object):
``xml``
The XML of the song to be parsed.
"""
self.song_xml = None
self.custom_xml = None
try:
self.song_xml = ElementTree(
self.custom_xml = ElementTree(
element=XML(unicode(xml).encode('unicode-escape')))
except ExpatError:
log.exception(u'Invalid xml %s', xml)
@ -138,7 +139,7 @@ class SongXMLParser(object):
Iterates through the verses in the XML and returns a list of verses
and their attributes.
"""
xml_iter = self.song_xml.getiterator()
xml_iter = self.custom_xml.getiterator()
verse_list = []
for element in xml_iter:
if element.tag == u'verse':
@ -152,4 +153,4 @@ class SongXMLParser(object):
"""
Debugging aid to dump XML so that we can see what we have.
"""
return dump(self.song_xml)
return dump(self.custom_xml)

View File

@ -27,8 +27,9 @@ import logging
from PyQt4 import QtCore, QtGui
from openlp.core.lib import MediaManagerItem, SongXMLParser, BaseListWithDnD, \
from openlp.core.lib import MediaManagerItem, BaseListWithDnD, \
Receiver, ItemCapabilities, translate, check_item_selected
from openlp.plugins.custom.lib import CustomXMLParser
from openlp.plugins.custom.lib.db import CustomSlide
log = logging.getLogger(__name__)
@ -170,8 +171,8 @@ class CustomMediaItem(MediaManagerItem):
theme = customSlide.theme_name
if theme:
service_item.theme = theme
songXML = SongXMLParser(customSlide.text)
verseList = songXML.get_verses()
customXML = CustomXMLParser(customSlide.text)
verseList = customXML.get_verses()
for verse in verseList:
raw_slides.append(verse[1])
service_item.title = title

View File

@ -28,9 +28,9 @@ import re
from PyQt4 import QtCore, QtGui
from openlp.core.lib import SongXMLBuilder, SongXMLParser, Receiver, translate
from openlp.core.lib import Receiver, translate
from openlp.plugins.songs.forms import EditVerseForm
from openlp.plugins.songs.lib import VerseType
from openlp.plugins.songs.lib import SongXMLBuilder, SongXMLParser, VerseType
from openlp.plugins.songs.lib.db import Book, Song, Author, Topic
from editsongdialog import Ui_EditSongDialog
@ -639,8 +639,6 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
log.debug(u'processLyrics')
try:
sxml = SongXMLBuilder()
sxml.new_document()
sxml.add_lyrics_to_song()
text = u''
multiple = []
for i in range (0, self.VerseListWidget.rowCount()):
@ -666,4 +664,3 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
log.debug(u'processTitle')
self.song.search_title = \
re.sub(r'[\'"`,;:(){}?]+', u'', unicode(self.song.search_title))

View File

@ -137,6 +137,7 @@ class VerseType(object):
unicode(VerseType.to_string(VerseType.Other)).lower():
return VerseType.Other
from xml import LyricsXML, SongXMLBuilder, SongXMLParser
from songstab import SongsTab
from mediaitem import SongMediaItem
from songimport import SongImport
@ -145,4 +146,3 @@ try:
from oooimport import OooImport
except ImportError:
pass

View File

@ -27,10 +27,11 @@ import logging
from PyQt4 import QtCore, QtGui
from openlp.core.lib import MediaManagerItem, SongXMLParser, Receiver, \
BaseListWithDnD, ItemCapabilities, translate, check_item_selected
from openlp.core.lib import MediaManagerItem, BaseListWithDnD, Receiver, \
ItemCapabilities, translate, check_item_selected
from openlp.plugins.songs.forms import EditSongForm, SongMaintenanceForm, \
ImportWizardForm
from openlp.plugins.songs.lib import SongXMLParser
from openlp.plugins.songs.lib.db import Author, Song
log = logging.getLogger(__name__)

View File

@ -25,8 +25,8 @@
import re
from openlp.core.lib import SongXMLBuilder, translate
from openlp.plugins.songs.lib import VerseType
from openlp.core.lib import translate
from openlp.plugins.songs.lib import SongXMLBuilder, VerseType
from openlp.plugins.songs.lib.db import Song, Author, Topic, Book
class SongImport(object):
@ -276,8 +276,6 @@ class SongImport(object):
song.song_number = self.song_number
song.search_lyrics = u''
sxml = SongXMLBuilder()
sxml.new_document()
sxml.add_lyrics_to_song()
for (versetag, versetext) in self.verses:
if versetag[0] == u'C':
versetype = VerseType.to_string(VerseType.Chorus)

View File

@ -24,18 +24,18 @@
###############################################################################
import logging
import sys
import os
#import sys
#import os
from types import ListType
from xml.etree.ElementTree import ElementTree, XML
sys.path.append(os.path.abspath(u'./../../../..'))
from openlp.core.lib import XmlRootClass
# Do we need these two lines?
#sys.path.append(os.path.abspath(u'./../../../..'))
#sys.path.append(os.path.abspath(os.path.join(u'.', u'..', u'..')))
log = logging.getLogger(__name__)
class SongException(Exception):
pass
@ -74,23 +74,78 @@ _BLANK_OPENSONG_XML = \
</song>
'''
class _OpenSong(XmlRootClass):
"""Class for import of OpenSong"""
class _OpenSong(object):
"""
Class for import of OpenSong
"""
def __init__(self, xmlContent = None):
"""Initialize from given xml content"""
super(_OpenSong, self).__init__()
self.from_buffer(xmlContent)
def _reset(self):
"""Reset all song attributes"""
self._setFromXml(_BLANK_OPENSONG_XML, 'song')
def from_buffer(self, xmlContent):
"""Initialize from buffer(string) with xml content"""
self._reset()
"""
Initialize from given xml content
"""
self._set_from_xml(_BLANK_OPENSONG_XML, 'song')
if xmlContent:
self._setFromXml(xmlContent, 'song')
self._set_from_xml(xmlContent, 'song')
def _set_from_xml(self, xml, root_tag):
"""
Set song properties from given xml content.
``xml``
Formatted xml tags and values.
``root_tag``
The root tag of the xml.
"""
root = ElementTree(element=XML(xml))
xml_iter = root.getiterator()
for element in xml_iter:
if element.tag != root_tag:
text = element.text
if text is None:
val = text
elif isinstance(text, basestring):
# Strings need special handling to sort the colours out
if text[0] == u'$':
# This might be a hex number, let's try to convert it.
try:
val = int(text[1:], 16)
except ValueError:
pass
else:
# Let's just see if it's a integer.
try:
val = int(text)
except ValueError:
# Ok, it seems to be a string.
val = text
if hasattr(self, u'post_tag_hook'):
(element.tag, val) = \
self.post_tag_hook(element.tag, val)
setattr(self, element.tag, val)
def __str__(self):
"""
Return string with all public attributes
The string is formatted with one attribute per line
If the string is split on newline then the length of the
list is equal to the number of attributes
"""
attributes = []
for attrib in dir(self):
if not attrib.startswith(u'_'):
attributes.append(
u'%30s : %s' % (attrib, getattr(self, attrib)))
return u'\n'.join(attributes)
def _get_as_string(self):
"""
Return one string with all public attributes
"""
result = u''
for attrib in dir(self):
if not attrib.startswith(u'_'):
result += u'_%s_' % getattr(self, attrib)
return result
def get_author_list(self):
"""Convert author field to an authorlist
@ -252,14 +307,6 @@ class Song(object):
self.set_lyrics(u'')
return
def set_songid(self, songid):
"""Set the songid for the database"""
self.songid = songid
def get_songid(self):
"""Return the songid for the database"""
return self.songid
def from_opensong_buffer(self, xmlcontent):
"""Initialize from buffer(string) of xml lines in opensong format"""
self._reset()
@ -323,10 +370,6 @@ class Song(object):
"""Return title value"""
return self.title
def get_search_title(self):
"""Return search_title"""
return self.search_title
def from_ccli_text_buffer(self, textList):
"""
Create song from a list of texts (strings) - CCLI text format expected

View File

@ -22,24 +22,123 @@
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
The :mod:`xml` module provides the XML functionality for songs
The basic XML is of the format::
<?xml version="1.0" encoding="UTF-8"?>
<song version="1.0">
<lyrics language="en">
<verse type="chorus" label="1">
<![CDATA[ ... ]]>
</verse>
</lyrics>
</song>
"""
import logging
from lxml import etree, objectify
log = logging.getLogger(__name__)
class SongXMLBuilder(object):
"""
This class builds the XML used to describe songs.
"""
log.info(u'SongXMLBuilder Loaded')
def __init__(self, song_language=None):
"""
Set up the song builder.
``song_language``
The language used in this song
"""
lang = u'en'
if song_language:
lang = song_language
self.song_xml = objectify.fromstring(u'<song version="1.0" />')
self.lyrics = etree.SubElement(self.song_xml, u'lyrics', language=lang)
def add_verse_to_lyrics(self, type, number, content):
"""
Add a verse to the ``<lyrics>`` tag.
``type``
A string denoting the type of verse. Possible values are "Chorus",
"Verse", "Bridge", and "Custom".
``number``
An integer denoting the number of the item, for example: verse 1.
``content``
The actual text of the verse to be stored.
"""
#log.debug(u'add_verse_to_lyrics %s, %s\n%s' % (type, number, content))
verse = etree.SubElement(self.lyrics, u'verse', type=type, label=number)
verse.text = etree.CDATA(content)
def dump_xml(self):
"""
Debugging aid to dump XML so that we can see what we have.
"""
return etree.tostring(self.song_xml, encoding=u'UTF-8',
xml_declaration=True, pretty_print=True)
def extract_xml(self):
"""
Extract our newly created XML song.
"""
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.
"""
log.info(u'SongXMLParser Loaded')
def __init__(self, xml):
"""
Set up our song XML parser.
``xml``
The XML of the song to be parsed.
"""
self.song_xml = None
try:
self.song_xml = objectify.fromstring(str(xml))
except etree.XMLSyntaxError:
log.exception(u'Invalid xml %s', xml)
def get_verses(self):
"""
Iterates through the verses in the XML and returns a list of verses
and their attributes.
"""
xml_iter = self.song_xml.getiterator()
verse_list = []
for element in xml_iter:
if element.tag == u'verse':
if element.text is None:
element.text = u''
verse_list.append([element.attrib,
unicode(element.text).decode('unicode-escape')])
return verse_list
def dump_xml(self):
"""
Debugging aid to dump XML so that we can see what we have.
"""
return etree.dump(self.song_xml)
from lxml import objectify
from lxml.etree import XMLSyntaxError
class LyricsXML(object):
"""
This class represents the XML in the ``lyrics`` field of a song.
The basic XML looks like this::
<?xml version="1.0" encoding="UTF-8"?>
<song version="1.0">
<lyrics language="en">
<verse type="chorus" label="1">
<![CDATA[ ... ]]>
</verse>
</lyrics>
</song>
"""
def __init__(self, song=None):
if song:
@ -74,7 +173,7 @@ class LyricsXML(object):
})
self.lyrics.append(language)
return True
except XMLSyntaxError:
except etree.XMLSyntaxError:
return False
def extract(self, text):
@ -136,4 +235,3 @@ 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