openlp-core team mailing list archive
-
openlp-core team
-
Mailing list archive
-
Message #15898
[Merge] lp:~sfindlay/openlp/zionworx-import into lp:openlp
Samuel Findlay has proposed merging lp:~sfindlay/openlp/zionworx-import into lp:openlp with lp:~sfindlay/openlp/refactor-song-import as a prerequisite.
Requested reviews:
phill (phill-ridout)
Raoul Snyman (raoul-snyman)
Jonathan Corwin (j-corwin)
For more details, see:
https://code.launchpad.net/~sfindlay/openlp/zionworx-import/+merge/109433
Added ZionWorx song database importer.
* Tested on win7 x64 with databases from 3 users. Total 3171 songs, including non-English characters.
* ZionWorx [1] is freeware and windows-only
* Users can download freeware utility "TurboDB Data Exchange" [2] (Win/Linux) and use this command to dump their ZionWorx database to a CSV file which can then be imported by OpenLP:
>> tdbdatax MainTable.dat songstable.csv -fsdf -s, -qd
* Since the importer is not a direct ZionWorx import, I added a descriptionLabel widget to the import wizard, pointing users to the Manual for further info.
* This descriptionLabel widget is also available to other importers (see bug 832345) [3]
[1] http://www.zionworx.org.uk/
[2] http://www.dataweb.de/en/support/downloads.html
[3] https://bugs.launchpad.net/openlp/+bug/832345
--
https://code.launchpad.net/~sfindlay/openlp/zionworx-import/+merge/109433
Your team OpenLP Core is subscribed to branch lp:openlp.
=== modified file 'openlp/core/ui/wizard.py'
--- openlp/core/ui/wizard.py 2012-06-08 21:10:26 +0000
+++ openlp/core/ui/wizard.py 2012-06-08 21:10:26 +0000
@@ -99,7 +99,7 @@
def setupUi(self, image):
"""
- Set up the wizard UI
+ Set up the wizard UI.
"""
self.setModal(True)
self.setWizardStyle(QtGui.QWizard.ModernStyle)
=== modified file 'openlp/plugins/bibles/lib/csvbible.py'
--- openlp/plugins/bibles/lib/csvbible.py 2011-12-27 10:33:55 +0000
+++ openlp/plugins/bibles/lib/csvbible.py 2012-06-08 21:10:26 +0000
@@ -73,7 +73,7 @@
def __init__(self, parent, **kwargs):
"""
- Loads a Bible from a set of CVS files.
+ Loads a Bible from a set of CSV files.
This class assumes the files contain all the information and
a clean bible is being loaded.
"""
=== modified file 'openlp/plugins/songs/forms/songimportform.py'
--- openlp/plugins/songs/forms/songimportform.py 2012-06-08 21:10:26 +0000
+++ openlp/plugins/songs/forms/songimportform.py 2012-06-08 21:10:26 +0000
@@ -133,6 +133,9 @@
self.formatLayout.setItem(1, QtGui.QFormLayout.LabelRole,
self.formatSpacer)
self.sourceLayout.addLayout(self.formatLayout)
+ self.formatHSpacing = self.formatLayout.horizontalSpacing()
+ self.formatVSpacing = self.formatLayout.verticalSpacing()
+ self.formatLayout.setVerticalSpacing(0)
self.stackSpacer = QtGui.QSpacerItem(10, 0, QtGui.QSizePolicy.Fixed,
QtGui.QSizePolicy.Expanding)
self.formatStack = QtGui.QStackedLayout()
@@ -160,11 +163,15 @@
self.sourcePage.setSubTitle(WizardStrings.ImportSelectLong)
self.formatLabel.setText(WizardStrings.FormatLabel)
for format in SongFormat.get_format_list():
- format_name, custom_combo_text, select_mode = SongFormat.get(
- format, u'name', u'comboBoxText', u'selectMode')
- combo_box_text = custom_combo_text if custom_combo_text \
- else format_name
+ format_name, custom_combo_text, description_text, select_mode = \
+ SongFormat.get(format, u'name', u'comboBoxText',
+ u'descriptionText', u'selectMode')
+ combo_box_text = (custom_combo_text if custom_combo_text else
+ format_name)
self.formatComboBox.setItemText(format, combo_box_text)
+ if description_text is not None:
+ self.formatWidgets[format][u'descriptionLabel'].setText(
+ description_text)
if select_mode == SongFormatSelect.MultipleFiles:
self.formatWidgets[format][u'addButton'].setText(
translate('SongsPlugin.ImportWizardForm', 'Add Files...'))
@@ -205,10 +212,16 @@
spacer.changeSize(
max_label_width - labels[index].minimumSizeHint().width(), 0,
QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
+ # Align descriptionLabels with rest of layout
+ for format in SongFormat.get_format_list():
+ if SongFormat.get(format, u'descriptionText') is not None:
+ self.formatWidgets[format][u'descriptionSpacer'].changeSize(
+ max_label_width + self.formatHSpacing, 0,
+ QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
def customPageChanged(self, pageId):
"""
- Called when changing to a page other than the progress page
+ Called when changing to a page other than the progress page.
"""
if self.page(pageId) == self.sourcePage:
self.onCurrentIndexChanged(self.formatStack.currentIndex())
@@ -235,8 +248,8 @@
else:
import_source = \
self.formatWidgets[format][u'filepathEdit'].text()
- error_title = UiStrings().IFSs if select_mode == \
- SongFormatSelect.SingleFile else UiStrings().IFdSs
+ error_title = (UiStrings().IFSs if select_mode ==
+ SongFormatSelect.SingleFile else UiStrings().IFdSs)
focus_button = self.formatWidgets[format][u'browseButton']
if not class_.isValidSource(import_source):
critical_error_message_box(error_title, error_msg)
@@ -395,8 +408,8 @@
def addFileSelectItem(self):
format = self.currentFormat
- prefix, can_disable, select_mode = SongFormat.get(format, u'prefix',
- u'canDisable', u'selectMode')
+ prefix, can_disable, description_text, select_mode = SongFormat.get(
+ format, u'prefix', u'canDisable', u'descriptionText', u'selectMode')
page = QtGui.QWidget()
page.setObjectName(prefix + u'Page')
if can_disable:
@@ -406,10 +419,25 @@
importLayout = QtGui.QVBoxLayout(importWidget)
importLayout.setMargin(0)
importLayout.setObjectName(prefix + u'ImportLayout')
+ if description_text is not None:
+ descriptionLayout = QtGui.QHBoxLayout()
+ descriptionLayout.setObjectName(prefix + u'DescriptionLayout')
+ descriptionSpacer = QtGui.QSpacerItem(0, 0, QtGui.QSizePolicy.Fixed,
+ QtGui.QSizePolicy.Fixed)
+ descriptionLayout.addSpacerItem(descriptionSpacer)
+ descriptionLabel = QtGui.QLabel(importWidget)
+ descriptionLabel.setWordWrap(True)
+ descriptionLabel.setOpenExternalLinks(True)
+ descriptionLabel.setObjectName(prefix + u'DescriptionLabel')
+ descriptionLayout.addWidget(descriptionLabel)
+ importLayout.addLayout(descriptionLayout)
+ self.formatWidgets[format][u'descriptionLabel'] = descriptionLabel
+ self.formatWidgets[format][u'descriptionSpacer'] = descriptionSpacer
if select_mode == SongFormatSelect.SingleFile or \
select_mode == SongFormatSelect.SingleFolder:
filepathLayout = QtGui.QHBoxLayout()
filepathLayout.setObjectName(prefix + u'FilepathLayout')
+ filepathLayout.setContentsMargins(0, self.formatVSpacing, 0, 0)
filepathLabel = QtGui.QLabel(importWidget)
filepathLabel.setObjectName(prefix + u'FilepathLabel')
filepathLayout.addWidget(filepathLabel)
=== modified file 'openlp/plugins/songs/lib/importer.py'
--- openlp/plugins/songs/lib/importer.py 2012-06-08 21:10:26 +0000
+++ openlp/plugins/songs/lib/importer.py 2012-06-08 21:10:26 +0000
@@ -44,6 +44,7 @@
from songbeamerimport import SongBeamerImport
from songshowplusimport import SongShowPlusImport
from foilpresenterimport import FoilPresenterImport
+from zionworximport import ZionWorxImport
# Imports that might fail
log = logging.getLogger(__name__)
try:
@@ -111,6 +112,8 @@
Title for ``QFileDialog`` (default includes the format's ``u'name'``).
``u'invalidSourceMsg'``
Message displayed if ``isValidSource()`` returns ``False``.
+ ``u'descriptionText'``
+ Short description (1-2 lines) about the song format.
"""
# Song formats (ordered alphabetically after Generic)
# * Numerical order of song formats is significant as it determines the
@@ -131,7 +134,8 @@
SongShowPlus = 12
SongsOfFellowship = 13
WordsOfWorship = 14
- #CSV = 15
+ ZionWorx = 15
+ #CSV = 16
# Set optional attribute defaults
__defaults__ = {
@@ -142,7 +146,8 @@
u'comboBoxText': None,
u'disabledLabelText': u'',
u'getFilesTitle': None,
- u'invalidSourceMsg': None
+ u'invalidSourceMsg': None,
+ u'descriptionText': None
}
# Set attribute values for each Song Format
@@ -264,6 +269,18 @@
u'prefix': u'wordsOfWorship',
u'filter': u'%s (*.wsg *.wow-song)' % translate(
'SongsPlugin.ImportWizardForm', 'Words Of Worship Song Files')
+ },
+ ZionWorx: {
+ u'class': ZionWorxImport,
+ u'name': u'ZionWorx',
+ u'prefix': u'zionWorx',
+ u'selectMode': SongFormatSelect.SingleFile,
+ u'comboBoxText': translate('SongsPlugin.ImportWizardForm',
+ 'ZionWorx (CSV)'),
+ u'descriptionText': translate('SongsPlugin.ImportWizardForm',
+ 'First convert your ZionWorx database to a CSV text file, as '
+ 'explained in the <a href="http://manual.openlp.org/songs.html'
+ '#importing-from-zionworx">User Manual</a>.')
# },
# CSV: {
# u'class': CSVImport,
@@ -293,7 +310,8 @@
SongFormat.SongBeamer,
SongFormat.SongShowPlus,
SongFormat.SongsOfFellowship,
- SongFormat.WordsOfWorship
+ SongFormat.WordsOfWorship,
+ SongFormat.ZionWorx
]
@staticmethod
=== added file 'openlp/plugins/songs/lib/zionworximport.py'
--- openlp/plugins/songs/lib/zionworximport.py 1970-01-01 00:00:00 +0000
+++ openlp/plugins/songs/lib/zionworximport.py 2012-06-08 21:10:26 +0000
@@ -0,0 +1,142 @@
+# -*- coding: utf-8 -*-
+# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
+
+###############################################################################
+# OpenLP - Open Source Lyrics Projection #
+# --------------------------------------------------------------------------- #
+# Copyright (c) 2008-2012 Raoul Snyman #
+# Portions copyright (c) 2008-2012 Tim Bentley, Gerald Britton, Jonathan #
+# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
+# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
+# Põldaru, Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
+# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund #
+# --------------------------------------------------------------------------- #
+# 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 #
+###############################################################################
+"""
+The :mod:`zionworximport` module provides the functionality for importing
+ZionWorx songs into the OpenLP database.
+"""
+import csv
+import logging
+
+from openlp.core.lib import translate
+from openlp.plugins.songs.lib.songimport import SongImport
+
+log = logging.getLogger(__name__)
+
+class ZionWorxImport(SongImport):
+ """
+ The :class:`ZionWorxImport` class provides the ability to import songs
+ from ZionWorx, via a dump of the ZionWorx database to a CSV file.
+
+ ZionWorx song database fields:
+
+ * ``SongNum`` Song ID. (Discarded by importer)
+ * ``Title1`` Main Title.
+ * ``Title2`` Alternate Title.
+ * ``Lyrics`` Song verses, separated by blank lines.
+ * ``Writer`` Song author(s).
+ * ``Copyright`` Copyright information
+ * ``Keywords`` (Discarded by importer)
+ * ``DefaultStyle`` (Discarded by importer)
+
+ ZionWorx has no native export function; it uses the proprietary TurboDB
+ database engine. The TurboDB vendor, dataWeb, provides tools which can
+ export TurboDB tables to other formats, such as freeware console tool
+ TurboDB Data Exchange which is available for Windows and Linux. This command
+ exports the ZionWorx songs table to a CSV file:
+
+ ``tdbdatax MainTable.dat songstable.csv -fsdf -s, -qd``
+
+ * -f Table format: ``sdf`` denotes text file.
+ * -s Separator character between fields.
+ * -q Quote character surrounding fields. ``d`` denotes double-quote.
+
+ CSV format expected by importer:
+
+ * Field separator character is comma ``,``
+ * Fields surrounded by double-quotes ``"``. This enables fields (such as
+ Lyrics) to include new-lines and commas. Double-quotes within a field
+ are denoted by two double-quotes ``""``
+ * Note: This is the default format of the Python ``csv`` module.
+
+ """
+ def doImport(self):
+ """
+ Receive a CSV file (from a ZionWorx database dump) to import.
+ """
+ # Used to strip control chars (10=LF, 13=CR, 127=DEL)
+ self.control_chars_map = dict.fromkeys(
+ range(10) + [11, 12] + range(14,32) + [127])
+ with open(self.importSource, 'rb') as songs_file:
+ fieldnames = [u'SongNum', u'Title1', u'Title2', u'Lyrics',
+ u'Writer', u'Copyright', u'Keywords', u'DefaultStyle']
+ songs_reader = csv.DictReader(songs_file, fieldnames)
+ try:
+ records = list(songs_reader)
+ except csv.Error, e:
+ self.logError(unicode(translate('SongsPlugin.ZionWorxImport',
+ 'Error reading CSV file.')),
+ unicode(translate('SongsPlugin.ZionWorxImport',
+ 'Line %d: %s' % (songs_reader.line_num, e))))
+ return
+ num_records = len(records)
+ log.info(u'%s records found in CSV file' % num_records)
+ self.importWizard.progressBar.setMaximum(num_records)
+ for index, record in enumerate(records, 1):
+ if self.stopImportFlag:
+ return
+ self.setDefaults()
+ try:
+ self.title = self._decode(record[u'Title1'])
+ if record[u'Title2']:
+ self.alternateTitle = self._decode(record[u'Title2'])
+ self.parseAuthor(self._decode(record[u'Writer']))
+ self.addCopyright(self._decode(record[u'Copyright']))
+ lyrics = self._decode(record[u'Lyrics'])
+ except UnicodeDecodeError, e:
+ self.logError(unicode(translate(
+ 'SongsPlugin.ZionWorxImport', 'Record %d' % index)),
+ unicode(translate('SongsPlugin.ZionWorxImport',
+ 'Decoding error: %s' % e)))
+ continue
+ except TypeError, e:
+ self.logError(unicode(translate(
+ 'SongsPlugin.ZionWorxImport', 'File not valid ZionWorx '
+ 'CSV format.')), u'TypeError: %s' % e)
+ return
+ verse = u''
+ for line in lyrics.splitlines():
+ if line and not line.isspace():
+ verse += line + u'\n'
+ elif verse:
+ self.addVerse(verse)
+ verse = u''
+ if verse:
+ self.addVerse(verse)
+ title = self.title
+ if not self.finish():
+ self.logError(unicode(translate(
+ 'SongsPlugin.ZionWorxImport', 'Record %d' % index))
+ + (u': "' + title + u'"' if title else u''))
+
+ def _decode(self, str):
+ """
+ Decodes CSV input to unicode, stripping all control characters (except
+ new lines).
+ """
+ # This encoding choice seems OK. ZionWorx has no option for setting the
+ # encoding for its songs, so we assume encoding is always the same.
+ return unicode(str, u'cp1252').translate(self.control_chars_map)
Follow ups