forked from openlp/openlp
194 lines
9.9 KiB
Python
194 lines
9.9 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
##########################################################################
|
|
# OpenLP - Open Source Lyrics Projection #
|
|
# ---------------------------------------------------------------------- #
|
|
# Copyright (c) 2008-2022 OpenLP Developers #
|
|
# ---------------------------------------------------------------------- #
|
|
# 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, either version 3 of the License, or #
|
|
# (at your option) any later version. #
|
|
# #
|
|
# 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, see <https://www.gnu.org/licenses/>. #
|
|
##########################################################################
|
|
"""
|
|
The :mod:`worshipassistant` module provides the functionality for importing
|
|
Worship Assistant songs into the OpenLP database.
|
|
"""
|
|
import csv
|
|
import logging
|
|
import re
|
|
|
|
from openlp.core.common import get_file_encoding
|
|
from openlp.core.common.i18n import translate
|
|
from openlp.plugins.songs.lib import VerseType
|
|
from openlp.plugins.songs.lib.importers.songimport import SongImport
|
|
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
EMPTY_STR = 'NULL'
|
|
|
|
|
|
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.
|
|
* ``COPYRIGHT`` Copyright information
|
|
* ``FIRSTLINE`` Unknown (Discarded by importer)
|
|
* ``PRIKEY`` Primary chord key (Discarded by importer)
|
|
* ``ALTKEY`` Alternate chord key (Discarded by importer)
|
|
* ``TEMPO`` Tempo (Discarded by importer)
|
|
* ``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)
|
|
* ``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 used for the presentation
|
|
* ``FILELINK1`` Associated file 1 (Discarded by importer)
|
|
* ``OVERMAP`` Verse order used for printing (Discarded by importer)
|
|
* ``FILELINK2`` Associated file 2 (Discarded by importer)
|
|
* ``LYRICS`` The song lyrics used for printing (Discarded by importer, LYRICS2 is used instead)
|
|
* ``INFO`` Unknown (Discarded by importer)
|
|
* ``LYRICS2`` The song lyrics used for the presentation
|
|
* ``BACKGROUND`` Custom background (Discarded by importer)
|
|
"""
|
|
def do_import(self):
|
|
"""
|
|
Receive a CSV file to import.
|
|
"""
|
|
# Get encoding
|
|
encoding = get_file_encoding(self.import_source)
|
|
with self.import_source.open('r', encoding=encoding) as songs_file:
|
|
songs_reader = csv.DictReader(songs_file, escapechar='\\')
|
|
try:
|
|
records = list(songs_reader)
|
|
except csv.Error as e:
|
|
self.log_error(translate('SongsPlugin.WorshipAssistantImport', 'Error reading CSV file.'),
|
|
translate('SongsPlugin.WorshipAssistantImport',
|
|
'Line {number:d}: {error}').format(number=songs_reader.line_num, error=e))
|
|
return
|
|
except UnicodeDecodeError as e:
|
|
self.log_error(translate('SongsPlugin.WorshipAssistantImport',
|
|
'Decoding error: {error}').format(error=e))
|
|
return
|
|
num_records = len(records)
|
|
log.info('{count} records found in CSV file'.format(count=num_records))
|
|
self.import_wizard.progress_bar.setMaximum(num_records)
|
|
# Create regex to strip html tags
|
|
re_html_strip = re.compile(r'<[^>]+>')
|
|
for index, record in enumerate(records, 1):
|
|
if self.stop_import_flag:
|
|
return
|
|
# Ensure that all keys are uppercase
|
|
record = dict((field.upper(), value) for field, value in record.items())
|
|
# The CSV file has a line in the middle of the file where the headers are repeated.
|
|
# We need to skip this line.
|
|
try:
|
|
if record['TITLE'] == "TITLE" and record['AUTHOR'] == 'AUTHOR' and record['LYRICS2'] == 'LYRICS2':
|
|
continue
|
|
self.set_defaults()
|
|
verse_order_list = []
|
|
self.title = record['TITLE']
|
|
if record['AUTHOR'] != EMPTY_STR:
|
|
self.parse_author(record['AUTHOR'])
|
|
if record['COPYRIGHT'] != EMPTY_STR:
|
|
self.add_copyright(record['COPYRIGHT'])
|
|
if record['CCLINR'] != EMPTY_STR:
|
|
self.ccli_number = record['CCLINR']
|
|
if record['ROADMAP'] != EMPTY_STR:
|
|
verse_order_list = [x.strip() for x in record['ROADMAP'].split(',')]
|
|
lyrics = record['LYRICS2']
|
|
except UnicodeDecodeError as e:
|
|
self.log_error(translate('SongsPlugin.WorshipAssistantImport', 'Record {count:d}').format(count=index),
|
|
translate('SongsPlugin.WorshipAssistantImport',
|
|
'Decoding error: {error}').format(error=e))
|
|
continue
|
|
except TypeError as e:
|
|
self.log_error(translate('SongsPlugin.WorshipAssistantImport',
|
|
'File not valid WorshipAssistant CSV format.'),
|
|
'TypeError: {error}'.format(error=e))
|
|
return
|
|
except KeyError as e:
|
|
self.log_error(translate('SongsPlugin.WorshipAssistantImport',
|
|
'File not valid WorshipAssistant CSV format.'),
|
|
'KeyError: {error}'.format(error=e))
|
|
return
|
|
verse = ''
|
|
used_verses = []
|
|
verse_id = VerseType.tags[VerseType.Verse] + '1'
|
|
for line in lyrics.splitlines():
|
|
if line.startswith('['): # verse marker
|
|
# Add previous verse
|
|
if verse:
|
|
# remove trailing linebreak, part of the WA syntax
|
|
self.add_verse(verse[:-1], verse_id)
|
|
used_verses.append(verse_id)
|
|
verse = ''
|
|
# drop the square brackets
|
|
right_bracket = line.find(']')
|
|
content = line[1:right_bracket].lower()
|
|
match = re.match(r'(\D*)(\d+)', content)
|
|
if match is not None:
|
|
verse_tag = match.group(1)
|
|
verse_num = match.group(2)
|
|
else:
|
|
# otherwise we assume number 1 and take the whole prefix as the verse tag
|
|
verse_tag = content
|
|
verse_num = '1'
|
|
verse_index = VerseType.from_loose_input(verse_tag) if verse_tag else 0
|
|
verse_tag = VerseType.tags[verse_index]
|
|
# Update verse order when the verse name has changed
|
|
verse_id = verse_tag + verse_num
|
|
# Make sure we've not choosen an id already used
|
|
while verse_id in verse_order_list and content in verse_order_list:
|
|
verse_num = str(int(verse_num) + 1)
|
|
verse_id = verse_tag + verse_num
|
|
if content != verse_id:
|
|
for i in range(len(verse_order_list)):
|
|
if verse_order_list[i].lower() == content.lower():
|
|
verse_order_list[i] = verse_id
|
|
else:
|
|
# add line text to verse. Strip out html
|
|
verse += re_html_strip.sub('', line) + '\n'
|
|
if verse:
|
|
# remove trailing linebreak, part of the WA syntax
|
|
if verse.endswith('\n\n'):
|
|
verse = verse[:-1]
|
|
self.add_verse(verse, verse_id)
|
|
used_verses.append(verse_id)
|
|
if verse_order_list:
|
|
# Use the verse order in the import, but remove entries that doesn't have a text
|
|
cleaned_verse_order_list = []
|
|
for verse in verse_order_list:
|
|
if verse in used_verses:
|
|
cleaned_verse_order_list.append(verse)
|
|
self.verse_order_list = cleaned_verse_order_list
|
|
if not self.finish():
|
|
self.log_error(translate('SongsPlugin.WorshipAssistantImport',
|
|
'Record {count:d}').format(count=index) +
|
|
(': "' + self.title + '"' if self.title else ''))
|