openlp-core team mailing list archive
-
openlp-core team
-
Mailing list archive
-
Message #01356
[Merge] lp:~j-corwin/openlp/sof into lp:openlp
Jonathan Corwin has proposed merging lp:~j-corwin/openlp/sof into lp:openlp.
Requested reviews:
OpenLP Core (openlp-core)
Songs of Fellowship RTF Import
User interface is basic, due to Raoul's plans to redo the song import screens.
--
https://code.launchpad.net/~j-corwin/openlp/sof/+merge/22695
Your team OpenLP Core is subscribed to branch lp:openlp.
=== modified file 'openlp/plugins/songs/lib/__init__.py'
--- openlp/plugins/songs/lib/__init__.py 2010-03-21 23:58:01 +0000
+++ openlp/plugins/songs/lib/__init__.py 2010-04-02 14:15:26 +0000
@@ -26,3 +26,5 @@
from manager import SongManager
from songstab import SongsTab
from mediaitem import SongMediaItem
+from sofimport import SofImport
+from songimport import SongImport
=== modified file 'openlp/plugins/songs/lib/manager.py'
--- openlp/plugins/songs/lib/manager.py 2010-03-21 23:58:01 +0000
+++ openlp/plugins/songs/lib/manager.py 2010-04-02 14:15:26 +0000
@@ -133,6 +133,12 @@
"""
return self.session.query(Author).get(id)
+ def get_author_by_name(self, name):
+ """
+ Get author by display name
+ """
+ return self.session.query(Author).filter_by(display_name=name).first()
+
def save_author(self, author):
"""
Save the Author and refresh the cache
@@ -172,6 +178,12 @@
"""
return self.session.query(Topic).get(id)
+ def get_topic_by_name(self, name):
+ """
+ Get topic by name
+ """
+ return self.session.query(Topic).filter_by(name=name).first()
+
def save_topic(self, topic):
"""
Save the Topic
@@ -211,6 +223,12 @@
"""
return self.session.query(Book).get(id)
+ def get_book_by_name(self, name):
+ """
+ Get book by name
+ """
+ return self.session.query(Book).filter_by(name=name).first()
+
def save_book(self, book):
"""
Save the Book
=== added file 'openlp/plugins/songs/lib/sofimport.py'
--- openlp/plugins/songs/lib/sofimport.py 1970-01-01 00:00:00 +0000
+++ openlp/plugins/songs/lib/sofimport.py 2010-04-02 14:15:26 +0000
@@ -0,0 +1,515 @@
+# -*- 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 #
+###############################################################################
+
+# OOo API documentation:
+# http://wiki.services.openoffice.org/wiki/Documentation/BASIC_Guide/Structure_of_Text_Documents
+# http://wiki.services.openoffice.org/wiki/Documentation/DevGuide/Text/Iterating_over_Text
+# http://www.oooforum.org/forum/viewtopic.phtml?t=14409
+# http://wiki.services.openoffice.org/wiki/Python
+
+import re
+import os
+import time
+from PyQt4 import QtCore
+from songimport import SongImport
+
+if os.name == u'nt':
+ from win32com.client import Dispatch
+ BOLD = 150.0
+ ITALIC = 2
+ PAGE_BEFORE = 4
+ PAGE_AFTER = 5
+ PAGE_BOTH = 6
+else:
+ import uno
+ from com.sun.star.beans import PropertyValue
+ from com.sun.star.awt.FontWeight import BOLD
+ from com.sun.star.awt.FontSlant import ITALIC
+ from com.sun.star.style.BreakType import PAGE_BEFORE, PAGE_AFTER, PAGE_BOTH
+
+class SofImport(object):
+ """
+ Import songs provided on disks with the Songs of Fellowship music books
+ VOLS1_2.RTF, sof3words.rtf and sof4words.rtf
+
+ Use OpenOffice.org Writer for processing the rtf file
+
+ The three books are not only inconsistant with each other, they are
+ inconsistant in themselves too with their formatting. Not only this, but
+ the 1+2 book does not space out verses correctly. This script attempts
+ to sort it out, but doesn't get it 100% right. But better than having to
+ type them all out!
+
+ It attempts to detect italiced verses, and treats these as choruses in
+ the verse ordering. Again not perfect, but a start.
+ """
+ def __init__(self, songmanager):
+ """
+ Initialise the class. Requires a songmanager class which is passed
+ to SongImport for writing song to disk
+ """
+ self.song = None
+ self.manager = songmanager
+
+ def import_sof(self, filename):
+ self.start_ooo()
+ self.open_ooo_file(filename)
+ self.process_doc()
+ self.close_ooo()
+
+ def start_ooo(self):
+ """
+ Start OpenOffice.org process
+ TODO: The presentation/Impress plugin may already have it running
+ """
+ if os.name == u'nt':
+ manager = Dispatch(u'com.sun.star.ServiceManager')
+ manager._FlagAsMethod(u'Bridge_GetStruct')
+ manager._FlagAsMethod(u'Bridge_GetValueObject')
+ self.desktop = manager.createInstance(u'com.sun.star.frame.Desktop')
+ else:
+ cmd = u'openoffice.org -nologo -norestore -minimized -invisible ' \
+ + u'-nofirststartwizard ' \
+ + '-accept="socket,host=localhost,port=2002;urp;"'
+ process = QtCore.QProcess()
+ process.startDetached(cmd)
+ process.waitForStarted()
+ context = uno.getComponentContext()
+ resolver = context.ServiceManager.createInstanceWithContext(
+ u'com.sun.star.bridge.UnoUrlResolver', context)
+ ctx = None
+ loop = 0
+ while ctx is None and loop < 5:
+ try:
+ ctx = resolver.resolve(u'uno:socket,host=localhost,' \
+ + 'port=2002;urp;StarOffice.ComponentContext')
+ except:
+ pass
+ time.sleep(1)
+ loop += 1
+ manager = ctx.ServiceManager
+ self.desktop = manager.createInstanceWithContext(
+ "com.sun.star.frame.Desktop", ctx )
+
+ def open_ooo_file(self, filepath):
+ """
+ Open the passed file in OpenOffice.org Writer
+ """
+ if os.name == u'nt':
+ url = u'file:///' + filepath.replace(u'\\', u'/')
+ url = url.replace(u':', u'|').replace(u' ', u'%20')
+ else:
+ url = uno.systemPathToFileUrl(filepath)
+ properties = []
+ properties = tuple(properties)
+ self.document = self.desktop.loadComponentFromURL(url, u'_blank',
+ 0, properties)
+
+ def close_ooo(self):
+ """
+ Close down OpenOffice.org.
+ TODO: Further checks that it have other docs open, e.g. Impress!
+ """
+ self.desktop.terminate()
+
+ def process_doc(self):
+ """
+ Process the RTF file, a paragraph at a time
+ """
+ self.blanklines = 0
+ self.new_song()
+ paragraphs = self.document.getText().createEnumeration()
+ while paragraphs.hasMoreElements():
+ paragraph = paragraphs.nextElement()
+ if paragraph.supportsService("com.sun.star.text.Paragraph"):
+ self.process_paragraph(paragraph)
+ if self.song:
+ self.song.finish()
+ self.song = None
+
+ def process_paragraph(self, paragraph):
+ """
+ Process a paragraph.
+ In the first book, a paragraph is a single line. In the latter ones
+ they may contain multiple lines.
+ Each paragraph contains textportions. Each textportion has it's own
+ styling, e.g. italics, bold etc.
+ Also check for page breaks, which indicates a new song in books 1+2.
+ In later books, there may not be line breaks, so check for 3 or more
+ newlines
+ """
+ text = u''
+ textportions = paragraph.createEnumeration()
+ while textportions.hasMoreElements():
+ textportion = textportions.nextElement()
+ if textportion.BreakType in (PAGE_BEFORE, PAGE_BOTH):
+ self.process_paragraph_text(text)
+ self.new_song()
+ text = u''
+ text += self.process_textportion(textportion)
+ if textportion.BreakType in (PAGE_AFTER, PAGE_BOTH):
+ self.process_paragraph_text(text)
+ self.new_song()
+ text = u''
+ self.process_paragraph_text(text)
+
+ def process_paragraph_text(self, text):
+ """
+ Split the paragraph text into multiple lines and process
+ """
+ for line in text.split(u'\n'):
+ self.process_paragraph_line(line)
+ if self.blanklines > 2:
+ self.new_song()
+
+ def process_paragraph_line(self, text):
+ """
+ Process a single line. Throw away that text which isn't relevant, i.e.
+ stuff that appears at the end of the song.
+ Anything that is OK, append to the current verse
+ """
+ text = text.strip()
+ if text == u'':
+ self.blanklines += 1
+ if self.blanklines > 1:
+ return
+ if self.song.get_title() != u'':
+ self.finish_verse()
+ return
+ self.blanklines = 0
+ if self.skip_to_close_bracket:
+ if text.endswith(u')'):
+ self.skip_to_close_bracket = False
+ return
+ if text.startswith(u'CCL Licence'):
+ self.italics = False
+ return
+ if text == u'A Songs of Fellowship Worship Resource':
+ return
+ if text.startswith(u'(NB.') or text.startswith(u'(Regrettably') \
+ or text.startswith(u'(From'):
+ self.skip_to_close_bracket = True
+ return
+ if text.startswith(u'Copyright'):
+ self.song.add_copyright(text)
+ return
+ if text == u'(Repeat)':
+ self.finish_verse()
+ self.song.repeat_verse()
+ return
+ if self.song.get_title() == u'':
+ if self.song.get_copyright() == u'':
+ self.add_author(text)
+ else:
+ self.song.add_copyright(text)
+ return
+ self.add_verse_line(text)
+
+ def process_textportion(self, textportion):
+ """
+ Process a text portion. Here we just get the text and detect if
+ it's bold or italics. If it's bold then its a song number or song title.
+ Song titles are in all capitals, so we must bring the capitalization
+ into line
+ """
+ text = textportion.getString()
+ text = self.tidy_text(text)
+ if text.strip() == u'':
+ return text
+ if textportion.CharWeight == BOLD:
+ boldtext = text.strip()
+ if boldtext.isdigit() and self.song.get_song_number() == '':
+ self.add_songnumber(boldtext)
+ return u''
+ if self.song.get_title() == u'':
+ text = self.uncap_text(text)
+ self.add_title(text)
+ return text
+ if text.strip().startswith(u'('):
+ return text
+ self.italics = (textportion.CharPosture == ITALIC)
+ return text
+
+ def new_song(self):
+ """
+ A change of song. Store the old, create a new
+ ... but only if the last song was complete. If not, stick with it
+ """
+ if self.song:
+ self.finish_verse()
+ if not self.song.check_complete():
+ return
+ self.song.finish()
+ self.song = SongImport(self.manager)
+ self.skip_to_close_bracket = False
+ self.is_chorus = False
+ self.italics = False
+ self.currentverse = u''
+
+ def add_songnumber(self, song_no):
+ """
+ Add a song number, store as alternate title. Also use the song
+ number to work out which songbook we're in
+ """
+ self.song.set_song_number(song_no)
+ if int(song_no) <= 640:
+ self.song.set_song_book(u'Songs of Fellowship 1',
+ u'Kingsway Publications')
+ elif int(song_no) <= 1150:
+ self.song.set_song_book(u'Songs of Fellowship 2',
+ u'Kingsway Publications')
+ elif int(song_no) <= 1690:
+ self.song.set_song_book(u'Songs of Fellowship 3',
+ u'Kingsway Publications')
+ else:
+ self.song.set_song_book(u'Songs of Fellowship 4',
+ u'Kingsway Publications')
+
+ def add_title(self, text):
+ """
+ Add the title to the song. Strip some leading/trailing punctuation that
+ we don't want in a title
+ """
+ title = text.strip()
+ if title.startswith(u'\''):
+ title = title[1:]
+ if title.endswith(u','):
+ title = title[:-1]
+ self.song.set_title(title)
+
+ def add_author(self, text):
+ """
+ Add the author. OpenLP stores them individually so split by 'and', '&'
+ and comma.
+ However need to check for "Mr and Mrs Smith" and turn it to
+ "Mr Smith" and "Mrs Smith".
+ """
+ text = text.replace(u' and ', u' & ')
+ for author in text.split(u','):
+ authors = author.split(u'&')
+ for i in range(len(authors)):
+ author2 = authors[i].strip()
+ if author2.find(u' ') == -1 and i < len(authors) - 1:
+ author2 = author2 + u' ' \
+ + authors[i + 1].strip().split(u' ')[-1]
+ self.song.add_author(author2)
+
+ def add_verse_line(self, text):
+ """
+ Add a line to the current verse. If the formatting has changed and
+ we're beyond the second line of first verse, then this indicates
+ a change of verse. Italics are a chorus
+ """
+ if self.italics != self.is_chorus and ((len(self.song.verses) > 0) or
+ (self.currentverse.count(u'\n') > 1)):
+ self.finish_verse()
+ if self.italics:
+ self.is_chorus = True
+ self.currentverse += text + u'\n'
+
+ def finish_verse(self):
+ """
+ Verse is finished, store it. Note in book 1+2, some songs are formatted
+ incorrectly. Here we try and split songs with missing line breaks into
+ the correct number of verses.
+ """
+ if self.currentverse.strip() == u'':
+ return
+ if self.is_chorus:
+ versetag = 'C'
+ splitat = None
+ else:
+ versetag = 'V'
+ splitat = self.verse_splits(self.song.get_song_number())
+ if splitat:
+ ln = 0
+ verse = u''
+ for line in self.currentverse.split(u'\n'):
+ ln += 1
+ if line == u'' or ln > splitat:
+ self.song.add_verse(verse, versetag)
+ ln = 0
+ if line:
+ verse = line + u'\n'
+ else:
+ verse = u''
+ else:
+ verse += line + u'\n'
+ if verse:
+ self.song.add_verse(verse, versetag)
+ else:
+ self.song.add_verse(self.currentverse, versetag)
+ self.currentverse = u''
+ self.is_chorus = False
+
+ def tidy_text(self, text):
+ """
+ Get rid of some dodgy unicode and formatting characters we're not
+ interested in. Some can be converted to ascii.
+ """
+ text = text.replace(u'\t', u' ')
+ text = text.replace(u'\r', u'\n')
+ text = text.replace(u'\u2018', u'\'')
+ text = text.replace(u'\u2019', u'\'')
+ text = text.replace(u'\u201c', u'"')
+ text = text.replace(u'\u201d', u'"')
+ text = text.replace(u'\u2026', u'...')
+ text = text.replace(u'\u2013', u'-')
+ text = text.replace(u'\u2014', u'-')
+ return text
+
+ def uncap_text(self, text):
+ """
+ Words in the title are in all capitals, so we lowercase them.
+ However some of these words, e.g. referring to God need a leading
+ capital letter.
+
+ There is a complicated word "One", which is sometimes lower and
+ sometimes upper depending on context. Never mind, keep it lower.
+ """
+ textarr = re.split(u'(\W+)', text)
+ textarr[0] = textarr[0].capitalize()
+ for i in range(1, len(textarr)):
+ # Do not translate these. Fixed strings in SOF song file
+ if textarr[i] in (u'JESUS', u'CHRIST', u'KING', u'ALMIGHTY',
+ u'REDEEMER', u'SHEPHERD', u'SON', u'GOD', u'LORD', u'FATHER',
+ u'HOLY', u'SPIRIT', u'LAMB', u'YOU', u'YOUR', u'I', u'I\'VE',
+ u'I\'M', u'I\'LL', u'SAVIOUR', u'O', u'YOU\'RE', u'HE', u'HIS',
+ u'HIM', u'ZION', u'EMMANUEL', u'MAJESTY', u'JESUS\'', u'JIREH',
+ u'JUDAH', u'LION', u'LORD\'S', u'ABRAHAM', u'GOD\'S',
+ u'FATHER\'S', u'ELIJAH'):
+ textarr[i] = textarr[i].capitalize()
+ else:
+ textarr[i] = textarr[i].lower()
+ text = u''.join(textarr)
+ return text
+
+ def verse_splits(self, song_number):
+ """
+ Because someone at Kingsway forgot to check the 1+2 RTF file,
+ some verses were not formatted correctly.
+ """
+ if song_number == 11: return 8
+ if song_number == 18: return 5
+ if song_number == 21: return 6
+ if song_number == 23: return 4
+ if song_number == 24: return 7
+ if song_number == 27: return 4
+ if song_number == 31: return 6
+ if song_number == 49: return 4
+ if song_number == 50: return 8
+ if song_number == 70: return 4
+ if song_number == 75: return 8
+ if song_number == 79: return 6
+ if song_number == 97: return 7
+ if song_number == 107: return 4
+ if song_number == 109: return 4
+ if song_number == 133: return 4
+ if song_number == 155: return 10
+ if song_number == 156: return 8
+ if song_number == 171: return 4
+ if song_number == 188: return 7
+ if song_number == 192: return 4
+ if song_number == 208: return 8
+ if song_number == 215: return 8
+ if song_number == 220: return 4
+ if song_number == 247: return 6
+ if song_number == 248: return 6
+ if song_number == 251: return 8
+ if song_number == 295: return 8
+ if song_number == 307: return 5
+ if song_number == 314: return 6
+ if song_number == 325: return 8
+ if song_number == 386: return 6
+ if song_number == 415: return 4
+ if song_number == 426: return 4
+ if song_number == 434: return 5
+ if song_number == 437: return 4
+ if song_number == 438: return 6
+ if song_number == 456: return 8
+ if song_number == 461: return 4
+ if song_number == 469: return 4
+ if song_number == 470: return 5
+ if song_number == 476: return 6
+ if song_number == 477: return 7
+ if song_number == 480: return 8
+ if song_number == 482: return 4
+ if song_number == 512: return 4
+ if song_number == 513: return 8
+ if song_number == 518: return 5
+ if song_number == 520: return 4
+ if song_number == 523: return 6
+ if song_number == 526: return 8
+ if song_number == 527: return 4
+ if song_number == 529: return 4
+ if song_number == 537: return 4
+ if song_number == 555: return 6
+ if song_number == 581: return 4
+ if song_number == 589: return 6
+ if song_number == 590: return 4
+ if song_number == 593: return 8
+ if song_number == 596: return 4
+ if song_number == 610: return 6
+ if song_number == 611: return 6
+ if song_number == 619: return 8
+ if song_number == 645: return 5
+ if song_number == 653: return 6
+ if song_number == 683: return 7
+ if song_number == 686: return 4
+ if song_number == 697: return 8
+ if song_number == 698: return 4
+ if song_number == 704: return 6
+ if song_number == 716: return 4
+ if song_number == 717: return 6
+ if song_number == 730: return 4
+ if song_number == 731: return 8
+ if song_number == 732: return 8
+ if song_number == 738: return 4
+ if song_number == 756: return 9
+ if song_number == 815: return 6
+ if song_number == 830: return 8
+ if song_number == 831: return 4
+ if song_number == 876: return 6
+ if song_number == 877: return 6
+ if song_number == 892: return 4
+ if song_number == 894: return 6
+ if song_number == 902: return 8
+ if song_number == 905: return 8
+ if song_number == 921: return 6
+ if song_number == 940: return 7
+ if song_number == 955: return 9
+ if song_number == 968: return 8
+ if song_number == 972: return 7
+ if song_number == 974: return 4
+ if song_number == 988: return 6
+ if song_number == 991: return 5
+ if song_number == 1002: return 8
+ if song_number == 1024: return 8
+ if song_number == 1044: return 9
+ if song_number == 1088: return 6
+ if song_number == 1117: return 6
+ if song_number == 1119: return 7
+ return None
+
=== added file 'openlp/plugins/songs/lib/songimport.py'
--- openlp/plugins/songs/lib/songimport.py 1970-01-01 00:00:00 +0000
+++ openlp/plugins/songs/lib/songimport.py 2010-04-02 14:15:26 +0000
@@ -0,0 +1,260 @@
+# -*- 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 string
+from openlp.core.lib import SongXMLBuilder
+from openlp.plugins.songs.lib.models import Song, Author, Topic, Book
+
+class SongImport(object):
+ """
+ Helper class for import a song from a third party source into OpenLP
+
+ This class just takes the raw strings, and will work out for itself
+ whether the authors etc already exist and add them or refer to them
+ as necessary
+ """
+
+ def __init__(self, song_manager):
+ """
+ Initialise and create defaults for properties
+
+ song_manager is an instance of a SongManager, through which all
+ database access is performed
+ """
+ self.manager = song_manager
+ self.title = u''
+ self.song_number = u''
+ self.copyright = u''
+ self.comment = u''
+ self.theme_name = u''
+ self.ccli_number = u''
+ self.authors = []
+ self.topics = []
+ self.song_book_name = u''
+ self.song_book_pub = u''
+ self.verse_order_list = []
+ self.verses = []
+ self.versecount = 0
+ self.choruscount = 0
+
+ def get_title(self):
+ """
+ Return the title
+ """
+ return self.title
+
+ def get_copyright(self):
+ """
+ Return the copyright
+ """
+ return self.copyright
+
+ def get_song_number(self):
+ """
+ Return the song number (also known as alternate title)
+ """
+ return self.song_number
+
+ def set_title(self, title):
+ """
+ Set the title
+ """
+ self.title = title
+
+ def set_song_number(self, song_number):
+ """
+ Set the song number/alternate title
+ """
+ self.song_number = song_number
+
+ def set_song_book(self, song_book, publisher):
+ """
+ Set the song book name and publisher
+ """
+ self.song_book_name = song_book
+ self.song_book_pub = publisher
+
+ def add_copyright(self, copyright):
+ """
+ Build the copyright field
+ """
+ if self.copyright != u'':
+ self.copyright += ' '
+ self.copyright += copyright
+
+ def add_author(self, text):
+ """
+ Add an author to the list
+ """
+ self.authors.append(text)
+
+ def add_verse(self, verse, versetag):
+ """
+ Add a verse. This is the whole verse, lines split by \n
+ Verse tag can be V1/C1/B1 etc, or 'V' and 'C' (will count the verses/
+ choruses itself) or None, where it will assume verse
+ It will also attempt to detect duplicates. In this case it will just
+ add to the verse order
+ """
+ for (oldversetag, oldverse) in self.verses:
+ if oldverse.strip() == verse.strip():
+ self.verse_order_list.append(oldversetag)
+ return
+ if versetag == u'C':
+ self.choruscount += 1
+ versetag += unicode(self.choruscount)
+ if versetag == u'V' or not versetag:
+ self.versecount += 1
+ versetag = u'V' + unicode(self.versecount)
+ self.verses.append([versetag, verse])
+ self.verse_order_list.append(versetag)
+ if self.choruscount > 0 and not versetag.startswith(u'C'):
+ self.verse_order_list.append(u'C1')
+
+ def repeat_verse(self):
+ """
+ Repeat the previous verse in the verse order
+ """
+ self.verse_order_list.append(self.verse_order_list[-1])
+
+ def check_complete(self):
+ """
+ Check the mandatory fields are entered (i.e. title and a verse)
+ Author not checked here, if no author then "Author unknown" is
+ automatically added
+ """
+ if self.title == u'' or len(self.verses) == 0:
+ return False
+ else:
+ return True
+
+ def remove_punctuation(self, text):
+ """
+ Remove punctuation from the string for searchable fields
+ """
+ for c in string.punctuation:
+ text = text.replace(c, u'')
+ return text
+
+ def finish(self):
+ """
+ All fields have been set to this song. Write it away
+ """
+ if len(self.authors) == 0:
+ self.authors.append(u'Author unknown')
+ self.commit_song()
+ #self.print_song()
+
+ def commit_song(self):
+ """
+ Write the song and it's fields to disk
+ """
+ song = Song()
+ song.title = self.title
+ song.search_title = self.remove_punctuation(self.title)
+ 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 = u'Chorus'
+ elif versetag[0] == u'V':
+ versetype = u'Verse'
+ elif versetag[0] == u'B':
+ versetype = u'Bridge'
+ elif versetag[0] == u'I':
+ versetype = u'Intro'
+ elif versetag[0] == u'P':
+ versetype = u'Prechorus'
+ elif versetag[0] == u'E':
+ versetype = u'Ending'
+ else:
+ versetype = u'Other'
+ sxml.add_verse_to_lyrics(versetype, versetag[1:], versetext)
+ song.search_lyrics += u' ' + self.remove_punctuation(versetext)
+ song.lyrics = unicode(sxml.extract_xml(), u'utf-8')
+ song.verse_order = u' '.join(self.verse_order_list)
+ song.copyright = self.copyright
+ song.comment = self.comment
+ song.theme_name = self.theme_name
+ song.ccli_number = self.ccli_number
+ for authortext in self.authors:
+ author = self.manager.get_author_by_name(authortext)
+ if author is None:
+ author = Author()
+ author.display_name = authortext
+ author.last_name = authortext.split(u' ')[-1]
+ author.first_name = u' '.join(authortext.split(u' ')[:-1])
+ self.manager.save_author(author)
+ song.authors.append(author)
+ if self.song_book_name:
+ song_book = self.manager.get_book_by_name(self.song_book_name)
+ if song_book is None:
+ song_book = Book()
+ song_book.name = self.song_book_name
+ song_book.publisher = self.song_book_pub
+ self.manager.save_book(song_book)
+ song.song_book_id = song_book.id
+ for topictext in self.topics:
+ topic = self.manager.get_topic_by_name(topictext)
+ if topic is None:
+ topic = Topic()
+ topic.name = topictext
+ self.manager.save_topic(topic)
+ song.topics.append(topictext)
+ self.manager.save_song(song)
+
+ def print_song(self):
+ """
+ For debugging
+ """
+ print u'========================================' \
+ + u'========================================'
+ print u'TITLE: ' + self.title
+ for (versetag, versetext) in self.verses:
+ print u'VERSE ' + versetag + u': ' + versetext
+ print u'ORDER: ' + u' '.join(self.verse_order_list)
+ for author in self.authors:
+ print u'AUTHOR: ' + author
+ if self.copyright:
+ print u'COPYRIGHT: ' + self.copyright
+ if self.song_book_name:
+ print u'BOOK: ' + self.song_book_name
+ if self.song_book_pub:
+ print u'BOOK PUBLISHER: ' + self.song_book_pub
+ if self.song_number:
+ print u'NUMBER: ' + self.song_number
+ for topictext in self.topics:
+ print u'TOPIC: ' + topictext
+ if self.comment:
+ print u'COMMENT: ' + self.comment
+ if self.theme_name:
+ print u'THEME: ' + self.theme_name
+ if self.ccli_number:
+ print u'CCLI: ' + self.ccli_number
+
+
=== modified file 'openlp/plugins/songs/songsplugin.py'
--- openlp/plugins/songs/songsplugin.py 2010-03-21 23:58:01 +0000
+++ openlp/plugins/songs/songsplugin.py 2010-04-02 14:15:26 +0000
@@ -28,7 +28,8 @@
from PyQt4 import QtCore, QtGui
from openlp.core.lib import Plugin, build_icon, PluginStatus
-from openlp.plugins.songs.lib import SongManager, SongMediaItem, SongsTab
+from openlp.plugins.songs.lib import SongManager, SongMediaItem, SongsTab, \
+ SofImport
from openlp.plugins.songs.forms import OpenLPImportForm, OpenSongExportForm, \
OpenSongImportForm, OpenLPExportForm
@@ -102,24 +103,35 @@
self.ImportOpenlp1Item.setObjectName(u'ImportOpenlp1Item')
self.ImportOpenlp2Item = QtGui.QAction(import_menu)
self.ImportOpenlp2Item.setObjectName(u'ImportOpenlp2Item')
+ self.ImportSofItem = QtGui.QAction(import_menu)
+ self.ImportSofItem.setObjectName(u'ImportSofItem')
# Add to menus
self.ImportSongMenu.addAction(self.ImportOpenlp1Item)
self.ImportSongMenu.addAction(self.ImportOpenlp2Item)
self.ImportSongMenu.addAction(self.ImportOpenSongItem)
+ self.ImportSongMenu.addAction(self.ImportSofItem)
import_menu.addAction(self.ImportSongMenu.menuAction())
# Translations...
self.ImportSongMenu.setTitle(import_menu.trUtf8('&Song'))
self.ImportOpenSongItem.setText(import_menu.trUtf8('OpenSong'))
self.ImportOpenlp1Item.setText(import_menu.trUtf8('openlp.org 1.0'))
self.ImportOpenlp1Item.setToolTip(
- import_menu.trUtf8('Export songs in openlp.org 1.0 format'))
+ import_menu.trUtf8('Import songs in openlp.org 1.0 format'))
self.ImportOpenlp1Item.setStatusTip(
- import_menu.trUtf8('Export songs in openlp.org 1.0 format'))
+ import_menu.trUtf8('Import songs in openlp.org 1.0 format'))
self.ImportOpenlp2Item.setText(import_menu.trUtf8('OpenLP 2.0'))
self.ImportOpenlp2Item.setToolTip(
- import_menu.trUtf8('Export songs in OpenLP 2.0 format'))
+ import_menu.trUtf8('Import songs in OpenLP 2.0 format'))
self.ImportOpenlp2Item.setStatusTip(
- import_menu.trUtf8('Export songs in OpenLP 2.0 format'))
+ import_menu.trUtf8('Import songs in OpenLP 2.0 format'))
+ self.ImportSofItem.setText(
+ import_menu.trUtf8('Songs of Fellowship'))
+ self.ImportSofItem.setToolTip(
+ import_menu.trUtf8('Import songs from the VOLS1_2.RTF, sof3words' \
+ + '.rtf and sof4words.rtf supplied with the music books'))
+ self.ImportSofItem.setStatusTip(
+ import_menu.trUtf8('Import songs from the VOLS1_2.RTF, sof3words' \
+ + '.rtf and sof4words.rtf supplied with the music books'))
# Signals and slots
QtCore.QObject.connect(self.ImportOpenlp1Item,
QtCore.SIGNAL(u'triggered()'), self.onImportOpenlp1ItemClick)
@@ -127,6 +139,8 @@
QtCore.SIGNAL(u'triggered()'), self.onImportOpenlp1ItemClick)
QtCore.QObject.connect(self.ImportOpenSongItem,
QtCore.SIGNAL(u'triggered()'), self.onImportOpenSongItemClick)
+ QtCore.QObject.connect(self.ImportSofItem,
+ QtCore.SIGNAL(u'triggered()'), self.onImportSofItemClick)
self.ImportSongMenu.menuAction().setVisible(False)
def add_export_menu_item(self, export_menu):
@@ -169,6 +183,13 @@
def onImportOpenSongItemClick(self):
self.opensong_import_form.show()
+ def onImportSofItemClick(self):
+ filename = QtGui.QFileDialog.getOpenFileName(
+ None, self.trUtf8('Open Songs of Fellowship file'),
+ u'', u'Songs of Fellowship file (*.rtf *.RTF)')
+ sofimport = SofImport(self.songmanager)
+ sofimport.import_sof(unicode(filename))
+
def onExportOpenlp1ItemClicked(self):
self.openlp_export_form.show()
Follow ups