openlyrics fixes, add meta data to songs, opensong import fix, signals for openlyrics export

This commit is contained in:
Andreas Preikschat 2011-01-24 20:36:44 +01:00
commit e7b1f88e53
4 changed files with 141 additions and 52 deletions

View File

@ -27,11 +27,10 @@
The song export function for OpenLP. The song export function for OpenLP.
""" """
import logging import logging
import os
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
from openlp.core.lib import Receiver, SettingsManager, translate from openlp.core.lib import Receiver, translate
from openlp.core.ui import criticalErrorMessageBox from openlp.core.ui import criticalErrorMessageBox
from openlp.core.ui.wizard import OpenLPWizard from openlp.core.ui.wizard import OpenLPWizard
from openlp.plugins.songs.lib.db import Song from openlp.plugins.songs.lib.db import Song
@ -59,6 +58,16 @@ class SongExportForm(OpenLPWizard):
self.plugin = plugin self.plugin = plugin
OpenLPWizard.__init__(self, parent, plugin, u'songExportWizard', OpenLPWizard.__init__(self, parent, plugin, u'songExportWizard',
u':/wizards/wizard_importsong.bmp') u':/wizards/wizard_importsong.bmp')
self.stop_export_flag = False
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'openlp_stop_wizard'), self.stop_export)
def stop_export(self):
"""
Sets the flag for exporters to stop their export
"""
log.debug(u'Stopping songs export')
self.stop_export_flag = True
def setupUi(self, image): def setupUi(self, image):
""" """
@ -70,25 +79,22 @@ class SongExportForm(OpenLPWizard):
""" """
Song wizard specific initialisation. Song wizard specific initialisation.
""" """
songs = self.plugin.manager.get_all_objects(Song) pass
for song in songs:
author_list = u''
for author in song.authors:
if author_list != u'':
author_list = author_list + u', '
author_list = author_list + author.display_name
song_title = unicode(song.title)
song_detail = u'%s (%s)' % (song_title, author_list)
song_name = QtGui.QListWidgetItem(song_detail)
song_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(song))
self.availableListWidget.addItem(song_name)
self.availableListWidget.selectAll()
def customSignals(self): def customSignals(self):
""" """
Song wizard specific signals. Song wizard specific signals.
""" """
pass QtCore.QObject.connect(self.addSelected,
QtCore.SIGNAL(u'clicked()'), self.onAddSelectedClicked)
QtCore.QObject.connect(self.removeSelected,
QtCore.SIGNAL(u'clicked()'), self.onRemoveSelectedClicked)
QtCore.QObject.connect(self.availableListWidget,
QtCore.SIGNAL(u'itemDoubleClicked(QListWidgetItem *)'),
self.onAvailableListItemDoubleClicked)
QtCore.QObject.connect(self.selectedListWidget,
QtCore.SIGNAL(u'itemDoubleClicked(QListWidgetItem *)'),
self.onSelectedListItemDoubleClicked)
def addCustomPages(self): def addCustomPages(self):
""" """
@ -105,6 +111,11 @@ class SongExportForm(OpenLPWizard):
self.verticalLayout.setObjectName(u'verticalLayout') self.verticalLayout.setObjectName(u'verticalLayout')
self.availableListWidget = QtGui.QListWidget(self.availableGroupBox) self.availableListWidget = QtGui.QListWidget(self.availableGroupBox)
self.availableListWidget.setObjectName(u'availableListWidget') self.availableListWidget.setObjectName(u'availableListWidget')
self.availableListWidget.setSelectionMode(
QtGui.QAbstractItemView.ExtendedSelection)
self.availableListWidget.setSizePolicy(
QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
self.availableListWidget.setSortingEnabled(True)
self.verticalLayout.addWidget(self.availableListWidget) self.verticalLayout.addWidget(self.availableListWidget)
self.sourceLayout.addWidget(self.availableGroupBox) self.sourceLayout.addWidget(self.availableGroupBox)
self.selectionWidget = QtGui.QWidget(self.sourcePage) self.selectionWidget = QtGui.QWidget(self.sourcePage)
@ -138,9 +149,15 @@ class SongExportForm(OpenLPWizard):
self.verticalLayout.setObjectName(u'verticalLayout') self.verticalLayout.setObjectName(u'verticalLayout')
self.selectedListWidget = QtGui.QListWidget(self.selectedGroupBox) self.selectedListWidget = QtGui.QListWidget(self.selectedGroupBox)
self.selectedListWidget.setObjectName(u'selectedListWidget') self.selectedListWidget.setObjectName(u'selectedListWidget')
self.selectedListWidget.setSelectionMode(
QtGui.QAbstractItemView.ExtendedSelection)
self.selectedListWidget.setSizePolicy(
QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
self.selectedListWidget.setSortingEnabled(True)
self.verticalLayout.addWidget(self.selectedListWidget) self.verticalLayout.addWidget(self.selectedListWidget)
self.sourceLayout.addWidget(self.selectedGroupBox) self.sourceLayout.addWidget(self.selectedGroupBox)
self.addPage(self.sourcePage) self.addPage(self.sourcePage)
#TODO: Add save dialog
def retranslateUi(self): def retranslateUi(self):
""" """
@ -158,10 +175,10 @@ class SongExportForm(OpenLPWizard):
'format. You can import these songs in all lyrics projection ' 'format. You can import these songs in all lyrics projection '
'software, which supports OpenLyrics.')) 'software, which supports OpenLyrics.'))
self.sourcePage.setTitle( self.sourcePage.setTitle(
translate('SongsPlugin.ExportWizardForm', 'Select Emport Source')) translate('SongsPlugin.ExportWizardForm', 'Select Songs'))
self.sourcePage.setSubTitle( self.sourcePage.setSubTitle(
translate('SongsPlugin.ExportWizardForm', translate('SongsPlugin.ExportWizardForm',
'Select the export format, and where to export from.')) 'Select the songs, you want to export.'))
self.progressPage.setTitle( self.progressPage.setTitle(
translate('SongsPlugin.ExportWizardForm', 'Exporting')) translate('SongsPlugin.ExportWizardForm', 'Exporting'))
@ -187,10 +204,35 @@ class SongExportForm(OpenLPWizard):
Validate the current page before moving on to the next page. Validate the current page before moving on to the next page.
""" """
if self.currentPage() == self.welcomePage: if self.currentPage() == self.welcomePage:
Receiver.send_message(u'cursor_busy')
songs = self.plugin.manager.get_all_objects(Song)
for song in songs:
author_list = u''
for author in song.authors:
if author_list != u'':
author_list = author_list + u', '
author_list = author_list + author.display_name
song_title = unicode(song.title)
song_detail = u'%s (%s)' % (song_title, author_list)
song_name = QtGui.QListWidgetItem(song_detail)
song_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(song))
self.availableListWidget.addItem(song_name)
self.availableListWidget.selectAll()
Receiver.send_message(u'cursor_normal')
return True return True
elif self.currentPage() == self.sourcePage: elif self.currentPage() == self.sourcePage:
self.selectedListWidget.selectAll()
if not self.selectedListWidget.selectedItems():
criticalErrorMessageBox(
translate('SongsPlugin.ExportWizardForm',
'No Song Selected'),
translate('SongsPlugin.ImportWizardForm',
'You need to add at least one Song to export.'))
return False
return True return True
elif self.currentPage() == self.progressPage: elif self.currentPage() == self.progressPage:
self.availableListWidget.clear()
self.selectedListWidget.clear()
return True return True
def registerFields(self): def registerFields(self):
@ -222,8 +264,9 @@ class SongExportForm(OpenLPWizard):
class, and then runs the ``do_export`` method of the exporter to do class, and then runs the ``do_export`` method of the exporter to do
the actual exporting. the actual exporting.
""" """
exporter = OpenLyricsExport(self.plugin.manager, songs = [item.data(QtCore.Qt.UserRole).toPyObject()
self.plugin.manager.get_all_objects(Song), u'/tmp/') for item in self.selectedListWidget.selectedItems()]
exporter = OpenLyricsExport(self, songs, u'/tmp/')
if exporter.do_export(): if exporter.do_export():
self.progressLabel.setText( self.progressLabel.setText(
translate('SongsPlugin.SongExportForm', 'Finished export.')) translate('SongsPlugin.SongExportForm', 'Finished export.'))
@ -231,3 +274,47 @@ class SongExportForm(OpenLPWizard):
self.progressLabel.setText( self.progressLabel.setText(
translate('SongsPlugin.SongExportForm', translate('SongsPlugin.SongExportForm',
'Your song export failed.')) 'Your song export failed.'))
def onAddSelectedClicked(self):
"""
Removes the selected items from the list of available songs and add them
to the list of selected songs.
"""
items = self.availableListWidget.selectedItems()
# Save a list with tuples which consist of the item row, and the item.
items = [(self.availableListWidget.row(item), item) for item in items]
items.sort(reverse=True)
for item in items:
self.availableListWidget.takeItem(item[0])
self.selectedListWidget.addItem(item[1])
def onRemoveSelectedClicked(self):
"""
Removes the selected items from the list of selected songs and add them
back to the list of available songs.
"""
items = self.selectedListWidget.selectedItems()
# Save a list with tuples which consist of the item row, and the item.
items = [(self.selectedListWidget.row(item), item) for item in items]
items.sort(reverse=True)
for item in items:
self.selectedListWidget.takeItem(item[0])
self.availableListWidget.addItem(item[1])
def onAvailableListItemDoubleClicked(self, item):
"""
Adds the double clicked item to the list of selected songs and removes
it from the list of availables songs.
"""
row = self.availableListWidget.row(item)
self.availableListWidget.takeItem(row)
self.selectedListWidget.addItem(item)
def onSelectedListItemDoubleClicked(self, item):
"""
Adds the double clicked item back to the list of available songs and
removes it from the list of selected songs.
"""
row = self.selectedListWidget.row(item)
self.selectedListWidget.takeItem(row)
self.availableListWidget.addItem(item)

View File

@ -27,13 +27,12 @@
The :mod:`openlyricsexport` module provides the functionality for exporting The :mod:`openlyricsexport` module provides the functionality for exporting
songs from the database. songs from the database.
""" """
import datetime
import logging import logging
import os import os
from lxml import etree, objectify from lxml import etree, objectify
from openlp.core.lib import translate from openlp.core.lib import Receiver, translate
from openlp.plugins.songs.lib import OpenLyrics from openlp.plugins.songs.lib import OpenLyrics
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -42,42 +41,34 @@ class OpenLyricsExport(object):
""" """
This provides the Openlyrics export. This provides the Openlyrics export.
""" """
def __init__(self, master_manager, song_ids, save_path): def __init__(self, parent, songs, save_path):
""" """
Initialise the export. Initialise the export.
""" """
log.debug(u'initialise OpenLyricsExport') log.debug(u'initialise OpenLyricsExport')
self.master_manager = master_manager self.parent = parent
self.songs = song_ids self.manager = parent.plugin.manager
self.songs = songs
self.save_path = save_path self.save_path = save_path
def do_export(self): def do_export(self):
""" """
Export the songs. Export the songs.
""" """
openLyrics = OpenLyrics(self.master_manager) openLyrics = OpenLyrics(self.manager)
# self.export_wizard.exportProgressBar.setMaximum(len(songs)) self.parent.progressBar.setMaximum(len(self.songs))
for song in self.songs: for song in self.songs:
# if self.stop_export_flag: Receiver.send_message(u'openlp_process_events')
# return False if self.parent.stop_export_flag:
# self.export_wizard.incrementProgressBar(unicode(translate( return False
# 'SongsPlugin.OpenLyricsExport', 'Exporting %s...')) % self.parent.incrementProgressBar(unicode(translate(
# song.title) 'SongsPlugin.OpenLyricsExport', 'Exporting %s...')) %
song.title)
# Check if path exists. If not, create the directories! # Check if path exists. If not, create the directories!
# What do we do with songs with the same title? I do not want to # What do we do with songs with the same title? I do not want to
# overwrite them! # overwrite them!
path = os.path.join(self.save_path, song.title + u'.xml') path = os.path.join(self.save_path, song.title + u'.xml')
# Convert the song object to an unicode string.
xml = openLyrics.song_to_xml(song) xml = openLyrics.song_to_xml(song)
song_xml = objectify.fromstring(xml)
# Append the necessary meta data to the song.
# (Maybe move this to the xml module?
song_xml.set(u'version', OpenLyrics.IMPLEMENTED_VERSION)
song_xml.set(u'createdIn', u'OpenLP 1.9.4') # Use variable
song_xml.set(u'modifiedIn', u'OpenLP 1.9.4') # Use variable
song_xml.set(u'modifiedDate',
datetime.datetime.now().strftime(u'%Y-%m-%dT%H:%M:%S'))
xml = etree.tostring(song_xml)
tree = etree.ElementTree(etree.fromstring(xml)) tree = etree.ElementTree(etree.fromstring(xml))
tree.write(path, encoding=u'utf-8', xml_declaration=True, tree.write(path, encoding=u'utf-8', xml_declaration=True,
pretty_print=True) pretty_print=True)

View File

@ -39,6 +39,7 @@ log = logging.getLogger(__name__)
class OpenSongImportError(Exception): class OpenSongImportError(Exception):
pass pass
#TODO: Use lxml for parsing and make sure we use methods of "SongImport" .
class OpenSongImport(SongImport): class OpenSongImport(SongImport):
""" """
Import songs exported from OpenSong Import songs exported from OpenSong
@ -279,7 +280,7 @@ class OpenSongImport(SongImport):
for num in versenums: for num in versenums:
versetag = u'%s%s' % (our_verse_type, num) versetag = u'%s%s' % (our_verse_type, num)
lines = u'\n'.join(verses[versetype][num]) lines = u'\n'.join(verses[versetype][num])
self.verses.append([versetag, lines]) self.add_verse(lines, versetag)
# Keep track of what we have for error checking later # Keep track of what we have for error checking later
versetags[versetag] = 1 versetags[versetag] = 1
# now figure out the presentation order # now figure out the presentation order
@ -295,6 +296,8 @@ class OpenSongImport(SongImport):
else: else:
log.warn(u'No verse order available for %s, skipping.', log.warn(u'No verse order available for %s, skipping.',
self.title) self.title)
# TODO: make sure that the default order list will be overwritten, if
# the songs provides its own order list.
for tag in order: for tag in order:
if tag[0].isdigit(): if tag[0].isdigit():
# Assume it's a verse if it has no prefix # Assume it's a verse if it has no prefix

View File

@ -60,6 +60,7 @@ The XML of `OpenLyrics <http://openlyrics.info/>`_ songs is of the format::
</song> </song>
""" """
import datetime
import logging import logging
import re import re
@ -207,7 +208,8 @@ class OpenLyrics(object):
This property is not supported. This property is not supported.
*<verse name="v1a" lang="he" translit="en">* *<verse name="v1a" lang="he" translit="en">*
The attribute *translit* is not supported. The attribute *translit* is not supported. Note, the attribute *lang* is
considered, but there is not further functionality implemented yet.
*<verseOrder>* *<verseOrder>*
OpenLP supports this property. OpenLP supports this property.
@ -222,8 +224,14 @@ class OpenLyrics(object):
""" """
sxml = SongXML() sxml = SongXML()
verse_list = sxml.get_verses(song.lyrics) verse_list = sxml.get_verses(song.lyrics)
song_xml = objectify.fromstring( song_xml = objectify.fromstring(u'<song/>')
u'<song version="0.7" createdIn="OpenLP 2.0"/>') # Append the necessary meta data to the song.
song_xml.set(u'xmlns', u'http://openlyrics.info/namespace/2009/song')
song_xml.set(u'version', OpenLyrics.IMPLEMENTED_VERSION)
song_xml.set(u'createdIn', u'OpenLP 1.9.4') # Use variable
song_xml.set(u'modifiedIn', u'OpenLP 1.9.4') # Use variable
song_xml.set(u'modifiedDate',
datetime.datetime.now().strftime(u'%Y-%m-%dT%H:%M:%S'))
properties = etree.SubElement(song_xml, u'properties') properties = etree.SubElement(song_xml, u'properties')
titles = etree.SubElement(properties, u'titles') titles = etree.SubElement(properties, u'titles')
self._add_text_to_element(u'title', titles, song.title.strip()) self._add_text_to_element(u'title', titles, song.title.strip())
@ -237,7 +245,7 @@ class OpenLyrics(object):
self._add_text_to_element(u'copyright', properties, song.copyright) self._add_text_to_element(u'copyright', properties, song.copyright)
if song.verse_order: if song.verse_order:
self._add_text_to_element( self._add_text_to_element(
u'verseOrder', properties, song.verse_order) u'verseOrder', properties, song.verse_order.lower())
if song.ccli_number: if song.ccli_number:
self._add_text_to_element(u'ccliNo', properties, song.ccli_number) self._add_text_to_element(u'ccliNo', properties, song.ccli_number)
if song.authors: if song.authors:
@ -450,7 +458,7 @@ class OpenLyrics(object):
text += u'\n' text += u'\n'
text += u'\n'.join([unicode(line) for line in lines.line]) text += u'\n'.join([unicode(line) for line in lines.line])
verse_name = self._get(verse, u'name') verse_name = self._get(verse, u'name')
verse_type = unicode(VerseType.to_string(verse_name[0]))[0] verse_type = unicode(VerseType.to_string(verse_name[0]))
verse_number = re.compile(u'[a-zA-Z]*').sub(u'', verse_name) verse_number = re.compile(u'[a-zA-Z]*').sub(u'', verse_name)
verse_part = re.compile(u'[0-9]*').sub(u'', verse_name[1:]) verse_part = re.compile(u'[0-9]*').sub(u'', verse_name[1:])
# OpenLyrics allows e. g. "c", but we need "c1". # OpenLyrics allows e. g. "c", but we need "c1".