Singing The Faith Importer - Add the importer, and tests

This commit is contained in:
john 2019-06-17 09:44:34 +01:00
parent 20d0b40f8b
commit 66712873b4
5 changed files with 535 additions and 0 deletions

View File

@ -0,0 +1,401 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2019 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; 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:`singingthefaith` module provides the functionality for importing songs which are
exported from Singing The Faith - an Authorised songbook for the Methodist Church of
Great Britain."""
import logging
import re
import os
from openlp.core.common.i18n import translate
from openlp.plugins.songs.lib.importers.songimport import SongImport
log = logging.getLogger(__name__)
class SingingTheFaithImport(SongImport):
"""
Import songs exported from SingingTheFaith
"""
hints_available = False
checks_needed = True
hintline = {}
hintfile_version = '0'
hint_verseOrder = ''
hint_songtitle = ''
hint_comments = ''
hint_ignoreIndent = False
def __init__(self, manager, **kwargs):
"""
Initialise the class.
"""
super(SingingTheFaithImport, self).__init__(manager, **kwargs)
def do_import(self):
"""
Receive a single file or a list of files to import.
"""
if not isinstance(self.import_source, list):
return
self.import_wizard.progress_bar.setMaximum(len(self.import_source))
for filename in self.import_source:
if self.stop_import_flag:
return
song_file = open(filename, 'rt', encoding='cp1251')
self.do_import_file(song_file)
song_file.close()
def do_import_file(self, file):
"""
Process the SingingTheFaith file - pass in a file-like object, not a file path.
"""
singingTheFaithVersion = 1
self.set_defaults()
# Setup variables
line_number = 0
old_indent = 0
chorus_indent = 5 # It might be 6, but we test for >=
song_title = 'STF000 -'
song_number = '0'
ccli = '0'
current_verse = ''
current_verse_type = 'v'
current_verse_number = 1
has_chorus = False
chorus_written = False
verses = []
author = ''
copyright = ''
check_flag = 'z' # Prepended to title, remove if we think import should be OK
self.add_comment("Imported with Singing The Faith Importer v "+str(singingTheFaithVersion))
# Get the file_song_number - so we can use it for hints
filename = file.name
song_number_file = os.path.splitext(os.path.basename(filename))[0]
song_number_match = re.search('\d+',song_number_file)
if song_number_match:
song_number_file=song_number_match.group()
# See if there are hints available at all
# See if there is a hints file in the same location as the file
dir_path = os.path.dirname(os.path.realpath(filename))
## print("Pathname is ",dir_path)
hints_file_name = os.path.join(dir_path,"hints.tag")
try:
hints_file=open(hints_file_name,"r")
hints_available = self.read_hints(hints_file,song_number_file)
except FileNotFoundError:
hints_available = False
try:
# Read the file
for line in file:
line_number += 1
## print("Read line",line_number,"-",line)
if (hints_available and (str(line_number) in self.hintline)):
## print("Found hint for line ",line_number)
hint = self.hintline[str(line_number)]
## print("Hint is ",hint)
if (hint == "Comment"):
line.strip()
## print("Comment hint for line ",line_number," line is ",line)
self.add_comment(line)
line_number += 1
next(file)
continue
elif (hint == "Ignore"):
line_number += 1
next(file)
continue
elif (hint == "Author"):
# add as a raw author - do not split and make them a words author
line.strip()
self.add_author(line,'words')
line_number += 1
next(file)
continue
elif (hint.startswith("VariantVerse")):
## print("VariantVerse found - hint is ",hint)
(vv,hintverse,replace)=hint.split(" ",2)
this_verse = self.verses[int(hintverse)-1]
this_verse_str = this_verse[1]
new_verse = this_verse_str
# There might be multiple replace pairs separated by |
replaces=replace.split("|")
for rep in replaces:
(source_str,dest_str)=rep.split("/")
new_verse = new_verse.replace(source_str,dest_str)
self.add_verse(new_verse,'v')
self.verse_order_list.append('v'+str(current_verse_number))
current_verse_number += 1
line_number += 1
next(file)
continue
else:
self.log_error(translate('SongsPlugin.SingingTheFaithImport', 'File %s' % file.name),
translate('SongsPlugin.SingingTheFaithImport', 'Unknown hint %s' % hint))
return
# STF exported lines have a leading verse number at the start of each verse.
# remove them - note that we want to track the indent as that shows a chorus
# so will deal with that before stipping all leading spaces.
indent = 0
if line.strip():
## print("Dealing non empty line ",line)
verse_num_match = re.search('^\d+',line)
if verse_num_match:
verse_num = verse_num_match.group()
## print("Verse num is ",verse_num)
line = line.lstrip("0123456789")
indent_match = re.search('^\s+',line)
if indent_match:
indent=len(indent_match.group())
## print("indent is ",indent)
# Assuming we have sorted out what is verse and what is chorus, strip lines, unless ignoreIndent
if not self.hint_ignoreIndent:
line = line.strip()
else:
line = line.rstrip()
## print("Read line",line_number,"(",indent,")",line)
# line_number += 1 # Now read earlier
if line_number == 2:
# note that songs seem to start with a blank line
song_title = line
## print("Set song title to "+song_title)
# Detect the 'Reproduced from Singing the Faith Electronic Words Edition' line
if line.startswith('Reproduced from Singing the Faith Electronic Words Edition'):
song_number_match = re.search('\d+',line)
if song_number_match:
song_number=song_number_match.group()
## print("Found Reproduced - song is ",song_number)
continue
# If the indent is 0 and it contains '(c)' then it is a Copyright line
elif (indent == 0) and ( "(c)" in line):
copyright = line
continue
elif (indent == 0) and (line.startswith('Liturgical ')):
self.add_comment(line)
continue
elif (indent == 0) and (line.startswith('From The ')):
self.add_comment(line)
continue
elif (indent == 0) and (line.startswith('From Common ')):
self.add_comment(line)
continue
# If indent is 0 it may well be the author (but not if it was the Reproduced line)
elif (indent == 0) and len(line)>0 :
## print ("Possible author ",line)
# May have more than one author, separated by ' and '
authors = line.split(' and ')
for a in authors:
self.parse_author(a)
# author = line
continue
if line == '':
## print("Starting a new verse")
if current_verse != '':
## print("About to add a verse - type ",current_verse_type," ** ",current_verse)
self.add_verse(current_verse, current_verse_type)
self.verse_order_list.append(current_verse_type+str(current_verse_number))
if (current_verse_type == 'c'):
chorus_written = True
else:
current_verse_number += 1
current_verse = ''
if chorus_written:
## print("Setting current_verse_type to v")
current_verse_type = 'v'
else:
# If the line is indented more than or equal chorus_indent then assume it is a chorus
# If then indent has just changed then start a new verse just like hitting a blank line
if not self.hint_ignoreIndent and ((indent >= chorus_indent) and (old_indent < indent)):
## print("Change of indent - close off old verse")
if current_verse != '':
## print("About to add a verse (indent change) - type ",current_verse_type," ** ",current_verse)
self.add_verse(current_verse, current_verse_type)
self.verse_order_list.append(current_verse_type+str(current_verse_number))
if current_verse_type == 'v':
current_verse_number += 1
current_verse = line
## print("Setting current_verse_type to c");
current_verse_type = 'c'
old_indent=indent
chorus_written = False
has_chorus = True
continue
if current_verse == '':
current_verse += line
else:
current_verse += '\n' + line
old_indent = indent
except Exception as e:
self.log_error(translate('SongsPlugin.SingingTheFaithImport', 'File %s' % file.name),
translate('SongsPlugin.SingingTheFaithImport', 'Error: %s') % e)
return
if self.hint_songtitle:
song_title = self.hint_songtitle
self.title = check_flag+"STF"+song_number.zfill(3)+" - "+song_title
self.song_book_name="Singing The Faith"
self.song_number = song_number
# self.parse_author(author)
self.ccli_number = ccli
self.add_copyright(copyright)
# If we have a chorus then the generated Verse order will not be useful, so clear the verse_order_list
if has_chorus:
## print ("Has chorus - verse order list is ",self.verse_order_list)
auto_verse_order_ok = False
# Popular case V1 C2 V2 ...
if len(self.verse_order_list) >= 1: # protect against odd cases
if (self.verse_order_list[0] == "v1") and (self.verse_order_list[1] == "c2"):
new_verse_order_list = ['v1','c1']
i = 2
auto_verse_order_ok = True
while i < len(self.verse_order_list):
# Should maybe check for vn
if self.verse_order_list[i].startswith('v'):
new_verse_order_list.append(self.verse_order_list[i])
new_verse_order_list.append("c1")
else:
### Fix this
### self.log_error(translate('SongsPlugin.SingingTheFaithImport', 'File %s' % file.name),
### translate('SongsPlugin.SingingTheFaithImport', 'Error: %s'",self.verse_order_list[i]," in ",str(self.song_number))
## Should be a logged error
## print("Found strange verseorder entry ",self.verse_order_list[i]," in ",file.name)
auto_verse_order_ok = False
i += 1
## print(" new verse_order_list is ",new_verse_order_list)
self.verse_order_list = new_verse_order_list
elif (self.verse_order_list[0] == "c1") and (self.verse_order_list[1] == "v1"):
new_verse_order_list = ['c1','v1','c1']
i = 2
while i < len(self.verse_order_list):
# Should maybe check for vn
if self.verse_order_list[i].startswith('v'):
new_verse_order_list.append(self.verse_order_list[i])
new_verse_order_list.append("c1")
else:
## print("Found strange verseorder entry ",self.verse_order_list[i]," in ",file.name)
auto_verse_order_ok = False
i += 1
## print(" new verse_order_list (Chorus first is ",new_verse_order_list)
self.verse_order_list = new_verse_order_list
else:
if not auto_verse_order_ok:
print ("setting verse_order_list to empty")
self.verse_order_list = []
# If it is a simple case,
if self.hint_verseOrder:
self.verse_order_list = self.hint_verseOrder.split(',')
if self.hint_comments:
self.add_comment(self.hint_comments)
# Write the title last as by now we will know if we need checks
if ( hints_available and not self.checks_needed):
check_flag=''
elif ( not hints_available and not has_chorus):
check_flag=''
elif ( not hints_available and has_chorus and auto_verse_order_ok):
check_flag=''
self.title = check_flag+"STF"+song_number.zfill(3)+" - "+song_title
if not self.finish():
self.log_error(file.name)
def read_hints(self, file, song_number ):
hintfound = False
# clear hints
self.hint_verseOrder = ''
self.hintline.clear()
self.hint_comments = ''
self.hint_songtitle = ''
self.hint_ignoreIndent = False
## print("Reading the hints file for ",song_number)
for tl in file:
# if the line is empty then return
if not tl.strip():
return hintfound
tagval = tl.split(':')
tag = tagval[0].strip()
val = tagval[1].strip()
if (tag == "Version") :
self.hintfile_version = val
continue
if (tag == "Hymn") and (val == song_number):
## print ("Found song ",song_number," in hints")
self.add_comment("Using hints version "+str(self.hintfile_version))
hintfound = True
# Assume, unless the hints has ManualCheck that if hinted all will be OK
self.checks_needed = False
for tl in file:
tagval = tl.split(':')
tag = tagval[0].strip()
val = tagval[1].strip()
if (tag == "End"):
return hintfound
elif (tag == "CommentsLine"):
vals = val.split(',')
for v in vals:
self.hintline[v] = "Comment"
elif (tag == "IgnoreLine"):
vals = val.split(',')
for v in vals:
self.hintline[v] = "Ignore"
elif (tag == "AuthorLine"):
vals = val.split(',')
for v in vals:
self.hintline[v] = "Author"
elif (tag == "VerseOrder"):
self.hint_verseOrder = val
elif (tag == "ManualCheck"):
self.checks_needed = True
elif (tag == "IgnoreIndent"):
self.hint_ignoreIndent = True
elif (tag == "VariantVerse"):
vvline = val.split(' ',1)
self.hintline[vvline[0].strip()] = "VariantVerse "+vvline[1].strip()
elif (tag == "SongTitle"):
self.hint_songtitle = val
elif (tag == "AddComment"):
self.hint_comments += '\n' + val
else:
print("Unknown tag ",tag," value ",val)
return hintfound

View File

@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2019 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/>. #
##########################################################################
"""
This module contains tests for the SingingTheFaith song importer.
"""
from tests.helpers.songfileimport import SongImportTestHelper
from tests.utils.constants import RESOURCE_PATH
TEST_PATH = RESOURCE_PATH / 'songs' / 'singingthefaith'
class TestSingingTheFaithFileImport(SongImportTestHelper):
def __init__(self, *args, **kwargs):
self.importer_class_name = 'SingingTheFaithImport'
self.importer_module_name = 'singingthefaith'
super(TestSingingTheFaithFileImport, self).__init__(*args, **kwargs)
def test_song_import(self):
"""
Test that loading a Singing The Faith file works correctly on various files
"""
self.file_import([TEST_PATH / 'H1.txt'],
self.load_external_result_data(TEST_PATH / 'STF001.json'))

View File

@ -0,0 +1,30 @@
1 Amazing Grace! how sweet the sound
That saved a wretch like me!
I once was lost, but now am found;
Was blind, but now I see.
2 'Twas grace that taught my heart to fear,
And grace my fears relieved.
How precious did that grace appear,
The hour I first believed.
3 The Lord has promised good to me,
His Word my hope secures.
He will my shield and portion be
As long as life endures.
4 Thro' many dangers, toils and snares
I have already come.
'Tis grace that brought me safe thus far,
And grace will lead me home.
5 When we've been there ten thousand years,
Bright shining as the sun,
We've no less days to sing God's praise,
Than when we first begun.
John Newton (d. 1807)
Reproduced from Singing the Faith Electronic Words Edition, number 1 - or not as this is a hand made test file

View File

@ -0,0 +1,30 @@
1 All people that on earth do dwell,
sing to the Lord with cheerful voice:
him serve with mirth, his praise forth tell;
come ye before him and rejoice.
2 The Lord, ye know, is God indeed;
without our aid he did us make:
we are his folk, he doth us feed;
and for his sheep he doth us take.
3 O enter then his gates with praise;
approach with joy his courts unto;
praise, laud, and bless his name always,
for it is seemly so to do.
4 For why, the Lord our God is good;
his mercy is for ever sure;
his truth at all times firmly stood,
and shall from age to age endure.
5 To Father, Son and Holy Ghost,
the God whom heaven and earth adore,
from earth and from the angel host
be praise and glory evermore.
John Newton (d. 1807)
Reproduced from Singing the Faith Electronic Words Edition, number 1 - or not as this is a hand made test file

View File

@ -0,0 +1,29 @@
{
"title": "STF001 - Amazing Grace! how sweet the sound",
"authors": [
"John Newton (d. 1807)"
],
"verse_order_list": ["v1", "v2", "v3", "v4", "v5"],
"verses": [
[
"Amazing grace! How sweet the sound!\nThat saved a wretch like me!\nI once was lost, but now am found;\nWas blind, but now I see.",
"v1"
],
[
"'Twas grace that taught my heart to fear,\nAnd grace my fears relieved.\nHow precious did that grace appear,\nThe hour I first believed.",
"v2"
],
[
"The Lord has promised good to me,\nHis Word my hope secures.\nHe will my shield and portion be\nAs long as life endures.",
"v3"
],
[
"Thro' many dangers, toils and snares\nI have already come.\n'Tis grace that brought me safe thus far,\nAnd grace will lead me home.",
"v4"
],
[
"When we've been there ten thousand years,\nBright shining as the sun,\nWe've no less days to sing God's praise,\nThan when we first begun.",
"v5"
]
]
}