forked from openlp/openlp
LiveWorship importer
This commit is contained in:
parent
efbadec78d
commit
1e8f72d49f
@ -222,6 +222,16 @@ def is_linux():
|
|||||||
return sys.platform.startswith('linux')
|
return sys.platform.startswith('linux')
|
||||||
|
|
||||||
|
|
||||||
|
def is_64bit_instance():
|
||||||
|
"""
|
||||||
|
Returns true if the python/OpenLP instance running is 64 bit. If running a 32 bit instance on
|
||||||
|
a 64 bit system this will return false.
|
||||||
|
|
||||||
|
:return: True if the python/OpenLP instance running is 64 bit, otherwise False.
|
||||||
|
"""
|
||||||
|
return (sys.maxsize > 2**32)
|
||||||
|
|
||||||
|
|
||||||
def verify_ipv4(addr):
|
def verify_ipv4(addr):
|
||||||
"""
|
"""
|
||||||
Validate an IPv4 address
|
Validate an IPv4 address
|
||||||
|
@ -34,6 +34,7 @@ from .importers.dreambeam import DreamBeamImport
|
|||||||
from .importers.easyslides import EasySlidesImport
|
from .importers.easyslides import EasySlidesImport
|
||||||
from .importers.easyworship import EasyWorshipSongImport
|
from .importers.easyworship import EasyWorshipSongImport
|
||||||
from .importers.foilpresenter import FoilPresenterImport
|
from .importers.foilpresenter import FoilPresenterImport
|
||||||
|
from .importers.liveworship import LiveWorshipImport
|
||||||
from .importers.lyrix import LyrixImport
|
from .importers.lyrix import LyrixImport
|
||||||
from .importers.openlp import OpenLPSongImport
|
from .importers.openlp import OpenLPSongImport
|
||||||
from .importers.openlyrics import OpenLyricsImport
|
from .importers.openlyrics import OpenLyricsImport
|
||||||
@ -166,25 +167,26 @@ class SongFormat(object):
|
|||||||
EasyWorshipSqliteDB = 8
|
EasyWorshipSqliteDB = 8
|
||||||
EasyWorshipService = 9
|
EasyWorshipService = 9
|
||||||
FoilPresenter = 10
|
FoilPresenter = 10
|
||||||
Lyrix = 11
|
LiveWorship = 11
|
||||||
MediaShout = 12
|
Lyrix = 12
|
||||||
OpenSong = 13
|
MediaShout = 13
|
||||||
OPSPro = 14
|
OpenSong = 14
|
||||||
PowerPraise = 15
|
OPSPro = 15
|
||||||
PowerSong = 16
|
PowerPraise = 16
|
||||||
PresentationManager = 17
|
PowerSong = 17
|
||||||
ProPresenter = 18
|
PresentationManager = 18
|
||||||
SingingTheFaith = 19
|
ProPresenter = 19
|
||||||
SongBeamer = 20
|
SingingTheFaith = 20
|
||||||
SongPro = 21
|
SongBeamer = 21
|
||||||
SongShowPlus = 22
|
SongPro = 22
|
||||||
SongsOfFellowship = 23
|
SongShowPlus = 23
|
||||||
SundayPlus = 24
|
SongsOfFellowship = 24
|
||||||
VideoPsalm = 25
|
SundayPlus = 25
|
||||||
WordsOfWorship = 26
|
VideoPsalm = 26
|
||||||
WorshipAssistant = 27
|
WordsOfWorship = 27
|
||||||
WorshipCenterPro = 28
|
WorshipAssistant = 28
|
||||||
ZionWorx = 29
|
WorshipCenterPro = 29
|
||||||
|
ZionWorx = 30
|
||||||
|
|
||||||
# Set optional attribute defaults
|
# Set optional attribute defaults
|
||||||
__defaults__ = {
|
__defaults__ = {
|
||||||
@ -282,6 +284,14 @@ class SongFormat(object):
|
|||||||
'filter': '{text} (*.foil)'.format(text=translate('SongsPlugin.ImportWizardForm',
|
'filter': '{text} (*.foil)'.format(text=translate('SongsPlugin.ImportWizardForm',
|
||||||
'Foilpresenter Song Files'))
|
'Foilpresenter Song Files'))
|
||||||
},
|
},
|
||||||
|
LiveWorship: {
|
||||||
|
'class': LiveWorshipImport,
|
||||||
|
'name': 'LiveWorship Database',
|
||||||
|
'prefix': 'liveWorship',
|
||||||
|
'selectMode': SongFormatSelect.SingleFile,
|
||||||
|
'filter': '{text} (*.vdb)'.format(text=translate('SongsPlugin.ImportWizardForm',
|
||||||
|
'LiveWorship Database'))
|
||||||
|
},
|
||||||
Lyrix: {
|
Lyrix: {
|
||||||
'class': LyrixImport,
|
'class': LyrixImport,
|
||||||
'name': 'LyriX',
|
'name': 'LyriX',
|
||||||
@ -466,6 +476,7 @@ class SongFormat(object):
|
|||||||
SongFormat.EasyWorshipSqliteDB,
|
SongFormat.EasyWorshipSqliteDB,
|
||||||
SongFormat.EasyWorshipService,
|
SongFormat.EasyWorshipService,
|
||||||
SongFormat.FoilPresenter,
|
SongFormat.FoilPresenter,
|
||||||
|
SongFormat.LiveWorship,
|
||||||
SongFormat.Lyrix,
|
SongFormat.Lyrix,
|
||||||
SongFormat.MediaShout,
|
SongFormat.MediaShout,
|
||||||
SongFormat.OpenSong,
|
SongFormat.OpenSong,
|
||||||
|
287
openlp/plugins/songs/lib/importers/liveworship.py
Normal file
287
openlp/plugins/songs/lib/importers/liveworship.py
Normal file
@ -0,0 +1,287 @@
|
|||||||
|
# -*- 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/>. #
|
||||||
|
##########################################################################
|
||||||
|
"""
|
||||||
|
The :mod:`liveworship` module provides the functionality for importing
|
||||||
|
a LiveWorship database into the OpenLP database.
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
import ctypes
|
||||||
|
|
||||||
|
from lxml import etree
|
||||||
|
from pathlib import Path
|
||||||
|
from tempfile import gettempdir
|
||||||
|
|
||||||
|
from openlp.core.common import is_win, is_linux, is_macosx, is_64bit_instance, delete_file
|
||||||
|
from openlp.core.common.i18n import translate
|
||||||
|
from openlp.core.lib.ui import critical_error_message_box
|
||||||
|
from openlp.plugins.songs.lib.importers.songimport import SongImport
|
||||||
|
from openlp.plugins.songs.lib.ui import SongStrings
|
||||||
|
|
||||||
|
# Copied from VCSDK_Enums.h
|
||||||
|
EVStorageType_kDisk = 1
|
||||||
|
EVDumpType_kSQL = 1
|
||||||
|
EVDumpType_kXML = 2
|
||||||
|
EVDataKind_kStructureAndRecords = 2
|
||||||
|
EVDataKind_kRecordsOnly = 3
|
||||||
|
|
||||||
|
pretty_print = 1
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class LiveWorshipImport(SongImport):
|
||||||
|
"""
|
||||||
|
The :class:`LiveWorshipImport` class provides the ability to import the
|
||||||
|
LiveWorship Valentina Database.
|
||||||
|
|
||||||
|
The approach is to use the Valentina DB ADK for C via ctypes to dump the database content to a XML
|
||||||
|
file that is then analysed for data extraction. It would also be possible to skip the XML and
|
||||||
|
extract the data directly from the database using ctypes, but that approach requires a lot of ctypes
|
||||||
|
interaction and type conversion that is rather fragile across platforms and versions, so the XML approach
|
||||||
|
is used for now.
|
||||||
|
It was implemented using Valentina DB ADK for C version 9.6.
|
||||||
|
"""
|
||||||
|
def __init__(self, manager, **kwargs):
|
||||||
|
"""
|
||||||
|
Initialise the LiveWorship importer.
|
||||||
|
"""
|
||||||
|
self.root = None
|
||||||
|
super(LiveWorshipImport, self).__init__(manager, **kwargs)
|
||||||
|
|
||||||
|
def do_import(self):
|
||||||
|
"""
|
||||||
|
Receive a path to a LiveWorship (valentina) DB.
|
||||||
|
"""
|
||||||
|
self.dump_file = Path(gettempdir()) / 'openlp-liveworship-dump.xml'
|
||||||
|
if not self.dump_valentina_to_xml():
|
||||||
|
return
|
||||||
|
self.load_xml_dump()
|
||||||
|
if self.root is None:
|
||||||
|
return
|
||||||
|
self.extract_songs()
|
||||||
|
delete_file(self.dump_file)
|
||||||
|
|
||||||
|
def dump_valentina_to_xml(self):
|
||||||
|
"""
|
||||||
|
Load the LiveWorship database using the Valentina DB ADK for C and dump the DB content to a XML file.
|
||||||
|
"""
|
||||||
|
self.import_wizard.increment_progress_bar(translate('SongsPlugin.LiveWorshipImport',
|
||||||
|
'Extracting data from database'), 0)
|
||||||
|
# Based on OS and bitness, try to load the dll
|
||||||
|
libVCSDK = None
|
||||||
|
if is_win():
|
||||||
|
# The DLL path must be set depending on the bitness of the OpenLP/Python instance
|
||||||
|
if is_64bit_instance():
|
||||||
|
vcdk_install_folder = 'VCDK_x64_{adkver}'
|
||||||
|
dll_name = '/vcsdk_release_x64.dll'
|
||||||
|
else:
|
||||||
|
vcdk_install_folder = 'VCDK_{adkver}'
|
||||||
|
dll_name = '/vcsdk_release_x86.dll'
|
||||||
|
dll_path = '{pf}\\Paradigma Software\\{vcdk}'.format(pf=os.getenv('PROGRAMFILES'), vcdk=vcdk_install_folder)
|
||||||
|
dll_path2 = '{pf}\\Paradigma Software\\vcomponents_win_vc'.format(pf=os.getenv('PROGRAMFILES'))
|
||||||
|
os.environ['PATH'] = ';'.join([os.environ['PATH'], dll_path, dll_path2])
|
||||||
|
libVCSDK_path = dll_path + dll_name
|
||||||
|
elif is_linux():
|
||||||
|
libVCSDK_path = '/opt/VCSDK/libVCSDK.so'
|
||||||
|
elif is_macosx():
|
||||||
|
# The DLL path must be set depending on the bitness of the OpenLP/Python instance
|
||||||
|
if is_64bit_instance():
|
||||||
|
vcdk_install_folder = 'VCDK_x64_{adkver}'
|
||||||
|
dll_name = '/vcsdk_x64.dylib'
|
||||||
|
else:
|
||||||
|
vcdk_install_folder = 'VCDK_{adkver}'
|
||||||
|
dll_name = '/vcsdk_x86.dylib'
|
||||||
|
libVCSDK_path = '/Users/Shared/Paradigma Software/{folder}/{dll}'.format(folder=vcdk_install_folder,
|
||||||
|
dll=dll_name)
|
||||||
|
# Try to make this somewhat future proof by trying versions 9 to 15
|
||||||
|
found_dll = False
|
||||||
|
if '{adkver}' in libVCSDK_path:
|
||||||
|
for i in range(9, 16):
|
||||||
|
if os.path.exists(libVCSDK_path.format(adkver=i)):
|
||||||
|
found_dll = True
|
||||||
|
libVCSDK_path = libVCSDK_path.format(adkver=i)
|
||||||
|
break
|
||||||
|
if not found_dll:
|
||||||
|
libVCSDK_path = libVCSDK_path.format(adkver=9)
|
||||||
|
elif os.path.exists(libVCSDK_path):
|
||||||
|
found_dll = True
|
||||||
|
if not found_dll:
|
||||||
|
adk_name = "Valentina DB ADK for C, {bitness} bit"
|
||||||
|
if is_64bit_instance():
|
||||||
|
adk_name = adk_name.format(bitness=64)
|
||||||
|
else:
|
||||||
|
adk_name = adk_name.format(bitness=32)
|
||||||
|
critical_error_message_box(translate('SongsPlugin.LiveWorshipImport',
|
||||||
|
'Could not find Valentina DB ADK libraries '),
|
||||||
|
translate('SongsPlugin.LiveWorshipImport',
|
||||||
|
'Could not find "{dllpath}", please install "{adk}"'
|
||||||
|
.format(dllpath=libVCSDK_path, adk=adk_name)))
|
||||||
|
return False
|
||||||
|
libVCSDK = ctypes.CDLL(libVCSDK_path)
|
||||||
|
# cache size set to 1024, got no idea what this means...
|
||||||
|
# serial numbers set to None - only 10 minutes access, should be enough :)
|
||||||
|
libVCSDK.Valentina_Init(1024, None, None, None)
|
||||||
|
# Create a DB instance
|
||||||
|
Database_New = libVCSDK.Database_New
|
||||||
|
Database_New.argtypes = [ctypes.c_int]
|
||||||
|
Database_New.restype = ctypes.c_void_p
|
||||||
|
database = Database_New(EVStorageType_kDisk)
|
||||||
|
database_ptr = ctypes.c_void_p(database)
|
||||||
|
# Load the file into our instance
|
||||||
|
libVCSDK.Database_Open(database_ptr, ctypes.c_char_p(str(self.import_source).encode()))
|
||||||
|
# Dump the database to XML
|
||||||
|
libVCSDK.Database_Dump(database_ptr, ctypes.c_char_p(str(self.dump_file).encode()), EVDumpType_kXML,
|
||||||
|
EVDataKind_kStructureAndRecords, pretty_print, ctypes.c_char_p(b'utf-8'))
|
||||||
|
# Close the DB
|
||||||
|
libVCSDK.Database_Close(database_ptr)
|
||||||
|
# Shutdown Valentina
|
||||||
|
libVCSDK.Valentina_Shutdown()
|
||||||
|
return True
|
||||||
|
|
||||||
|
def load_xml_dump(self):
|
||||||
|
self.import_wizard.increment_progress_bar(translate('SongsPlugin.LiveWorshipImport',
|
||||||
|
'Loading the extracting data'), 0)
|
||||||
|
# The file can contain the EOT control character and certain invalid tags with missing attributewhich
|
||||||
|
# will make lxml fail, so it must be removed.
|
||||||
|
xml_file = open(self.dump_file, 'rt')
|
||||||
|
xml_content = xml_file.read()
|
||||||
|
xml_file.close()
|
||||||
|
xml_content = xml_content.replace('\4', '**EOT**').replace('CustomProperty =""', 'CustomProperty a=""', 1)
|
||||||
|
# Now load the XML
|
||||||
|
parser = etree.XMLParser(remove_blank_text=True, recover=True)
|
||||||
|
try:
|
||||||
|
self.root = etree.fromstring(xml_content, parser)
|
||||||
|
except etree.XMLSyntaxError:
|
||||||
|
self.log_error(self.dump_file, SongStrings.XMLSyntaxError)
|
||||||
|
log.exception('XML syntax error in file {path}'.format(path=str(self.dump_file)))
|
||||||
|
|
||||||
|
def extract_songs(self):
|
||||||
|
"""
|
||||||
|
Extract all the songs from the XML object
|
||||||
|
"""
|
||||||
|
# Find song records
|
||||||
|
xpath = "//BaseObjectData[@Name='SlideCol']/Record/f[@n='Type' and normalize-space(text())='song']/.."
|
||||||
|
song_records = self.root.xpath(xpath)
|
||||||
|
# Song count for progress bar
|
||||||
|
song_count = len(song_records)
|
||||||
|
# set progress bar to songcount
|
||||||
|
self.import_wizard.progress_bar.setMaximum(song_count)
|
||||||
|
for record in song_records:
|
||||||
|
if self.stop_import_flag:
|
||||||
|
break
|
||||||
|
# reset to default values
|
||||||
|
self.set_defaults()
|
||||||
|
# Get song metadata
|
||||||
|
title = record.xpath("f[@n='Title']/text()")
|
||||||
|
song_rowid = record.xpath("f[@n='_rowid']/text()")
|
||||||
|
if title and song_rowid:
|
||||||
|
self.title = self.clean_string(title[0])
|
||||||
|
song_rowid = self.clean_string(song_rowid[0])
|
||||||
|
else:
|
||||||
|
# if no title or no rowid we skip the song
|
||||||
|
continue
|
||||||
|
authors_line = record.xpath("f[@n='Author']/text()")
|
||||||
|
if authors_line:
|
||||||
|
self.extract_authors(authors_line[0])
|
||||||
|
cpr = record.xpath("f[@n='Copyright']/text()")
|
||||||
|
if cpr:
|
||||||
|
self.add_copyright(self.clean_string(cpr[0]))
|
||||||
|
ccli = record.xpath("f[@n='CCLI']/text()")
|
||||||
|
if ccli:
|
||||||
|
self.ccli = self.clean_string(ccli[0])
|
||||||
|
# Get song tags
|
||||||
|
self.extract_tags(song_rowid)
|
||||||
|
# Get song verses
|
||||||
|
self.extract_verses(song_rowid)
|
||||||
|
if not self.finish():
|
||||||
|
self.log_error(self.title)
|
||||||
|
|
||||||
|
def extract_tags(self, song_id):
|
||||||
|
"""
|
||||||
|
Extract the tags for a particular song
|
||||||
|
"""
|
||||||
|
xpath = "//BaseObjectData[@Name='TagGroup']/Record/f[@n='SlideCol_rowid' "\
|
||||||
|
"and normalize-space(text())='{rowid}']/.."
|
||||||
|
tag_group_records = self.root.xpath(xpath.format(rowid=song_id))
|
||||||
|
for record in tag_group_records:
|
||||||
|
tag_rowid = record.xpath("f[@n='Tag_rowid']/text()")
|
||||||
|
if tag_rowid:
|
||||||
|
tag_rowid = self.clean_string(tag_rowid[0])
|
||||||
|
xpath = "//BaseObjectData[@Name='Tag']/Record/f[@n='_rowid' and normalize-space(text())='{rowid}']/"\
|
||||||
|
"../f[@n='Description']/text()"
|
||||||
|
tag = self.root.xpath(xpath.format(rowid=tag_rowid))
|
||||||
|
if tag:
|
||||||
|
tag = self.clean_string(tag[0])
|
||||||
|
self.topics.append(tag)
|
||||||
|
|
||||||
|
def extract_verses(self, song_id):
|
||||||
|
"""
|
||||||
|
Extract the verses for a particular song
|
||||||
|
"""
|
||||||
|
xpath = "//BaseObjectData[@Name='SlideColSlides']/Record/f[@n='SlideCol_rowid' and "\
|
||||||
|
"normalize-space(text())='{rowid}']/.."
|
||||||
|
slides_records = self.root.xpath(xpath.format(rowid=song_id))
|
||||||
|
for record in slides_records:
|
||||||
|
verse_text = record.xpath("f[@n='kText']/text()")
|
||||||
|
if verse_text:
|
||||||
|
verse_text = self.clean_verse(verse_text[0])
|
||||||
|
verse_tag = record.xpath("f[@n='Description']/text()")
|
||||||
|
if verse_tag:
|
||||||
|
verse_tag = self.convert_verse_name(verse_tag[0])
|
||||||
|
else:
|
||||||
|
verse_tag = 'v'
|
||||||
|
self.add_verse(verse_text, verse_tag)
|
||||||
|
|
||||||
|
def extract_authors(self, authors_line):
|
||||||
|
"""
|
||||||
|
Extract the authors as a list of str from the authors record
|
||||||
|
"""
|
||||||
|
if not authors_line:
|
||||||
|
return
|
||||||
|
for author_name in authors_line.split('/'):
|
||||||
|
name_parts = [self.clean_string(part) for part in author_name.split(',')][::-1]
|
||||||
|
self.parse_author(' '.join(name_parts))
|
||||||
|
|
||||||
|
def clean_string(self, string):
|
||||||
|
"""
|
||||||
|
Clean up the strings
|
||||||
|
"""
|
||||||
|
# 	 is tab
|
||||||
|
return string.replace('^`^', '\'').replace('/', '-').replace('	', ' ').strip()
|
||||||
|
|
||||||
|
def clean_verse(self, verse_line):
|
||||||
|
"""
|
||||||
|
Extract the verse lines from the verse record
|
||||||
|
"""
|
||||||
|
# 
 is carriage return
|
||||||
|
return self.clean_string(verse_line.replace('
', '\n'))
|
||||||
|
|
||||||
|
def convert_verse_name(self, verse_name):
|
||||||
|
"""
|
||||||
|
Convert the Verse # to v#
|
||||||
|
"""
|
||||||
|
name_parts = verse_name.lower().split()
|
||||||
|
if len(name_parts) > 1:
|
||||||
|
return name_parts[0][0] + name_parts[1]
|
||||||
|
else:
|
||||||
|
return name_parts[0][0]
|
@ -0,0 +1,93 @@
|
|||||||
|
# -*- 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 LiveWorship song importer.
|
||||||
|
"""
|
||||||
|
from unittest import TestCase
|
||||||
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
|
from tests.utils import load_external_result_data
|
||||||
|
from tests.utils.constants import RESOURCE_PATH
|
||||||
|
|
||||||
|
from openlp.core.common.registry import Registry
|
||||||
|
from openlp.plugins.songs.lib.importers.liveworship import LiveWorshipImport
|
||||||
|
|
||||||
|
|
||||||
|
TEST_PATH = RESOURCE_PATH / 'songs' / 'liveworship'
|
||||||
|
|
||||||
|
|
||||||
|
def _get_item(data, key):
|
||||||
|
"""
|
||||||
|
Get an item or return a blank string
|
||||||
|
"""
|
||||||
|
if key in data:
|
||||||
|
return data[key]
|
||||||
|
return ''
|
||||||
|
|
||||||
|
|
||||||
|
class TestLiveWorshipSongImport(TestCase):
|
||||||
|
"""
|
||||||
|
Test the functions in the :mod:`liveworshipimport` module.
|
||||||
|
"""
|
||||||
|
def setUp(self):
|
||||||
|
"""
|
||||||
|
Create the registry
|
||||||
|
"""
|
||||||
|
Registry.create()
|
||||||
|
|
||||||
|
@patch('openlp.plugins.songs.lib.importers.liveworship.SongImport')
|
||||||
|
def test_create_importer(self, mocked_songimport):
|
||||||
|
"""
|
||||||
|
Test creating an instance of the LiveWorship file importer
|
||||||
|
"""
|
||||||
|
# GIVEN: A mocked out SongImport class, and a mocked out "manager"
|
||||||
|
mocked_manager = MagicMock()
|
||||||
|
|
||||||
|
# WHEN: An importer object is created
|
||||||
|
importer = LiveWorshipImport(mocked_manager, file_paths=[])
|
||||||
|
|
||||||
|
# THEN: The importer object should not be None
|
||||||
|
assert importer is not None, 'Import should not be none'
|
||||||
|
|
||||||
|
@patch('openlp.plugins.songs.lib.importers.liveworship.SongImport')
|
||||||
|
def test_parse_xml_dump(self, mocked_songimport):
|
||||||
|
"""
|
||||||
|
Test importing a simple XML dump in LiveWorshipImport
|
||||||
|
"""
|
||||||
|
# GIVEN: A mocked out SongImport class, a mocked out "manager" and a simple XML dump
|
||||||
|
mocked_manager = MagicMock()
|
||||||
|
importer = LiveWorshipImport(mocked_manager, file_paths=[])
|
||||||
|
importer.finish = MagicMock()
|
||||||
|
importer.import_wizard = MagicMock()
|
||||||
|
importer.dump_file = TEST_PATH / 'valentina-db-simplified-dump.xml'
|
||||||
|
|
||||||
|
# WHEN: The XML is loaded and processed
|
||||||
|
importer.load_xml_dump()
|
||||||
|
importer.extract_songs()
|
||||||
|
|
||||||
|
# THEN: The imported data should look like expected
|
||||||
|
result_data = load_external_result_data(TEST_PATH / 'A Child Of The King.json')
|
||||||
|
assert importer.title == _get_item(result_data, 'title')
|
||||||
|
assert importer.verses == _get_item(result_data, 'verses')
|
||||||
|
assert importer.topics[0] == _get_item(result_data, 'topics')[0]
|
||||||
|
assert importer.authors[0][0] == _get_item(result_data, 'authors')[0]
|
||||||
|
assert importer.authors[1][0] == _get_item(result_data, 'authors')[1]
|
35
tests/resources/songs/liveworship/A Child Of The King.json
Normal file
35
tests/resources/songs/liveworship/A Child Of The King.json
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"title": "A Child Of The King",
|
||||||
|
"authors": [
|
||||||
|
"Harriet Eugenia Peck Buell",
|
||||||
|
"John Bunnell Sumner"
|
||||||
|
],
|
||||||
|
"topics": [ "Worship" ],
|
||||||
|
"verses": [
|
||||||
|
[
|
||||||
|
"v1",
|
||||||
|
"My Father is rich in houses and lands,\nHe holdeth the wealth of the world in His hands!\nOf rubies and diamonds, of silver and gold,\nHis coffers are full, He has riches untold.",
|
||||||
|
null
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"v2",
|
||||||
|
"My Father's own Son, the Savior of men,\nOnce wandered on earth as the poorest of them;\nBut now He is pleading our pardon on high,\nThat we may be His when He comes by and by.",
|
||||||
|
null
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"v3",
|
||||||
|
"I once was an outcast stranger on earth,\nA sinner by choice, an alien by birth,\nBut I've been adopted, my name's written down,\nAn heir to a mansion, a robe and a crown.",
|
||||||
|
null
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"v4",
|
||||||
|
"A tent or a cottage, why should I care?\nThey're building a palace for me over there;\nThough exiled from home, yet still may I sing:\nAll glory to God, I'm a child of the King.",
|
||||||
|
null
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"c1",
|
||||||
|
"I'm a child of the King,\nA child of the King:\nWith Jesus my Savior,\nI'm a child of the King.",
|
||||||
|
null
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,362 @@
|
|||||||
|
<?xml version="1.1"?>
|
||||||
|
<Database>
|
||||||
|
<DatabaseDesc Name="Ebenezer7.vdb">
|
||||||
|
<DatabaseMode>1</DatabaseMode>
|
||||||
|
<DatabaseSegment>32768</DatabaseSegment>
|
||||||
|
<SchemaVersion>7</SchemaVersion>
|
||||||
|
<IdentsCaseSensitive>0</IdentsCaseSensitive>
|
||||||
|
<DateFormat>0</DateFormat>
|
||||||
|
<DateSeparator>/</DateSeparator>
|
||||||
|
<TimeSeparator>:</TimeSeparator>
|
||||||
|
<KeyDelimiter>.</KeyDelimiter>
|
||||||
|
<LocaleInfo>
|
||||||
|
<LocaleName>en_US_POSIX</LocaleName>
|
||||||
|
<CollationAttributes>
|
||||||
|
<FrenchCollation>16</FrenchCollation>
|
||||||
|
<AlternateHandling>21</AlternateHandling>
|
||||||
|
<CaseFirst>16</CaseFirst>
|
||||||
|
<CaseLevel>16</CaseLevel>
|
||||||
|
<NormalizationMode>16</NormalizationMode>
|
||||||
|
<Strength>2</Strength>
|
||||||
|
<HiraganaQuaternaryMode>16</HiraganaQuaternaryMode>
|
||||||
|
<NumericCollation>16</NumericCollation>
|
||||||
|
</CollationAttributes>
|
||||||
|
<IOEncoding>UTF-16</IOEncoding>
|
||||||
|
<StorageEncoding>UTF-16</StorageEncoding>
|
||||||
|
</LocaleInfo>
|
||||||
|
<SequenceCount>0</SequenceCount>
|
||||||
|
<KeyValueCount>0</KeyValueCount>
|
||||||
|
<TypeCount>0</TypeCount>
|
||||||
|
<Types>
|
||||||
|
</Types>
|
||||||
|
<IndexStyleCount>0</IndexStyleCount>
|
||||||
|
<BaseObjectCount>16</BaseObjectCount>
|
||||||
|
<LinkCount>0</LinkCount>
|
||||||
|
<ViewCount>0</ViewCount>
|
||||||
|
<SToredProcedureCount>0</SToredProcedureCount>
|
||||||
|
<TriggerCount>0</TriggerCount>
|
||||||
|
</DatabaseDesc>
|
||||||
|
<DatabaseData>
|
||||||
|
<DatabaseStructureData>
|
||||||
|
</DatabaseStructureData>
|
||||||
|
<DatabaseUserData>
|
||||||
|
<BaseObjectData Name="SlideCol">
|
||||||
|
<Record>
|
||||||
|
<f n="RecID">1</f>
|
||||||
|
<f n="_rowid"> 1.000000</f>
|
||||||
|
<f n="Type">song</f>
|
||||||
|
<f n="Title">A Charge To Keep I Have</f>
|
||||||
|
<f n="CreateDate">00/00/0000 00:00:00:000</f>
|
||||||
|
<f n="Author">Wesley, Charles / Mason, Lowell</f>
|
||||||
|
<f n="Copyright">Public Domain </f>
|
||||||
|
<f n="CCLI" />
|
||||||
|
<f n="UseCount"> 0.000000</f>
|
||||||
|
</Record>
|
||||||
|
<Record>
|
||||||
|
<f n="RecID">2</f>
|
||||||
|
<f n="_rowid"> 2.000000</f>
|
||||||
|
<f n="Type">song</f>
|
||||||
|
<f n="Title">A Child Of The King</f>
|
||||||
|
<f n="CreateDate">00/00/0000 00:00:00:000</f>
|
||||||
|
<f n="Author">Buell, Harriet Eugenia Peck / Sumner, John Bunnell</f>
|
||||||
|
<f n="Copyright">Public Domain </f>
|
||||||
|
<f n="CCLI" />
|
||||||
|
<f n="UseCount"> 0.000000</f>
|
||||||
|
</Record>
|
||||||
|
</BaseObjectData>
|
||||||
|
<BaseObjectData Name="SlideColSlides">
|
||||||
|
<RecordCount>9478</RecordCount>
|
||||||
|
<Record>
|
||||||
|
<f n="RecID">1</f>
|
||||||
|
<f n="_rowid"> 1.000000</f>
|
||||||
|
<f n="SlideCol_rowid"> 1.000000</f>
|
||||||
|
<f n="Description">Verse 1</f>
|
||||||
|
<f n="Delay"> 0.000000</f>
|
||||||
|
<f n="AudioDone">0</f>
|
||||||
|
<f n="AudioId"> 0.000000</f>
|
||||||
|
<f n="AudioPath" />
|
||||||
|
<f n="BkColor" />
|
||||||
|
<f n="BkDone">0</f>
|
||||||
|
<f n="BKFullScreen">0</f>
|
||||||
|
<f n="BkId"> 0.000000</f>
|
||||||
|
<f n="BkPath" />
|
||||||
|
<f n="BkSize"> 0.000000</f>
|
||||||
|
<f n="BkStretch">0</f>
|
||||||
|
<f n="BkX"> 0.000000</f>
|
||||||
|
<f n="BkY"> 0.000000</f>
|
||||||
|
<f n="kText"><![CDATA[A charge to keep I have,
A God to glorify,
A never dying soul to save,
And fit it for the sky.]]></f>
|
||||||
|
<f n="TextDone">0</f>
|
||||||
|
<f n="TextDS">0</f>
|
||||||
|
<f n="TextDSColor" />
|
||||||
|
<f n="TextDSOffSet"> 0.000000</f>
|
||||||
|
<f n="TextHalign" />
|
||||||
|
<f n="TextId"> 0.000000</f>
|
||||||
|
<f n="StyledText" />
|
||||||
|
<f n="TextValign" />
|
||||||
|
<f n="TextX1"> 0.000000</f>
|
||||||
|
<f n="TextX2"> 0.000000</f>
|
||||||
|
</Record>
|
||||||
|
<Record>
|
||||||
|
<f n="RecID">2</f>
|
||||||
|
<f n="_rowid"> 2.000000</f>
|
||||||
|
<f n="SlideCol_rowid"> 1.000000</f>
|
||||||
|
<f n="Description">Verse 2</f>
|
||||||
|
<f n="Delay"> 0.000000</f>
|
||||||
|
<f n="AudioDone">0</f>
|
||||||
|
<f n="AudioId"> 0.000000</f>
|
||||||
|
<f n="AudioPath" />
|
||||||
|
<f n="BkColor" />
|
||||||
|
<f n="BkDone">0</f>
|
||||||
|
<f n="BKFullScreen">0</f>
|
||||||
|
<f n="BkId"> 0.000000</f>
|
||||||
|
<f n="BkPath" />
|
||||||
|
<f n="BkSize"> 0.000000</f>
|
||||||
|
<f n="BkStretch">0</f>
|
||||||
|
<f n="BkX"> 0.000000</f>
|
||||||
|
<f n="BkY"> 0.000000</f>
|
||||||
|
<f n="kText"><![CDATA[To serve the present age,
My calling to fulfill;
O may it all my powr^`^s engage
To do my Master^`^s will!]]></f>
|
||||||
|
<f n="TextDone">0</f>
|
||||||
|
<f n="TextDS">0</f>
|
||||||
|
<f n="TextDSColor" />
|
||||||
|
<f n="TextDSOffSet"> 0.000000</f>
|
||||||
|
<f n="TextHalign" />
|
||||||
|
<f n="TextId"> 0.000000</f>
|
||||||
|
<f n="StyledText" />
|
||||||
|
<f n="TextValign" />
|
||||||
|
<f n="TextX1"> 0.000000</f>
|
||||||
|
<f n="TextX2"> 0.000000</f>
|
||||||
|
</Record>
|
||||||
|
<Record>
|
||||||
|
<f n="RecID">3</f>
|
||||||
|
<f n="_rowid"> 3.000000</f>
|
||||||
|
<f n="SlideCol_rowid"> 1.000000</f>
|
||||||
|
<f n="Description">Verse 3</f>
|
||||||
|
<f n="Delay"> 0.000000</f>
|
||||||
|
<f n="AudioDone">0</f>
|
||||||
|
<f n="AudioId"> 0.000000</f>
|
||||||
|
<f n="AudioPath" />
|
||||||
|
<f n="BkColor" />
|
||||||
|
<f n="BkDone">0</f>
|
||||||
|
<f n="BKFullScreen">0</f>
|
||||||
|
<f n="BkId"> 0.000000</f>
|
||||||
|
<f n="BkPath" />
|
||||||
|
<f n="BkSize"> 0.000000</f>
|
||||||
|
<f n="BkStretch">0</f>
|
||||||
|
<f n="BkX"> 0.000000</f>
|
||||||
|
<f n="BkY"> 0.000000</f>
|
||||||
|
<f n="kText"><![CDATA[Arm me with jealous care,
As in Thy sight to live,
And O, Thy servant, Lord, prepare,
A strict account to give!]]></f>
|
||||||
|
<f n="TextDone">0</f>
|
||||||
|
<f n="TextDS">0</f>
|
||||||
|
<f n="TextDSColor" />
|
||||||
|
<f n="TextDSOffSet"> 0.000000</f>
|
||||||
|
<f n="TextHalign" />
|
||||||
|
<f n="TextId"> 0.000000</f>
|
||||||
|
<f n="StyledText" />
|
||||||
|
<f n="TextValign" />
|
||||||
|
<f n="TextX1"> 0.000000</f>
|
||||||
|
<f n="TextX2"> 0.000000</f>
|
||||||
|
</Record>
|
||||||
|
<Record>
|
||||||
|
<f n="RecID">4</f>
|
||||||
|
<f n="_rowid"> 4.000000</f>
|
||||||
|
<f n="SlideCol_rowid"> 1.000000</f>
|
||||||
|
<f n="Description">Verse 4</f>
|
||||||
|
<f n="Delay"> 0.000000</f>
|
||||||
|
<f n="AudioDone">0</f>
|
||||||
|
<f n="AudioId"> 0.000000</f>
|
||||||
|
<f n="AudioPath" />
|
||||||
|
<f n="BkColor" />
|
||||||
|
<f n="BkDone">0</f>
|
||||||
|
<f n="BKFullScreen">0</f>
|
||||||
|
<f n="BkId"> 0.000000</f>
|
||||||
|
<f n="BkPath" />
|
||||||
|
<f n="BkSize"> 0.000000</f>
|
||||||
|
<f n="BkStretch">0</f>
|
||||||
|
<f n="BkX"> 0.000000</f>
|
||||||
|
<f n="BkY"> 0.000000</f>
|
||||||
|
<f n="kText"><![CDATA[Help me to watch and pray,
And on Thyself rely,
Assured, if I my trust betray,
I shall forever die.]]></f>
|
||||||
|
<f n="TextDone">0</f>
|
||||||
|
<f n="TextDS">0</f>
|
||||||
|
<f n="TextDSColor" />
|
||||||
|
<f n="TextDSOffSet"> 0.000000</f>
|
||||||
|
<f n="TextHalign" />
|
||||||
|
<f n="TextId"> 0.000000</f>
|
||||||
|
<f n="StyledText" />
|
||||||
|
<f n="TextValign" />
|
||||||
|
<f n="TextX1"> 0.000000</f>
|
||||||
|
<f n="TextX2"> 0.000000</f>
|
||||||
|
</Record>
|
||||||
|
<Record>
|
||||||
|
<f n="RecID">5</f>
|
||||||
|
<f n="_rowid"> 5.000000</f>
|
||||||
|
<f n="SlideCol_rowid"> 2.000000</f>
|
||||||
|
<f n="Description">Verse 1</f>
|
||||||
|
<f n="Delay"> 0.000000</f>
|
||||||
|
<f n="AudioDone">0</f>
|
||||||
|
<f n="AudioId"> 0.000000</f>
|
||||||
|
<f n="AudioPath" />
|
||||||
|
<f n="BkColor" />
|
||||||
|
<f n="BkDone">0</f>
|
||||||
|
<f n="BKFullScreen">0</f>
|
||||||
|
<f n="BkId"> 0.000000</f>
|
||||||
|
<f n="BkPath" />
|
||||||
|
<f n="BkSize"> 0.000000</f>
|
||||||
|
<f n="BkStretch">0</f>
|
||||||
|
<f n="BkX"> 0.000000</f>
|
||||||
|
<f n="BkY"> 0.000000</f>
|
||||||
|
<f n="kText"><![CDATA[My Father is rich in houses and lands,
He holdeth the wealth of the world in His hands!
Of rubies and diamonds, of silver and gold,
His coffers are full, He has riches untold.]]></f>
|
||||||
|
<f n="TextDone">0</f>
|
||||||
|
<f n="TextDS">0</f>
|
||||||
|
<f n="TextDSColor" />
|
||||||
|
<f n="TextDSOffSet"> 0.000000</f>
|
||||||
|
<f n="TextHalign" />
|
||||||
|
<f n="TextId"> 0.000000</f>
|
||||||
|
<f n="StyledText" />
|
||||||
|
<f n="TextValign" />
|
||||||
|
<f n="TextX1"> 0.000000</f>
|
||||||
|
<f n="TextX2"> 0.000000</f>
|
||||||
|
</Record>
|
||||||
|
<Record>
|
||||||
|
<f n="RecID">6</f>
|
||||||
|
<f n="_rowid"> 6.000000</f>
|
||||||
|
<f n="SlideCol_rowid"> 2.000000</f>
|
||||||
|
<f n="Description">Verse 2</f>
|
||||||
|
<f n="Delay"> 0.000000</f>
|
||||||
|
<f n="AudioDone">0</f>
|
||||||
|
<f n="AudioId"> 0.000000</f>
|
||||||
|
<f n="AudioPath" />
|
||||||
|
<f n="BkColor" />
|
||||||
|
<f n="BkDone">0</f>
|
||||||
|
<f n="BKFullScreen">0</f>
|
||||||
|
<f n="BkId"> 0.000000</f>
|
||||||
|
<f n="BkPath" />
|
||||||
|
<f n="BkSize"> 0.000000</f>
|
||||||
|
<f n="BkStretch">0</f>
|
||||||
|
<f n="BkX"> 0.000000</f>
|
||||||
|
<f n="BkY"> 0.000000</f>
|
||||||
|
<f n="kText"><![CDATA[My Father^`^s own Son, the Savior of men,
Once wandered on earth as the poorest of them;
But now He is pleading our pardon on high,
That we may be His when He comes by and by.]]></f>
|
||||||
|
<f n="TextDone">0</f>
|
||||||
|
<f n="TextDS">0</f>
|
||||||
|
<f n="TextDSColor" />
|
||||||
|
<f n="TextDSOffSet"> 0.000000</f>
|
||||||
|
<f n="TextHalign" />
|
||||||
|
<f n="TextId"> 0.000000</f>
|
||||||
|
<f n="StyledText" />
|
||||||
|
<f n="TextValign" />
|
||||||
|
<f n="TextX1"> 0.000000</f>
|
||||||
|
<f n="TextX2"> 0.000000</f>
|
||||||
|
</Record>
|
||||||
|
<Record>
|
||||||
|
<f n="RecID">7</f>
|
||||||
|
<f n="_rowid"> 7.000000</f>
|
||||||
|
<f n="SlideCol_rowid"> 2.000000</f>
|
||||||
|
<f n="Description">Verse 3</f>
|
||||||
|
<f n="Delay"> 0.000000</f>
|
||||||
|
<f n="AudioDone">0</f>
|
||||||
|
<f n="AudioId"> 0.000000</f>
|
||||||
|
<f n="AudioPath" />
|
||||||
|
<f n="BkColor" />
|
||||||
|
<f n="BkDone">0</f>
|
||||||
|
<f n="BKFullScreen">0</f>
|
||||||
|
<f n="BkId"> 0.000000</f>
|
||||||
|
<f n="BkPath" />
|
||||||
|
<f n="BkSize"> 0.000000</f>
|
||||||
|
<f n="BkStretch">0</f>
|
||||||
|
<f n="BkX"> 0.000000</f>
|
||||||
|
<f n="BkY"> 0.000000</f>
|
||||||
|
<f n="kText"><![CDATA[I once was an outcast stranger on earth,
A sinner by choice, an alien by birth,
But I^`^ve been adopted, my name^`^s written down,
An heir to a mansion, a robe and a crown.]]></f>
|
||||||
|
<f n="TextDone">0</f>
|
||||||
|
<f n="TextDS">0</f>
|
||||||
|
<f n="TextDSColor" />
|
||||||
|
<f n="TextDSOffSet"> 0.000000</f>
|
||||||
|
<f n="TextHalign" />
|
||||||
|
<f n="TextId"> 0.000000</f>
|
||||||
|
<f n="StyledText" />
|
||||||
|
<f n="TextValign" />
|
||||||
|
<f n="TextX1"> 0.000000</f>
|
||||||
|
<f n="TextX2"> 0.000000</f>
|
||||||
|
</Record>
|
||||||
|
<Record>
|
||||||
|
<f n="RecID">8</f>
|
||||||
|
<f n="_rowid"> 8.000000</f>
|
||||||
|
<f n="SlideCol_rowid"> 2.000000</f>
|
||||||
|
<f n="Description">Verse 4</f>
|
||||||
|
<f n="Delay"> 0.000000</f>
|
||||||
|
<f n="AudioDone">0</f>
|
||||||
|
<f n="AudioId"> 0.000000</f>
|
||||||
|
<f n="AudioPath" />
|
||||||
|
<f n="BkColor" />
|
||||||
|
<f n="BkDone">0</f>
|
||||||
|
<f n="BKFullScreen">0</f>
|
||||||
|
<f n="BkId"> 0.000000</f>
|
||||||
|
<f n="BkPath" />
|
||||||
|
<f n="BkSize"> 0.000000</f>
|
||||||
|
<f n="BkStretch">0</f>
|
||||||
|
<f n="BkX"> 0.000000</f>
|
||||||
|
<f n="BkY"> 0.000000</f>
|
||||||
|
<f n="kText"><![CDATA[A tent or a cottage, why should I care?
They^`^re building a palace for me over there;
Though exiled from home, yet still may I sing:
All glory to God, I^`^m a child of the King.]]></f>
|
||||||
|
<f n="TextDone">0</f>
|
||||||
|
<f n="TextDS">0</f>
|
||||||
|
<f n="TextDSColor" />
|
||||||
|
<f n="TextDSOffSet"> 0.000000</f>
|
||||||
|
<f n="TextHalign" />
|
||||||
|
<f n="TextId"> 0.000000</f>
|
||||||
|
<f n="StyledText" />
|
||||||
|
<f n="TextValign" />
|
||||||
|
<f n="TextX1"> 0.000000</f>
|
||||||
|
<f n="TextX2"> 0.000000</f>
|
||||||
|
</Record>
|
||||||
|
<Record>
|
||||||
|
<f n="RecID">9</f>
|
||||||
|
<f n="_rowid"> 9.000000</f>
|
||||||
|
<f n="SlideCol_rowid"> 2.000000</f>
|
||||||
|
<f n="Description">Chorus</f>
|
||||||
|
<f n="Delay"> 0.000000</f>
|
||||||
|
<f n="AudioDone">0</f>
|
||||||
|
<f n="AudioId"> 0.000000</f>
|
||||||
|
<f n="AudioPath" />
|
||||||
|
<f n="BkColor" />
|
||||||
|
<f n="BkDone">0</f>
|
||||||
|
<f n="BKFullScreen">0</f>
|
||||||
|
<f n="BkId"> 0.000000</f>
|
||||||
|
<f n="BkPath" />
|
||||||
|
<f n="BkSize"> 0.000000</f>
|
||||||
|
<f n="BkStretch">0</f>
|
||||||
|
<f n="BkX"> 0.000000</f>
|
||||||
|
<f n="BkY"> 0.000000</f>
|
||||||
|
<f n="kText"><![CDATA[I^`^m a child of the King,
A child of the King:
With Jesus my Savior,
I^`^m a child of the King.]]></f>
|
||||||
|
<f n="TextDone">0</f>
|
||||||
|
<f n="TextDS">0</f>
|
||||||
|
<f n="TextDSColor" />
|
||||||
|
<f n="TextDSOffSet"> 0.000000</f>
|
||||||
|
<f n="TextHalign" />
|
||||||
|
<f n="TextId"> 0.000000</f>
|
||||||
|
<f n="StyledText" />
|
||||||
|
<f n="TextValign" />
|
||||||
|
<f n="TextX1"> 0.000000</f>
|
||||||
|
<f n="TextX2"> 0.000000</f>
|
||||||
|
</Record>
|
||||||
|
</BaseObjectData>
|
||||||
|
<BaseObjectData Name="TagGroup">
|
||||||
|
<RecordCount>158</RecordCount>
|
||||||
|
<Record>
|
||||||
|
<f n="RecID">1</f>
|
||||||
|
<f n="_rowid"> 854.000000</f>
|
||||||
|
<f n="Tag_rowid"> 28.000000</f>
|
||||||
|
<f n="Lib_rowid"> 0.000000</f>
|
||||||
|
<f n="gText_rowid"> 0.000000</f>
|
||||||
|
<f n="SlideCol_rowid"> 2.000000</f>
|
||||||
|
<f n="Presentation_rowid"> 0.000000</f>
|
||||||
|
</Record>
|
||||||
|
</BaseObjectData>
|
||||||
|
<BaseObjectData Name="Tag">
|
||||||
|
<RecordCount>11</RecordCount>
|
||||||
|
<Record>
|
||||||
|
<f n="RecID">1</f>
|
||||||
|
<f n="_rowid"> 28.000000</f>
|
||||||
|
<f n="MediaType_rowid"> 6.000000</f>
|
||||||
|
<f n="Description"> Worship</f>
|
||||||
|
</Record>
|
||||||
|
</BaseObjectData>
|
||||||
|
</DatabaseUserData>
|
||||||
|
</DatabaseData></Database>
|
Loading…
Reference in New Issue
Block a user