diff --git a/openlp/song/song.py b/openlp/song/song.py
index a66c4b9f5..70e0d9887 100644
--- a/openlp/song/song.py
+++ b/openlp/song/song.py
@@ -56,6 +56,122 @@ _blankSongXml = \
'''
+_blankOpenSongXml = \
+'''
+
+
+
+
+
+
+
+
+
+
+'''
+
+class _OpenSong(XmlRootClass):
+ """Class for import of OpenSogn"""
+
+ def __init__(self, xmlContent = None):
+ """Initialize from given xml content"""
+ super(_OpenSong, self).__init__()
+ self.FromBuffer(xmlContent)
+
+ def _reset(self):
+ """Reset all song attributes"""
+ global _blankOpenSongXml
+ self._setFromXml(_blankOpenSongXml, "song")
+
+ def FromBuffer(self, xmlContent):
+ """Initialize from buffer(string) with xml content"""
+ self._reset()
+ if xmlContent != None :
+ self._setFromXml(xmlContent, "song")
+
+ def GetAuthorList(self):
+ """Convert author field to an authorlist
+
+ in OpenSong an author list may be separated by '/'
+ return as a string
+ """
+ res = []
+ if self.author != None :
+ lst = self.author.split(' and ')
+ for l in lst :
+ res.append(l.strip())
+ s = ", ".join(res)
+ return s
+
+ def GetCategoryArray(self):
+ """Convert theme and alttheme into categoryArray
+
+ return as a string
+ """
+ res = []
+ if self.theme != None :
+ res.append(self.theme)
+ if self.alttheme != None :
+ res.append(self.alttheme)
+ s = ", ".join(res)
+ return s
+
+ def _reorderVerse(self, tag, tmpVerse):
+ """Reorder the verse in case of first char is a number
+
+ tag -- the tag of this verse / verse group
+ tmpVerse -- list of strings
+ """
+ res = []
+ for c in '1234567890 ':
+ tagPending = True
+ for l in tmpVerse :
+ if l.startswith(c) :
+ if tagPending :
+ tagPending = False
+ t = tag.strip("[]").lower()
+ if 'v' == t :
+ newtag = "Verse"
+ elif 'c' == t :
+ newtag = "Chorus"
+ else :
+ #TODO: what is the tags for bridge, pre-chorus?
+ newtag = t
+ s = ("# %s %s"%(newtag, c)).rstrip()
+ res.append(s)
+ res.append(l[1:])
+ if (len(l) == 0) and (not tagPending) :
+ res.append(l)
+ return res
+
+ def GetLyrics(self):
+ """Convert the lyrics to openlp lyrics format
+
+ return as list of strings
+ """
+ lyrics = self.lyrics.split("\n")
+ tmpVerse = []
+ finalLyrics = []
+ tag = ""
+ for l in lyrics:
+ line = l.rstrip()
+ if not line.startswith('.') :
+ # drop all chords
+ tmpVerse.append(line)
+ if len(line) > 0 :
+ if line.startswith('['):
+ tag = line
+ else :
+ r = self._reorderVerse(tag, tmpVerse)
+ finalLyrics.extend(r)
+ tag = ""
+ tmpVerse = []
+ # catch up final verse
+ r = self._reorderVerse(tag, tmpVerse)
+ finalLyrics.extend(r)
+ return finalLyrics
+
+
class Song(XmlRootClass) :
"""Class for handling song properties"""
@@ -85,12 +201,43 @@ class Song(XmlRootClass) :
self._reset()
if xmlContent != None :
self._setFromXml(xmlContent, "Song")
+ self._parseLyrics()
def _reset(self):
"""Reset all song attributes"""
global _blankSongXml
+ self.slideList = []
self._setFromXml(_blankSongXml, "Song")
+ def FromOpenSongBuffer(self, xmlcontent):
+ """Initialize from buffer(string) of xml lines in opensong format"""
+ self._reset()
+ opensong = _OpenSong(xmlcontent)
+ if opensong.title != None:
+ self.SetTitle(opensong.title)
+ if opensong.copyright != None :
+ self.SetCopyright(opensong.copyright)
+ if opensong.presentation != None:
+ self.SetVerseOrder(opensong.presentation)
+ if opensong.ccli != None:
+ self.SetSongCcliNo(opensong.ccli)
+ self.SetAuthorList(opensong.GetAuthorList())
+ self.SetCategoryArray(opensong.GetCategoryArray())
+ self.SetLyrics(opensong.GetLyrics())
+
+ def FromOpenSongFile(self, xmlfilename):
+ """Initialize from file containing xml
+
+ xmlfilename -- path to xml file
+ """
+ lst = []
+ f = open(xmlfilename, 'r')
+ for line in f :
+ lst.append(line)
+ f.close()
+ xml = "".join(lst)
+ self.FromOpenSongBuffer(xml)
+
def _RemovePunctuation(self, title):
"""Remove the puntuation chars from title
@@ -320,25 +467,43 @@ class Song(XmlRootClass) :
def SetLyrics(self, lyrics):
"""Set the lyrics as a list of strings"""
- # TODO: check font formatting
self.lyrics = lyrics
+ self._parseLyrics()
- def GetNumberOfVerses(self):
- """Return the number of verses in the song (int)"""
- numOfVerses = 0
+ def _parseLyrics(self):
+ """Parse lyrics into the slidelist"""
+ # TODO: check font formatting
+ self.slideList = []
+ tmpSlide = []
+ metContent = False
+ for l in self.lyrics :
+ if len(l) > 0 :
+ metContent = True
+ tmpSlide.append(l)
+ else :
+ if metContent :
+ metContent = False
+ self.slideList.append(tmpSlide)
+ tmpSlide = []
#
- return numOfVerses
+ if len(tmpSlide) > 0:
+ self.slideList.append(tmpSlide)
+
+ def GetNumberOfSlides(self):
+ """Return the number of slides in the song (int)"""
+ numOfSlides = len(self.slideList)
+ return numOfSlides
- def GetPreviewVerse(self, verseNumber):
- """Return the preview text for specified verse number
+ def GetPreviewSlide(self, slideNumber):
+ """Return the preview text for specified slide number
- verseNumber -- 0: all verses, 1..n : specific verse
+ slideNumber -- 0: all slides, 1..n : specific slide
a list of strings are returned
"""
return []
- def GetRenderVerse(self, verseNumber):
- """Return the verse to be rendered including the additional
+ def GetRenderSlide(self, slideNumber):
+ """Return the slide to be rendered including the additional
properties
Returns a list as:
@@ -347,29 +512,31 @@ class Song(XmlRootClass) :
authorlist (string),
copyright (string),
cclino (string),
- lyric-verse as a list of strings]
+ lyric-part as a list of strings]
"""
res = []
- res.append(self.GetTheme())
if self.showTitle :
title = self.GetTitle()
else :
title = ""
- res.append(title)
if self.showAuthorList :
author = self.GetAuthorList(True)
else :
author = ""
- res.append(author)
if self.showCopyright :
cpright = self.GetCopyright()
else :
cpright = ""
- res.append(cpright)
if self.showSongCcliNo :
ccli = self.GetSongCcliNo()
else :
ccli = ""
+ # examine the slide for a theme
+ res.append(self.GetTheme())
+ res.append(title)
+ res.append(author)
+ res.append(cpright)
res.append(ccli)
+ # append the correct slide
return res
diff --git a/openlp/song/test/data_opensong/Amazing Grace b/openlp/song/test/data_opensong/Amazing Grace
new file mode 100644
index 000000000..06f3edd92
--- /dev/null
+++ b/openlp/song/test/data_opensong/Amazing Grace
@@ -0,0 +1,38 @@
+
+
+ Amazing Grace
+ John Newton
+ 1982 Jubilate Hymns Limited
+
+
+
+
+ 1037882
+ God: Attributes
+
+
+
+
+ [V1]
+. D D7 G D Bm E A A7
+ Amazing grace how sweet the sound that saved a wretch like me;
+. D D7 G D Bm A G D
+ I once was lost but now I'm found, was blind but now I see.
+
+[V2]
+. D D7 G D Bm E A A7
+ Twas grace that taught my heart to fear, and grace my fears relieved;
+. D D7 G D Bm A G D
+ How precious did that grace appear the hour I first believed!
+
+[V3]
+. D D7 G D Bm E A A7
+ Through many dangers, toils, and snares I have already come;
+. D D7 G D Bm A G D
+ 'Tis grace that brought me safe thus far and grace will lead me home.
+
+[V4]
+. D D7 G D Bm E A A7
+ When we've been there ten thousand years bright shining as the sun;
+. D D7 G D Bm A G D
+ We've no less days to sing God's praise than when we'd first begun!
\ No newline at end of file
diff --git a/openlp/song/test/data_opensong/På en fjern ensom høj b/openlp/song/test/data_opensong/På en fjern ensom høj
new file mode 100644
index 000000000..44ffca307
--- /dev/null
+++ b/openlp/song/test/data_opensong/På en fjern ensom høj
@@ -0,0 +1,56 @@
+På en fjern ensom høj[V1]
+ På en fjern ensom høj,
+ Jesu kors dyrest stod,
+ symbolet på smerte og skam.
+ O, jeg elsker det kors,
+ hvor Guds søn gjorde bod,
+ da forbandelsen blev lagt på ham.
+
+[C1]
+ Jeg vil elske det urgamle kors,
+ i det kraft er der sejer og sang.
+ Lad mig favne det hellige kors,
+ det med kronen ombyttes engang.
+
+[V2]
+ O, det urgamle kors,
+ med sin hvile og fred,
+ tilhyllet i verdens foragt.
+ Se, det hellige lam,
+ som på Golgatha stred,
+ og til jorden Guds nåde har bragt.
+
+[C2]
+ Jeg vil elske det urgamle kors,
+ i det kraft er der sejer og sang.
+ Lad mig favne det hellige kors,
+ det med kronen ombyttes engang.
+
+[V3]
+ I det urgamle kors,
+ i hans blod farvet rødt,
+ en underfuld skønhed jeg ser.
+ Ja, det var på det kors,
+ at han selv blev forstødt,
+ nu skal aldrig for dommen jeg mer.
+
+[C3]
+ Jeg vil elske det urgamle kors,
+ i det kraft er der sejer og sang.
+ Lad mig favne det hellige kors,
+ det med kronen ombyttes engang.
+
+[V4]
+ For det urgamle kors,
+ står mit hjerte i brand,
+ min plads jeg nu har ved dets fod.
+ Til han kalder en dag,
+ mig til himmelens land,
+ og til hvilen hos Faderen god.
+
+[C4]
+ Jeg vil elske det urgamle kors,
+ i det kraft er der sejer og sang.
+ Lad mig favne det hellige kors,
+ det med kronen ombyttes engang.
+V1 C1 V2 C2 V3 C3 V4 C4
\ No newline at end of file
diff --git a/openlp/song/test/data_opensong/The Solid Rock b/openlp/song/test/data_opensong/The Solid Rock
new file mode 100644
index 000000000..bb1eecc97
--- /dev/null
+++ b/openlp/song/test/data_opensong/The Solid Rock
@@ -0,0 +1,28 @@
+
+
+ The Solid Rock
+ Edward Mote and John B. Dykes
+ Public Domain
+ V1 C V2 C V3 C V4 C
+ 101740
+ Christ: Victory
+ Fruit: Peace/Comfort
+ [V]
+. E B A B E
+1My hope is built on nothing less than Jesus' blood and righteousness;
+2When darkness veils His lovely face, I rest on His un___changing grace.
+3His oath, His cove_____nant, His blood|support me in the whelming flood;
+4When He shall come with trumpet sound, O may I then in Him be found;
+. E B A B E
+1I dare not trust the sweetest frame, but wholly lean on Jesus' name.
+2In every high and stormy gale, my anchor holds within the veil.
+3When all around my soul gives way, He then is all my hope and stay.
+4Dressed in His righteous___ness alone, fault___less to stand be_fore the throne.
+
+[C]
+. E A
+ On Christ, the solid rock I stand;
+. E B
+ All other ground is sinking sand,
+. A B E
+ All other ground is sinking sand.
\ No newline at end of file
diff --git a/openlp/song/test/test_song_basic.py b/openlp/song/test/test_song_basic.py
index 70e10a5b9..430668d11 100644
--- a/openlp/song/test/test_song_basic.py
+++ b/openlp/song/test/test_song_basic.py
@@ -38,13 +38,14 @@ class Test_Basic(object):
s = Song()
r = s.__str__()
l = r.split("\n")
- assert(len(l) == 52)
+ assert(len(l) == 55)
def test_asString(self):
"""Init: Empty asString - initial values"""
s = Song()
r = s._get_as_string()
- flag = r.endswith("__None__None__None__None__None__None__1__1__1__1__None__None__None__None__BlankSong__None_")
+ #print r
+ flag = r.endswith("__None__None__None__None__None__None__1__1__1__1__[]__None__None__None__None__BlankSong__None_")
assert(flag)
def test_Title1(self):
@@ -60,7 +61,7 @@ class Test_Basic(object):
assert(s.GetTitle() == t)
assert(s.GetSearchableTitle() == t)
- def test_Title2(self):
+ def test_Title3(self):
"""Set a titel with punctuation 1"""
s = Song()
t1 = "Hey! Come on, ya programmers*"
@@ -69,7 +70,7 @@ class Test_Basic(object):
assert(s.GetTitle() == t1)
assert(s.GetSearchableTitle() == t2)
- def test_Title3(self):
+ def test_Title4(self):
"""Set a titel with punctuation 2"""
s = Song()
t1 = "??#Hey! Come on, ya programmers*"
@@ -78,7 +79,7 @@ class Test_Basic(object):
assert(s.GetTitle() == t1)
assert(s.GetSearchableTitle() == t2)
- def test_Title4(self):
+ def test_Title5(self):
"""Set a title, where searchable title becomes empty - raises an exception"""
s = Song()
py.test.raises(SongTitleError, s.SetTitle, ",*")
@@ -176,4 +177,6 @@ class Test_Basic(object):
assert(s.GetCategoryArray(True) == "")
assert(s.GetCategoryArray(False) == [""])
-
\ No newline at end of file
+if '__main__' == __name__:
+ r = Test_Basic()
+ r.test_asString()
diff --git a/openlp/song/test/test_song_opensong.py b/openlp/song/test/test_song_opensong.py
index e0d95166c..10cec81fc 100644
--- a/openlp/song/test/test_song_opensong.py
+++ b/openlp/song/test/test_song_opensong.py
@@ -1,3 +1,4 @@
+# -*- coding:iso-8859-1 -*-
"""
OpenLP - Open Source Lyrics Projection
Copyright (c) 2008 Raoul Snyman
@@ -15,17 +16,126 @@ 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
sys.path.append(os.path.abspath("./../../.."))
+from openlp.song import *
-from openlp.song import Song
+__ThisDir__ = os.path.abspath(".")
+
+
+_sample1 = \
+'''
+
+
+
+
+
+
+
+ [V1]
+. chord line 1
+ verse 1 line 1
+. chord line 2
+ verse 1 line 2
+
+[V2]
+ verse 2 line 1
+ verse 2 line 2
+
+[V3]
+ verse 3 line 1
+ verse 3 line 2
+
+[C]
+. chorus chord line 1
+ chorus line 1
+. chorus chord line 2
+ chorus line 2
+
+'''
+
+_sample2 = \
+'''
+
+
+
+
+
+
+
+ [V]
+1verse 1 line 1
+2verse 2 line 1
+3verse 3 line 1
+1verse 1 line 2
+2verse 2 line 2
+3verse 3 line 2
+
+[C]
+ chorus line 1
+ chorus line 2
+
+'''
class Test_OpenSong(object):
"""Test cases for converting from OpenSong xml format to Song"""
- def test_Simple(self):
- """OpenSong: Simply return True"""
- assert(True)
+ def test_sample1(self):
+ """OpenSong: handwritten sample1"""
+ s = Song()
+ s.FromOpenSongBuffer(_sample1)
+ l = s.GetLyrics()
+ assert(len(l) == (4*3+3))
+ assert(s.GetNumberOfSlides() == 4)
+
+ def test_sample2(self):
+ """OpenSong: handwritten sample2"""
+ s = Song()
+ s.FromOpenSongBuffer(_sample2)
+ l = s.GetLyrics()
+ assert(len(l) == (4*3+3))
+ assert(s.GetNumberOfSlides() == 4)
+
+ def test_file1(self):
+ """OpenSong: parse Amazing Grace"""
+ global __ThisDir__
+ s = Song()
+ s.FromOpenSongFile("%s/data_opensong/Amazing Grace"%(__ThisDir__))
+ assert(s.GetTitle() == "Amazing Grace")
+ assert(s.GetCopyright() == "1982 Jubilate Hymns Limited")
+ assert(s.GetSongCcliNo() == "1037882")
+ assert(s.GetCategoryArray(True) == "God: Attributes")
+ assert(s.GetAuthorList(True) == "John Newton")
+ assert(s.GetVerseOrder() == "")
+ assert(s.GetNumberOfSlides() == 4)
+
+ def test_file2(self):
+ """OpenSong: parse The Solid Rock"""
+ s = Song()
+ s.FromOpenSongFile("%s/data_opensong/The Solid Rock"%(__ThisDir__))
+ assert(s.GetTitle() == "The Solid Rock")
+ assert(s.GetCopyright() == "Public Domain")
+ assert(s.GetSongCcliNo() == "101740")
+ assert(s.GetCategoryArray(True) == "Christ: Victory, Fruit: Peace/Comfort")
+ assert(s.GetAuthorList(True) == "Edward Mote, John B. Dykes")
+ assert(s.GetVerseOrder() == "V1 C V2 C V3 C V4 C")
+ assert(s.GetNumberOfSlides() == 5)
+
+ def atest_file3(self):
+ """OpenSong: parse 'På en fjern ensom høj' (danish)"""
+ #FIXME: problem with XML convert and danish characters
+ s = Song()
+ s.FromOpenSongFile("%s/data_opensong/På en fjern ensom høj"%(__ThisDir__))
+ assert(s.GetTitle() == "På en fjern ensom høj")
+ assert(s.GetCopyright() == "")
+ assert(s.GetSongCcliNo() == "")
+ assert(s.GetCategoryArray(True) == "")
+ assert(s.GetAuthorList(True) == "")
+ assert(s.GetVerseOrder() == "V1 C1 V2 C2 V3 C3 V4 C4")
+ assert(s.GetNumberOfSlides() == 8)
+
+if '__main__' == __name__:
+ r = Test_OpenSong()
+ r.atest_file3()
diff --git a/openlp/song/test/test_song_verse.py b/openlp/song/test/test_song_verse.py
index fcdff3d0d..3dde24bb7 100644
--- a/openlp/song/test/test_song_verse.py
+++ b/openlp/song/test/test_song_verse.py
@@ -63,48 +63,84 @@ class Test_Verse(object):
def test_title_show_noshow(self):
"""Test the show title flag"""
s = self.stdSong()
- r = s.GetRenderVerse(1)
+ r = s.GetRenderSlide(1)
self.check_allfields(r)
s.SetShowTitle(False)
- r = s.GetRenderVerse(1)
+ r = s.GetRenderSlide(1)
self.check_allfields(r, 1)
s.SetShowTitle(True)
- r = s.GetRenderVerse(1)
+ r = s.GetRenderSlide(1)
self.check_allfields(r)
def test_author_show_noshow(self):
"""Test the show author flag"""
s = self.stdSong()
- r = s.GetRenderVerse(1)
+ r = s.GetRenderSlide(1)
self.check_allfields(r)
s.SetShowAuthorList(False)
- r = s.GetRenderVerse(1)
+ r = s.GetRenderSlide(1)
self.check_allfields(r, 2)
s.SetShowAuthorList(True)
- r = s.GetRenderVerse(1)
+ r = s.GetRenderSlide(1)
self.check_allfields(r)
def test_copyright_show_noshow(self):
"""Test the show copyright flag"""
s = self.stdSong()
- r = s.GetRenderVerse(1)
+ r = s.GetRenderSlide(1)
self.check_allfields(r)
s.SetShowCopyright(False)
- r = s.GetRenderVerse(1)
+ r = s.GetRenderSlide(1)
self.check_allfields(r, 3)
s.SetShowCopyright(True)
- r = s.GetRenderVerse(1)
+ r = s.GetRenderSlide(1)
self.check_allfields(r)
def test_ccli_show_noshow(self):
"""Test the show copyright flag"""
s = self.stdSong()
- r = s.GetRenderVerse(1)
+ r = s.GetRenderSlide(1)
self.check_allfields(r)
s.SetShowSongCcliNo(False)
- r = s.GetRenderVerse(1)
+ r = s.GetRenderSlide(1)
self.check_allfields(r, 4)
s.SetShowSongCcliNo(True)
- r = s.GetRenderVerse(1)
+ r = s.GetRenderSlide(1)
self.check_allfields(r)
+
+ def test_verse1(self):
+ """Test an empty verse list"""
+ s = Song()
+ s.SetLyrics([])
+ assert(s.GetNumberOfSlides() == 0)
+
+ def test_verse2(self):
+ """Test a list with an empty string"""
+ s = Song()
+ s.SetLyrics([""])
+ assert(s.GetNumberOfSlides() == 0)
+
+ def test_verse3a(self):
+ """Test a one liner song"""
+ s = Song()
+ s.SetLyrics(["Single verse"])
+ assert(s.GetNumberOfSlides() == 1)
+
+ def test_verse3b(self):
+ """Test a one liner song"""
+ s = Song()
+ s.SetLyrics(["", "Single verse"])
+ assert(s.GetNumberOfSlides() == 1)
+
+ def test_verse3c(self):
+ """Test a one liner song"""
+ s = Song()
+ s.SetLyrics(["", "Single verse", "", ""])
+ assert(s.GetNumberOfSlides() == 1)
+
+ def test_verse3d(self):
+ """Test a one liner song"""
+ s = Song()
+ s.SetLyrics(["", "# Verse", "", ""])
+ assert(s.GetNumberOfSlides() == 1)
\ No newline at end of file