Just a few more inline comments. Diff comments:
> === modified file 'openlp/plugins/songs/lib/importer.py' > --- openlp/plugins/songs/lib/importer.py 2019-04-13 13:00:22 +0000 > +++ openlp/plugins/songs/lib/importer.py 2019-07-28 08:47:32 +0000 > @@ -343,6 +345,15 @@ > 'filter': '{text} (*.pro4 *.pro5 > *.pro6)'.format(text=translate('SongsPlugin.ImportWizardForm', > > 'ProPresenter Song Files')) > }, > + SingingTheFaith: { > + 'class': SingingTheFaithImport, > + 'name': 'SingingTheFaith', > + 'prefix': 'singingTheFaith', > + 'filter': '%s (*.txt)' % > translate('SongsPlugin.ImportWizardForm', 'Singing The Faith Exported Files'), use .format here please > + 'descriptionText': translate('SongsPlugin.ImportWizardForm', > + 'First use Singing The Faith > Electonic edition to export ' > + 'the song(s) in Text format.') > + }, > SongBeamer: { > 'class': SongBeamerImport, > 'name': 'SongBeamer', > > === added file 'openlp/plugins/songs/lib/importers/singingthefaith.py' > --- openlp/plugins/songs/lib/importers/singingthefaith.py 1970-01-01 > 00:00:00 +0000 > +++ openlp/plugins/songs/lib/importers/singingthefaith.py 2019-07-28 > 08:47:32 +0000 > @@ -0,0 +1,409 @@ > +# -*- 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 3 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 > +from pathlib import Path > + > +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 > + """ > + > + def __init__(self, manager, **kwargs): > + """ > + Initialise the class. > + """ > + super(SingingTheFaithImport, self).__init__(manager, **kwargs) > + self.hints_available = False > + self.checks_needed = True > + self.hint_line = {} > + self.hint_file_version = '0' > + self.hint_verse_order = '' > + self.hint_song_title = '' > + self.hint_comments = '' > + self.hint_ccli = '' > + self.hint_ignore_indent = False > + > + 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 file_path in self.import_source: > + if self.stop_import_flag: > + return > + if isinstance(file_path, str): > + song_file = open(file_path, 'rt', encoding='cp1251') is *should* be a path object by here. Were you having issues where it wasn't? > + self.do_import_file(song_file) > + song_file.close() > + else: > + with file_path.open('rt', encoding='cp1251') as song_file: > + self.do_import_file(song_file) > + > + def do_import_file(self, file): > + """ > + Process the SingingTheFaith file - pass in a file-like object, not a > file path. > + """ > + singing_the_faith_version = 1 > + self.set_defaults() > + # Setup variables > + line_number = 0 > + old_indent = 0 > + # The chorus indent is how many spaces the chorus is indented - it > might be 6, > + # but we test for >= and I do not know how consistent to formatting > of the > + # exported songs is. > + chorus_indent = 5 > + song_title = 'STF000 -' I'm not sure about this, we can store the songbook and number in the song. > + song_number = '0' > + ccli = '0' > + current_verse = '' > + current_verse_type = 'v' > + current_verse_number = 1 > + # Potentially we could try to track current chorus number to > automatically handle > + # more than 1 chorus, currently unused. > + # current_chorus_number = 1 > + has_chorus = False > + chorus_written = False > + auto_verse_order_ok = False > + copyright = '' > + # the check_flag is prepended to the title, removed if the import > should be OK > + # all the songs which need manual editing should sort below all the > OK songs > + check_flag = 'z' > + > + self.add_comment('Imported with Singing The Faith Importer v ' + > str(singing_the_faith_version)) use format here please: 'Imported with Singing The Faith Importer v{no}'.format(no=singing_the_faith_version)) > + > + # Get the file_song_number - so we can use it for hints > + filename = Path(file.name) > + song_number_file = filename.stem > + song_number_match = re.search(r'\d+', song_number_file) > + if song_number_match: > + song_number_file = song_number_match.group() > + > + # See if there is a hints file in the same location as the file > + dir_path = filename.parent > + hints_file_path = dir_path / 'hints.tag' > + try: > + with hints_file_path.open('r') as hints_file: > + hints_available = self.read_hints(hints_file, > song_number_file) > + except FileNotFoundError: > + hints_available = False > + > + try: > + for line in file: > + line_number += 1 > + # Strip out leftover formatting (\i and \b) > + line = line.replace('\\i', '') > + line = line.replace('\\b', '') > + if hints_available and str(line_number) in self.hint_line: > + hint = self.hint_line[str(line_number)] > + # Set to false if this hint does not replace the line > + line_replaced = True > + if hint == 'Comment': > + line.strip() > + self.add_comment(line) > + continue > + elif hint == 'Ignore': > + 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'): > + 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{}'.format(str(current_verse_number))) > + current_verse_number += 1 > + line_number += 1 > + next(file) > + continue > + elif hint == 'AddSpaceAfterSemi': > + line = line.replace(';', '; ') > + line_replaced = False > + # note - do not use contine here as the line should > now be processed as normal. > + elif hint == 'AddSpaceAfterColon': > + line = line.replace(':', ': ') > + line_replaced = False > + elif hint == 'BlankLine': > + line = ' *Blank*' > + line_replaced = False > + elif hint == 'BoldLine': > + # processing of the hint is deferred, but pick it up > as a known hint here > + line_replaced = False > + else: > + > self.log_error(translate('SongsPlugin.SingingTheFaithImport', > + 'File > {file})'.format(file=file.name)), > + > translate('SongsPlugin.SingingTheFaithImport', > + 'Unknown hint > {hint}').format(hint=hint)) > + if line_replaced: > + 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 stripping all leading spaces. > + indent = 0 > + if line.strip(): > + # One hymn has one line which starts '* 6' at the start > of a verse > + # Strip this out > + if line.startswith('* 6'): > + line = line.lstrip('* ') > + verse_num_match = re.search(r'^\d+', line) > + if verse_num_match: > + # Could extract the verse number and check it > against the calculated > + # verse number - TODO > + # verse_num = verse_num_match.group() > + line = line.lstrip('0123456789') > + indent_match = re.search(r'^\s+', line) > + if indent_match: > + indent = len(indent_match.group()) > + # Assuming we have sorted out what is verse and what is > chorus, strip lines, > + # unless ignoreIndent > + if self.hint_ignore_indent: > + line = line.rstrip() > + else: > + line = line.strip() > + if line_number == 2: > + # note that songs seem to start with a blank line so the > title is line 2 > + # Also we strip blanks from the title, even if ignoring > indent. > + song_title = line.strip() > + # Process possible line formatting hints after the verse > number has been removed > + if hints_available and str(line_number) in self.hint_line > and hint == 'BoldLine': > + line = '{{st}}{0}{{/st}}'.format(line) > + # 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(r'\d+', line) > + if song_number_match: > + song_number = song_number_match.group() > + continue > + elif indent == 0: > + # If the indent is 0 and it contains '(c)' then it is a > Copyright line > + if '(c)' in line: > + copyright = line > + continue > + elif (line.startswith('Liturgical ') or > line.startswith('From The ') or > + line.startswith('From Common ')): > + self.add_comment(line) > + continue > + # If indent is 0 it may be the author, unless it was one > of the cases covered above > + elif len(line) > 0: > + # May have more than one author, separated by ' and ' > + authors = line.split(' and ') > + for a in authors: > + self.parse_author(a) > + continue > + # If a blank line has bee replaced by *Blank* then put it > back to being > + # a simple space since this is past stripping blanks > + if '*Blank*' in line: > + line = ' ' > + if line == '': > + if 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: > + current_verse_type = 'v' > + else: > + # If the line is indented more than or equal > chorus_indent then assume it is a chorus > + # If the indent has just changed then start a new verse > just like hitting a blank line > + if not self.hint_ignore_indent and ((indent >= > chorus_indent) and (old_indent < indent)): > + if 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 > + 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 {file}').format(file=file.name), > + translate('SongsPlugin.SingingTheFaithImport', > 'Error: {error}').format(error=e)) > + return > + > + if self.hint_song_title: > + song_title = self.hint_song_title > + self.title = '{}STF{} - {title}'.format(check_flag, > song_number.zfill(3), title=song_title) > + self.song_book_name = 'Singing The Faith' > + self.song_number = song_number > + self.ccli_number = ccli > + self.add_copyright(copyright) > + # If we have a chorus then the generated Verse order can not be used > directly, but we can generate > + # one for two special cases - Verse followed by one chorus (to be > repeated after every verse) > + # of Chorus, followed by verses. If hints for ManualCheck or > VerseOrder are supplied ignore this > + if has_chorus and not self.hint_verse_order and not > self.checks_needed: > + auto_verse_order_ok = False > + # Popular case V1 C2 V2 ... > + if self.verse_order_list: # protect against odd cases > + if (self.verse_order_list[0] == 'v1') and > (self.verse_order_list[1] == 'c2'): brackets not required > + new_verse_order_list = ['v1', 'c1'] > + i = 2 > + auto_verse_order_ok = True > + elif (self.verse_order_list[0] == 'c1') and > (self.verse_order_list[1] == 'v1'): brackets not required > + new_verse_order_list = ['c1', 'v1', 'c1'] > + i = 2 > + auto_verse_order_ok = True > + # if we are in a case we can deal with > + if auto_verse_order_ok: > + while i < len(self.verse_order_list): > + 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: > + auto_verse_order_ok = False > + self.add_comment('Importer detected unexpected > verse order entry {}'.format( > + self.verse_order_list[i])) > + i += 1 > + self.verse_order_list = new_verse_order_list > + else: > + if not auto_verse_order_ok: > + self.verse_order_list = [] > + if self.hint_verse_order: > + self.verse_order_list = self.hint_verse_order.split(',') > + if self.hint_comments: > + self.add_comment(self.hint_comments) > + if self.hint_ccli: > + self.ccli_number = self.hint_ccli > + # 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 = '{}STF{} - {title}'.format(check_flag, > song_number.zfill(3), title=song_title) Again, not sure about the STF at the beginning of the title, we have songbooks and numbers for that! > + if not self.finish(): > + self.log_error(file.name) > + > + def read_hints(self, file, song_number): > + """ > + Read the hints used to transform a particular song into version > which can be projected, > + or improve the transformation process beyond the standard > heuristics. Not every song will > + have, or need, hints. > + """ > + hintfound = False > + self.hint_verse_order = '' > + self.hint_line.clear() > + self.hint_comments = '' > + self.hint_song_title = '' > + self.hint_ignore_indent = False > + self.hint_ccli = '' > + for tl in file: > + if not tl.strip(): > + return hintfound > + tagval = tl.split(':') > + tag = tagval[0].strip() > + val = tagval[1].strip() > + if tag == 'Version': > + self.hint_file_version = val > + continue > + if (tag == 'Hymn') and (val == song_number): > + self.add_comment('Using hints version > {}'.format(str(self.hint_file_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.hint_line[v] = 'Comment' > + elif tag == 'IgnoreLine': > + vals = val.split(',') > + for v in vals: > + self.hint_line[v] = 'Ignore' > + elif tag == 'AuthorLine': > + vals = val.split(',') > + for v in vals: > + self.hint_line[v] = 'Author' > + elif tag == 'AddSpaceAfterSemi': > + vals = val.split(',') > + for v in vals: > + self.hint_line[v] = 'AddSpaceAfterSemi' > + elif tag == 'AddSpaceAfterColon': > + vals = val.split(',') > + for v in vals: > + self.hint_line[v] = 'AddSpaceAfterColon' > + elif tag == 'BlankLine': > + vals = val.split(',') > + for v in vals: > + self.hint_line[v] = 'BlankLine' > + elif tag == 'BoldLine': > + vals = val.split(',') > + for v in vals: > + self.hint_line[v] = 'BoldLine' > + elif tag == 'VerseOrder': > + self.hint_verse_order = val > + elif tag == 'ManualCheck': > + self.checks_needed = True > + elif tag == 'IgnoreIndent': > + self.hint_ignore_indent = True > + elif tag == 'VariantVerse': > + vvline = val.split(' ', 1) > + self.hint_line[vvline[0].strip()] = 'VariantVerse > {}'.format(vvline[1].strip()) > + elif tag == 'SongTitle': > + self.hint_song_title = val > + elif tag == 'AddComment': > + self.hint_comments += '\n' + val > + elif tag == 'CCLI': > + self.hint_ccli = val > + elif tag == 'Hymn': > + self.log_error(file.name, 'Missing End tag in hint > for Hymn: {}'.format(song_number)) > + else: > + self.log_error(file.name, 'Unknown tag {} value > {}'.format(tag, val)) > + return hintfound -- https://code.launchpad.net/~john+ubuntu-g/openlp/singingthefaith/+merge/370364 Your team OpenLP Core is subscribed to branch lp:openlp. _______________________________________________ Mailing list: https://launchpad.net/~openlp-core Post to : openlp-core@lists.launchpad.net Unsubscribe : https://launchpad.net/~openlp-core More help : https://help.launchpad.net/ListHelp