diff --git a/openlp/plugins/songs/lib/importer.py b/openlp/plugins/songs/lib/importer.py index eb3d3f8cc..2603f2f23 100644 --- a/openlp/plugins/songs/lib/importer.py +++ b/openlp/plugins/songs/lib/importer.py @@ -42,6 +42,7 @@ from .importers.powerpraise import PowerPraiseImport from .importers.powersong import PowerSongImport from .importers.presentationmanager import PresentationManagerImport from .importers.propresenter import ProPresenterImport +from .importers.singingthefaith import SingingTheFaithImport from .importers.songbeamer import SongBeamerImport from .importers.songpro import SongProImport from .importers.songshowplus import SongShowPlusImport @@ -173,16 +174,17 @@ class SongFormat(object): PowerSong = 16 PresentationManager = 17 ProPresenter = 18 - SongBeamer = 19 - SongPro = 20 - SongShowPlus = 21 - SongsOfFellowship = 22 - SundayPlus = 23 - VideoPsalm = 24 - WordsOfWorship = 25 - WorshipAssistant = 26 - WorshipCenterPro = 27 - ZionWorx = 28 + SingingTheFaith = 19 + SongBeamer = 20 + SongPro = 21 + SongShowPlus = 22 + SongsOfFellowship = 23 + SundayPlus = 24 + VideoPsalm = 25 + WordsOfWorship = 26 + WorshipAssistant = 27 + WorshipCenterPro = 28 + ZionWorx = 29 # Set optional attribute defaults __defaults__ = { @@ -343,6 +345,16 @@ class SongFormat(object): 'filter': '{text} (*.pro4 *.pro5 *.pro6)'.format(text=translate('SongsPlugin.ImportWizardForm', 'ProPresenter Song Files')) }, + SingingTheFaith: { + 'class': SingingTheFaithImport, + 'name': 'SingingTheFaith', + 'prefix': 'singingTheFaith', + 'filter': '{text} (*.txt)'.format(text=translate('SongsPlugin.ImportWizardForm', + 'Singing The Faith Exported Files')), + 'descriptionText': translate('SongsPlugin.ImportWizardForm', + 'First use Singing The Faith Electonic edition to export ' + 'the song(s) in Text format.') + }, SongBeamer: { 'class': SongBeamerImport, 'name': 'SongBeamer', @@ -462,6 +474,7 @@ class SongFormat(object): SongFormat.PowerSong, SongFormat.PresentationManager, SongFormat.ProPresenter, + SongFormat.SingingTheFaith, SongFormat.SongBeamer, SongFormat.SongPro, SongFormat.SongShowPlus, diff --git a/openlp/plugins/songs/lib/importers/singingthefaith-hints.tag b/openlp/plugins/songs/lib/importers/singingthefaith-hints.tag new file mode 100644 index 000000000..f865e0c3b --- /dev/null +++ b/openlp/plugins/songs/lib/importers/singingthefaith-hints.tag @@ -0,0 +1,666 @@ +Tag-STFHints-version: 1.0 +Version: 2 +SongbookNumberInTitle: False +End: +Hymn: 2 +VerseOrder: V1,C1,V2,C1,V3,C1 +End: +Hymn: 8 +AddSpaceAfterColon: 2,11,20,33 +End: +Hymn: 10 +CommentsLine: 17 +End: +Hymn: 11 +CommentsLine: 24 +End: +Hymn: 15 +VerseOrder: V1,C1,V2,C1,C2,C1 +End: +Hymn: 18 +CommentsLine: 16 +End: +Hymn: 19 +CommentsLine: 8 +End: +Hymn: 22 +CommentsLine: 20 +End: +Hymn: 24 +IgnoreLine: 13 +VerseOrder: V1,V2,V1 +End: +Hymn: 26 +VerseOrder: V1,C1,V2,C1,V3,C1,V4,C1,V5,C1 +End: +Hymn: 27 +AddComment: Verse 1 is original Shona +SongTitle: Jesu, tawa pano +IgnoreLine: 2 +CommentsLine: 31 +End: +Hymn: 28 +CommentsLine: 41 +End: +Hymn: 29 +CommentsLine: 18 +End: +Hymn: 30 +CommentsLine: 26 +End: +Hymn: 35 +VerseOrder: V1,C1,V2,C1,V3,C1 +End: +Hymn: 37 +IgnoreLine: 42 +VerseOrder: V1,V2,C1,V3,V4,C1 +End: +Hymn: 38 +ManualCheck: Yes +AddComment: Make all and cantor words Bold tagged for readability +SongTitle: Wa wa wa Emimimo +End: +Hymn: 40 +VariantVerse: 15 1 Blessed be the name of the Lord/Glory to the name of the Lord|blessed be the name/glory to the name +VariantVerse: 17 1 Blessed be the name of the Lord/Holy is the name of the Lord|blessed be the name/holy is the name +VerseOrder: V1,C1,V2,C1,V3,C1 +End: +Hymn: 41 +IgnoreIndent: Yes +IgnoreLine: 35,42 +VerseOrder: V1,V2,V3,V4,V2,V3,V5,V3 +End: +Hymn: 43 +IgnoreIndent: Yes +CommentsLine: 40 +End: +Hymn: 45 +IgnoreIndent: Yes +CommentsLine: 104 +End: +Hymn: 46 +VerseOrder: V1,C1,V2,C1,C2,C1 +End: +Hymn: 48 +VerseOrder: V1,C1,V2,C2 +End: +Hymn: 51 +VerseOrder: V1,C1,V2,C1,V3,C1 +End: +Hymn: 55 +AddSpaceAfterSemi: 15 +End: +Hymn: 60 +CommentsLine: 22 +End: +Hymn: 61 +VerseOrder: C1,V1,C1 +End: +Hymn: 64 +IgnoreLine: 23,25 +VerseOrder: V1,C1,C2,V2,C1,C2,C3 +End: +Hymn: 65 +VerseOrder: V1,C1,V2,C2,V3,C1,V4,C1 +End: +Hymn: 68 +IgnoreLine: 15,31 +VerseOrder: C1,V1,C1,V2,C2,C1 +End: +Hymn: 71 +IgnoreLine: 23 +VerseOrder: V1,C1,V2,C1,V3 +End: +Hymn: 74 +IgnoreIndent: Yes +End: +Hymn: 77 +IgnoreLine: 32 +CommentsLine: 37 +VerseOrder: V1,V2,C1,V3,C1 +End: +Hymn: 78 +VerseOrder: V1,V2,V1,V2,V3 +End: +Hymn: 82 +VerseOrder: V1,C1,V2,C1,V3,C1,V4,C1 +End: +Hymn: 84 +IgnoreIndent: Yes +End: +Hymn: 86 +CommentsLine: 86 +End: +Hymn: 89 +VerseOrder: V1,V2,C1 +End: +Hymn: 92 +IgnoreIndent: Yes +IgnoreLine: 2,18,48 +SongTitle: Think of a world without any flowers +End: +Hymn: 93 +AddSpaceAfterSemi: 9,10,11,12 +IgnoreLine: 21,30,31,32,33 +VerseOrder: V1,C1,V2,C1,V3,C1 +End: +Hymn: 94 +AuthorLine: 24 +VerseOrder: V1,C1,V2,C1,V3,C1 +End: +Hymn: 95 +AddSpaceAfterSemi: 2 +End: +Hymn: 98 +IgnoreLine: 17,19,24 +VerseOrder: V1,C1,V2,C1,C2,C1 +AddComment: C2 is an optional Bridge +End: +Hymn: 100 +VerseOrder: C1,V1,C1,V2,C1,V3,C1,V4,C1 +End: +Hymn: 102 +VerseOrder: V1,C1,V2,C1,V3,C1,V4,C1,V5,C1 +End: +Hymn: 103 +AddSpaceAfterColon: 2,3,11,20 +End: +Hymn: 105 +VerseOrder: C1,V1,C1,V2,C1 +End: +Hymn: 118 +IgnoreLine: 15,21 +CommentsLine: 28 +VerseOrder: V1,C1,C1,V2,C1,V3,C1,C1,C2 +End: +Hymn: 123 +CommentsLine: 40 +End: +Hymn: 140 +IgnoreLine: 14 +VerseOrder: V1,C1,V2,C1 +End: +Hymn: 141 +AddSpaceAfterSemi: 2 +End: +Hymn: 145 +ManualCheck: Yes +AddComment: Make cantor and all bold, and add to all verses +SongTitle: Night has fallen +End: +Hymn: 147 +AddSpaceAfterSemi: 22 +End: +Hymn: 165 +IgnoreLine: 2,11,20,29,38 +CommentsLine: 50 +SongTitle: Advent candles tell their story +End: +Hymn: 166 +CommentsLine: 40 +VerseOrder: V1,C1,V2,C1,V3,C1,V4,C1,V5,C2 +End: +Hymn: 168 +CommentsLine: 30 +End: +Hymn: 170 +VerseOrder: V1,C1,V2,C1,V3,C1,V4,C2 +End: +Hymn: 173 +VerseOrder: V1,C1,V2,C1,V3,C2 +End: +Hymn: 174 +CommentsLine: 41 +End: +Hymn: 175 +IgnoreLine: 22,29 +VerseOrder: V1,C1,V2,C1,C2,C1 +End: +Hymn: 176 +CommentsLine: 26 +ManualCheck: Yes +End: +Hymn: 178 +VerseOrder: V1,C1,V2,C1,V3,C1,V4,C2 +End: +Hymn: 186 +IgnoreIndent: Yes +CommentsLine: 24 +End: +Hymn: 194 +IgnoreIndent: Yes +End: +Hymn: 200 +AddSpaceAfterColon: 2 +CommentsLine: 24 +End: +Hymn: 209 +IgnoreLine: 11,17,23 +VerseOrder: V1,C1,C1,V2,C1,V3,C1 +End: +Hymn: 212 +CommentsLine: 46 +End: +Hymn: 220 +AddSpaceAfterColon: 30 +End: +Hymn: 227 +VerseOrder: V1,V2,V3,V4,V1 +End: +Hymn: 228 +CommentsLine: 49 +End: +Hymn: 234 +CommentsLine: 7 +End: +Hymn: 235 +IgnoreIndent: Yes +End: +Hymn: 240 +CommentsLine: 24 +End: +Hymn: 241 +IgnoreLine: 39 +VerseOrder: V1,C1,V2,C2,C3,C2 +AddComment: is the final chorus a repeat of C1 or C2 ? +ManualCheck: Yes +End: +Hymn: 246 +VerseOrder: V1,C1,V2,C1,V3,C1,V4,C1 +End: +Hymn: 247 +VerseOrder: V1,C1,V2,C1,V3,C1,V4,C1,V5,C1 +End: +Hymn: 248 +AddSpaceAfterSemi: 13 +End: +Hymn: 249 +VerseOrder: C1,V1,C1,V2,C1,V3,C1,V4,C1 +End: +Hymn: 252 +AddSpaceAfterColon: 1,6,9,13,16,20,23,27,30,34 +End: +Hymn: 254 +CommentsLine: 22 +End: +Hymn: 256 +VerseOrder: V1,C1,V2,C1,V3,C1,V4,C1,V5,C1,V6,C2 +End: +Hymn: 258 +IgnoreLine: 14 +VerseOrder: C1,V1,C1 +End: +Hymn: 261 +AddSpaceAfterSemi: 4 +End: +Hymn: 267 +CommentsLine: 33 +End: +Hymn: 274 +IgnoreLine: 21,30 +VerseOrder: V1,C1,V2,C1,C2,C1 +End: +Hymn: 279 +IgnoreLine: 35 +VerseOrder: V1,V2,C1,V3,V4,C1 +End: +Hymn: 285 +CommentsLine: 25 +End: +Hymn: 298 +IgnoreIndent: Yes +AddSpaceAfterSemi: 8 +End: +Hymn: 299 +IgnoreLine: 26 +CommentsLine: 32 +VerseOrder: V1,C1,V2,V3,V4,C1,C2 +End: +Hymn: 300 +AddSpaceAfterSemi: 8,9,13,17,18 +AddSpaceAfterColon: 7,15,20,25,30 +End: +Hymn: 302 +VerseOrder: C1,V1,C1,V2,C1,V3,C1,V4,C1,V5,C1 +End: +Hymn: 316 +VerseOrder: V1,C1,V2,C1,V3,C1,V4,C1,V5,C1,V6,C1,V7,C2 +End: +Hymn: 321 +CommentsLine: 15 +End: +Hymn: 323 +AddSpaceAfterSemi: 11 +End: +Hymn: 331 +VerseOrder: V1,C1,V2,C2 +End: +Hymn: 335 +VerseOrder: V1,C1,V2,C1,V3,C1,V4,C1,V5,C2 +End: +Hymn: 343 +IgnoreLine: 24 +VerseOrder: V1,V2,C1,V3,C1 +End: +Hymn: 349 +AddSpaceAfterSemi: 20,24 +IgnoreLine: 17 +AddComment: The refrain is optional +VerseOrder: V1,V2,V3,C1,V4,V3 +End: +Hymn: 351 +CCLI: 3350395 +End: +Hymn: 353 +AddSpaceAfterSemi: 15 +End: +Hymn: 364 +AddSpaceAfterSemi: 22 +End: +Hymn: 367 +IgnoreLine: 28 +VerseOrder: V1,C1,V2,C1 +End: +Hymn: 373 +CommentsLine: 26 +End: +Hymn: 374 +ManualCheck: Yes +CommentsLine: 24 +End: +Hymn: 377 +VerseOrder: V1,C1,V2,C1,V3,C1 +End: +Hymn: 380 +VerseOrder: V1,C1,V2,C1 +End: +Hymn: 386 +BlankLine: 6,16 +IgnoreLine: 19,26 +VerseOrder: V1,C1,V2,C1,C2,C1 +End: +Hymn: 389 +AuthorLine: 21 +End: +Hymn: 401 +AddSpaceAfterSemi: 27 +End: +Hymn: 403 +AddSpaceAfterColon: 2 +End: +Hymn: 404 +SongTitle: Go tell everyone +VerseOrder: V1,C1,V2,C1,V3,C1,V4,C1 +End: +Hymn: 405 +VerseOrder: V1,C1,V2,C1,V3,C1 +End: +Hymn: 406 +IgnoreLine: 18,27 +End: +Hymn: 407 +IgnoreLine: 31 +VerseOrder: V1,V2,C1,V3,C1 +End: +Hymn: 408 +IgnoreIndent: Yes +CommentsLine: 25 +End: +Hymn: 410 +AddSpaceAfterSemi: 15,16 +End: +Hymn: 419 +CommentsLine: 25 +End: +Hymn: 420 +IgnoreIndent: Yes +End: +Hymn: 421 +IgnoreIndent: Yes +CommentsLine: 55 +End: +Hymn: 424 +IgnoreLine: 16 +VerseOrder: V1,C1,V2,C1 +End: +Hymn: 428 +IgnoreLine: 25 +VerseOrder: V1,V2,C1,V3,C1 +End: +Hymn: 432 +AddSpaceAfterSemi: 8 +End: +Hymn: 433 +CommentsLine: 44 +End: +Hymn: 447 +IgnoreLine: 19,21 +VerseOrder: V1,V2,C1,V3,C1,V1 +End: +Hymn: 451 +IgnoreLine: 14,16 +VerseOrder: C1,V1,C1,V1,V2 +End: +Hymn: 454 +CommentsLine: 2,42 +SongTitle: Where shall my wondering soul begin +End: +Hymn: 458 +CommentsLine: 2 +SongTitle: Away with our fears The glad morning appears +End: +Hymn: 469 +ManualCheck: Yes +AddComment: Need a VariantChorus, Chorus2 and Chorus3 are Variants of Chorus1 +VerseOrder: V1,C1,V2,C2,V3,C2,V4,C3 +End: +Hymn: 470 +IgnoreIndent: Yes +End: +Hymn: 476 +VerseOrder: V1,C1,V2,C1,V3,C1,V4,C1,V5,C1 +End: +Hymn: 477 +VerseOrder: C1,V1,C1,V2,C1 +End: +Hymn: 480 +CommentsLine: 28 +End: +Hymn: 483 +AddComment: Verses 4 and 5 are the +CommentsLine: 29 +End: +Hymn: 488 +IgnoreLine: 25 +VerseOrder: V1,V2,C1,V3,C1 +End: +Hymn: 492 +AddSpaceAfterSemi: 9,10,15 +End: +Hymn: 499 +CommentsLine: 37 +End: +Hymn: 509 +IgnoreIndent: Yes +CommentsLine: 40 +End: +Hymn: 517 +VerseOrder: V1,C1,V2,C1,V3,C1,V4,C2 +End: +Hymn: 528 +CommentsLine: 32 +End: +Hymn: 541 +IgnoreLine: 17,18,25 +VerseOrder: V1,V2,C1,V1,V2,V3,C1 +End: +Hymn: 545 +CommentsLine: 28 +End: +Hymn: 548 +AuthorLine: 22 +End: +Hymn: 554 +IgnoreLine: 15,21 +VerseOrder: V1,C1,V2,C1,V3,C1,V4 +End: +Hymn: 555 +IgnoreLine: 15 +VerseOrder: V1,V2,C1,V1 +End: +Hymn: 559 +IgnoreIndent: Yes +End: +Hymn: 561 +CommentsLine: 45 +End: +Hymn: 562 +CommentsLine: 38 +End: +Hymn: 565 +IgnoreLine: 21 +VerseOrder: V1,V2,V1 +End: +Hymn: 566 +AddSpaceAfterSemi: 27 +End: +Hymn: 567 +ManualCheck: Yes +AddComment: Check with musician what should be on the screen +End: +Hymn: 570 +VerseOrder: V1,C1,V2,C1,V3,C1 +End: +Hymn: 575 +CommentsLine: 30 +End: +Hymn: 578 +IgnoreLine: 11,18,25 +VerseOrder: C1,V1,C1,V2,C1,V3,C1 +End: +Hymn: 586 +IgnoreLine: 22 +VerseOrder: V1,V2,C1,V3,C1 +End: +Hymn: 587 +VerseOrder: V1,C1,V2,C2,V3 +End: +Hymn: 582 +CommentsLine: 25 +End: +Hymn: 594 +CommentsLine: 41 +End: +Hymn: 601 +CommentsLine: 16 +End: +Hymn: 603 +ManualCheck: Yes +AddComment: Update Verse 4 for names of the couple +CommentsLine: 22 +IgnoreIndent: Yes +End: +Hymn: 609 +CommentsLine: 37 +End: +Hymn: 610 +CommentsLine: 29 +End: +Hymn: 626 +IgnoreLine: 29,31,44 +VerseOrder: V1,C1,C2,V2,C1,C2,C3,C2 +End: +Hymn: 631 +CommentsLine: 15 +End: +Hymn: 627 +IgnoreLine: 25,36 +VerseOrder: V1,C1,V2,C1,C2,C1 +End: +Hymn: 632 +IgnoreLine: 17,24 +VerseOrder: V1,C1,V2,C1,C2,C1 +End: +Hymn: 635 +IgnoreLine: 23,25 +VerseOrder: V1,C1,C2,V2,C1,C2 +End: +Hymn: 637 +CommentsLine: 41 +End: +Hymn: 638 +CommentsLine: 34 +End: +Hymn: 640 +CommentsLine: 19,20 +End: +Hymn: 654 +IgnoreIndent: Yes +End: +Hymn: 657 +IgnoreLine: 20,21,23 +VerseOrder: V1,C1,C2,V2,C1,C2,C3 +End: +Hymn: 662 +AddSpaceAfterSemi: 2,12 +End: +Hymn: 670 +IgnoreLine: 27 +VerseOrder: V1,C1,V2,C1 +End: +Hymn: 677 +CommentsLine: 31 +End: +Hymn: 681 +IgnoreIndent: Yes +End: +Hymn: 684 +IgnoreIndent: Yes +End: +Hymn: 693 +IgnoreLine: 46,57 +VerseOrder: V1,V2,C1,V3,V4,C1,V5,C1 +End: +Hymn: 697 +IgnoreIndent: Yes +CommentsLine: 21 +End: +Hymn: 699 +IgnoreLine: 23,32 +VerseOrder: V1,C1,V2,C1,C1,C2,C1 +End: +Hymn: 700 +IgnoreIndent: Yes +End: +Hymn: 707 +IgnoreLine: 15 +VerseOrder: V1,C1,V2,C1,V3 +End: +Hymn: 729 +IgnoreIndent: Yes +End: +Hymn: 741 +CommentsLine: 25 +End: +Hymn: 753 +CommentsLine: 14 +IgnoreIndent: Yes +End: +Hymn: 754 +IgnoreIndent: Yes +End: +Hymn: 764 +IgnoreIndent: Yes +CommentsLine: 31 +End: +Hymn: 783 +BlankLine: 5 +End: +Hymn: 819 +AddSpaceAfterSemi: 32 +End: +Hymn: 820 +CommentsLine: 2 +BoldLine: 6,7,12,13,17,18,23,24,29,30,32,33,34,35 +SongTitle: Psalm 98 - O sing to the Lord a new song +End: diff --git a/openlp/plugins/songs/lib/importers/singingthefaith.py b/openlp/plugins/songs/lib/importers/singingthefaith.py new file mode 100644 index 000000000..8c5a86c10 --- /dev/null +++ b/openlp/plugins/songs/lib/importers/singingthefaith.py @@ -0,0 +1,437 @@ +# -*- 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 +from openlp.core.common.applocation import AppLocation + + +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 + self.hint_songbook_number_in_title = 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 this is backported to version 2.4 then do_import is called with a filename + # rather than a path object if called from the development version. + # Check here to minimise differences between versions. + if isinstance(file_path, str): + song_file = open(file_path, 'rt', encoding='cp1251') + 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. + """ + hints_file_name = 'singingthefaith-hints.tag' + 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 + # Initialise the song title - the format of the title finally produced can be affected + # by the SongbookNumberInTitle option in the hints file + song_title = 'STF000 -' + 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{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_file_name + try: + with hints_file_path.open('r') as hints_file: + hints_available = self.read_hints(hints_file, song_number_file) + except FileNotFoundError: + # Look for the hints file in the Plugins directory + hints_file_path = AppLocation.get_directory(AppLocation.PluginsDir) / 'songs' / 'lib' / \ + 'importers' / hints_file_name + 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 + line.strip() + self.add_author(line) + 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 ') or line.startswith('Based on Psalm ')): + 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': + 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': + 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 = '' + if self.hint_songbook_number_in_title: + self.title = '{}STF{} - {title}'.format(check_flag, song_number.zfill(3), title=song_title) + else: + self.title = '{}{title}'.format(check_flag, title=song_title) + 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 + elif tag == 'SongbookNumberInTitle': + if val == 'False': + self.hint_songbook_number_in_title = False + else: + self.hint_songbook_number_in_title = True + continue + elif tag == 'Comment': + 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 diff --git a/tests/functional/openlp_plugins/songs/test_singingthefaithimport.py b/tests/functional/openlp_plugins/songs/test_singingthefaithimport.py new file mode 100644 index 000000000..70aa39544 --- /dev/null +++ b/tests/functional/openlp_plugins/songs/test_singingthefaithimport.py @@ -0,0 +1,63 @@ +# -*- 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 . # +########################################################################## +""" +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 + """ + # Note that the previous tests without hints no longer apply as there is always a + # hints file, which contains the hints for the real Singing The Faith Songs. + # Unhinted songs here must be numbered above any real Singing The Faith Song + # Single verse + self.file_import([TEST_PATH / 'H901.txt'], + self.load_external_result_data(TEST_PATH / 'STF901.json')) + # Whole song + self.file_import([TEST_PATH / 'H902.txt'], + self.load_external_result_data(TEST_PATH / 'STF902.json')) + + # Tests with hints - note that the hints directory has a hints.tag which specifies + # SongbookNumberInTitle: True + # The default is false, so the unhinted tests will not have the title, but the hinted + # song tests will need it + + # Single verse + self.file_import([TEST_PATH / 'hints' / 'H1.txt'], + self.load_external_result_data(TEST_PATH / 'hints' / 'STF001.json')) + # Whole song + self.file_import([TEST_PATH / 'hints' / 'H2.txt'], + self.load_external_result_data(TEST_PATH / 'hints' / 'STF002.json')) diff --git a/tests/helpers/songfileimport.py b/tests/helpers/songfileimport.py index 8f0f338b2..542cc1860 100644 --- a/tests/helpers/songfileimport.py +++ b/tests/helpers/songfileimport.py @@ -123,7 +123,8 @@ class SongImportTestHelper(TestCase): log.debug("Song copyright imported: %s" % importer.song_number) log.debug("Topics imported: %s" % importer.topics) - assert importer.title == title, 'title for %s should be "%s"' % (source_file_name, title) + assert importer.title == title, \ + 'title for %s should be "%s" and is "%s"' % (source_file_name, title, importer.title) for author in author_calls: if isinstance(author, str): self.mocked_add_author.assert_any_call(author) @@ -152,7 +153,8 @@ class SongImportTestHelper(TestCase): 'song_number for %s should be %s' % (source_file_name, song_number) if verse_order_list: assert importer.verse_order_list == verse_order_list, \ - 'verse_order_list for %s should be %s' % (source_file_name, verse_order_list) + 'verse_order_list for %s should be %s and is %s' % (source_file_name, + verse_order_list, importer.verse_order_list) self.mocked_finish.assert_called_with() def _get_data(self, data, key): diff --git a/tests/resources/songs/singingthefaith/H901.txt b/tests/resources/songs/singingthefaith/H901.txt new file mode 100644 index 000000000..877bbfa1b --- /dev/null +++ b/tests/resources/songs/singingthefaith/H901.txt @@ -0,0 +1,9 @@ + +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. + +John Newton (d. 1807) + +Reproduced from Singing the Faith Electronic Words Edition, number 901 - or not as this is a hand made test file diff --git a/tests/resources/songs/singingthefaith/H902.txt b/tests/resources/songs/singingthefaith/H902.txt new file mode 100644 index 000000000..3066293e1 --- /dev/null +++ b/tests/resources/songs/singingthefaith/H902.txt @@ -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 2 - or not as this is a hand made test file diff --git a/tests/resources/songs/singingthefaith/STF901.json b/tests/resources/songs/singingthefaith/STF901.json new file mode 100644 index 000000000..02125d303 --- /dev/null +++ b/tests/resources/songs/singingthefaith/STF901.json @@ -0,0 +1,13 @@ +{ + "title": "Amazing Grace! how sweet the sound!", + "authors": [ + "John Newton (d. 1807)" + ], + "verse_order_list": ["v1"], + "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.", + "v" + ] + ] +} diff --git a/tests/resources/songs/singingthefaith/STF902.json b/tests/resources/songs/singingthefaith/STF902.json new file mode 100644 index 000000000..f78d23a18 --- /dev/null +++ b/tests/resources/songs/singingthefaith/STF902.json @@ -0,0 +1,29 @@ +{ + "title": "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.", + "v" + ], + [ + "'Twas grace that taught my heart to fear,\nAnd grace my fears relieved.\nHow precious did that grace appear,\nThe hour I first believed.", + "v" + ], + [ + "The Lord has promised good to me,\nHis Word my hope secures.\nHe will my shield and portion be\nAs long as life endures.", + "v" + ], + [ + "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.", + "v" + ], + [ + "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.", + "v" + ] + ] +} diff --git a/tests/resources/songs/singingthefaith/hints/H1.txt b/tests/resources/songs/singingthefaith/hints/H1.txt new file mode 100644 index 000000000..3caf1cd11 --- /dev/null +++ b/tests/resources/songs/singingthefaith/hints/H1.txt @@ -0,0 +1,9 @@ + +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. + +John Newton (d. 1807) + +Reproduced from Singing the Faith Electronic Words Edition, number 1 - or not as this is a hand made test file diff --git a/tests/resources/songs/singingthefaith/hints/H2.txt b/tests/resources/songs/singingthefaith/hints/H2.txt new file mode 100644 index 000000000..3066293e1 --- /dev/null +++ b/tests/resources/songs/singingthefaith/hints/H2.txt @@ -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 2 - or not as this is a hand made test file diff --git a/tests/resources/songs/singingthefaith/hints/STF001.json b/tests/resources/songs/singingthefaith/hints/STF001.json new file mode 100644 index 000000000..edf3ee682 --- /dev/null +++ b/tests/resources/songs/singingthefaith/hints/STF001.json @@ -0,0 +1,13 @@ +{ + "title": "STF001 - Amazing Grace! how sweet the sound!", + "authors": [ + "John Newton (d. 1807)" + ], + "verse_order_list": ["v1"], + "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.", + "v" + ] + ] +} diff --git a/tests/resources/songs/singingthefaith/hints/STF002.json b/tests/resources/songs/singingthefaith/hints/STF002.json new file mode 100644 index 000000000..6bfc655bf --- /dev/null +++ b/tests/resources/songs/singingthefaith/hints/STF002.json @@ -0,0 +1,29 @@ +{ + "title": "STF002 - 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.", + "v" + ], + [ + "'Twas grace that taught my heart to fear,\nAnd grace my fears relieved.\nHow precious did that grace appear,\nThe hour I first believed.", + "v" + ], + [ + "The Lord has promised good to me,\nHis Word my hope secures.\nHe will my shield and portion be\nAs long as life endures.", + "v" + ], + [ + "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.", + "v" + ], + [ + "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.", + "v" + ] + ] +} diff --git a/tests/resources/songs/singingthefaith/hints/singingthefaith-hints.tag b/tests/resources/songs/singingthefaith/hints/singingthefaith-hints.tag new file mode 100644 index 000000000..d3f791d1e --- /dev/null +++ b/tests/resources/songs/singingthefaith/hints/singingthefaith-hints.tag @@ -0,0 +1,5 @@ +Tag-STFHints-version: 1.0 +Version: 2 +SongbookNumberInTitle: True +End: +