diff --git a/openlp/plugins/songs/lib/importer.py b/openlp/plugins/songs/lib/importer.py index 7ce637285..6d01da309 100644 --- a/openlp/plugins/songs/lib/importer.py +++ b/openlp/plugins/songs/lib/importer.py @@ -50,6 +50,7 @@ from .sundayplusimport import SundayPlusImport from .foilpresenterimport import FoilPresenterImport from .zionworximport import ZionWorxImport from .propresenterimport import ProPresenterImport +from .worshipassistantimport import WorshipAssistantImport # Imports that might fail @@ -167,8 +168,9 @@ class SongFormat(object): SongsOfFellowship = 16 SundayPlus = 17 WordsOfWorship = 18 - WorshipCenterPro = 19 - ZionWorx = 20 + WorshipAssistant = 19 + WorshipCenterPro = 20 + ZionWorx = 21 # Set optional attribute defaults __defaults__ = { @@ -321,6 +323,16 @@ class SongFormat(object): 'prefix': 'wordsOfWorship', 'filter': '%s (*.wsg *.wow-song)' % translate('SongsPlugin.ImportWizardForm', 'Words Of Worship Song Files') }, + WorshipAssistant: { + 'class': WorshipAssistantImport, + 'name': 'Worship Assistant 0', + 'prefix': 'worshipAssistant', + 'selectMode': SongFormatSelect.SingleFile, + 'filter': '%s (*.csv)' % translate('SongsPlugin.ImportWizardForm', 'Worship Assistant Files'), + 'comboBoxText': translate('SongsPlugin.ImportWizardForm', 'Worship Assistant (CSV)'), + 'descriptionText': translate('SongsPlugin.ImportWizardForm', + 'In Worship Assistant, export your Database to a CSV file.') + }, WorshipCenterPro: { 'name': 'WorshipCenter Pro', 'prefix': 'worshipCenterPro', @@ -370,16 +382,17 @@ class SongFormat(object): SongFormat.SongsOfFellowship, SongFormat.SundayPlus, SongFormat.WordsOfWorship, + SongFormat.WorshipAssistant, SongFormat.WorshipCenterPro, SongFormat.ZionWorx ] @staticmethod - def get(format, *attributes): + def get(song_format, *attributes): """ Return requested song format attribute(s). - :param format: A song format from SongFormat. + :param song_format: A song format from SongFormat. :param attributes: Zero or more song format attributes from SongFormat. Return type depends on number of supplied attributes: @@ -389,23 +402,23 @@ class SongFormat(object): :>1: Return tuple of requested attribute values. """ if not attributes: - return SongFormat.__attributes__.get(format) + return SongFormat.__attributes__.get(song_format) elif len(attributes) == 1: default = SongFormat.__defaults__.get(attributes[0]) - return SongFormat.__attributes__[format].get(attributes[0], default) + return SongFormat.__attributes__[song_format].get(attributes[0], default) else: values = [] for attr in attributes: default = SongFormat.__defaults__.get(attr) - values.append(SongFormat.__attributes__[format].get(attr, default)) + values.append(SongFormat.__attributes__[song_format].get(attr, default)) return tuple(values) @staticmethod - def set(format, attribute, value): + def set(song_format, attribute, value): """ Set specified song format attribute to the supplied value. """ - SongFormat.__attributes__[format][attribute] = value + SongFormat.__attributes__[song_format][attribute] = value SongFormat.set(SongFormat.SongsOfFellowship, 'availability', HAS_SOF) diff --git a/openlp/plugins/songs/lib/worshipassistantimport.py b/openlp/plugins/songs/lib/worshipassistantimport.py new file mode 100644 index 000000000..683ec6456 --- /dev/null +++ b/openlp/plugins/songs/lib/worshipassistantimport.py @@ -0,0 +1,144 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2014 Raoul Snyman # +# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan # +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # +# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, # +# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, # +# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, # +# Frode Woldsund, Martin Zibricky, Patrick Zimmermann # +# --------------------------------------------------------------------------- # +# 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:`worshipassistantimport` module provides the functionality for importing +Worship Assistant songs into the OpenLP database. +""" +import csv +import logging + +from openlp.core.common import translate +from openlp.plugins.songs.lib.songimport import SongImport + +log = logging.getLogger(__name__) + +# Used to strip control chars (except 10=LF, 13=CR) +CONTROL_CHARS_MAP = dict.fromkeys(list(range(10)) + [11, 12] + list(range(14, 32)) + [127]) + + +class WorshipAssistantImport(SongImport): + """ + The :class:`WorshipAssistantImport` class provides the ability to import songs + from Worship Assistant, via a dump of the database to a CSV file. + + The following fields are in the exported CSV file: + + * ``SONGNR`` Song ID (Discarded by importer) + * ``TITLE`` Song title + * ``AUTHOR`` Song author. May containt multiple authors. + * ``COPYRIGHT`` Copyright information + * ``FIRSTLINE`` Unknown (Discarded by importer) + * ``PRIKEY`` Primary chord key + * ``ALTKEY`` Alternate chord key + * ``TEMPO`` Tempo + * ``FOCUS`` Unknown (Discarded by importer) + * ``THEME`` Theme (Discarded by importer) + * ``SCRIPTURE`` Associated scripture (Discarded by importer) + * ``ACTIVE`` Boolean value (Discarded by importer) + * ``SONGBOOK`` Boolean value (Discarded by importer) + * ``TIMESIG`` Unknown (Discarded by importer) + * ``INTRODUCED`` Date the song was created (Discarded by importer) + * ``LASTUSED`` Date the song was last used (Discarded by importer) + * ``TIMESUSED`` How many times the song was used (Discarded by importer) + * ``TIMESUSED`` How many times the song was used (Discarded by importer) + * ``CCLINR`` CCLI Number + * ``USER1`` User Field 1 (Discarded by importer) + * ``USER2`` User Field 2 (Discarded by importer) + * ``USER3`` User Field 3 (Discarded by importer) + * ``USER4`` User Field 4 (Discarded by importer) + * ``USER5`` User Field 5 (Discarded by importer) + * ``ROADMAP`` Verse order + * ``FILELINK1`` Associated file 1 (Discarded by importer) + * ``OVERMAP`` Unknown (Discarded by importer) + * ``FILELINK2`` Associated file 2 (Discarded by importer) + * ``LYRICS`` The song lyrics as plain text (Discarded by importer) + * ``INFO`` Unknown (Discarded by importer) + * ``LYRICS2`` The song lyrics with verse numbers + * ``BACKGROUND`` Unknown (Discarded by importer) + """ + def do_import(self): + """ + Receive a CSV file to import. + """ + with open(self.import_source, 'r', encoding='latin-1') as songs_file: + field_names = ['SongNum', 'Title1', 'Title2', 'Lyrics', 'Writer', 'Copyright', 'Keywords', + 'DefaultStyle'] + songs_reader = csv.DictReader(songs_file)#, field_names) + try: + records = list(songs_reader) + except csv.Error as e: + self.log_error(translate('SongsPlugin.WorshipAssistantImport', 'Error reading CSV file.'), + translate('SongsPlugin.WorshipAssistantImport', 'Line %d: %s') % + (songs_reader.line_num, e)) + return + num_records = len(records) + log.info('%s records found in CSV file' % num_records) + self.import_wizard.progress_bar.setMaximum(num_records) + for index, record in enumerate(records, 1): + if self.stop_import_flag: + return + # The CSV file has a line in the middle of the file where the headers are repeated. + # We need to skip this line. + if record['TITLE'] == "TITLE" and record['COPYRIGHT'] == 'COPYRIGHT' and record['AUTHOR'] == 'AUTHOR': + continue + self.set_defaults() + try: + self.title = self._decode(record['TITLE']) + self.parse_author(self._decode(record['AUTHOR'])) + self.add_copyright(self._decode(record['COPYRIGHT'])) + lyrics = self._decode(record['LYRICS2']) + except UnicodeDecodeError as e: + self.log_error(translate('SongsPlugin.WorshipAssistantImport', 'Record %d' % index), + translate('SongsPlugin.WorshipAssistantImport', 'Decoding error: %s') % e) + continue + except TypeError as e: + self.log_error(translate('SongsPlugin.WorshipAssistantImport', + 'File not valid WorshipAssistant CSV format.'), 'TypeError: %s' % e) + return + verse = '' + for line in lyrics.splitlines(): + if line and not line.isspace(): + verse += line + '\n' + elif verse: + self.add_verse(verse) + verse = '' + if verse: + self.add_verse(verse) + if not self.finish(): + self.log_error(translate('SongsPlugin.ZionWorxImport', 'Record %d') % index + + (': "' + self.title + '"' if self.title else '')) + + 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 str + #return str(str, 'cp1252').translate(CONTROL_CHARS_MAP)