Merge lp:~john+ubuntu-g/openlp/singingthefaith into lp:openlp

Proposed by John Lines on 2019-07-19
Status: Needs review
Proposed branch: lp:~john+ubuntu-g/openlp/singingthefaith
Merge into: lp:openlp
Diff against target: 665 lines (+581/-11)
8 files modified
openlp/plugins/songs/lib/importer.py (+23/-10)
openlp/plugins/songs/lib/importers/singingthefaith.py (+427/-0)
tests/functional/openlp_plugins/songs/test_singingthefaithimport.py (+48/-0)
tests/helpers/songfileimport.py (+2/-1)
tests/resources/songs/singingthefaith/H1.txt (+9/-0)
tests/resources/songs/singingthefaith/H2.txt (+30/-0)
tests/resources/songs/singingthefaith/STF001.json (+13/-0)
tests/resources/songs/singingthefaith/STF002.json (+29/-0)
To merge this branch: bzr merge lp:~john+ubuntu-g/openlp/singingthefaith
Reviewer Review Type Date Requested Status
Tomas Groth 2019-07-19 Pending
Phill 2019-07-19 Pending
Raoul Snyman 2019-07-19 Pending
Review via email: mp+370364@code.launchpad.net

This proposal supersedes a proposal from 2019-06-30.

Commit message

Initial merge of SingingTheFaithImport, including update to importer.py

Description of the change

Singing The Faith is the new Authorized Hymn book for the Methodist Church of Great Britain.
There is an electronic version of the Hymn book, for Windows only, which can export Hymns as text files.

This import module smooths the process of converting these text files into OpenLP. The input format is messy and not intended for automatic processing so the importer uses a combination of heuristics and a hints file. This version has not been tested on all the hymns in Singing The Faith, but deals with most of the, more than 600, hymns it has been tested with.

Documentation for the source format and hints file is at https://wiki.openlp.org/Development:SingingTheFaith_Format

The change includes a test module, which works for the single verse case, and for a whole song.

To post a comment you must log in.
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal

Linux tests failed, please see https://ci.openlp.io/job/MP-02-Linux_Tests/191/ for more details

Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal

The test is failing because the "add_verse" method is not called, or not called with that exact data. I've broken up the error message into 3 parts below for easier reading.

AssertionError:

add_verse('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')

call not found

review: Needs Fixing
Phill (phill-ridout) wrote : Posted in a previous version of this proposal

A few in-line comments. Also you don't need to use parenthesis around the expressions in the if statements.

John Lines (john+ubuntu-g) wrote : Posted in a previous version of this proposal

> A few in-line comments. Also you don't need to use parenthesis around the
> expressions in the if statements.

Thanks - have updated to use Path more, and have removed redudant parentheses round expressions in if statements

Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal

Hey John, a couple things.

1. You have a ton of linting issues. I started commenting, and then I realised that it would be better to just point you to flake8. See the link at the bottom of my comment for a quick introduction to linting and flake8.

2. You have some inconsistent indentation. Indentation in Python is very important, so we take it seriously. Also, please make sure you are indenting using spaces and not tabs.

3. You are not committing your code with the e-mail address associated with Launchpad. Please can you fix that by issuing a bzr whoami "John Lines <email address hidden>"

4. Once you've made all your changes, and you're ready for another review, you need to resubmit your merge proposal. Do this by clicking the "Resubmit" link in the top right hand corner of the page.

5. If you're struggling with anything, pop into our IRC channel, there's usually someone around who is happy to help.

https://medium.com/python-pandemonium/what-is-flake8-and-why-we-should-use-it-b89bd78073f2

review: Needs Fixing
John Lines (john+ubuntu-g) wrote : Posted in a previous version of this proposal

1. Thanks for the info on flake8 - is now flake8 clean - you are quite right - it is better for me to learn about the tools.
2. Think indentation is fixed - flake8 was handy as well
3. Email address now set in my bzr config
4. Can you have another look at the change now.

Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal

Linux tests passed!

Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal

Linting failed, please see https://ci.openlp.io/job/MP-03-Linting/132/ for more details

John Lines (john+ubuntu-g) wrote : Posted in a previous version of this proposal

Fix lint tests outside main importer code

Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal

Linux tests passed!

Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal

Linting passed!

Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal

macOS tests passed!

Phill (phill-ridout) wrote : Posted in a previous version of this proposal

Please change your string formatting to use the 'new' style with the format function. ( https://pyformat.info/ ) also string formatting is preferred over concatenation (i.e, "part1" + var + "part2")

single quotes for strings, not double quotes

do_import_file is very long can this be split in to smaller methods?

review: Needs Fixing
Tomas Groth (tomasgroth) wrote : Posted in a previous version of this proposal

Generally it looks good to me, though I haven't tested. But a few things to fix.

review: Needs Fixing
John Lines (john+ubuntu-g) wrote : Posted in a previous version of this proposal

Using new string formatting, constructor for class.

Raoul Snyman (raoul-snyman) wrote :

Linux tests passed!

Raoul Snyman (raoul-snyman) wrote :

Linting passed!

2899. By John Lines on 2019-07-27

Strip unwanted formatting characters

2900. By John Lines on 2019-07-28

Implement BoldLine hint

Phill (phill-ridout) wrote :

Just a few more inline comments.

2901. By John Lines on 2019-07-28

Make Based on Psalm an automatic comment, do not automatically make Authors type Word

2902. By John Lines on 2019-07-28

Fix typo

2903. By John Lines on 2019-08-12

Merge trunk updates

Raoul Snyman (raoul-snyman) wrote :

Hey John, if you're ready for us to take another look at this, make sure to resubmit it.

Tomas Groth (tomasgroth) wrote :

Is there any reason the hint file isn't included? If it is not bundled with OpenLP, then where is the user supposed to get it from?

2904. By John Lines on 2019-08-23

Use .format in importer.py

2905. By John Lines on 2019-08-23

Implement SongbookNumberInTitle hint

John Lines (john+ubuntu-g) wrote :

The use of STFnnn - in the title is now controlled by a hint. Also other code tidying.

Unmerged revisions

2905. By John Lines on 2019-08-23

Implement SongbookNumberInTitle hint

2904. By John Lines on 2019-08-23

Use .format in importer.py

2903. By John Lines on 2019-08-12

Merge trunk updates

2902. By John Lines on 2019-07-28

Fix typo

2901. By John Lines on 2019-07-28

Make Based on Psalm an automatic comment, do not automatically make Authors type Word

2900. By John Lines on 2019-07-28

Implement BoldLine hint

2899. By John Lines on 2019-07-27

Strip unwanted formatting characters

2898. By John Lines on 2019-07-19

enable whole song impoerter test

2897. By John Lines on 2019-07-19

Merge trunk updates

2896. By John Lines on 2019-07-17

Add BlankLine hint and deal with a leading asterisk on a verse number

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'openlp/plugins/songs/lib/importer.py'
2--- openlp/plugins/songs/lib/importer.py 2019-04-13 13:00:22 +0000
3+++ openlp/plugins/songs/lib/importer.py 2019-08-23 16:32:02 +0000
4@@ -42,6 +42,7 @@
5 from .importers.powersong import PowerSongImport
6 from .importers.presentationmanager import PresentationManagerImport
7 from .importers.propresenter import ProPresenterImport
8+from .importers.singingthefaith import SingingTheFaithImport
9 from .importers.songbeamer import SongBeamerImport
10 from .importers.songpro import SongProImport
11 from .importers.songshowplus import SongShowPlusImport
12@@ -173,16 +174,17 @@
13 PowerSong = 16
14 PresentationManager = 17
15 ProPresenter = 18
16- SongBeamer = 19
17- SongPro = 20
18- SongShowPlus = 21
19- SongsOfFellowship = 22
20- SundayPlus = 23
21- VideoPsalm = 24
22- WordsOfWorship = 25
23- WorshipAssistant = 26
24- WorshipCenterPro = 27
25- ZionWorx = 28
26+ SingingTheFaith = 19
27+ SongBeamer = 20
28+ SongPro = 21
29+ SongShowPlus = 22
30+ SongsOfFellowship = 23
31+ SundayPlus = 24
32+ VideoPsalm = 25
33+ WordsOfWorship = 26
34+ WorshipAssistant = 27
35+ WorshipCenterPro = 28
36+ ZionWorx = 29
37
38 # Set optional attribute defaults
39 __defaults__ = {
40@@ -343,6 +345,16 @@
41 'filter': '{text} (*.pro4 *.pro5 *.pro6)'.format(text=translate('SongsPlugin.ImportWizardForm',
42 'ProPresenter Song Files'))
43 },
44+ SingingTheFaith: {
45+ 'class': SingingTheFaithImport,
46+ 'name': 'SingingTheFaith',
47+ 'prefix': 'singingTheFaith',
48+ 'filter': '{text} (*.txt)'.format(text=translate('SongsPlugin.ImportWizardForm',
49+ 'Singing The Faith Exported Files')),
50+ 'descriptionText': translate('SongsPlugin.ImportWizardForm',
51+ 'First use Singing The Faith Electonic edition to export '
52+ 'the song(s) in Text format.')
53+ },
54 SongBeamer: {
55 'class': SongBeamerImport,
56 'name': 'SongBeamer',
57@@ -462,6 +474,7 @@
58 SongFormat.PowerSong,
59 SongFormat.PresentationManager,
60 SongFormat.ProPresenter,
61+ SongFormat.SingingTheFaith,
62 SongFormat.SongBeamer,
63 SongFormat.SongPro,
64 SongFormat.SongShowPlus,
65
66=== added file 'openlp/plugins/songs/lib/importers/singingthefaith.py'
67--- openlp/plugins/songs/lib/importers/singingthefaith.py 1970-01-01 00:00:00 +0000
68+++ openlp/plugins/songs/lib/importers/singingthefaith.py 2019-08-23 16:32:02 +0000
69@@ -0,0 +1,427 @@
70+# -*- coding: utf-8 -*-
71+# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
72+
73+###############################################################################
74+# OpenLP - Open Source Lyrics Projection #
75+# --------------------------------------------------------------------------- #
76+# Copyright (c) 2008-2019 OpenLP Developers #
77+# --------------------------------------------------------------------------- #
78+# This program is free software; you can redistribute it and/or modify it #
79+# under the terms of the GNU General Public License as published by the Free #
80+# Software Foundation; version 3 of the License. #
81+# #
82+# This program is distributed in the hope that it will be useful, but WITHOUT #
83+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
84+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
85+# more details. #
86+# #
87+# You should have received a copy of the GNU General Public License along #
88+# with this program; if not, write to the Free Software Foundation, Inc., 59 #
89+# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
90+###############################################################################
91+"""
92+The :mod:`singingthefaith` module provides the functionality for importing songs which are
93+exported from Singing The Faith - an Authorised songbook for the Methodist Church of
94+Great Britain."""
95+
96+import logging
97+import re
98+from pathlib import Path
99+
100+from openlp.core.common.i18n import translate
101+from openlp.plugins.songs.lib.importers.songimport import SongImport
102+
103+log = logging.getLogger(__name__)
104+
105+
106+class SingingTheFaithImport(SongImport):
107+ """
108+ Import songs exported from SingingTheFaith
109+ """
110+
111+ def __init__(self, manager, **kwargs):
112+ """
113+ Initialise the class.
114+ """
115+ super(SingingTheFaithImport, self).__init__(manager, **kwargs)
116+ self.hints_available = False
117+ self.checks_needed = True
118+ self.hint_line = {}
119+ self.hint_file_version = '0'
120+ self.hint_verse_order = ''
121+ self.hint_song_title = ''
122+ self.hint_comments = ''
123+ self.hint_ccli = ''
124+ self.hint_ignore_indent = False
125+ self.hint_songbook_number_in_title = False
126+
127+ def do_import(self):
128+ """
129+ Receive a single file or a list of files to import.
130+ """
131+ if not isinstance(self.import_source, list):
132+ return
133+ self.import_wizard.progress_bar.setMaximum(len(self.import_source))
134+ for file_path in self.import_source:
135+ if self.stop_import_flag:
136+ return
137+ # If this is backported to version 2.4 then do_import is called with a filename
138+ # rather than a path object if called from the development version.
139+ # Check here to minimise differences between versions.
140+ if isinstance(file_path, str):
141+ song_file = open(file_path, 'rt', encoding='cp1251')
142+ self.do_import_file(song_file)
143+ song_file.close()
144+ else:
145+ with file_path.open('rt', encoding='cp1251') as song_file:
146+ self.do_import_file(song_file)
147+
148+ def do_import_file(self, file):
149+ """
150+ Process the SingingTheFaith file - pass in a file-like object, not a file path.
151+ """
152+ singing_the_faith_version = 1
153+ self.set_defaults()
154+ # Setup variables
155+ line_number = 0
156+ old_indent = 0
157+ # The chorus indent is how many spaces the chorus is indented - it might be 6,
158+ # but we test for >= and I do not know how consistent to formatting of the
159+ # exported songs is.
160+ chorus_indent = 5
161+ # Initialise the song title - the format of the title finally produced can be affected
162+ # by the SongbookNumberInTitle option in the hints file
163+ song_title = 'STF000 -'
164+ song_number = '0'
165+ ccli = '0'
166+ current_verse = ''
167+ current_verse_type = 'v'
168+ current_verse_number = 1
169+ # Potentially we could try to track current chorus number to automatically handle
170+ # more than 1 chorus, currently unused.
171+ # current_chorus_number = 1
172+ has_chorus = False
173+ chorus_written = False
174+ auto_verse_order_ok = False
175+ copyright = ''
176+ # the check_flag is prepended to the title, removed if the import should be OK
177+ # all the songs which need manual editing should sort below all the OK songs
178+ check_flag = 'z'
179+
180+ self.add_comment(
181+ 'Imported with Singing The Faith Importer v{no}'.format(no=singing_the_faith_version))
182+
183+ # Get the file_song_number - so we can use it for hints
184+ filename = Path(file.name)
185+ song_number_file = filename.stem
186+ song_number_match = re.search(r'\d+', song_number_file)
187+ if song_number_match:
188+ song_number_file = song_number_match.group()
189+
190+ # See if there is a hints file in the same location as the file
191+ dir_path = filename.parent
192+ hints_file_path = dir_path / 'hints.tag'
193+ try:
194+ with hints_file_path.open('r') as hints_file:
195+ hints_available = self.read_hints(hints_file, song_number_file)
196+ except FileNotFoundError:
197+ hints_available = False
198+
199+ try:
200+ for line in file:
201+ line_number += 1
202+ # Strip out leftover formatting (\i and \b)
203+ line = line.replace('\\i', '')
204+ line = line.replace('\\b', '')
205+ if hints_available and str(line_number) in self.hint_line:
206+ hint = self.hint_line[str(line_number)]
207+ # Set to false if this hint does not replace the line
208+ line_replaced = True
209+ if hint == 'Comment':
210+ line.strip()
211+ self.add_comment(line)
212+ continue
213+ elif hint == 'Ignore':
214+ continue
215+ elif hint == 'Author':
216+ # add as a raw author - do not split
217+ line.strip()
218+ self.add_author(line)
219+ line_number += 1
220+ next(file)
221+ continue
222+ elif hint.startswith('VariantVerse'):
223+ vv, hintverse, replace = hint.split(' ', 2)
224+ this_verse = self.verses[int(hintverse) - 1]
225+ this_verse_str = this_verse[1]
226+ new_verse = this_verse_str
227+ # There might be multiple replace pairs separated by |
228+ replaces = replace.split('|')
229+ for rep in replaces:
230+ source_str, dest_str = rep.split('/')
231+ new_verse = new_verse.replace(source_str, dest_str)
232+ self.add_verse(new_verse, 'v')
233+ self.verse_order_list.append('v{}'.format(str(current_verse_number)))
234+ current_verse_number += 1
235+ line_number += 1
236+ next(file)
237+ continue
238+ elif hint == 'AddSpaceAfterSemi':
239+ line = line.replace(';', '; ')
240+ line_replaced = False
241+ # note - do not use contine here as the line should now be processed as normal.
242+ elif hint == 'AddSpaceAfterColon':
243+ line = line.replace(':', ': ')
244+ line_replaced = False
245+ elif hint == 'BlankLine':
246+ line = ' *Blank*'
247+ line_replaced = False
248+ elif hint == 'BoldLine':
249+ # processing of the hint is deferred, but pick it up as a known hint here
250+ line_replaced = False
251+ else:
252+ self.log_error(translate('SongsPlugin.SingingTheFaithImport',
253+ 'File {file})'.format(file=file.name)),
254+ translate('SongsPlugin.SingingTheFaithImport',
255+ 'Unknown hint {hint}').format(hint=hint))
256+ if line_replaced:
257+ return
258+ # STF exported lines have a leading verse number at the start of each verse.
259+ # remove them - note that we want to track the indent as that shows a chorus
260+ # so will deal with that before stripping all leading spaces.
261+ indent = 0
262+ if line.strip():
263+ # One hymn has one line which starts '* 6' at the start of a verse
264+ # Strip this out
265+ if line.startswith('* 6'):
266+ line = line.lstrip('* ')
267+ verse_num_match = re.search(r'^\d+', line)
268+ if verse_num_match:
269+ # Could extract the verse number and check it against the calculated
270+ # verse number - TODO
271+ # verse_num = verse_num_match.group()
272+ line = line.lstrip('0123456789')
273+ indent_match = re.search(r'^\s+', line)
274+ if indent_match:
275+ indent = len(indent_match.group())
276+ # Assuming we have sorted out what is verse and what is chorus, strip lines,
277+ # unless ignoreIndent
278+ if self.hint_ignore_indent:
279+ line = line.rstrip()
280+ else:
281+ line = line.strip()
282+ if line_number == 2:
283+ # note that songs seem to start with a blank line so the title is line 2
284+ # Also we strip blanks from the title, even if ignoring indent.
285+ song_title = line.strip()
286+ # Process possible line formatting hints after the verse number has been removed
287+ if hints_available and str(line_number) in self.hint_line and hint == 'BoldLine':
288+ line = '{{st}}{0}{{/st}}'.format(line)
289+ # Detect the 'Reproduced from Singing the Faith Electronic Words Edition' line
290+ if line.startswith('Reproduced from Singing the Faith Electronic Words Edition'):
291+ song_number_match = re.search(r'\d+', line)
292+ if song_number_match:
293+ song_number = song_number_match.group()
294+ continue
295+ elif indent == 0:
296+ # If the indent is 0 and it contains '(c)' then it is a Copyright line
297+ if '(c)' in line:
298+ copyright = line
299+ continue
300+ elif (line.startswith('Liturgical ') or line.startswith('From The ') or
301+ line.startswith('From Common ') or line.startswith('Based on Psalm ')):
302+ self.add_comment(line)
303+ continue
304+ # If indent is 0 it may be the author, unless it was one of the cases covered above
305+ elif len(line) > 0:
306+ # May have more than one author, separated by ' and '
307+ authors = line.split(' and ')
308+ for a in authors:
309+ self.parse_author(a)
310+ continue
311+ # If a blank line has bee replaced by *Blank* then put it back to being
312+ # a simple space since this is past stripping blanks
313+ if '*Blank*' in line:
314+ line = ' '
315+ if line == '':
316+ if current_verse != '':
317+ self.add_verse(current_verse, current_verse_type)
318+ self.verse_order_list.append(current_verse_type + str(current_verse_number))
319+ if current_verse_type == 'c':
320+ chorus_written = True
321+ else:
322+ current_verse_number += 1
323+ current_verse = ''
324+ if chorus_written:
325+ current_verse_type = 'v'
326+ else:
327+ # If the line is indented more than or equal chorus_indent then assume it is a chorus
328+ # If the indent has just changed then start a new verse just like hitting a blank line
329+ if not self.hint_ignore_indent and ((indent >= chorus_indent) and (old_indent < indent)):
330+ if current_verse != '':
331+ self.add_verse(current_verse, current_verse_type)
332+ self.verse_order_list.append(current_verse_type + str(current_verse_number))
333+ if current_verse_type == 'v':
334+ current_verse_number += 1
335+ current_verse = line
336+ current_verse_type = 'c'
337+ old_indent = indent
338+ chorus_written = False
339+ has_chorus = True
340+ continue
341+ if current_verse == '':
342+ current_verse += line
343+ else:
344+ current_verse += '\n' + line
345+ old_indent = indent
346+ except Exception as e:
347+ self.log_error(translate('SongsPlugin.SingingTheFaithImport', 'File {file}').format(file=file.name),
348+ translate('SongsPlugin.SingingTheFaithImport', 'Error: {error}').format(error=e))
349+ return
350+
351+ if self.hint_song_title:
352+ song_title = self.hint_song_title
353+ self.title = '{}STF{} - {title}'.format(check_flag, song_number.zfill(3), title=song_title)
354+ self.song_book_name = 'Singing The Faith'
355+ self.song_number = song_number
356+ self.ccli_number = ccli
357+ self.add_copyright(copyright)
358+ # If we have a chorus then the generated Verse order can not be used directly, but we can generate
359+ # one for two special cases - Verse followed by one chorus (to be repeated after every verse)
360+ # of Chorus, followed by verses. If hints for ManualCheck or VerseOrder are supplied ignore this
361+ if has_chorus and not self.hint_verse_order and not self.checks_needed:
362+ auto_verse_order_ok = False
363+ # Popular case V1 C2 V2 ...
364+ if self.verse_order_list: # protect against odd cases
365+ if self.verse_order_list[0] == 'v1' and self.verse_order_list[1] == 'c2':
366+ new_verse_order_list = ['v1', 'c1']
367+ i = 2
368+ auto_verse_order_ok = True
369+ elif self.verse_order_list[0] == 'c1' and self.verse_order_list[1] == 'v1':
370+ new_verse_order_list = ['c1', 'v1', 'c1']
371+ i = 2
372+ auto_verse_order_ok = True
373+ # if we are in a case we can deal with
374+ if auto_verse_order_ok:
375+ while i < len(self.verse_order_list):
376+ if self.verse_order_list[i].startswith('v'):
377+ new_verse_order_list.append(self.verse_order_list[i])
378+ new_verse_order_list.append('c1')
379+ else:
380+ auto_verse_order_ok = False
381+ self.add_comment('Importer detected unexpected verse order entry {}'.format(
382+ self.verse_order_list[i]))
383+ i += 1
384+ self.verse_order_list = new_verse_order_list
385+ else:
386+ if not auto_verse_order_ok:
387+ self.verse_order_list = []
388+ if self.hint_verse_order:
389+ self.verse_order_list = self.hint_verse_order.split(',')
390+ if self.hint_comments:
391+ self.add_comment(self.hint_comments)
392+ if self.hint_ccli:
393+ self.ccli_number = self.hint_ccli
394+ # Write the title last as by now we will know if we need checks
395+ if hints_available and not self.checks_needed:
396+ check_flag = ''
397+ elif not hints_available and not has_chorus:
398+ check_flag = ''
399+ elif not hints_available and has_chorus and auto_verse_order_ok:
400+ check_flag = ''
401+ if self.hint_songbook_number_in_title:
402+ self.title = '{}STF{} - {title}'.format(check_flag, song_number.zfill(3), title=song_title)
403+ else:
404+ self.title = '{}{title}'.format(check_flag, title=song_title)
405+ if not self.finish():
406+ self.log_error(file.name)
407+
408+ def read_hints(self, file, song_number):
409+ """
410+ Read the hints used to transform a particular song into version which can be projected,
411+ or improve the transformation process beyond the standard heuristics. Not every song will
412+ have, or need, hints.
413+ """
414+ hintfound = False
415+ self.hint_verse_order = ''
416+ self.hint_line.clear()
417+ self.hint_comments = ''
418+ self.hint_song_title = ''
419+ self.hint_ignore_indent = False
420+ self.hint_ccli = ''
421+ for tl in file:
422+ if not tl.strip():
423+ return hintfound
424+ tagval = tl.split(':')
425+ tag = tagval[0].strip()
426+ val = tagval[1].strip()
427+ if tag == 'Version':
428+ self.hint_file_version = val
429+ continue
430+ elif tag == 'SongbookNumberInTitle':
431+ if val == 'False':
432+ self.hint_songbook_number_in_title = False
433+ else:
434+ self.hint_songbook_number_in_title = True
435+ continue
436+ elif tag == 'Comment':
437+ continue
438+ if (tag == 'Hymn') and (val == song_number):
439+ self.add_comment('Using hints version {}'.format(str(self.hint_file_version)))
440+ hintfound = True
441+ # Assume, unless the hints has ManualCheck that if hinted all will be OK
442+ self.checks_needed = False
443+ for tl in file:
444+ tagval = tl.split(':')
445+ tag = tagval[0].strip()
446+ val = tagval[1].strip()
447+ if tag == 'End':
448+ return hintfound
449+ elif tag == 'CommentsLine':
450+ vals = val.split(',')
451+ for v in vals:
452+ self.hint_line[v] = 'Comment'
453+ elif tag == 'IgnoreLine':
454+ vals = val.split(',')
455+ for v in vals:
456+ self.hint_line[v] = 'Ignore'
457+ elif tag == 'AuthorLine':
458+ vals = val.split(',')
459+ for v in vals:
460+ self.hint_line[v] = 'Author'
461+ elif tag == 'AddSpaceAfterSemi':
462+ vals = val.split(',')
463+ for v in vals:
464+ self.hint_line[v] = 'AddSpaceAfterSemi'
465+ elif tag == 'AddSpaceAfterColon':
466+ vals = val.split(',')
467+ for v in vals:
468+ self.hint_line[v] = 'AddSpaceAfterColon'
469+ elif tag == 'BlankLine':
470+ vals = val.split(',')
471+ for v in vals:
472+ self.hint_line[v] = 'BlankLine'
473+ elif tag == 'BoldLine':
474+ vals = val.split(',')
475+ for v in vals:
476+ self.hint_line[v] = 'BoldLine'
477+ elif tag == 'VerseOrder':
478+ self.hint_verse_order = val
479+ elif tag == 'ManualCheck':
480+ self.checks_needed = True
481+ elif tag == 'IgnoreIndent':
482+ self.hint_ignore_indent = True
483+ elif tag == 'VariantVerse':
484+ vvline = val.split(' ', 1)
485+ self.hint_line[vvline[0].strip()] = 'VariantVerse {}'.format(vvline[1].strip())
486+ elif tag == 'SongTitle':
487+ self.hint_song_title = val
488+ elif tag == 'AddComment':
489+ self.hint_comments += '\n' + val
490+ elif tag == 'CCLI':
491+ self.hint_ccli = val
492+ elif tag == 'Hymn':
493+ self.log_error(file.name, 'Missing End tag in hint for Hymn: {}'.format(song_number))
494+ else:
495+ self.log_error(file.name, 'Unknown tag {} value {}'.format(tag, val))
496+ return hintfound
497
498=== added file 'tests/functional/openlp_plugins/songs/test_singingthefaithimport.py'
499--- tests/functional/openlp_plugins/songs/test_singingthefaithimport.py 1970-01-01 00:00:00 +0000
500+++ tests/functional/openlp_plugins/songs/test_singingthefaithimport.py 2019-08-23 16:32:02 +0000
501@@ -0,0 +1,48 @@
502+# -*- coding: utf-8 -*-
503+# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
504+
505+##########################################################################
506+# OpenLP - Open Source Lyrics Projection #
507+# ---------------------------------------------------------------------- #
508+# Copyright (c) 2008-2019 OpenLP Developers #
509+# ---------------------------------------------------------------------- #
510+# This program is free software: you can redistribute it and/or modify #
511+# it under the terms of the GNU General Public License as published by #
512+# the Free Software Foundation, either version 3 of the License, or #
513+# (at your option) any later version. #
514+# #
515+# This program is distributed in the hope that it will be useful, #
516+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
517+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
518+# GNU General Public License for more details. #
519+# #
520+# You should have received a copy of the GNU General Public License #
521+# along with this program. If not, see <https://www.gnu.org/licenses/>. #
522+##########################################################################
523+"""
524+This module contains tests for the SingingTheFaith song importer.
525+"""
526+from tests.helpers.songfileimport import SongImportTestHelper
527+from tests.utils.constants import RESOURCE_PATH
528+
529+
530+TEST_PATH = RESOURCE_PATH / 'songs' / 'singingthefaith'
531+
532+
533+class TestSingingTheFaithFileImport(SongImportTestHelper):
534+
535+ def __init__(self, *args, **kwargs):
536+ self.importer_class_name = 'SingingTheFaithImport'
537+ self.importer_module_name = 'singingthefaith'
538+ super(TestSingingTheFaithFileImport, self).__init__(*args, **kwargs)
539+
540+ def test_song_import(self):
541+ """
542+ Test that loading a Singing The Faith file works correctly on various files
543+ """
544+ # Single verse
545+ self.file_import([TEST_PATH / 'H1.txt'],
546+ self.load_external_result_data(TEST_PATH / 'STF001.json'))
547+ # Whole song
548+ self.file_import([TEST_PATH / 'H2.txt'],
549+ self.load_external_result_data(TEST_PATH / 'STF002.json'))
550
551=== modified file 'tests/helpers/songfileimport.py'
552--- tests/helpers/songfileimport.py 2019-05-22 06:47:00 +0000
553+++ tests/helpers/songfileimport.py 2019-08-23 16:32:02 +0000
554@@ -123,7 +123,8 @@
555 log.debug("Song copyright imported: %s" % importer.song_number)
556 log.debug("Topics imported: %s" % importer.topics)
557
558- assert importer.title == title, 'title for %s should be "%s"' % (source_file_name, title)
559+ assert importer.title == title, \
560+ 'title for %s should be "%s" and is "%s"' % (source_file_name, title, importer.title)
561 for author in author_calls:
562 if isinstance(author, str):
563 self.mocked_add_author.assert_any_call(author)
564
565=== added directory 'tests/resources/songs/singingthefaith'
566=== added file 'tests/resources/songs/singingthefaith/H1.txt'
567--- tests/resources/songs/singingthefaith/H1.txt 1970-01-01 00:00:00 +0000
568+++ tests/resources/songs/singingthefaith/H1.txt 2019-08-23 16:32:02 +0000
569@@ -0,0 +1,9 @@
570+
571+1 Amazing Grace! how sweet the sound!
572+ That saved a wretch like me!
573+ I once was lost, but now am found;
574+ Was blind, but now I see.
575+
576+John Newton (d. 1807)
577+
578+Reproduced from Singing the Faith Electronic Words Edition, number 1 - or not as this is a hand made test file
579
580=== added file 'tests/resources/songs/singingthefaith/H2.txt'
581--- tests/resources/songs/singingthefaith/H2.txt 1970-01-01 00:00:00 +0000
582+++ tests/resources/songs/singingthefaith/H2.txt 2019-08-23 16:32:02 +0000
583@@ -0,0 +1,30 @@
584+
585+1 Amazing Grace! how sweet the sound!
586+ That saved a wretch like me!
587+ I once was lost, but now am found;
588+ Was blind, but now I see.
589+
590+2 'Twas grace that taught my heart to fear,
591+ And grace my fears relieved.
592+ How precious did that grace appear,
593+ The hour I first believed.
594+
595+3 The Lord has promised good to me,
596+ His Word my hope secures.
597+ He will my shield and portion be
598+ As long as life endures.
599+
600+4 Thro' many dangers, toils and snares
601+ I have already come.
602+ 'Tis grace that brought me safe thus far,
603+ And grace will lead me home.
604+
605+5 When we've been there ten thousand years,
606+ Bright shining as the sun,
607+ We've no less days to sing God's praise,
608+ Than when we first begun.
609+
610+
611+John Newton (d. 1807)
612+
613+Reproduced from Singing the Faith Electronic Words Edition, number 2 - or not as this is a hand made test file
614
615=== added file 'tests/resources/songs/singingthefaith/STF001.json'
616--- tests/resources/songs/singingthefaith/STF001.json 1970-01-01 00:00:00 +0000
617+++ tests/resources/songs/singingthefaith/STF001.json 2019-08-23 16:32:02 +0000
618@@ -0,0 +1,13 @@
619+{
620+ "title": "STF001 - Amazing Grace! how sweet the sound!",
621+ "authors": [
622+ "John Newton (d. 1807)"
623+ ],
624+ "verse_order_list": ["v1"],
625+ "verses": [
626+ [
627+ "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.",
628+ "v"
629+ ]
630+ ]
631+}
632
633=== added file 'tests/resources/songs/singingthefaith/STF002.json'
634--- tests/resources/songs/singingthefaith/STF002.json 1970-01-01 00:00:00 +0000
635+++ tests/resources/songs/singingthefaith/STF002.json 2019-08-23 16:32:02 +0000
636@@ -0,0 +1,29 @@
637+{
638+ "title": "STF002 - Amazing Grace! how sweet the sound!",
639+ "authors": [
640+ "John Newton (d. 1807)"
641+ ],
642+ "verse_order_list": ["v1", "v2", "v3", "v4", "v5"],
643+ "verses": [
644+ [
645+ "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.",
646+ "v"
647+ ],
648+ [
649+ "'Twas grace that taught my heart to fear,\nAnd grace my fears relieved.\nHow precious did that grace appear,\nThe hour I first believed.",
650+ "v"
651+ ],
652+ [
653+ "The Lord has promised good to me,\nHis Word my hope secures.\nHe will my shield and portion be\nAs long as life endures.",
654+ "v"
655+ ],
656+ [
657+ "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.",
658+ "v"
659+ ],
660+ [
661+ "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.",
662+ "v"
663+ ]
664+ ]
665+}