This commit is contained in:
Tomas Groth 2014-06-16 09:01:12 +02:00
commit 44eccb8360
63 changed files with 2236 additions and 208 deletions

View File

@ -72,15 +72,15 @@ class AppLocation(object):
:param dir_type: The directory type you want, for instance the data directory. Default *AppLocation.AppDir*
"""
if dir_type == AppLocation.AppDir:
return get_frozen_path(os.path.abspath(os.path.split(sys.argv[0])[0]), os.path.split(openlp.__file__)[0])
return get_frozen_path(os.path.abspath(os.path.dirname(sys.argv[0])), os.path.dirname(openlp.__file__))
elif dir_type == AppLocation.PluginsDir:
app_path = os.path.abspath(os.path.split(sys.argv[0])[0])
app_path = os.path.abspath(os.path.dirname(sys.argv[0]))
return get_frozen_path(os.path.join(app_path, 'plugins'),
os.path.join(os.path.split(openlp.__file__)[0], 'plugins'))
os.path.join(os.path.dirname(openlp.__file__), 'plugins'))
elif dir_type == AppLocation.VersionDir:
return get_frozen_path(os.path.abspath(os.path.split(sys.argv[0])[0]), os.path.split(openlp.__file__)[0])
return get_frozen_path(os.path.abspath(os.path.dirname(sys.argv[0])), os.path.dirname(openlp.__file__))
elif dir_type == AppLocation.LanguageDir:
app_path = get_frozen_path(os.path.abspath(os.path.split(sys.argv[0])[0]), _get_os_dir_path(dir_type))
app_path = get_frozen_path(os.path.abspath(os.path.dirname(sys.argv[0])), _get_os_dir_path(dir_type))
return os.path.join(app_path, 'i18n')
elif dir_type == AppLocation.DataDir and AppLocation.BaseDir:
return os.path.join(AppLocation.BaseDir, 'data')
@ -140,18 +140,22 @@ def _get_os_dir_path(dir_type):
"""
Return a path based on which OS and environment we are running in.
"""
# If running from source, return the language directory from the source directory
if dir_type == AppLocation.LanguageDir:
directory = os.path.abspath(os.path.join(os.path.dirname(openlp.__file__), '..', 'resources'))
if os.path.exists(directory):
return directory
if sys.platform == 'win32':
if dir_type == AppLocation.DataDir:
return os.path.join(str(os.getenv('APPDATA')), 'openlp', 'data')
elif dir_type == AppLocation.LanguageDir:
return os.path.split(openlp.__file__)[0]
return os.path.dirname(openlp.__file__)
return os.path.join(str(os.getenv('APPDATA')), 'openlp')
elif sys.platform == 'darwin':
if dir_type == AppLocation.DataDir:
return os.path.join(str(os.getenv('HOME')),
'Library', 'Application Support', 'openlp', 'Data')
return os.path.join(str(os.getenv('HOME')), 'Library', 'Application Support', 'openlp', 'Data')
elif dir_type == AppLocation.LanguageDir:
return os.path.split(openlp.__file__)[0]
return os.path.dirname(openlp.__file__)
return os.path.join(str(os.getenv('HOME')), 'Library', 'Application Support', 'openlp')
else:
if dir_type == AppLocation.LanguageDir:

View File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #

View File

@ -495,8 +495,8 @@ s
if service_item:
service_item.from_plugin = True
self.preview_controller.add_service_item(service_item)
if keep_focus:
self.list_view.setFocus()
if not keep_focus:
self.preview_controller.preview_widget.setFocus()
def on_live_click(self):
"""
@ -535,6 +535,7 @@ s
if remote:
service_item.will_auto_start = True
self.live_controller.add_service_item(service_item)
self.live_controller.preview_widget.setFocus()
def create_item_from_id(self, item_id):
"""

View File

@ -82,11 +82,6 @@ class PluginManager(RegistryMixin, OpenLPMixin, RegistryProperties):
present_plugin_dir = os.path.join(self.base_path, 'presentations')
self.log_debug('finding plugins in %s at depth %d' % (self.base_path, start_depth))
for root, dirs, files in os.walk(self.base_path):
if sys.platform == 'darwin' and root.startswith(present_plugin_dir):
# TODO Presentation plugin is not yet working on Mac OS X.
# For now just ignore it. The following code will ignore files from the presentation plugin directory
# and thereby never import the plugin.
continue
for name in files:
if name.endswith('.py') and not name.startswith('__'):
path = os.path.abspath(os.path.join(root, name))

View File

@ -295,7 +295,7 @@ def set_case_insensitive_completer(cache, widget):
Sets a case insensitive text completer for a widget.
:param cache: The list of items to use as suggestions.
:param widget: A widget to set the completer (QComboBox or QTextEdit instance)
:param widget: A widget to set the completer (QComboBox or QLineEdit instance)
"""
completer = QtGui.QCompleter(cache)
completer.setCaseSensitivity(QtCore.Qt.CaseInsensitive)

View File

@ -412,10 +412,7 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard, RegistryProperties):
self._increment_progress_bar(translate('OpenLP.FirstTimeWizard', 'Enabling selected plugins...'))
self._set_plugin_status(self.songs_check_box, 'songs/status')
self._set_plugin_status(self.bible_check_box, 'bibles/status')
# TODO Presentation plugin is not yet working on Mac OS X.
# For now just ignore it.
if sys.platform != 'darwin':
self._set_plugin_status(self.presentation_check_box, 'presentations/status')
self._set_plugin_status(self.presentation_check_box, 'presentations/status')
self._set_plugin_status(self.image_check_box, 'images/status')
self._set_plugin_status(self.media_check_box, 'media/status')
self._set_plugin_status(self.remote_check_box, 'remotes/status')

View File

@ -95,13 +95,10 @@ class Ui_FirstTimeWizard(object):
self.image_check_box.setChecked(True)
self.image_check_box.setObjectName('image_check_box')
self.plugin_layout.addWidget(self.image_check_box)
# TODO Presentation plugin is not yet working on Mac OS X.
# For now just ignore it.
if sys.platform != 'darwin':
self.presentation_check_box = QtGui.QCheckBox(self.plugin_page)
self.presentation_check_box.setChecked(True)
self.presentation_check_box.setObjectName('presentation_check_box')
self.plugin_layout.addWidget(self.presentation_check_box)
self.presentation_check_box = QtGui.QCheckBox(self.plugin_page)
self.presentation_check_box.setChecked(True)
self.presentation_check_box.setObjectName('presentation_check_box')
self.plugin_layout.addWidget(self.presentation_check_box)
self.media_check_box = QtGui.QCheckBox(self.plugin_page)
self.media_check_box.setChecked(True)
self.media_check_box.setObjectName('media_check_box')
@ -222,10 +219,7 @@ class Ui_FirstTimeWizard(object):
self.custom_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Custom Slides'))
self.bible_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Bible'))
self.image_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Images'))
# TODO Presentation plugin is not yet working on Mac OS X.
# For now just ignore it.
if sys.platform != 'darwin':
self.presentation_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Presentations'))
self.presentation_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Presentations'))
self.media_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Media (Audio and Video)'))
self.remote_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Allow remote access'))
self.song_usage_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Monitor Song Usage'))

View File

@ -91,10 +91,9 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog, FormattingTagCont
"""
new_row = self.tag_table_widget.rowCount()
self.tag_table_widget.insertRow(new_row)
self.tag_table_widget.setItem(new_row, 0,
QtGui.QTableWidgetItem(translate('OpenLP.FormattingTagForm', 'New Tag%s')
% str(new_row)))
self.tag_table_widget.setItem(new_row, 1, QtGui.QTableWidgetItem('n%s' % str(new_row)))
self.tag_table_widget.setItem(new_row, 0, QtGui.QTableWidgetItem(translate('OpenLP.FormattingTagForm',
'New Tag %d' % new_row)))
self.tag_table_widget.setItem(new_row, 1, QtGui.QTableWidgetItem('n%d' % new_row))
self.tag_table_widget.setItem(new_row, 2,
QtGui.QTableWidgetItem(translate('OpenLP.FormattingTagForm', '<HTML here>')))
self.tag_table_widget.setItem(new_row, 3, QtGui.QTableWidgetItem(''))

View File

@ -136,7 +136,6 @@ class ListPreviewWidget(QtGui.QTableWidget, RegistryProperties):
if self.service_item.is_text():
self.resizeRowsToContents()
self.setColumnWidth(0, self.viewport().width())
self.setFocus()
self.change_slide(slide_number)
def change_slide(self, slide):

View File

@ -598,7 +598,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow, RegistryProperties):
if self.arguments:
self.open_cmd_line_files()
elif Settings().value(self.general_settings_section + '/auto open'):
self.service_manager_contents.load_Last_file()
self.service_manager_contents.load_last_file()
self.timer_version_id = self.startTimer(1000)
view_mode = Settings().value('%s/view mode' % self.general_settings_section)
if view_mode == 'default':

View File

@ -401,9 +401,12 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ServiceManage
:param suffix_list: New Suffix's to be supported
"""
for suffix in suffix_list:
if suffix not in self.suffixes:
self.suffixes.append(suffix)
if isinstance(suffix_list, str):
self.suffixes.append(suffix_list)
else:
for suffix in suffix_list:
if suffix not in self.suffixes:
self.suffixes.append(suffix)
def on_new_service_clicked(self, field=None):
"""
@ -1081,6 +1084,7 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ServiceManage
:param field:
:param message: The data passed in from a remove message
"""
self.log_debug(message)
self.set_item(int(message))
def set_item(self, index):
@ -1089,7 +1093,7 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ServiceManage
:param index: The index of the service item list to be actioned.
"""
if 0 >= index < self.service_manager_list.topLevelItemCount():
if 0 <= index < self.service_manager_list.topLevelItemCount():
item = self.service_manager_list.topLevelItem(index)
self.service_manager_list.setCurrentItem(item)
self.make_live()

View File

@ -18,11 +18,11 @@
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# AN_y WARRANT_y; without even the implied warranty of MERCHANTABILIT_y or #
# 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 #
# 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 #
###############################################################################
@ -179,7 +179,7 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard, RegistryProperties):
if self.page(self.currentId()) == self.background_page and \
self.theme.background_type == background_image and is_not_image_file(self.theme.background_filename):
QtGui.QMessageBox.critical(self, translate('OpenLP.ThemeWizard', 'Background Image Empty'),
translate('OpenLP.ThemeWizard', '_you have not selected a '
translate('OpenLP.ThemeWizard', 'You have not selected a '
'background image. Please select one before continuing.'))
return False
else:

View File

@ -51,6 +51,7 @@ class WizardStrings(object):
CSV = 'CSV'
OS = 'OpenSong'
OSIS = 'OSIS'
ZEF = 'Zefania'
# These strings should need a good reason to be retranslated elsewhere.
FinishedImport = translate('OpenLP.Ui', 'Finished import.')
FormatLabel = translate('OpenLP.Ui', 'Format:')

View File

@ -71,8 +71,7 @@ class LanguageManager(object):
"""
Find all available language files in this OpenLP install
"""
log.debug('Translation files: %s', AppLocation.get_directory(
AppLocation.LanguageDir))
log.debug('Translation files: %s', AppLocation.get_directory(AppLocation.LanguageDir))
trans_dir = QtCore.QDir(AppLocation.get_directory(AppLocation.LanguageDir))
file_names = trans_dir.entryList(['*.qm'], QtCore.QDir.Files, QtCore.QDir.Name)
# Remove qm files from the list which start with "qt_".

View File

@ -110,6 +110,7 @@ class BibleImportForm(OpenLPWizard):
self.csv_books_button.clicked.connect(self.on_csv_books_browse_button_clicked)
self.csv_verses_button.clicked.connect(self.on_csv_verses_browse_button_clicked)
self.open_song_browse_button.clicked.connect(self.on_open_song_browse_button_clicked)
self.zefania_browse_button.clicked.connect(self.on_zefania_browse_button_clicked)
def add_custom_pages(self):
"""
@ -125,7 +126,7 @@ class BibleImportForm(OpenLPWizard):
self.format_label = QtGui.QLabel(self.select_page)
self.format_label.setObjectName('FormatLabel')
self.format_combo_box = QtGui.QComboBox(self.select_page)
self.format_combo_box.addItems(['', '', '', ''])
self.format_combo_box.addItems(['', '', '', '', ''])
self.format_combo_box.setObjectName('FormatComboBox')
self.format_layout.addRow(self.format_label, self.format_combo_box)
self.spacer = QtGui.QSpacerItem(10, 0, QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Minimum)
@ -247,6 +248,25 @@ class BibleImportForm(OpenLPWizard):
self.web_proxy_layout.setWidget(2, QtGui.QFormLayout.FieldRole, self.web_password_edit)
self.web_tab_widget.addTab(self.web_proxy_tab, '')
self.select_stack.addWidget(self.web_tab_widget)
self.zefania_widget = QtGui.QWidget(self.select_page)
self.zefania_widget.setObjectName('ZefaniaWidget')
self.zefania_layout = QtGui.QFormLayout(self.zefania_widget)
self.zefania_layout.setMargin(0)
self.zefania_layout.setObjectName('ZefaniaLayout')
self.zefania_file_label = QtGui.QLabel(self.zefania_widget)
self.zefania_file_label.setObjectName('ZefaniaFileLabel')
self.zefania_file_layout = QtGui.QHBoxLayout()
self.zefania_file_layout.setObjectName('ZefaniaFileLayout')
self.zefania_file_edit = QtGui.QLineEdit(self.zefania_widget)
self.zefania_file_edit.setObjectName('ZefaniaFileEdit')
self.zefania_file_layout.addWidget(self.zefania_file_edit)
self.zefania_browse_button = QtGui.QToolButton(self.zefania_widget)
self.zefania_browse_button.setIcon(self.open_icon)
self.zefania_browse_button.setObjectName('ZefaniaBrowseButton')
self.zefania_file_layout.addWidget(self.zefania_browse_button)
self.zefania_layout.addRow(self.zefania_file_label, self.zefania_file_layout)
self.zefania_layout.setItem(5, QtGui.QFormLayout.LabelRole, self.spacer)
self.select_stack.addWidget(self.zefania_widget)
self.select_page_layout.addLayout(self.select_stack)
self.addPage(self.select_page)
# License Page
@ -294,11 +314,13 @@ class BibleImportForm(OpenLPWizard):
self.format_combo_box.setItemText(BibleFormat.OpenSong, WizardStrings.OS)
self.format_combo_box.setItemText(BibleFormat.WebDownload, translate('BiblesPlugin.ImportWizardForm',
'Web Download'))
self.format_combo_box.setItemText(BibleFormat.Zefania, WizardStrings.ZEF)
self.osis_file_label.setText(translate('BiblesPlugin.ImportWizardForm', 'Bible file:'))
self.csv_books_label.setText(translate('BiblesPlugin.ImportWizardForm', 'Books file:'))
self.csv_verses_label.setText(translate('BiblesPlugin.ImportWizardForm', 'Verses file:'))
self.open_song_file_label.setText(translate('BiblesPlugin.ImportWizardForm', 'Bible file:'))
self.web_source_label.setText(translate('BiblesPlugin.ImportWizardForm', 'Location:'))
self.zefania_file_label.setText(translate('BiblesPlugin.ImportWizardForm', 'Bible file:'))
self.web_source_combo_box.setItemText(WebDownload.Crosswalk, translate('BiblesPlugin.ImportWizardForm',
'Crosswalk'))
self.web_source_combo_box.setItemText(WebDownload.BibleGateway, translate('BiblesPlugin.ImportWizardForm',
@ -331,7 +353,8 @@ class BibleImportForm(OpenLPWizard):
self.osis_file_label.minimumSizeHint().width(),
self.csv_books_label.minimumSizeHint().width(),
self.csv_verses_label.minimumSizeHint().width(),
self.open_song_file_label.minimumSizeHint().width())
self.open_song_file_label.minimumSizeHint().width(),
self.zefania_file_label.minimumSizeHint().width())
self.spacer.changeSize(label_width, 0, QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
def validateCurrentPage(self):
@ -366,6 +389,11 @@ class BibleImportForm(OpenLPWizard):
critical_error_message_box(UiStrings().NFSs, WizardStrings.YouSpecifyFile % WizardStrings.OS)
self.open_song_file_edit.setFocus()
return False
elif self.field('source_format') == BibleFormat.Zefania:
if not self.field('zefania_file'):
critical_error_message_box(UiStrings().NFSs, WizardStrings.YouSpecifyFile % WizardStrings.ZEF)
self.zefania_file_edit.setFocus()
return False
elif self.field('source_format') == BibleFormat.WebDownload:
self.version_name_edit.setText(self.web_translation_combo_box.currentText())
return True
@ -447,6 +475,13 @@ class BibleImportForm(OpenLPWizard):
self.get_file_name(WizardStrings.OpenTypeFile % WizardStrings.OS, self.open_song_file_edit,
'last directory import')
def on_zefania_browse_button_clicked(self):
"""
Show the file open dialog for the Zefania file.
"""
self.get_file_name(WizardStrings.OpenTypeFile % WizardStrings.ZEF, self.zefania_file_edit,
'last directory import')
def register_fields(self):
"""
Register the bible import wizard fields.
@ -456,6 +491,7 @@ class BibleImportForm(OpenLPWizard):
self.select_page.registerField('csv_booksfile', self.csv_books_edit)
self.select_page.registerField('csv_versefile', self.csv_verses_edit)
self.select_page.registerField('opensong_file', self.open_song_file_edit)
self.select_page.registerField('zefania_file', self.zefania_file_edit)
self.select_page.registerField('web_location', self.web_source_combo_box)
self.select_page.registerField('web_biblename', self.web_translation_combo_box)
self.select_page.registerField('proxy_server', self.web_server_edit)
@ -479,6 +515,7 @@ class BibleImportForm(OpenLPWizard):
self.setField('csv_booksfile', '')
self.setField('csv_versefile', '')
self.setField('opensong_file', '')
self.setField('zefania_file', '')
self.setField('web_location', WebDownload.Crosswalk)
self.setField('web_biblename', self.web_translation_combo_box.currentIndex())
self.setField('proxy_server', settings.value('proxy address'))
@ -562,6 +599,10 @@ class BibleImportForm(OpenLPWizard):
proxy_username=self.field('proxy_username'),
proxy_password=self.field('proxy_password')
)
elif bible_type == BibleFormat.Zefania:
# Import an Zefania bible.
importer = self.manager.import_bible(BibleFormat.Zefania, name=license_version,
filename=self.field('zefania_file'))
if importer.do_import(license_version):
self.manager.save_meta_data(license_version, license_version, license_copyright, license_permissions)
self.manager.reload_bibles()

View File

@ -149,7 +149,7 @@ class CSVBible(BibleDB):
book_ptr = book.name
self.wizard.increment_progress_bar(
translate('BiblesPlugin.CSVBible',
'Importing verses from %s... Importing verses from <book name>...') % book.name)
'Importing verses from %s...' % book.name, 'Importing verses from <book name>...'))
self.session.commit()
try:
verse_text = str(line[3], details['encoding'])

View File

@ -38,6 +38,7 @@ from .csvbible import CSVBible
from .http import HTTPBible
from .opensong import OpenSongBible
from .osis import OSISBible
from .zefania import ZefaniaBible
log = logging.getLogger(__name__)
@ -52,6 +53,7 @@ class BibleFormat(object):
CSV = 1
OpenSong = 2
WebDownload = 3
Zefania = 4
@staticmethod
def get_class(bible_format):
@ -68,6 +70,8 @@ class BibleFormat(object):
return OpenSongBible
elif bible_format == BibleFormat.WebDownload:
return HTTPBible
elif bible_format == BibleFormat.Zefania:
return ZefaniaBible
else:
return None
@ -81,6 +85,7 @@ class BibleFormat(object):
BibleFormat.CSV,
BibleFormat.OpenSong,
BibleFormat.WebDownload,
BibleFormar.Zefania,
]

View File

@ -81,6 +81,13 @@ class OpenSongBible(BibleDB):
import_file = open(self.filename, 'rb')
opensong = objectify.parse(import_file)
bible = opensong.getroot()
# Check that we're not trying to import a Zefania XML bible, it is sometimes refered to as 'OpenSong'
if bible.tag.upper() == 'XMLBIBLE':
critical_error_message_box(
message=translate('BiblesPlugin.OpenSongImport',
'Incorrect Bible file type supplied. This looks like a Zefania XML bible, '
'please use the Zefania import option.'))
return False
language_id = self.get_language(bible_name)
if not language_id:
log.error('Importing books from "%s" failed' % self.filename)

View File

@ -0,0 +1,113 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2014 Raoul Snyman #
# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan #
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
import logging
from lxml import etree, objectify
from openlp.core.common import translate
from openlp.core.lib.ui import critical_error_message_box
from openlp.plugins.bibles.lib.db import BibleDB, BiblesResourcesDB
log = logging.getLogger(__name__)
class ZefaniaBible(BibleDB):
"""
Zefania Bible format importer class.
"""
def __init__(self, parent, **kwargs):
"""
Constructor to create and set up an instance of the ZefaniaBible class. This class is used to import Bibles
from ZefaniaBible's XML format.
"""
log.debug(self.__class__.__name__)
BibleDB.__init__(self, parent, **kwargs)
self.filename = kwargs['filename']
def do_import(self, bible_name=None):
"""
Loads a Bible from file.
"""
log.debug('Starting Zefania import from "%s"' % self.filename)
if not isinstance(self.filename, str):
self.filename = str(self.filename, 'utf8')
import_file = None
success = True
try:
# NOTE: We don't need to do any of the normal encoding detection here, because lxml does it's own encoding
# detection, and the two mechanisms together interfere with each other.
import_file = open(self.filename, 'rb')
language_id = self.get_language(bible_name)
if not language_id:
log.error('Importing books from "%s" failed' % self.filename)
return False
zefania_bible_tree = etree.parse(import_file)
num_books = int(zefania_bible_tree.xpath("count(//BIBLEBOOK)"))
# Strip tags we don't use - keep content
etree.strip_tags(zefania_bible_tree, ('STYLE', 'GRAM', 'NOTE', 'SUP', 'XREF'))
# Strip tags we don't use - remove content
etree.strip_elements(zefania_bible_tree, ('PROLOG', 'REMARK', 'CAPTION', 'MEDIA'), with_tail=False)
xmlbible = zefania_bible_tree.getroot()
for BIBLEBOOK in xmlbible:
book_ref_id = self.get_book_ref_id_by_name(str(BIBLEBOOK.get('bname')), num_books)
if not book_ref_id:
book_ref_id = self.get_book_ref_id_by_localised_name(str(BIBLEBOOK.get('bname')))
if not book_ref_id:
log.error('Importing books from "%s" failed' % self.filename)
return False
book_details = BiblesResourcesDB.get_book_by_id(book_ref_id)
db_book = self.create_book(book_details['name'], book_ref_id, book_details['testament_id'])
for CHAPTER in BIBLEBOOK:
if self.stop_import_flag:
break
chapter_number = CHAPTER.get("cnumber")
for VERS in CHAPTER:
verse_number = VERS.get("vnumber")
self.create_verse(db_book.id, chapter_number, verse_number, VERS.text.replace('<BR/>', '\n'))
self.wizard.increment_progress_bar(
translate('BiblesPlugin.Zefnia', 'Importing %(bookname)s %(chapter)s...' %
{'bookname': db_book.name, 'chapter': chapter_number}))
self.session.commit()
self.application.process_events()
except Exception as e:
print(str(e))
critical_error_message_box(
message=translate('BiblesPlugin.ZefaniaImport',
'Incorrect Bible file type supplied. Zefania Bibles may be '
'compressed. You must decompress them before import.'))
log.exception(str(e))
success = False
finally:
if import_file:
import_file.close()
if self.stop_import_flag:
return False
else:
return success

View File

@ -126,8 +126,8 @@ class PdfController(PresentationController):
if os.name == 'nt':
# for windows we only accept mudraw.exe in the base folder
application_path = AppLocation.get_directory(AppLocation.AppDir)
if os.path.isfile(application_path + '/../mudraw.exe'):
self.mudrawbin = application_path + '/../mudraw.exe'
if os.path.isfile(os.path.join(application_path, 'mudraw.exe')):
self.mudrawbin = os.path.join(application_path, 'mudraw.exe')
else:
DEVNULL = open(os.devnull, 'wb')
# First try to find mupdf
@ -145,8 +145,8 @@ class PdfController(PresentationController):
# Last option: check if mudraw is placed in OpenLP base folder
if not self.mudrawbin and not self.gsbin:
application_path = AppLocation.get_directory(AppLocation.AppDir)
if os.path.isfile(application_path + '/../mudraw'):
self.mudrawbin = application_path + '/../mudraw'
if os.path.isfile(os.path.join(application_path, 'mudraw')):
self.mudrawbin = os.path.join(application_path, 'mudraw')
if self.mudrawbin:
self.also_supports = ['xps']
return True

View File

@ -42,7 +42,7 @@ from openlp.core.common import Registry, RegistryProperties, AppLocation, UiStri
from openlp.core.lib import FileDialog, PluginStatus, MediaType, create_separated_list
from openlp.core.lib.ui import set_case_insensitive_completer, critical_error_message_box, find_and_set_in_combo_box
from openlp.plugins.songs.lib import VerseType, clean_song
from openlp.plugins.songs.lib.db import Book, Song, Author, AuthorSong, AuthorType, Topic, MediaFile
from openlp.plugins.songs.lib.db import Book, Song, Author, AuthorType, Topic, MediaFile
from openlp.plugins.songs.lib.ui import SongStrings
from openlp.plugins.songs.lib.xml import SongXML
from openlp.plugins.songs.forms.editsongdialog import Ui_EditSongDialog
@ -966,10 +966,8 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties):
self.song.authors_songs = []
for row in range(self.authors_list_view.count()):
item = self.authors_list_view.item(row)
author_song = AuthorSong()
author_song.author_id = item.data(QtCore.Qt.UserRole)[0]
author_song.author_type = item.data(QtCore.Qt.UserRole)[1]
self.song.authors_songs.append(author_song)
self.song.add_author(self.manager.get_object(Author, item.data(QtCore.Qt.UserRole)[0]),
item.data(QtCore.Qt.UserRole)[1])
self.song.topics = []
for row in range(self.topics_list_view.count()):
item = self.topics_list_view.item(row)

View File

@ -400,7 +400,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog, RegistryPrope
"""
Merges two authors into one author.
:param old_author: The object, which was edited, that will be deleted
:param old_author: The object, which was edited, that will be deleted
"""
# Find the duplicate.
existing_author = self.manager.get_object_filtered(
@ -415,11 +415,9 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog, RegistryPrope
# Find the songs, which have the old_author as author.
songs = self.manager.get_all_objects(Song, Song.authors.contains(old_author))
for song in songs:
# We check if the song has already existing_author as author. If
# that is not the case we add it.
if existing_author not in song.authors:
song.authors.append(existing_author)
song.authors.remove(old_author)
for author_song in song.authors_songs:
song.add_author(existing_author, author_song.author_type)
song.remove_author(old_author, author_song.author_type)
self.manager.save_object(song)
self.manager.delete_object(Author, old_author.id)

View File

@ -278,7 +278,7 @@ class VerseType(object):
if verse_index is None:
verse_index = VerseType.from_string(verse_name, default)
elif len(verse_name) == 1:
verse_index = VerseType.from_translated_tag(verse_name, None)
verse_index = VerseType.from_translated_tag(verse_name, default)
if verse_index is None:
verse_index = VerseType.from_tag(verse_name, default)
else:
@ -390,12 +390,12 @@ def clean_song(manager, song):
verses = SongXML().get_verses(song.lyrics)
song.search_lyrics = ' '.join([clean_string(verse[1]) for verse in verses])
# The song does not have any author, add one.
if not song.authors and not song.authors_songs: # Need to check both relations
if not song.authors_songs:
name = SongStrings.AuthorUnknown
author = manager.get_object_filtered(Author, Author.display_name == name)
if author is None:
author = Author.populate(display_name=name, last_name='', first_name='')
song.authors.append(author)
song.add_author(author)
if song.copyright:
song.copyright = CONTROL_CHARS.sub('', song.copyright).strip()

View File

@ -63,7 +63,6 @@ class CCLIFileImport(SongImport):
for filename in self.import_source:
filename = str(filename)
log.debug('Importing CCLI File: %s', filename)
lines = []
if os.path.isfile(filename):
detect_file = open(filename, 'r')
detect_content = detect_file.read(2048)
@ -250,7 +249,7 @@ class CCLIFileImport(SongImport):
# e.g. For use solely with the SongSelect Terms of Use.
All rights Reserved. www.ccli.com
CCLI Licence number of user
# e.g. CCL-Liedlizenznummer: 14 / CCLI License No. 14
# e.g. CCLI-Liedlizenznummer: 14 / CCLI License No. 14
"""
log.debug('TXT file text: %s', text_list)

View File

@ -74,10 +74,11 @@ class AuthorType(object):
WordsAndMusic = 'words+music'
Translation = 'translation'
Types = {
Words: translate('OpenLP.Ui', 'Words'),
Music: translate('OpenLP.Ui', 'Music'),
WordsAndMusic: translate('OpenLP.Ui', 'Words and Music'),
Translation: translate('OpenLP.Ui', 'Translation')
Words: translate('SongsPlugin.AuthorType', 'Words', 'Author who wrote the lyrics of a song'),
Music: translate('SongsPlugin.AuthorType', 'Music', 'Author who wrote the music of a song'),
WordsAndMusic: translate('SongsPlugin.AuthorType', 'Words and Music',
'Author who wrote both lyrics and music of a song'),
Translation: translate('SongsPlugin.AuthorType', 'Translation', 'Author who translated the song')
}
@ -114,6 +115,33 @@ class Song(BaseModel):
"""
self.sort_key = get_natural_key(self.title)
def add_author(self, author, author_type=None):
"""
Add an author to the song if it not yet exists
:param author: Author object
:param author_type: AuthorType constant or None
"""
for author_song in self.authors_songs:
if author_song.author == author and author_song.author_type == author_type:
return
new_author_song = AuthorSong()
new_author_song.author = author
new_author_song.author_type = author_type
self.authors_songs.append(new_author_song)
def remove_author(self, author, author_type=None):
"""
Remove an existing author from the song
:param author: Author object
:param author_type: AuthorType constant or None
"""
for author_song in self.authors_songs:
if author_song.author == author and author_song.author_type == author_type:
self.authors_songs.remove(author_song)
return
class Topic(BaseModel):
"""
@ -283,9 +311,10 @@ def init_schema(url):
mapper(Book, song_books_table)
mapper(MediaFile, media_files_table)
mapper(Song, songs_table, properties={
# Use the authors_songs relation when you need access to the 'author_type' attribute.
# Use the authors_songs relation when you need access to the 'author_type' attribute
# or when creating new relations
'authors_songs': relation(AuthorSong, cascade="all, delete-orphan"),
'authors': relation(Author, secondary=authors_songs_table),
'authors': relation(Author, secondary=authors_songs_table, viewonly=True),
'book': relation(Book, backref='songs'),
'media_files': relation(MediaFile, backref='songs', order_by=media_files_table.c.weight),
'topics': relation(Topic, backref='songs', secondary=songs_topics_table)

View File

@ -200,11 +200,20 @@ class EasyWorshipSongImport(SongImport):
Import the songs from the database
"""
# Open the DB and MB files if they exist
import_source_mb = self.import_source.replace('.DB', '.MB')
if not os.path.isfile(self.import_source) or not os.path.isfile(import_source_mb):
import_source_mb = self.import_source.replace('.DB', '.MB').replace('.db', '.mb')
if not os.path.isfile(self.import_source):
self.log_error(self.import_source, translate('SongsPlugin.EasyWorshipSongImport',
'This file does not exist.'))
return
if not os.path.isfile(import_source_mb):
self.log_error(self.import_source, translate('SongsPlugin.EasyWorshipSongImport',
'Could not find the "Songs.MB" file. It must be in the same '
'folder as the "Songs.DB" file.'))
return
db_size = os.path.getsize(self.import_source)
if db_size < 0x800:
self.log_error(self.import_source, translate('SongsPlugin.EasyWorshipSongImport',
'This file is not a valid EasyWorship database.'))
return
db_file = open(self.import_source, 'rb')
self.memo_file = open(import_source_mb, 'rb')
@ -213,6 +222,8 @@ class EasyWorshipSongImport(SongImport):
if header_size != 0x800 or block_size < 1 or block_size > 4:
db_file.close()
self.memo_file.close()
self.log_error(self.import_source, translate('SongsPlugin.EasyWorshipSongImport',
'This file is not a valid EasyWorship database.'))
return
# Take a stab at how text is encoded
self.encoding = 'cp1252'
@ -240,6 +251,8 @@ class EasyWorshipSongImport(SongImport):
self.encoding = 'cp874'
self.encoding = retrieve_windows_encoding(self.encoding)
if not self.encoding:
self.log_error(self.import_source, translate('SongsPlugin.EasyWorshipSongImport',
'Could not retrieve encoding.'))
return
# Read the field description information
db_file.seek(120)
@ -344,7 +357,7 @@ class EasyWorshipSongImport(SongImport):
try:
decoded_words = words.decode()
except UnicodeDecodeError:
# The unicode chars in the rtf was not escaped in the expected manor
# The unicode chars in the rtf was not escaped in the expected manner
self.entry_error_log = translate('SongsPlugin.EasyWorshipSongImport',
'Unexpected data formatting.')
return

View File

@ -343,7 +343,7 @@ class FoilPresenter(object):
author = Author.populate(display_name=display_name, last_name=display_name.split(' ')[-1],
first_name=' '.join(display_name.split(' ')[:-1]))
self.manager.save_object(author)
song.authors.append(author)
song.add_author(author)
def _process_cclinumber(self, foilpresenterfolie, song):
"""

View File

@ -49,6 +49,7 @@ from .songproimport import SongProImport
from .sundayplusimport import SundayPlusImport
from .foilpresenterimport import FoilPresenterImport
from .zionworximport import ZionWorxImport
from .propresenterimport import ProPresenterImport
# Imports that might fail
@ -159,14 +160,15 @@ class SongFormat(object):
MediaShout = 9
OpenSong = 10
PowerSong = 11
SongBeamer = 12
SongPro = 13
SongShowPlus = 14
SongsOfFellowship = 15
SundayPlus = 16
WordsOfWorship = 17
WorshipCenterPro = 18
ZionWorx = 19
ProPresenter = 12
SongBeamer = 13
SongPro = 14
SongShowPlus = 15
SongsOfFellowship = 16
SundayPlus = 17
WordsOfWorship = 18
WorshipCenterPro = 19
ZionWorx = 20
# Set optional attribute defaults
__defaults__ = {
@ -270,6 +272,12 @@ class SongFormat(object):
'invalidSourceMsg': translate('SongsPlugin.ImportWizardForm', 'You need to specify a valid PowerSong 1.0 '
'database folder.')
},
ProPresenter: {
'class': ProPresenterImport,
'name': 'ProPresenter',
'prefix': 'proPresenter',
'filter': '%s (*.pro4)' % translate('SongsPlugin.ImportWizardForm', 'ProPresenter Song Files')
},
SongBeamer: {
'class': SongBeamerImport,
'name': 'SongBeamer',
@ -355,6 +363,7 @@ class SongFormat(object):
SongFormat.MediaShout,
SongFormat.OpenSong,
SongFormat.PowerSong,
SongFormat.ProPresenter,
SongFormat.SongBeamer,
SongFormat.SongPro,
SongFormat.SongShowPlus,

View File

@ -527,15 +527,7 @@ class SongMediaItem(MediaManagerItem):
add_song = True
if search_results:
for song in search_results:
author_list = item.data_string['authors']
same_authors = True
for author in song.authors:
if author.display_name in author_list:
author_list = author_list.replace(author.display_name, '', 1)
else:
same_authors = False
break
if same_authors and author_list.strip(', ') == '':
if self._authors_match(song, item.data_string['authors']):
add_song = False
edit_id = song.id
break
@ -561,6 +553,23 @@ class SongMediaItem(MediaManagerItem):
self.generate_footer(item, song)
return item
def _authors_match(self, song, authors):
"""
Checks whether authors from a song in the database match the authors of the song to be imported.
:param song: A list of authors from the song in the database
:param authors: A string with authors from the song to be imported
:return: True when Authors do match, else False.
"""
author_list = authors.split(', ')
for author in song.authors:
if author.display_name in author_list:
author_list.remove(author.display_name)
else:
return False
# List must be empty at the end
return not author_list
def search(self, string, show_error):
"""
Search for some songs

View File

@ -187,7 +187,7 @@ class OpenLPSongImport(SongImport):
first_name=author.first_name,
last_name=author.last_name,
display_name=author.display_name)
new_song.authors.append(existing_author)
new_song.add_author(existing_author)
if song.book:
existing_song_book = self.manager.get_object_filtered(Book, Book.name == song.book.name)
if existing_song_book is None:

View File

@ -45,11 +45,11 @@ class OpenSongImport(SongImport):
"""
Import songs exported from OpenSong
The format is described loosly on the `OpenSong File Format Specification
The format is described loosely on the `OpenSong File Format Specification
<http://www.opensong.org/d/manual/song_file_format_specification>`_ page on the OpenSong web site. However, it
doesn't describe the <lyrics> section, so here's an attempt:
If the first charachter of a line is a space, then the rest of that line is lyrics. If it is not a space the
If the first character of a line is a space, then the rest of that line is lyrics. If it is not a space the
following applies.
Verses can be expressed in one of 2 ways, either in complete verses, or by line grouping, i.e. grouping all line 1's
@ -93,12 +93,19 @@ class OpenSongImport(SongImport):
All verses are imported and tagged appropriately.
Guitar chords can be provided "above" the lyrics (the line is preceeded by a period "."), and one or more "_" can
Guitar chords can be provided "above" the lyrics (the line is preceded by a period "."), and one or more "_" can
be used to signify long-drawn-out words. Chords and "_" are removed by this importer. For example::
. A7 Bm
1 Some____ Words
Lines that contain only whitespace are ignored.
| indicates a blank line, and || a new slide.
Slide 1 Line 1|Slide 1 Line 2||Slide 2 Line 1|Slide 2 Line 2
Lines beginning with ; are comments
The <presentation> tag is used to populate the OpenLP verse display order field. The Author and Copyright tags are
also imported to the appropriate places.
"""
@ -107,9 +114,14 @@ class OpenSongImport(SongImport):
"""
Initialise the class.
"""
SongImport.__init__(self, manager, **kwargs)
super(OpenSongImport, self).__init__(manager, **kwargs)
def do_import(self):
"""
Receive a single file or a list of files to import.
"""
if not isinstance(self.import_source, list):
return
self.import_wizard.progress_bar.setMaximum(len(self.import_source))
for filename in self.import_source:
if self.stop_import_flag:
@ -141,19 +153,41 @@ class OpenSongImport(SongImport):
'author': self.parse_author,
'title': 'title',
'aka': 'alternate_title',
'hymn_number': 'song_number'
'hymn_number': self.parse_song_book_name_and_number,
'user1': self.add_comment,
'user2': self.add_comment,
'user3': self.add_comment
}
for attr, fn_or_string in list(decode.items()):
if attr in fields:
ustring = str(root.__getattr__(attr))
if isinstance(fn_or_string, str):
setattr(self, fn_or_string, ustring)
if attr in ['ccli']:
if ustring:
setattr(self, fn_or_string, int(ustring))
else:
setattr(self, fn_or_string, None)
else:
setattr(self, fn_or_string, ustring)
else:
fn_or_string(ustring)
if 'theme' in fields and str(root.theme) not in self.topics:
self.topics.append(str(root.theme))
if 'alttheme' in fields and str(root.alttheme) not in self.topics:
self.topics.append(str(root.alttheme))
# Themes look like "God: Awe/Wonder", but we just want
# "Awe" and "Wonder". We use a set to ensure each topic
# is only added once, in case it is already there, which
# is actually quite likely if the alttheme is set
topics = set(self.topics)
if 'theme' in fields:
theme = str(root.theme)
subthemes = theme[theme.find(':')+1:].split('/')
for topic in subthemes:
topics.add(topic.strip())
if 'alttheme' in fields:
theme = str(root.alttheme)
subthemes = theme[theme.find(':')+1:].split('/')
for topic in subthemes:
topics.add(topic.strip())
self.topics = list(topics)
self.topics.sort()
# data storage while importing
verses = {}
# keep track of verses appearance order
@ -168,7 +202,7 @@ class OpenSongImport(SongImport):
else:
lyrics = ''
for this_line in lyrics.split('\n'):
if not this_line:
if not this_line.strip():
continue
# skip this line if it is a comment
if this_line.startswith(';'):
@ -209,8 +243,14 @@ class OpenSongImport(SongImport):
# Tidy text and remove the ____s from extended words
this_line = self.tidy_text(this_line)
this_line = this_line.replace('_', '')
this_line = this_line.replace('|', '\n')
this_line = this_line.replace('||', '\n[---]\n')
this_line = this_line.strip()
# If the line consists solely of a '|', then just use the implicit newline
# Otherwise, add a newline for each '|'
if this_line == '|':
this_line = ''
else:
this_line = this_line.replace('|', '\n')
verses[verse_tag][verse_num][inst].append(this_line)
# done parsing
# add verses in original order
@ -223,7 +263,14 @@ class OpenSongImport(SongImport):
verse_def = '%s%s' % (verse_tag, verse_num[:length])
verse_joints[verse_def] = '%s\n[---]\n%s' % (verse_joints[verse_def], lines) \
if verse_def in verse_joints else lines
for verse_def, lines in verse_joints.items():
# Parsing the dictionary produces the elements in a non-intuitive order. While it "works", it's not a
# natural layout should the user come back to edit the song. Instead we sort by the verse type, so that we
# get all the verses in order (v1, v2, ...), then the chorus(es), bridge(s), pre-chorus(es) etc. We use a
# tuple for the key, since tuples naturally sort in this manner.
verse_defs = sorted(verse_joints.keys(),
key=lambda verse_def: (VerseType.from_tag(verse_def[0]), int(verse_def[1:])))
for verse_def in verse_defs:
lines = verse_joints[verse_def]
self.add_verse(lines, verse_def)
if not self.verses:
self.add_verse('')
@ -244,6 +291,8 @@ class OpenSongImport(SongImport):
# Assume it's no.1 if there are no digits
verse_tag = verse_def
verse_num = '1'
verse_index = VerseType.from_loose_input(verse_tag)
verse_tag = VerseType.tags[verse_index]
verse_def = '%s%s' % (verse_tag, verse_num)
if verse_num in verses.get(verse_tag, {}):
self.verse_order_list.append(verse_def)

View File

@ -0,0 +1,75 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2013 Raoul Snyman #
# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan #
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
The :mod:`propresenterimport` module provides the functionality for importing
ProPresenter song files into the current installation database.
"""
import os
import base64
from lxml import objectify
from openlp.core.ui.wizard import WizardStrings
from openlp.plugins.songs.lib import strip_rtf
from .songimport import SongImport
class ProPresenterImport(SongImport):
"""
The :class:`ProPresenterImport` class provides OpenLP with the
ability to import ProPresenter song files.
"""
def do_import(self):
self.import_wizard.progress_bar.setMaximum(len(self.import_source))
for file_path in self.import_source:
if self.stop_import_flag:
return
self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % os.path.basename(file_path))
root = objectify.parse(open(file_path, 'rb')).getroot()
self.process_song(root)
def process_song(self, root):
self.set_defaults()
self.title = root.get('CCLISongTitle')
self.copyright = root.get('CCLICopyrightInfo')
self.comments = root.get('notes')
self.ccli_number = root.get('CCLILicenseNumber')
for author_key in ['author', 'artist', 'CCLIArtistCredits']:
author = root.get(author_key)
if len(author) > 0:
self.parse_author(author)
count = 0
for slide in root.slides.RVDisplaySlide:
count += 1
RTFData = slide.displayElements.RVTextElement.get('RTFData')
rtf = base64.standard_b64decode(RTFData)
words, encoding = strip_rtf(rtf.decode())
self.add_verse(words, "v%d" % count)
if not self.finish():
self.log_error(self.import_source)

View File

@ -188,13 +188,61 @@ class SongImport(QtCore.QObject):
self.title = lines[0]
self.add_verse(text)
def parse_song_book_name_and_number(self, book_and_number):
"""
Build the book name and song number from a single string
"""
# Turn 'Spring Harvest 1997 No. 34' or
# 'Spring Harvest 1997 (34)' or
# 'Spring Harvest 1997 34' into
# Book name:'Spring Harvest 1997' and
# Song number: 34
#
# Also, turn 'NRH231.' into
# Book name:'NRH' and
# Song number: 231
book_and_number = book_and_number.strip()
if not book_and_number:
return
book_and_number = book_and_number.replace('No.', ' ')
if ' ' in book_and_number:
parts = book_and_number.split(' ')
self.song_book_name = ' '.join(parts[:-1])
self.song_number = parts[-1].strip('()')
else:
# Something like 'ABC123.'
match = re.match(r'(.*\D)(\d+)', book_and_number)
match_num = re.match(r'(\d+)', book_and_number)
if match:
# Name and number
self.song_book_name = match.group(1)
self.song_number = match.group(2)
# These last two cases aren't tested yet, but
# are here in an attempt to do something vaguely
# sensible if we get a string in a different format
elif match_num:
# Number only
self.song_number = match_num.group(1)
else:
# Name only
self.song_book_name = book_and_number
def add_comment(self, comment):
"""
Build the comments field
"""
if self.comments.find(comment) >= 0:
return
if comment:
self.comments += comment.strip() + '\n'
def add_copyright(self, copyright):
"""
Build the copyright field
"""
if self.copyright.find(copyright) >= 0:
return
if self.copyright != '':
if self.copyright:
self.copyright += ' '
self.copyright += copyright
@ -325,7 +373,7 @@ class SongImport(QtCore.QObject):
author = Author.populate(display_name=author_text,
last_name=author_text.split(' ')[-1],
first_name=' '.join(author_text.split(' ')[:-1]))
song.authors.append(author)
song.add_author(author)
if self.song_book_name:
song_book = self.manager.get_object_filtered(Book, Book.name == self.song_book_name)
if song_book is None:

View File

@ -196,13 +196,13 @@ class SongSelectImport(object):
db_song.lyrics = song_xml.extract_xml()
clean_song(self.db_manager, db_song)
self.db_manager.save_object(db_song)
db_song.authors = []
db_song.authors_songs = []
for author_name in song['authors']:
author = self.db_manager.get_object_filtered(Author, Author.display_name == author_name)
if not author:
author = Author.populate(first_name=author_name.rsplit(' ', 1)[0],
last_name=author_name.rsplit(' ', 1)[1],
display_name=author_name)
db_song.authors.append(author)
db_song.add_author(author)
self.db_manager.save_object(db_song)
return db_song

View File

@ -71,7 +71,7 @@ from lxml import etree, objectify
from openlp.core.common import translate
from openlp.core.lib import FormattingTags
from openlp.plugins.songs.lib import VerseType, clean_song
from openlp.plugins.songs.lib.db import Author, AuthorSong, AuthorType, Book, Song, Topic
from openlp.plugins.songs.lib.db import Author, AuthorType, Book, Song, Topic
from openlp.core.utils import get_application_version
log = logging.getLogger(__name__)
@ -519,10 +519,7 @@ class OpenLyrics(object):
author = Author.populate(display_name=display_name,
last_name=display_name.split(' ')[-1],
first_name=' '.join(display_name.split(' ')[:-1]))
author_song = AuthorSong()
author_song.author = author
author_song.author_type = author_type
song.authors_songs.append(author_song)
song.add_author(author, author_type)
def _process_cclinumber(self, properties, song):
"""
@ -664,7 +661,7 @@ class OpenLyrics(object):
# OpenLyrics 0.8 uses <br/> for new lines. Append text from "lines" element to verse text.
if version > '0.7':
text = self._process_lines_mixed_content(element)
# OpenLyrics version <= 0.7 contais <line> elements to represent lines. First child element is tested.
# OpenLyrics version <= 0.7 contains <line> elements to represent lines. First child element is tested.
else:
# Loop over the "line" elements removing comments and chords.
for line in element:

0
scripts/jenkins_script.py Normal file → Executable file
View File

View File

@ -63,7 +63,7 @@ import webbrowser
from optparse import OptionParser
from PyQt4 import QtCore
SERVER_URL = 'http://www.transifex.net/api/2/project/openlp/'
SERVER_URL = 'http://www.transifex.net/api/2/project/openlp/resource/openlp-22x/'
IGNORED_PATHS = ['scripts']
IGNORED_FILES = ['setup.py']
@ -175,7 +175,7 @@ def run(command):
process = QtCore.QProcess()
process.start(command)
while process.waitForReadyRead():
print_verbose('ReadyRead: %s' % QtCore.QString(process.readAll()))
print_verbose('ReadyRead: %s' % process.readAll())
print_verbose('Error(s):\n%s' % process.readAllStandardError())
print_verbose('Output:\n%s' % process.readAllStandardOutput())
@ -193,27 +193,26 @@ def download_translations():
if not password:
password = getpass(' Transifex password: ')
# First get the list of languages
url = SERVER_URL + 'resource/ents/'
base64string = base64.encodbytes('%s:%s' % (username, password))[:-1]
auth_header = 'Basic %s' % base64string
request = urllib.request.Request(url + '?details')
base64string = base64.encodebytes(('%s:%s' % (username, password)).encode())[:-1]
auth_header = 'Basic %s' % base64string.decode()
request = urllib.request.Request(SERVER_URL + '?details')
request.add_header('Authorization', auth_header)
print_verbose('Downloading list of languages from: %s' % url)
print_verbose('Downloading list of languages from: %s' % SERVER_URL)
try:
json_response = urllib.request.urlopen(request)
except urllib.error.HTTPError:
print_quiet('Username or password incorrect.')
return False
json_dict = json.loads(json_response.read())
json_dict = json.loads(json_response.read().decode())
languages = [lang['code'] for lang in json_dict['available_languages']]
for language in languages:
lang_url = url + 'translation/%s/?file' % language
lang_url = SERVER_URL + 'translation/%s/?file' % language
request = urllib.request.Request(lang_url)
request.add_header('Authorization', auth_header)
filename = os.path.join(os.path.abspath('..'), 'resources', 'i18n', language + '.ts')
print_verbose('Get Translation File: %s' % filename)
response = urllib.request.urlopen(request)
fd = open(filename, 'w')
fd = open(filename, 'wb')
fd.write(response.read())
fd.close()
print_quiet(' Done.')
@ -261,7 +260,7 @@ def prepare_project():
lines.append('TRANSLATIONS += %s' % line)
lines.sort()
file = open(os.path.join(start_dir, 'openlp.pro'), 'w')
file.write('\n'.join(lines).encode('utf8'))
file.write('\n'.join(lines))
file.close()
print_quiet(' Done.')

3
setup.cfg Normal file
View File

@ -0,0 +1,3 @@
[pep8]
exclude=resources.py,vlc.py
max-line-length = 120

View File

@ -32,7 +32,7 @@ Functional tests to test the AppLocation class and related methods.
from unittest import TestCase
from openlp.core.common import de_hump, trace_error_handler
from openlp.core.common import check_directory_exists, de_hump, trace_error_handler, translate
from tests.functional import MagicMock, patch
@ -40,6 +40,45 @@ class TestCommonFunctions(TestCase):
"""
A test suite to test out various functions in the openlp.core.common module.
"""
def check_directory_exists_test(self):
"""
Test the check_directory_exists() function
"""
with patch('openlp.core.lib.os.path.exists') as mocked_exists, \
patch('openlp.core.lib.os.makedirs') as mocked_makedirs:
# GIVEN: A directory to check and a mocked out os.makedirs and os.path.exists
directory_to_check = 'existing/directory'
# WHEN: os.path.exists returns True and we check to see if the directory exists
mocked_exists.return_value = True
check_directory_exists(directory_to_check)
# THEN: Only os.path.exists should have been called
mocked_exists.assert_called_with(directory_to_check)
self.assertIsNot(mocked_makedirs.called, 'os.makedirs should not have been called')
# WHEN: os.path.exists returns False and we check the directory exists
mocked_exists.return_value = False
check_directory_exists(directory_to_check)
# THEN: Both the mocked functions should have been called
mocked_exists.assert_called_with(directory_to_check)
mocked_makedirs.assert_called_with(directory_to_check)
# WHEN: os.path.exists raises an IOError
mocked_exists.side_effect = IOError()
check_directory_exists(directory_to_check)
# THEN: We shouldn't get an exception though the mocked exists has been called
mocked_exists.assert_called_with(directory_to_check)
# WHEN: Some other exception is raised
mocked_exists.side_effect = ValueError()
# THEN: check_directory_exists raises an exception
mocked_exists.assert_called_with(directory_to_check)
self.assertRaises(ValueError, check_directory_exists, directory_to_check)
def de_hump_conversion_test(self):
"""
Test the de_hump function with a class name
@ -81,3 +120,22 @@ class TestCommonFunctions(TestCase):
# THEN: The mocked_logger.error() method should have been called with the correct parameters
mocked_logger.error.assert_called_with(
'OpenLP Error trace\n File openlp.fake at line 56 \n\t called trace_error_handler_test')
def translate_test(self):
"""
Test the translate() function
"""
# GIVEN: A string to translate and a mocked Qt translate function
context = 'OpenLP.Tests'
text = 'Untranslated string'
comment = 'A comment'
encoding = 1
n = 1
mocked_translate = MagicMock(return_value='Translated string')
# WHEN: we call the translate function
result = translate(context, text, comment, encoding, n, mocked_translate)
# THEN: the translated string should be returned, and the mocked function should have been called
mocked_translate.assert_called_with(context, text, comment, encoding, n)
self.assertEqual('Translated string', result, 'The translated string should have been returned')

View File

@ -36,9 +36,8 @@ from datetime import datetime, timedelta
from PyQt4 import QtCore, QtGui
from openlp.core.common import check_directory_exists, translate
from openlp.core.lib import str_to_bool, create_thumb, get_text_file_string, \
build_icon, image_to_byte, check_item_selected, validate_thumb, create_separated_list, clean_tags, expand_tags
from openlp.core.lib import build_icon, check_item_selected, clean_tags, create_thumb, create_separated_list, \
expand_tags, get_text_file_string, image_to_byte, resize_image, str_to_bool, validate_thumb
from tests.functional import MagicMock, patch
TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'resources'))
@ -152,64 +151,6 @@ class TestLib(TestCase):
# THEN: we should get back a true
self.assertTrue(str_result, 'The result should be True')
def translate_test(self):
"""
Test the translate() function
"""
# GIVEN: A string to translate and a mocked Qt translate function
context = 'OpenLP.Tests'
text = 'Untranslated string'
comment = 'A comment'
encoding = 1
n = 1
mocked_translate = MagicMock(return_value='Translated string')
# WHEN: we call the translate function
result = translate(context, text, comment, encoding, n, mocked_translate)
# THEN: the translated string should be returned, and the mocked function should have been called
mocked_translate.assert_called_with(context, text, comment, encoding, n)
self.assertEqual('Translated string', result, 'The translated string should have been returned')
def check_directory_exists_test(self):
"""
Test the check_directory_exists() function
"""
with patch('openlp.core.lib.os.path.exists') as mocked_exists, \
patch('openlp.core.lib.os.makedirs') as mocked_makedirs:
# GIVEN: A directory to check and a mocked out os.makedirs and os.path.exists
directory_to_check = 'existing/directory'
# WHEN: os.path.exists returns True and we check to see if the directory exists
mocked_exists.return_value = True
check_directory_exists(directory_to_check)
# THEN: Only os.path.exists should have been called
mocked_exists.assert_called_with(directory_to_check)
self.assertIsNot(mocked_makedirs.called, 'os.makedirs should not have been called')
# WHEN: os.path.exists returns False and we check the directory exists
mocked_exists.return_value = False
check_directory_exists(directory_to_check)
# THEN: Both the mocked functions should have been called
mocked_exists.assert_called_with(directory_to_check)
mocked_makedirs.assert_called_with(directory_to_check)
# WHEN: os.path.exists raises an IOError
mocked_exists.side_effect = IOError()
check_directory_exists(directory_to_check)
# THEN: We shouldn't get an exception though the mocked exists has been called
mocked_exists.assert_called_with(directory_to_check)
# WHEN: Some other exception is raised
mocked_exists.side_effect = ValueError()
# THEN: check_directory_exists raises an exception
mocked_exists.assert_called_with(directory_to_check)
self.assertRaises(ValueError, check_directory_exists, directory_to_check)
def get_text_file_string_no_file_test(self):
"""
Test the get_text_file_string() function when a file does not exist
@ -353,7 +294,7 @@ class TestLib(TestCase):
Test that the check_item_selected() function returns True when there are selected indexes
"""
# GIVEN: A mocked out QtGui module and a list widget with selected indexes
MockedQtGui = patch('openlp.core.lib.QtGui')
mocked_QtGui = patch('openlp.core.lib.QtGui')
mocked_list_widget = MagicMock()
mocked_list_widget.selectedIndexes.return_value = True
message = 'message'
@ -508,6 +449,27 @@ class TestLib(TestCase):
mocked_os.stat.assert_any_call(thumb_path)
assert result is False, 'The result should be False'
def resize_thumb_test(self):
"""
Test the resize_thumb() function
"""
# GIVEN: A path to an image.
image_path = os.path.join(TEST_PATH, 'church.jpg')
wanted_width = 777
wanted_height = 72
# We want the background to be white.
wanted_background_hex = '#FFFFFF'
wanted_background_rgb = QtGui.QColor(wanted_background_hex).rgb()
# WHEN: Resize the image and add a background.
image = resize_image(image_path, wanted_width, wanted_height, wanted_background_hex)
# THEN: Check if the size is correct and the background was set.
result_size = image.size()
self.assertEqual(wanted_height, result_size.height(), 'The image should have the requested height.')
self.assertEqual(wanted_width, result_size.width(), 'The image should have the requested width.')
self.assertEqual(image.pixel(0, 0), wanted_background_rgb, 'The background should be white.')
def create_separated_list_qlocate_test(self):
"""
Test the create_separated_list function using the Qt provided method

View File

@ -154,6 +154,21 @@ class TestUi(TestCase):
self.assertEqual('my tooltip', action.toolTip())
self.assertEqual('my statustip', action.statusTip())
def test_create_checked_enabled_visible_action(self):
"""
Test creating an action with the 'checked', 'enabled' and 'visible' properties.
"""
# GIVEN: A dialog
dialog = QtGui.QDialog()
# WHEN: We create an action with some properties
action = create_action(dialog, 'my_action', checked=True, enabled=False, visible=False)
# THEN: These properties should be set
self.assertEqual(True, action.isChecked())
self.assertEqual(False, action.isEnabled())
self.assertEqual(False, action.isVisible())
def test_create_valign_selection_widgets(self):
"""
Test creating a combo box for valign selection
@ -197,3 +212,33 @@ class TestUi(TestCase):
# THEN: The index should have changed
self.assertEqual(2, combo.currentIndex())
def test_create_widget_action(self):
"""
Test creating an action for a widget
"""
# GIVEN: A button
button = QtGui.QPushButton()
# WHEN: We call the function
action = create_widget_action(button, 'some action')
# THEN: The action should be returned
self.assertIsInstance(action, QtGui.QAction)
self.assertEqual(action.objectName(), 'some action')
def test_set_case_insensitive_completer(self):
"""
Test setting a case insensitive completer on a widget
"""
# GIVEN: A QComboBox and a list of completion items
line_edit = QtGui.QLineEdit()
suggestions = ['one', 'Two', 'THRee', 'FOUR']
# WHEN: We call the function
set_case_insensitive_completer(suggestions, line_edit)
# THEN: The Combobox should have a completer which is case insensitive
completer = line_edit.completer()
self.assertIsInstance(completer, QtGui.QCompleter)
self.assertEqual(completer.caseSensitivity(), QtCore.Qt.CaseInsensitive)

View File

@ -71,7 +71,21 @@ class TestServiceManager(TestCase):
service_manager._save_lite = False
service_manager.service_theme = 'test_theme'
service = service_manager.create_basic_service()[0]
# THEN: The the controller should be registered in the registry.
# THEN: The controller should be registered in the registry.
self.assertNotEqual(service, None, 'The base service should be created')
self.assertEqual(service['openlp_core']['service-theme'], 'test_theme', 'The test theme should be saved')
self.assertEqual(service['openlp_core']['lite-service'], False, 'The lite service should be saved')
def supported_suffixes_test(self):
"""
Test the create basic service array
"""
# GIVEN: A new service manager instance.
service_manager = ServiceManager(None)
# WHEN: a suffix is added as an individual or a list.
service_manager.supported_suffixes('txt')
service_manager.supported_suffixes(['pptx', 'ppt'])
# THEN: The suffixes should be available to test.
self.assertEqual('txt' in service_manager.suffixes, True, 'The suffix txt should be in the list')
self.assertEqual('ppt' in service_manager.suffixes, True, 'The suffix ppt should be in the list')
self.assertEqual('pptx' in service_manager.suffixes, True, 'The suffix pptx should be in the list')

View File

@ -0,0 +1,140 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2014 Raoul Snyman #
# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan #
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
This module contains tests for the OpenSong Bible importer.
"""
import os
from unittest import TestCase
from tests.functional import MagicMock, patch
from openlp.plugins.bibles.lib.opensong import OpenSongBible
from openlp.plugins.bibles.lib.db import BibleDB
TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__),
'..', '..', '..', 'resources', 'bibles'))
OPENSONG_TEST_DATA = {
'opensong-dk1933.xml': {
'book': 'Genesis',
'chapter': 1,
'verses': [
(1, 'I Begyndelsen skabte Gud Himmelen og Jorden.'),
(2, 'Og Jorden var øde og tom, og der var Mørke over Verdensdybet. '
'Men Guds Ånd svævede over Vandene.'),
(3, 'Og Gud sagde: "Der blive Lys!" Og der blev Lys.'),
(4, 'Og Gud så, at Lyset var godt, og Gud satte Skel mellem Lyset og Mørket,'),
(5, 'og Gud kaldte Lyset Dag, og Mørket kaldte han Nat. Og det blev Aften, '
'og det blev Morgen, første Dag.'),
(6, 'Derpå sagde Gud: "Der blive en Hvælving midt i Vandene til at skille Vandene ad!"'),
(7, 'Og således skete det: Gud gjorde Hvælvingen og skilte Vandet under Hvælvingen '
'fra Vandet over Hvælvingen;'),
(8, 'og Gud kaldte Hvælvingen Himmel. Og det blev Aften, og det blev Morgen, anden Dag.'),
(9, 'Derpå sagde Gud: "Vandet under Himmelen samle sig på eet Sted, så det faste Land kommer til Syne!" '
'Og således skete det;'),
(10, 'og Gud kaldte det faste Land Jord, og Stedet, hvor Vandet samlede sig, kaldte han Hav. Og Gud så, '
'at det var godt.')
]
}
}
class TestOpenSongImport(TestCase):
"""
Test the functions in the :mod:`opensongimport` module.
"""
def setUp(self):
self.registry_patcher = patch('openlp.plugins.bibles.lib.db.Registry')
self.registry_patcher.start()
self.manager_patcher = patch('openlp.plugins.bibles.lib.db.Manager')
self.manager_patcher.start()
def tearDown(self):
self.registry_patcher.stop()
self.manager_patcher.stop()
def create_importer_test(self):
"""
Test creating an instance of the OpenSong file importer
"""
# GIVEN: A mocked out "manager"
mocked_manager = MagicMock()
# WHEN: An importer object is created
importer = OpenSongBible(mocked_manager, path='.', name='.', filename='')
# THEN: The importer should be an instance of BibleDB
self.assertIsInstance(importer, BibleDB)
def file_import_test(self):
"""
Test the actual import of real song files
"""
# GIVEN: Test files with a mocked out "manager", "import_wizard", and mocked functions
# get_book_ref_id_by_name, create_verse, create_book, session and get_language.
with patch('openlp.plugins.bibles.lib.opensong.OpenSongBible.application'):
for bible_file in OPENSONG_TEST_DATA:
mocked_manager = MagicMock()
mocked_import_wizard = MagicMock()
importer = OpenSongBible(mocked_manager, path='.', name='.', filename='')
importer.wizard = mocked_import_wizard
importer.get_book_ref_id_by_name = MagicMock()
importer.create_verse = MagicMock()
importer.create_book = MagicMock()
importer.session = MagicMock()
importer.get_language = MagicMock()
importer.get_language.return_value = 'Danish'
# WHEN: Importing bible file
importer.filename = os.path.join(TEST_PATH, bible_file)
importer.do_import()
# THEN: The create_verse() method should have been called with each verse in the file.
self.assertTrue(importer.create_verse.called)
for verse_tag, verse_text in OPENSONG_TEST_DATA[bible_file]['verses']:
importer.create_verse.assert_any_call(importer.create_book().id, 1, verse_tag, verse_text)
def zefania_import_error_test(self):
"""
Test that we give an error message if trying to import a zefania bible
"""
# GIVEN: A mocked out "manager" and mocked out critical_error_message_box and an import
with patch('openlp.plugins.bibles.lib.opensong.critical_error_message_box') as \
mocked_critical_error_message_box:
mocked_manager = MagicMock()
importer = OpenSongBible(mocked_manager, path='.', name='.', filename='')
# WHEN: An trying to import a zefania bible
importer.filename = os.path.join(TEST_PATH, 'zefania-dk1933.xml')
importer.do_import()
# THEN: The importer should have "shown" an error message
mocked_critical_error_message_box.assert_called_with(message='Incorrect Bible file type supplied. '
'This looks like a Zefania XML bible, '
'please use the Zefania import option.')

View File

@ -0,0 +1,121 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2014 Raoul Snyman #
# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan #
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
This module contains tests for the Zefania Bible importer.
"""
import os
from unittest import TestCase
from tests.functional import MagicMock, patch
from openlp.plugins.bibles.lib.zefania import ZefaniaBible
from openlp.plugins.bibles.lib.db import BibleDB
TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__),
'..', '..', '..', 'resources', 'bibles'))
ZEFANIA_TEST_DATA = {
'zefania-dk1933.xml': {
'book': 'Genesis',
'chapter': 1,
'verses': [
('1', 'I Begyndelsen skabte Gud Himmelen og Jorden.'),
('2', 'Og Jorden var øde og tom, og der var Mørke over Verdensdybet. '
'Men Guds Ånd svævede over Vandene.'),
('3', 'Og Gud sagde: "Der blive Lys!" Og der blev Lys.'),
('4', 'Og Gud så, at Lyset var godt, og Gud satte Skel mellem Lyset og Mørket,'),
('5', 'og Gud kaldte Lyset Dag, og Mørket kaldte han Nat. Og det blev Aften, '
'og det blev Morgen, første Dag.'),
('6', 'Derpå sagde Gud: "Der blive en Hvælving midt i Vandene til at skille Vandene ad!"'),
('7', 'Og således skete det: Gud gjorde Hvælvingen og skilte Vandet under Hvælvingen '
'fra Vandet over Hvælvingen;'),
('8', 'og Gud kaldte Hvælvingen Himmel. Og det blev Aften, og det blev Morgen, anden Dag.'),
('9', 'Derpå sagde Gud: "Vandet under Himmelen samle sig på eet Sted, så det faste Land kommer til Syne!" '
'Og således skete det;'),
('10', 'og Gud kaldte det faste Land Jord, og Stedet, hvor Vandet samlede sig, kaldte han Hav. Og Gud så, '
'at det var godt.')
]
}
}
class TestZefaniaImport(TestCase):
"""
Test the functions in the :mod:`zefaniaimport` module.
"""
def setUp(self):
self.registry_patcher = patch('openlp.plugins.bibles.lib.db.Registry')
self.registry_patcher.start()
self.manager_patcher = patch('openlp.plugins.bibles.lib.db.Manager')
self.manager_patcher.start()
def tearDown(self):
self.registry_patcher.stop()
self.manager_patcher.stop()
def create_importer_test(self):
"""
Test creating an instance of the Zefania file importer
"""
# GIVEN: A mocked out "manager"
mocked_manager = MagicMock()
# WHEN: An importer object is created
importer = ZefaniaBible(mocked_manager, path='.', name='.', filename='')
# THEN: The importer should be an instance of BibleDB
self.assertIsInstance(importer, BibleDB)
def file_import_test(self):
"""
Test the actual import of real song files
"""
# GIVEN: Test files with a mocked out "manager", "import_wizard", and mocked functions
# get_book_ref_id_by_name, create_verse, create_book, session and get_language.
with patch('openlp.plugins.bibles.lib.zefania.ZefaniaBible.application'):
for bible_file in ZEFANIA_TEST_DATA:
mocked_manager = MagicMock()
mocked_import_wizard = MagicMock()
importer = ZefaniaBible(mocked_manager, path='.', name='.', filename='')
importer.wizard = mocked_import_wizard
importer.get_book_ref_id_by_name = MagicMock()
importer.create_verse = MagicMock()
importer.create_book = MagicMock()
importer.session = MagicMock()
importer.get_language = MagicMock()
importer.get_language.return_value = 'Danish'
# WHEN: Importing bible file
importer.filename = os.path.join(TEST_PATH, bible_file)
importer.do_import()
# THEN: The create_verse() method should have been called with each verse in the file.
self.assertTrue(importer.create_verse.called)
for verse_tag, verse_text in ZEFANIA_TEST_DATA[bible_file]['verses']:
importer.create_verse.assert_any_call(importer.create_book().id, '1', verse_tag, verse_text)

View File

@ -0,0 +1,114 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2014 Raoul Snyman #
# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan #
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
This module contains tests for the db submodule of the Songs plugin.
"""
from unittest import TestCase
from openlp.plugins.songs.lib.db import Song, Author, AuthorType
class TestDB(TestCase):
"""
Test the functions in the :mod:`db` module.
"""
def test_add_author(self):
"""
Test adding an author to a song
"""
# GIVEN: A song and an author
song = Song()
song.authors_songs = []
author = Author()
author.first_name = "Max"
author.last_name = "Mustermann"
# WHEN: We add an author to the song
song.add_author(author)
# THEN: The author should have been added with author_type=None
self.assertEqual(1, len(song.authors_songs))
self.assertEqual("Max", song.authors_songs[0].author.first_name)
self.assertEqual("Mustermann", song.authors_songs[0].author.last_name)
self.assertIsNone(song.authors_songs[0].author_type)
def test_add_author_with_type(self):
"""
Test adding an author with a type specified to a song
"""
# GIVEN: A song and an author
song = Song()
song.authors_songs = []
author = Author()
author.first_name = "Max"
author.last_name = "Mustermann"
# WHEN: We add an author to the song
song.add_author(author, AuthorType.Words)
# THEN: The author should have been added with author_type=None
self.assertEqual(1, len(song.authors_songs))
self.assertEqual("Max", song.authors_songs[0].author.first_name)
self.assertEqual("Mustermann", song.authors_songs[0].author.last_name)
self.assertEqual(AuthorType.Words, song.authors_songs[0].author_type)
def test_remove_author(self):
"""
Test removing an author from a song
"""
# GIVEN: A song with an author
song = Song()
song.authors_songs = []
author = Author()
song.add_author(author)
# WHEN: We remove the author
song.remove_author(author)
# THEN: It should have been removed
self.assertEqual(0, len(song.authors_songs))
def test_remove_author_with_type(self):
"""
Test removing an author with a type specified from a song
"""
# GIVEN: A song with two authors
song = Song()
song.authors_songs = []
author = Author()
song.add_author(author)
song.add_author(author, AuthorType.Translation)
# WHEN: We remove the author with a certain type
song.remove_author(author, AuthorType.Translation)
# THEN: It should have been removed and the other author should still be there
self.assertEqual(1, len(song.authors_songs))
self.assertEqual(None, song.authors_songs[0].author_type)

View File

@ -314,6 +314,26 @@ class TestEasyWorshipSongImport(TestCase):
mocked_os_path.isfile.assert_any_call('Songs.DB')
mocked_os_path.isfile.assert_any_call('Songs.MB')
def do_import_source_invalid_test(self):
"""
Test the :mod:`do_import` module produces an error when Songs.MB not found.
"""
# GIVEN: A mocked out SongImport class, a mocked out "manager"
with patch('openlp.plugins.songs.lib.ewimport.SongImport'), \
patch('openlp.plugins.songs.lib.ewimport.os.path') as mocked_os_path:
mocked_manager = MagicMock()
importer = EasyWorshipSongImport(mocked_manager, filenames=[])
importer.log_error = MagicMock()
mocked_os_path.isfile.side_effect = [True, False]
# WHEN: do_import is supplied with an import source (Songs.MB missing)
importer.import_source = 'Songs.DB'
importer.do_import()
# THEN: do_import should have logged an error that the Songs.MB file could not be found.
importer.log_error.assert_any_call(importer.import_source, 'Could not find the "Songs.MB" file. It must be '
'in the same folder as the "Songs.DB" file.')
def do_import_database_validity_test(self):
"""
Test the :mod:`do_import` module handles invalid database files correctly

View File

@ -153,3 +153,52 @@ class TestMediaItem(TestCase, TestMixin):
# THEN: The songbook should be in the footer
self.assertEqual(service_item.raw_footer, ['My Song', 'My copyright', 'My songbook #12'])
def authors_match_test(self):
"""
Test the author matching when importing a song from a service
"""
# GIVEN: A song and a string with authors
song = MagicMock()
song.authors = []
author = MagicMock()
author.display_name = "Hans Wurst"
song.authors.append(author)
author2 = MagicMock()
author2.display_name = "Max Mustermann"
song.authors.append(author2)
# There are occasions where an author appears twice in a song (with different types).
# We need to make sure that this case works (lp#1313538)
author3 = MagicMock()
author3.display_name = "Max Mustermann"
song.authors.append(author3)
authors_str = "Hans Wurst, Max Mustermann, Max Mustermann"
# WHEN: Checking for matching
result = self.media_item._authors_match(song, authors_str)
# THEN: They should match
self.assertTrue(result, "Authors should match")
def authors_dont_match_test(self):
# GIVEN: A song and a string with authors
song = MagicMock()
song.authors = []
author = MagicMock()
author.display_name = "Hans Wurst"
song.authors.append(author)
author2 = MagicMock()
author2.display_name = "Max Mustermann"
song.authors.append(author2)
# There are occasions where an author appears twice in a song (with different types).
# We need to make sure that this case works (lp#1313538)
author3 = MagicMock()
author3.display_name = "Max Mustermann"
song.authors.append(author3)
# WHEN: An author is missing in the string
authors_str = "Hans Wurst, Max Mustermann"
result = self.media_item._authors_match(song, authors_str)
# THEN: They should not match
self.assertFalse(result, "Authors should not match")

View File

@ -0,0 +1,121 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2014 Raoul Snyman #
# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan #
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
This module contains tests for the OpenSong song importer.
"""
import os
from unittest import TestCase
from tests.helpers.songfileimport import SongImportTestHelper
from openlp.plugins.songs.lib.opensongimport import OpenSongImport
from tests.functional import patch, MagicMock
TEST_PATH = os.path.abspath(
os.path.join(os.path.dirname(__file__), '..', '..', '..', 'resources', 'opensongsongs'))
class TestOpenSongFileImport(SongImportTestHelper):
def __init__(self, *args, **kwargs):
self.importer_class_name = 'OpenSongImport'
self.importer_module_name = 'opensongimport'
super(TestOpenSongFileImport, self).__init__(*args, **kwargs)
def test_song_import(self):
"""
Test that loading an OpenSong file works correctly on various files
"""
self.file_import(os.path.join(TEST_PATH, 'Amazing Grace'),
self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace.json')))
self.file_import(os.path.join(TEST_PATH, 'Beautiful Garden Of Prayer'),
self.load_external_result_data(os.path.join(TEST_PATH, 'Beautiful Garden Of Prayer.json')))
self.file_import(os.path.join(TEST_PATH, 'One, Two, Three, Four, Five'),
self.load_external_result_data(os.path.join(TEST_PATH, 'One, Two, Three, Four, Five.json')))
class TestOpenSongImport(TestCase):
"""
Test the functions in the :mod:`opensongimport` module.
"""
def create_importer_test(self):
"""
Test creating an instance of the OpenSong file importer
"""
# GIVEN: A mocked out SongImport class, and a mocked out "manager"
with patch('openlp.plugins.songs.lib.opensongimport.SongImport'):
mocked_manager = MagicMock()
# WHEN: An importer object is created
importer = OpenSongImport(mocked_manager, filenames=[])
# THEN: The importer object should not be None
self.assertIsNotNone(importer, 'Import should not be none')
def invalid_import_source_test(self):
"""
Test OpenSongImport.do_import handles different invalid import_source values
"""
# GIVEN: A mocked out SongImport class, and a mocked out "manager"
with patch('openlp.plugins.songs.lib.opensongimport.SongImport'):
mocked_manager = MagicMock()
mocked_import_wizard = MagicMock()
importer = OpenSongImport(mocked_manager, filenames=[])
importer.import_wizard = mocked_import_wizard
importer.stop_import_flag = True
# WHEN: Import source is not a list
for source in ['not a list', 0]:
importer.import_source = source
# THEN: do_import should return none and the progress bar maximum should not be set.
self.assertIsNone(importer.do_import(), 'do_import should return None when import_source is not a list')
self.assertEqual(mocked_import_wizard.progress_bar.setMaximum.called, False,
'setMaximum on import_wizard.progress_bar should not have been called')
def valid_import_source_test(self):
"""
Test OpenSongImport.do_import handles different invalid import_source values
"""
# GIVEN: A mocked out SongImport class, and a mocked out "manager"
with patch('openlp.plugins.songs.lib.opensongimport.SongImport'):
mocked_manager = MagicMock()
mocked_import_wizard = MagicMock()
importer = OpenSongImport(mocked_manager, filenames=[])
importer.import_wizard = mocked_import_wizard
importer.stop_import_flag = True
# WHEN: Import source is a list
importer.import_source = ['List', 'of', 'files']
# THEN: do_import should return none and the progress bar setMaximum should be called with the length of
# import_source.
self.assertIsNone(importer.do_import(), 'do_import should return None when import_source is a list '
'and stop_import_flag is True')
mocked_import_wizard.progress_bar.setMaximum.assert_called_with(len(importer.import_source))

View File

@ -0,0 +1,54 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2013 Raoul Snyman #
# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan #
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
The :mod:`propresenterimport` module provides the functionality for importing
ProPresenter song files into the current installation database.
"""
import os
from tests.helpers.songfileimport import SongImportTestHelper
TEST_PATH = os.path.abspath(
os.path.join(os.path.dirname(__file__), '..', '..', '..', 'resources', 'propresentersongs'))
class TestProPresenterFileImport(SongImportTestHelper):
def __init__(self, *args, **kwargs):
self.importer_class_name = 'ProPresenterImport'
self.importer_module_name = 'propresenterimport'
super(TestProPresenterFileImport, self).__init__(*args, **kwargs)
def test_song_import(self):
"""
Test that loading an ProPresenter file works correctly
"""
self.file_import(os.path.join(TEST_PATH, 'Amazing Grace.pro4'),
self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace.json')))

View File

@ -344,7 +344,7 @@ class TestSongSelect(TestCase):
mocked_db_manager.get_object_filtered.assert_called_with(MockedAuthor, False)
MockedAuthor.populate.assert_called_with(first_name='Public', last_name='Domain',
display_name='Public Domain')
self.assertEqual(1, len(result.authors), 'There should only be one author')
self.assertEqual(1, len(result.authors_songs), 'There should only be one author')
def save_song_existing_author_test(self):
"""
@ -379,4 +379,4 @@ class TestSongSelect(TestCase):
'The save_object() method should have been called twice')
mocked_db_manager.get_object_filtered.assert_called_with(MockedAuthor, False)
self.assertEqual(0, MockedAuthor.populate.call_count, 'A new author should not have been instantiated')
self.assertEqual(1, len(result.authors), 'There should only be one author')
self.assertEqual(1, len(result.authors_songs), 'There should only be one author')

View File

@ -46,7 +46,7 @@ class TestZionWorxImport(TestCase):
Test creating an instance of the ZionWorx file importer
"""
# GIVEN: A mocked out SongImport class, and a mocked out "manager"
with patch('openlp.plugins.songs.lib.songbeamerimport.SongImport'):
with patch('openlp.plugins.songs.lib.zionworximport.SongImport'):
mocked_manager = MagicMock()
# WHEN: An importer object is created

View File

@ -33,7 +33,7 @@ song files from third party applications.
import json
from unittest import TestCase
from tests.functional import patch, MagicMock
from tests.functional import patch, MagicMock, call
class SongImportTestHelper(TestCase):
@ -56,13 +56,13 @@ class SongImportTestHelper(TestCase):
'openlp.plugins.songs.lib.%s.%s.add_verse' % (self.importer_module_name, self.importer_class_name))
self.finish_patcher = patch(
'openlp.plugins.songs.lib.%s.%s.finish' % (self.importer_module_name, self.importer_class_name))
self.parse_author_patcher = patch(
'openlp.plugins.songs.lib.%s.%s.parse_author' % (self.importer_module_name, self.importer_class_name))
self.add_author_patcher = patch(
'openlp.plugins.songs.lib.%s.%s.add_author' % (self.importer_module_name, self.importer_class_name))
self.song_import_patcher = patch('openlp.plugins.songs.lib.%s.SongImport' % self.importer_module_name)
self.mocked_add_copyright = self.add_copyright_patcher.start()
self.mocked_add_verse = self.add_verse_patcher.start()
self.mocked_finish = self.finish_patcher.start()
self.mocked_parse_author = self.parse_author_patcher.start()
self.mocked_add_author = self.add_author_patcher.start()
self.mocked_song_importer = self.song_import_patcher.start()
self.mocked_manager = MagicMock()
self.mocked_import_wizard = MagicMock()
@ -75,7 +75,7 @@ class SongImportTestHelper(TestCase):
self.add_copyright_patcher.stop()
self.add_verse_patcher.stop()
self.finish_patcher.stop()
self.parse_author_patcher.stop()
self.add_author_patcher.stop()
self.song_import_patcher.stop()
def load_external_result_data(self, file_name):
@ -112,14 +112,17 @@ class SongImportTestHelper(TestCase):
self.assertIsNone(importer.do_import(), 'do_import should return None when it has completed')
self.assertEqual(importer.title, title, 'title for %s should be "%s"' % (source_file_name, title))
for author in author_calls:
self.mocked_parse_author.assert_any_call(author)
self.mocked_add_author.assert_any_call(author)
if song_copyright:
self.mocked_add_copyright.assert_called_with(song_copyright)
if ccli_number:
self.assertEqual(importer.ccli_number, ccli_number,
'ccli_number for %s should be %s' % (source_file_name, ccli_number))
expected_calls = []
for verse_text, verse_tag in add_verse_calls:
self.mocked_add_verse.assert_any_call(verse_text, verse_tag)
expected_calls.append(call(verse_text, verse_tag))
self.mocked_add_verse.assert_has_calls(expected_calls, any_order=False)
if topics:
self.assertEqual(importer.topics, topics, 'topics for %s should be %s' % (source_file_name, topics))
if comments:
@ -132,7 +135,7 @@ class SongImportTestHelper(TestCase):
self.assertEqual(importer.song_number, song_number,
'song_number for %s should be %s' % (source_file_name, song_number))
if verse_order_list:
self.assertEqual(importer.verse_order_list, [],
self.assertEqual(importer.verse_order_list, verse_order_list,
'verse_order_list for %s should be %s' % (source_file_name, verse_order_list))
self.mocked_finish.assert_called_with()

View File

@ -88,7 +88,7 @@ class TestPluginManager(TestCase, TestMixin):
plugin_names = [plugin.name for plugin in plugin_manager.plugins]
assert 'songs' in plugin_names, 'There should be a "songs" plugin.'
assert 'bibles' in plugin_names, 'There should be a "bibles" plugin.'
assert 'presentations' not in plugin_names, 'There should NOT be a "presentations" plugin.'
assert 'presentations' in plugin_names, 'There should be a "presentations" plugin.'
assert 'images' in plugin_names, 'There should be a "images" plugin.'
assert 'media' in plugin_names, 'There should be a "media" plugin.'
assert 'custom' in plugin_names, 'There should be a "custom" plugin.'

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<!--Visit the online documentation for Zefania XML Markup-->
<!--http://bgfdb.de/zefaniaxml/bml/-->
<!--Download another Zefania XML files from-->
<!--http://sourceforge.net/projects/zefania-sharp-->
<bible>
<!-- The INFORMATION-tag is a leftover from the original zefania bible, but is ignored by opensong format -->
<INFORMATION>
<title>Danish Version</title>
<creator>
</creator>
<subject>The Holy Bible</subject>
<description>The electronic edition of this Bible comes from the Danish 1933 edition. The Old Testament is an update from the 1931 edition, and the New Testament is an update from the 1907 edition.</description>
<publisher>Free Bible Software Group</publisher>
<contributors>
The Unbound Bible
Biola University: Administrative Computing
13800 Biola Ave.
La Mirada, CA 90639
United States of America
562-903-4722
</contributors>
<date>2009-01-20</date>
<format>Zefania XML Bible Markup Language</format>
<identifier>DAN</identifier>
<source>ftp://unboundftp.biola.edu/pub/danish1933_utf8.zip</source>
<language>DAN</language>
<coverage>provide the bible to the world</coverage>
<rights></rights>
<type />
</INFORMATION>
<b n="Genesis">
<c n="1">
<v n="1">I Begyndelsen skabte Gud Himmelen og Jorden.</v>
<v n="2">Og Jorden var øde og tom, og der var Mørke over Verdensdybet. Men Guds Ånd svævede over Vandene.</v>
<v n="3">Og Gud sagde: "Der blive Lys!" Og der blev Lys.</v>
<v n="4">Og Gud så, at Lyset var godt, og Gud satte Skel mellem Lyset og Mørket,</v>
<v n="5">og Gud kaldte Lyset Dag, og Mørket kaldte han Nat. Og det blev Aften, og det blev Morgen, første Dag.</v>
<v n="6">Derpå sagde Gud: "Der blive en Hvælving midt i Vandene til at skille Vandene ad!"</v>
<v n="7">Og således skete det: Gud gjorde Hvælvingen og skilte Vandet under Hvælvingen fra Vandet over Hvælvingen;</v>
<v n="8">og Gud kaldte Hvælvingen Himmel. Og det blev Aften, og det blev Morgen, anden Dag.</v>
<v n="9">Derpå sagde Gud: "Vandet under Himmelen samle sig på eet Sted, så det faste Land kommer til Syne!" Og således skete det;</v>
<v n="10">og Gud kaldte det faste Land Jord, og Stedet, hvor Vandet samlede sig, kaldte han Hav. Og Gud så, at det var godt.</v>
</c>
</b>
</bible>

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<!--Visit the online documentation for Zefania XML Markup-->
<!--http://bgfdb.de/zefaniaxml/bml/-->
<!--Download another Zefania XML files from-->
<!--http://sourceforge.net/projects/zefania-sharp-->
<XMLBIBLE xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="C:\Users\tgc\Downloads\zef2005.xsd" biblename="Danish Version" status="v" version="2.0.1.18" type="x-bible" revision="0">
<INFORMATION>
<title>Danish Version</title>
<creator>
</creator>
<subject>The Holy Bible</subject>
<description>The electronic edition of this Bible comes from the Danish 1933 edition. The Old Testament is an update from the 1931 edition, and the New Testament is an update from the 1907 edition.</description>
<publisher>Free Bible Software Group</publisher>
<contributors>
The Unbound Bible
Biola University: Administrative Computing
13800 Biola Ave.
La Mirada, CA 90639
United States of America
562-903-4722
</contributors>
<date>2009-01-20</date>
<format>Zefania XML Bible Markup Language</format>
<identifier>DAN</identifier>
<source>ftp://unboundftp.biola.edu/pub/danish1933_utf8.zip</source>
<language>DAN</language>
<coverage>provide the bible to the world</coverage>
<rights></rights>
<type />
</INFORMATION>
<BIBLEBOOK bnumber="1" bname="Genesis" bsname="1Mo">
<CHAPTER cnumber="1">
<VERS vnumber="1">I <STYLE css="font-style:italic">Begyndelsen</STYLE> skabte Gud Himmelen og Jorden.</VERS>
<VERS vnumber="2">Og Jorden var øde og tom, og der var Mørke over Verdensdybet. Men Guds Ånd svævede over Vandene.</VERS>
<VERS vnumber="3">Og Gud sagde: "Der blive Lys!" Og der blev Lys.</VERS>
<VERS vnumber="4">Og Gud så, at Lyset var godt, og Gud satte Skel mellem Lyset og Mørket,</VERS>
<VERS vnumber="5">og Gud kaldte Lyset Dag, og Mørket kaldte han Nat. Og det blev Aften, og det blev Morgen, første Dag.</VERS>
<VERS vnumber="6">Derpå sagde Gud: "Der blive en Hvælving midt i Vandene til at skille Vandene ad!"</VERS>
<VERS vnumber="7">Og således skete det: Gud gjorde Hvælvingen og skilte Vandet under Hvælvingen fra Vandet over Hvælvingen;</VERS>
<VERS vnumber="8">og Gud kaldte Hvælvingen Himmel. Og det blev Aften, og det blev Morgen, anden Dag.</VERS>
<VERS vnumber="9">Derpå sagde Gud: "Vandet under Himmelen samle sig på eet Sted, så det faste Land kommer til Syne!" Og således skete det;</VERS>
<VERS vnumber="10">og Gud kaldte det faste Land Jord, og Stedet, hvor Vandet samlede sig, kaldte han Hav. Og Gud så, at det var godt.</VERS>
</CHAPTER>
</BIBLEBOOK>
</XMLBIBLE>

View File

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?>
<song>
<title>Amazing Grace (Demonstration)</title>
<author>John Newton, Edwin Excell &amp; John P. Rees</author>
<copyright>Public Domain </copyright>
<presentation>V1 V2 V3 V4 V5</presentation>
<capo print="false"></capo>
<tempo></tempo>
<ccli>22025</ccli>
<theme>God: Assurance/Grace/Salvation</theme>
<alttheme>Worship: Praise</alttheme>
<user1> </user1>
<user2> </user2>
<user3> </user3>
<lyrics>[V]
;Test the chords format
;Chords beging with .
;Verses begin with their verse number
;Link words with _
;Comments begin with ;
. D D7 G D
1A______ma________zing grace! How sweet the sound!
2'Twas grace that taught my heart to fear,
3The Lord has pro____mised good to me,
4Thro' ma________ny dan____gers, toils and snares
5When we've been there ten thou__sand years,
. Bm E A A7
1That saved a wretch like me!
2And grace my fears re___lieved.
3His Word my hope se___cures.
4I have al___rea____dy come.
5Bright shi___ning as the sun,
. D D7 G D
1I once was lost, but now am found;
2How pre___cious did that grace ap____pear,
3He will my shield and por___tion be
4'Tis grace that brought me safe thus far,
5We've no less days to sing God's praise,
. Bm A G D
1Was blind, but now I see.
2The hour I first be_lieved.
3As long as life en_dures.
4And grace will lead me home.
5Than when we first be_gun.
</lyrics>
<hymn_number>Demonstration Songs 0</hymn_number>
<key></key>
<aka></aka>
<key_line></key_line>
<time_sig></time_sig>
<style index="default_style"></style>
</song>

View File

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

View File

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?>
<song>
<title>Beautiful Garden Of Prayer (Demonstration)</title>
<author>Eleanor Allen Schroll &amp; James H. Fillmore</author>
<copyright>Public Domain </copyright>
<presentation>V1 C V2 C V3 C</presentation>
<capo print="false"></capo>
<tempo></tempo>
<ccli>60252</ccli>
<theme>God: Prayer/Devotion</theme>
<alttheme>Prayer: Prayer/Devotion</alttheme>
<user1></user1>
<user2></user2>
<user3></user3>
<lyrics>
;Test breaks and newlines
;A single | on the end of a line adds an extra \n
;Blank lines are ignored, even with a space prefix
;We also check that the chorus is added after the verses, despite the order in the file
[V1]
There's a garden where Jesus is waiting,
There's a place that is wondrously fair.
For it glows with the light of His presence,|
'Tis the beautiful garden of prayer.
;A double || on a line adds a new slide
[C]
O the beautiful garden, the garden of prayer,
O the beautiful garden of prayer.
There my Savior awaits, and He opens the gates
||
To the beautiful garden of prayer.
;A double || on the end of a line adds a new slide
[V2]
There's a garden where Jesus is waiting,
And I go with my burden and care.
Just to learn from His lips, words of comfort,||
In the beautiful garden of prayer.
;A single | on a line adds just one line break
[V3]
There's a garden where Jesus is waiting,
And He bids you to come meet Him there,
Just to bow and receive a new blessing,
|
In the beautiful garden of prayer.
</lyrics>
<hymn_number>DS0</hymn_number>
<key></key>
<aka></aka>
<key_line></key_line>
<time_sig></time_sig>
<style index="default_style"></style>
</song>

View File

@ -0,0 +1,35 @@
{
"authors": [
"Eleanor Allen Schroll",
"James H. Fillmore"
],
"ccli_number": 60252,
"comments": "",
"copyright": "Public Domain ",
"song_book_name": "DS",
"song_number": 0,
"title": "Beautiful Garden Of Prayer (Demonstration)",
"topics": [
"Devotion",
"Prayer"
],
"verse_order_list": ["v1", "c1", "v2", "c1", "v3", "c1"],
"verses": [
[
"There's a garden where Jesus is waiting,\nThere's a place that is wondrously fair.\nFor it glows with the light of His presence,\n\n'Tis the beautiful garden of prayer.",
"v1"
],
[
"There's a garden where Jesus is waiting,\nAnd I go with my burden and care.\nJust to learn from His lips, words of comfort,\n[---]\nIn the beautiful garden of prayer.",
"v2"
],
[
"There's a garden where Jesus is waiting,\nAnd He bids you to come meet Him there,\nJust to bow and receive a new blessing,\n\nIn the beautiful garden of prayer.",
"v3"
],
[
"O the beautiful garden, the garden of prayer,\nO the beautiful garden of prayer.\nThere my Savior awaits, and He opens the gates\n[---]\nTo the beautiful garden of prayer.",
"c1"
]
]
}

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<song>
<title>12345</title>
<author>Traditional</author>
<copyright>Public Domain </copyright>
<presentation>T</presentation>
<capo print="false"></capo>
<ccli></ccli>
<tempo></tempo>
<theme></theme>
<alttheme></alttheme>
<user1></user1>
<user2></user2>
<user3></user3>
<lyrics>
;Test [T]ag element - should be turned into [o]ther
;And lines beginning with numbers
;And a title that contains only numeric characters
;That isdiffernt to the filename
;And most elements are empty
[T]
1, 2, 3, 4, 5,
Once I caught a fish alive.
6, 7, 8, 9, 10,
Then I let it go again.
Why did you let it go?
Because it bit my finger so.
Which finger did it bite?
This little finger on my right.
</lyrics>
<hymn_number></hymn_number>
<key></key>
<aka></aka>
<key_line></key_line>
<time_sig></time_sig>
<style index="default_style"></style>
</song>

View File

@ -0,0 +1,17 @@
{
"authors": [
"Traditional"
],
"comments": "",
"copyright": "Public Domain ",
"title": "12345",
"topics": [
],
"verse_order_list": ["o1"],
"verses": [
[
"1, 2, 3, 4, 5,\nOnce I caught a fish alive.\n6, 7, 8, 9, 10,\nThen I let it go again.\nWhy did you let it go?\nBecause it bit my finger so.\nWhich finger did it bite?\nThis little finger on my right.",
"o1"
]
]
}

View File

@ -0,0 +1,121 @@
{
"authors": [
"John Newton"
],
"title": "Amazing Grace",
"verse_order_list": [],
"verses": [
[
"Amazing grace! How sweet the sound\n",
"v1"
],
[
"That saved a wretch like me!\n",
"v2"
],
[
"I once was lost, but now am found;\n",
"v3"
],
[
"Was blind, but now I see.\n",
"v4"
],
[
"'Twas grace that taught my heart to fear,\n",
"v5"
],
[
"And grace my fears relieved;\n",
"v6"
],
[
"How precious did that grace appear\n",
"v7"
],
[
"The hour I first believed.\n",
"v8"
],
[
"Through many dangers, toils and snares,\n",
"v9"
],
[
"I have already come;\n",
"v10"
],
[
"'Tis grace hath brought me safe thus far,\n",
"v11"
],
[
"And grace will lead me home.\n",
"v12"
],
[
"The Lord has promised good to me,\n",
"v13"
],
[
"His Word my hope secures;\n",
"v14"
],
[
"He will my Shield and Portion be,\n",
"v15"
],
[
"As long as life endures.\n",
"v16"
],
[
"Yea, when this flesh and heart shall fail,\n",
"v17"
],
[
"And mortal life shall cease,\n",
"v18"
],
[
"I shall possess, within the veil,\n",
"v19"
],
[
"A life of joy and peace.\n",
"v20"
],
[
"The earth shall soon dissolve like snow,\n",
"v21"
],
[
"The sun forbear to shine;\n",
"v22"
],
[
"But God, Who called me here below,\n",
"v23"
],
[
"Shall be forever mine.\n",
"v24"
],
[
"When we've been there ten thousand years,\n",
"v25"
],
[
"Bright shining as the sun,\n",
"v26"
],
[
"We've no less days to sing God's praise\n",
"v27"
],
[
"Than when we'd first begun.\n",
"v28"
]
]
}

View File

@ -0,0 +1,486 @@
<?xml version="1.0"?>
<RVPresentationDocument height="768" width="1024" versionNumber="401" creatorCode="1349676880" lastDateUsed="2014-06-09T07:47:29+0000" category="Hymn" backgroundColor="0 0 0 1" drawingBackgroundColor="0" notes="" artist="John Newton" author="" album="" CCLIDisplay="0" CCLIArtistCredits="" CCLISongTitle="Amazing Grace" CCLIPublisher="" CCLICopyrightInfo="" CCLILicenseNumber="" resourcesDirectory="">
<slides containerClass="NSMutableArray">
<RVDisplaySlide backgroundColor="0 0 0 0" enabled="1" highlightColor="0 0 0 0" hotKey="" label="" notes="" slideType="1" sort_index="0" UUID="A67250CF-0DCC-4169-80EA-7E417CC233B3" drawingBackgroundColor="0" serialization-array-index="0">
<cues containerClass="NSMutableArray" />
<displayElements containerClass="NSMutableArray">
<RVTextElement displayDelay="0" locked="0" persistent="0" typeID="0" fromTemplate="0" bezelRadius="0" drawingFill="0" drawingShadow="1" drawingStroke="0" fillColor="0 0 0 0" rotation="0" source="" displayName="" adjustsHeightToFit="0" verticalAlignment="0" RTFData="e1xydGYxXHByb3J0ZjFcYW5zaVxhbnNpY3BnMTI1Mlx1YzFcaHRtYXV0c3BcZGVmZjJ7XGZvbnR0Ymx7XGYwXGZjaGFyc2V0MCBUaW1lcyBOZXcgUm9tYW47fXtcZjJcZmNoYXJzZXQwIEdlb3JnaWE7fXtcZjNcZmNoYXJzZXQwIEhlbHZldGljYTt9fXtcY29sb3J0Ymw7XHJlZDBcZ3JlZW4wXGJsdWUwO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTt9XGxvY2hcaGljaFxkYmNoXHBhcmRcc2xsZWFkaW5nMFxwbGFpblxsdHJwYXJcaXRhcDB7XGxhbmcxMDMzXGZzMTIwXGYzXGNmMSBcY2YxXHFse1xmMyB7XGNmMlxsdHJjaCBBbWF6aW5nIGdyYWNlISBIb3cgc3dlZXQgdGhlIHNvdW5kfVxsaTBcc2EwXHNiMFxmaTBccWNccGFyfQ0KfQ0KfQ==" serialization-array-index="0">
<_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" />
<_-D-_serializedShadow containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="shadowColor" />
<NSMutableString serialization-native-value="{0, 0}" serialization-dictionary-key="shadowOffset" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="shadowBlurRadius" />
</_-D-_serializedShadow>
<stroke containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="RVShapeElementStrokeColorKey" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="RVShapeElementStrokeWidthKey" />
</stroke>
</RVTextElement>
</displayElements>
</RVDisplaySlide>
<RVDisplaySlide backgroundColor="0 0 0 0" enabled="1" highlightColor="0 0 0 0" hotKey="" label="" notes="" slideType="1" sort_index="1" UUID="B09CAF20-B402-43C3-AA35-C09B1A2CEB25" drawingBackgroundColor="0" serialization-array-index="1">
<cues containerClass="NSMutableArray" />
<displayElements containerClass="NSMutableArray">
<RVTextElement displayDelay="0" locked="0" persistent="0" typeID="0" fromTemplate="0" bezelRadius="0" drawingFill="0" drawingShadow="1" drawingStroke="0" fillColor="0 0 0 0" rotation="0" source="" displayName="" adjustsHeightToFit="0" verticalAlignment="0" RTFData="e1xydGYxXHByb3J0ZjFcYW5zaVxhbnNpY3BnMTI1Mlx1YzFcaHRtYXV0c3BcZGVmZjJ7XGZvbnR0Ymx7XGYwXGZjaGFyc2V0MCBUaW1lcyBOZXcgUm9tYW47fXtcZjJcZmNoYXJzZXQwIEdlb3JnaWE7fXtcZjNcZmNoYXJzZXQwIEhlbHZldGljYTt9fXtcY29sb3J0Ymw7XHJlZDBcZ3JlZW4wXGJsdWUwO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTt9XGxvY2hcaGljaFxkYmNoXHBhcmRcc2xsZWFkaW5nMFxwbGFpblxsdHJwYXJcaXRhcDB7XGxhbmcxMDMzXGZzMTIwXGYzXGNmMSBcY2YxXHFse1xmMyB7XGNmMlxsdHJjaCBUaGF0IHNhdmVkIGEgd3JldGNoIGxpa2UgbWUhfVxsaTBcc2EwXHNiMFxmaTBccWNccGFyfQ0KfQ0KfQ==" serialization-array-index="0">
<_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" />
<_-D-_serializedShadow containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="shadowColor" />
<NSMutableString serialization-native-value="{0, 0}" serialization-dictionary-key="shadowOffset" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="shadowBlurRadius" />
</_-D-_serializedShadow>
<stroke containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="RVShapeElementStrokeColorKey" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="RVShapeElementStrokeWidthKey" />
</stroke>
</RVTextElement>
</displayElements>
</RVDisplaySlide>
<RVDisplaySlide backgroundColor="0 0 0 0" enabled="1" highlightColor="0 0 0 0" hotKey="" label="" notes="" slideType="1" sort_index="2" UUID="CCF908FC-0FE4-4C16-BE70-9543C2A76DC7" drawingBackgroundColor="0" serialization-array-index="2">
<cues containerClass="NSMutableArray" />
<displayElements containerClass="NSMutableArray">
<RVTextElement displayDelay="0" locked="0" persistent="0" typeID="0" fromTemplate="0" bezelRadius="0" drawingFill="0" drawingShadow="1" drawingStroke="0" fillColor="0 0 0 0" rotation="0" source="" displayName="" adjustsHeightToFit="0" verticalAlignment="0" RTFData="e1xydGYxXHByb3J0ZjFcYW5zaVxhbnNpY3BnMTI1Mlx1YzFcaHRtYXV0c3BcZGVmZjJ7XGZvbnR0Ymx7XGYwXGZjaGFyc2V0MCBUaW1lcyBOZXcgUm9tYW47fXtcZjJcZmNoYXJzZXQwIEdlb3JnaWE7fXtcZjNcZmNoYXJzZXQwIEhlbHZldGljYTt9fXtcY29sb3J0Ymw7XHJlZDBcZ3JlZW4wXGJsdWUwO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTt9XGxvY2hcaGljaFxkYmNoXHBhcmRcc2xsZWFkaW5nMFxwbGFpblxsdHJwYXJcaXRhcDB7XGxhbmcxMDMzXGZzMTIwXGYzXGNmMSBcY2YxXHFse1xmMyB7XGNmMlxsdHJjaCBJIG9uY2Ugd2FzIGxvc3QsIGJ1dCBub3cgYW0gZm91bmQ7fVxsaTBcc2EwXHNiMFxmaTBccWNccGFyfQ0KfQ0KfQ==" serialization-array-index="0">
<_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" />
<_-D-_serializedShadow containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="shadowColor" />
<NSMutableString serialization-native-value="{0, 0}" serialization-dictionary-key="shadowOffset" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="shadowBlurRadius" />
</_-D-_serializedShadow>
<stroke containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="RVShapeElementStrokeColorKey" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="RVShapeElementStrokeWidthKey" />
</stroke>
</RVTextElement>
</displayElements>
</RVDisplaySlide>
<RVDisplaySlide backgroundColor="0 0 0 0" enabled="1" highlightColor="0 0 0 0" hotKey="" label="" notes="" slideType="1" sort_index="3" UUID="B33AF602-61BB-47FE-95E3-98640D6BD76A" drawingBackgroundColor="0" serialization-array-index="3">
<cues containerClass="NSMutableArray" />
<displayElements containerClass="NSMutableArray">
<RVTextElement displayDelay="0" locked="0" persistent="0" typeID="0" fromTemplate="0" bezelRadius="0" drawingFill="0" drawingShadow="1" drawingStroke="0" fillColor="0 0 0 0" rotation="0" source="" displayName="" adjustsHeightToFit="0" verticalAlignment="0" RTFData="e1xydGYxXHByb3J0ZjFcYW5zaVxhbnNpY3BnMTI1Mlx1YzFcaHRtYXV0c3BcZGVmZjJ7XGZvbnR0Ymx7XGYwXGZjaGFyc2V0MCBUaW1lcyBOZXcgUm9tYW47fXtcZjJcZmNoYXJzZXQwIEdlb3JnaWE7fXtcZjNcZmNoYXJzZXQwIEhlbHZldGljYTt9fXtcY29sb3J0Ymw7XHJlZDBcZ3JlZW4wXGJsdWUwO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTt9XGxvY2hcaGljaFxkYmNoXHBhcmRcc2xsZWFkaW5nMFxwbGFpblxsdHJwYXJcaXRhcDB7XGxhbmcxMDMzXGZzMTIwXGYzXGNmMSBcY2YxXHFse1xmMyB7XGNmMlxsdHJjaCBXYXMgYmxpbmQsIGJ1dCBub3cgSSBzZWUufVxsaTBcc2EwXHNiMFxmaTBccWNccGFyfQ0KfQ0KfQ==" serialization-array-index="0">
<_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" />
<_-D-_serializedShadow containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="shadowColor" />
<NSMutableString serialization-native-value="{0, 0}" serialization-dictionary-key="shadowOffset" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="shadowBlurRadius" />
</_-D-_serializedShadow>
<stroke containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="RVShapeElementStrokeColorKey" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="RVShapeElementStrokeWidthKey" />
</stroke>
</RVTextElement>
</displayElements>
</RVDisplaySlide>
<RVDisplaySlide backgroundColor="0 0 0 0" enabled="1" highlightColor="0 0 0 0" hotKey="" label="" notes="" slideType="1" sort_index="4" UUID="02932C9A-C505-4856-A48E-339CA927B020" drawingBackgroundColor="0" serialization-array-index="4">
<cues containerClass="NSMutableArray" />
<displayElements containerClass="NSMutableArray">
<RVTextElement displayDelay="0" locked="0" persistent="0" typeID="0" fromTemplate="0" bezelRadius="0" drawingFill="0" drawingShadow="1" drawingStroke="0" fillColor="0 0 0 0" rotation="0" source="" displayName="" adjustsHeightToFit="0" verticalAlignment="0" RTFData="e1xydGYxXHByb3J0ZjFcYW5zaVxhbnNpY3BnMTI1Mlx1YzFcaHRtYXV0c3BcZGVmZjJ7XGZvbnR0Ymx7XGYwXGZjaGFyc2V0MCBUaW1lcyBOZXcgUm9tYW47fXtcZjJcZmNoYXJzZXQwIEdlb3JnaWE7fXtcZjNcZmNoYXJzZXQwIEhlbHZldGljYTt9fXtcY29sb3J0Ymw7XHJlZDBcZ3JlZW4wXGJsdWUwO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTt9XGxvY2hcaGljaFxkYmNoXHBhcmRcc2xsZWFkaW5nMFxwbGFpblxsdHJwYXJcaXRhcDB7XGxhbmcxMDMzXGZzMTIwXGYzXGNmMSBcY2YxXHFse1xmMyB7XGNmMlxsdHJjaCAnVHdhcyBncmFjZSB0aGF0IHRhdWdodCBteSBoZWFydCB0byBmZWFyLH1cbGkwXHNhMFxzYjBcZmkwXHFjXHBhcn0NCn0NCn0=" serialization-array-index="0">
<_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" />
<_-D-_serializedShadow containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="shadowColor" />
<NSMutableString serialization-native-value="{0, 0}" serialization-dictionary-key="shadowOffset" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="shadowBlurRadius" />
</_-D-_serializedShadow>
<stroke containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="RVShapeElementStrokeColorKey" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="RVShapeElementStrokeWidthKey" />
</stroke>
</RVTextElement>
</displayElements>
</RVDisplaySlide>
<RVDisplaySlide backgroundColor="0 0 0 0" enabled="1" highlightColor="0 0 0 0" hotKey="" label="" notes="" slideType="1" sort_index="5" UUID="F6FC7C3F-2A38-4D63-83B1-7EDA30371535" drawingBackgroundColor="0" serialization-array-index="5">
<cues containerClass="NSMutableArray" />
<displayElements containerClass="NSMutableArray">
<RVTextElement displayDelay="0" locked="0" persistent="0" typeID="0" fromTemplate="0" bezelRadius="0" drawingFill="0" drawingShadow="1" drawingStroke="0" fillColor="0 0 0 0" rotation="0" source="" displayName="" adjustsHeightToFit="0" verticalAlignment="0" RTFData="e1xydGYxXHByb3J0ZjFcYW5zaVxhbnNpY3BnMTI1Mlx1YzFcaHRtYXV0c3BcZGVmZjJ7XGZvbnR0Ymx7XGYwXGZjaGFyc2V0MCBUaW1lcyBOZXcgUm9tYW47fXtcZjJcZmNoYXJzZXQwIEdlb3JnaWE7fXtcZjNcZmNoYXJzZXQwIEhlbHZldGljYTt9fXtcY29sb3J0Ymw7XHJlZDBcZ3JlZW4wXGJsdWUwO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTt9XGxvY2hcaGljaFxkYmNoXHBhcmRcc2xsZWFkaW5nMFxwbGFpblxsdHJwYXJcaXRhcDB7XGxhbmcxMDMzXGZzMTIwXGYzXGNmMSBcY2YxXHFse1xmMyB7XGNmMlxsdHJjaCBBbmQgZ3JhY2UgbXkgZmVhcnMgcmVsaWV2ZWQ7fVxsaTBcc2EwXHNiMFxmaTBccWNccGFyfQ0KfQ0KfQ==" serialization-array-index="0">
<_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" />
<_-D-_serializedShadow containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="shadowColor" />
<NSMutableString serialization-native-value="{0, 0}" serialization-dictionary-key="shadowOffset" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="shadowBlurRadius" />
</_-D-_serializedShadow>
<stroke containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="RVShapeElementStrokeColorKey" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="RVShapeElementStrokeWidthKey" />
</stroke>
</RVTextElement>
</displayElements>
</RVDisplaySlide>
<RVDisplaySlide backgroundColor="0 0 0 0" enabled="1" highlightColor="0 0 0 0" hotKey="" label="" notes="" slideType="1" sort_index="6" UUID="EEBFE94B-D0F2-4E41-BBE7-B5A7D4F62243" drawingBackgroundColor="0" serialization-array-index="6">
<cues containerClass="NSMutableArray" />
<displayElements containerClass="NSMutableArray">
<RVTextElement displayDelay="0" locked="0" persistent="0" typeID="0" fromTemplate="0" bezelRadius="0" drawingFill="0" drawingShadow="1" drawingStroke="0" fillColor="0 0 0 0" rotation="0" source="" displayName="" adjustsHeightToFit="0" verticalAlignment="0" RTFData="e1xydGYxXHByb3J0ZjFcYW5zaVxhbnNpY3BnMTI1Mlx1YzFcaHRtYXV0c3BcZGVmZjJ7XGZvbnR0Ymx7XGYwXGZjaGFyc2V0MCBUaW1lcyBOZXcgUm9tYW47fXtcZjJcZmNoYXJzZXQwIEdlb3JnaWE7fXtcZjNcZmNoYXJzZXQwIEhlbHZldGljYTt9fXtcY29sb3J0Ymw7XHJlZDBcZ3JlZW4wXGJsdWUwO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTt9XGxvY2hcaGljaFxkYmNoXHBhcmRcc2xsZWFkaW5nMFxwbGFpblxsdHJwYXJcaXRhcDB7XGxhbmcxMDMzXGZzMTIwXGYzXGNmMSBcY2YxXHFse1xmMyB7XGNmMlxsdHJjaCBIb3cgcHJlY2lvdXMgZGlkIHRoYXQgZ3JhY2UgYXBwZWFyfVxsaTBcc2EwXHNiMFxmaTBccWNccGFyfQ0KfQ0KfQ==" serialization-array-index="0">
<_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" />
<_-D-_serializedShadow containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="shadowColor" />
<NSMutableString serialization-native-value="{0, 0}" serialization-dictionary-key="shadowOffset" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="shadowBlurRadius" />
</_-D-_serializedShadow>
<stroke containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="RVShapeElementStrokeColorKey" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="RVShapeElementStrokeWidthKey" />
</stroke>
</RVTextElement>
</displayElements>
</RVDisplaySlide>
<RVDisplaySlide backgroundColor="0 0 0 0" enabled="1" highlightColor="0 0 0 0" hotKey="" label="" notes="" slideType="1" sort_index="7" UUID="0CDDEA18-F48E-4B85-8054-01140192EDC7" drawingBackgroundColor="0" serialization-array-index="7">
<cues containerClass="NSMutableArray" />
<displayElements containerClass="NSMutableArray">
<RVTextElement displayDelay="0" locked="0" persistent="0" typeID="0" fromTemplate="0" bezelRadius="0" drawingFill="0" drawingShadow="1" drawingStroke="0" fillColor="0 0 0 0" rotation="0" source="" displayName="" adjustsHeightToFit="0" verticalAlignment="0" RTFData="e1xydGYxXHByb3J0ZjFcYW5zaVxhbnNpY3BnMTI1Mlx1YzFcaHRtYXV0c3BcZGVmZjJ7XGZvbnR0Ymx7XGYwXGZjaGFyc2V0MCBUaW1lcyBOZXcgUm9tYW47fXtcZjJcZmNoYXJzZXQwIEdlb3JnaWE7fXtcZjNcZmNoYXJzZXQwIEhlbHZldGljYTt9fXtcY29sb3J0Ymw7XHJlZDBcZ3JlZW4wXGJsdWUwO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTt9XGxvY2hcaGljaFxkYmNoXHBhcmRcc2xsZWFkaW5nMFxwbGFpblxsdHJwYXJcaXRhcDB7XGxhbmcxMDMzXGZzMTIwXGYzXGNmMSBcY2YxXHFse1xmMyB7XGNmMlxsdHJjaCBUaGUgaG91ciBJIGZpcnN0IGJlbGlldmVkLn1cbGkwXHNhMFxzYjBcZmkwXHFjXHBhcn0NCn0NCn0=" serialization-array-index="0">
<_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" />
<_-D-_serializedShadow containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="shadowColor" />
<NSMutableString serialization-native-value="{0, 0}" serialization-dictionary-key="shadowOffset" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="shadowBlurRadius" />
</_-D-_serializedShadow>
<stroke containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="RVShapeElementStrokeColorKey" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="RVShapeElementStrokeWidthKey" />
</stroke>
</RVTextElement>
</displayElements>
</RVDisplaySlide>
<RVDisplaySlide backgroundColor="0 0 0 0" enabled="1" highlightColor="0 0 0 0" hotKey="" label="" notes="" slideType="1" sort_index="8" UUID="E1193588-A9B8-4371-8DC0-F16DC6D3B489" drawingBackgroundColor="0" serialization-array-index="8">
<cues containerClass="NSMutableArray" />
<displayElements containerClass="NSMutableArray">
<RVTextElement displayDelay="0" locked="0" persistent="0" typeID="0" fromTemplate="0" bezelRadius="0" drawingFill="0" drawingShadow="1" drawingStroke="0" fillColor="0 0 0 0" rotation="0" source="" displayName="" adjustsHeightToFit="0" verticalAlignment="0" RTFData="e1xydGYxXHByb3J0ZjFcYW5zaVxhbnNpY3BnMTI1Mlx1YzFcaHRtYXV0c3BcZGVmZjJ7XGZvbnR0Ymx7XGYwXGZjaGFyc2V0MCBUaW1lcyBOZXcgUm9tYW47fXtcZjJcZmNoYXJzZXQwIEdlb3JnaWE7fXtcZjNcZmNoYXJzZXQwIEhlbHZldGljYTt9fXtcY29sb3J0Ymw7XHJlZDBcZ3JlZW4wXGJsdWUwO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTt9XGxvY2hcaGljaFxkYmNoXHBhcmRcc2xsZWFkaW5nMFxwbGFpblxsdHJwYXJcaXRhcDB7XGxhbmcxMDMzXGZzMTIwXGYzXGNmMSBcY2YxXHFse1xmMyB7XGNmMlxsdHJjaCBUaHJvdWdoIG1hbnkgZGFuZ2VycywgdG9pbHMgYW5kIHNuYXJlcyx9XGxpMFxzYTBcc2IwXGZpMFxxY1xwYXJ9DQp9DQp9" serialization-array-index="0">
<_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" />
<_-D-_serializedShadow containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="shadowColor" />
<NSMutableString serialization-native-value="{0, 0}" serialization-dictionary-key="shadowOffset" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="shadowBlurRadius" />
</_-D-_serializedShadow>
<stroke containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="RVShapeElementStrokeColorKey" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="RVShapeElementStrokeWidthKey" />
</stroke>
</RVTextElement>
</displayElements>
</RVDisplaySlide>
<RVDisplaySlide backgroundColor="0 0 0 0" enabled="1" highlightColor="0 0 0 0" hotKey="" label="" notes="" slideType="1" sort_index="9" UUID="EBD1940D-7360-4E04-9E8F-E752B1F9ECBF" drawingBackgroundColor="0" serialization-array-index="9">
<cues containerClass="NSMutableArray" />
<displayElements containerClass="NSMutableArray">
<RVTextElement displayDelay="0" locked="0" persistent="0" typeID="0" fromTemplate="0" bezelRadius="0" drawingFill="0" drawingShadow="1" drawingStroke="0" fillColor="0 0 0 0" rotation="0" source="" displayName="" adjustsHeightToFit="0" verticalAlignment="0" RTFData="e1xydGYxXHByb3J0ZjFcYW5zaVxhbnNpY3BnMTI1Mlx1YzFcaHRtYXV0c3BcZGVmZjJ7XGZvbnR0Ymx7XGYwXGZjaGFyc2V0MCBUaW1lcyBOZXcgUm9tYW47fXtcZjJcZmNoYXJzZXQwIEdlb3JnaWE7fXtcZjNcZmNoYXJzZXQwIEhlbHZldGljYTt9fXtcY29sb3J0Ymw7XHJlZDBcZ3JlZW4wXGJsdWUwO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTt9XGxvY2hcaGljaFxkYmNoXHBhcmRcc2xsZWFkaW5nMFxwbGFpblxsdHJwYXJcaXRhcDB7XGxhbmcxMDMzXGZzMTIwXGYzXGNmMSBcY2YxXHFse1xmMyB7XGNmMlxsdHJjaCBJIGhhdmUgYWxyZWFkeSBjb21lO31cbGkwXHNhMFxzYjBcZmkwXHFjXHBhcn0NCn0NCn0=" serialization-array-index="0">
<_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" />
<_-D-_serializedShadow containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="shadowColor" />
<NSMutableString serialization-native-value="{0, 0}" serialization-dictionary-key="shadowOffset" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="shadowBlurRadius" />
</_-D-_serializedShadow>
<stroke containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="RVShapeElementStrokeColorKey" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="RVShapeElementStrokeWidthKey" />
</stroke>
</RVTextElement>
</displayElements>
</RVDisplaySlide>
<RVDisplaySlide backgroundColor="0 0 0 0" enabled="1" highlightColor="0 0 0 0" hotKey="" label="" notes="" slideType="1" sort_index="10" UUID="76F80153-DF39-4CE8-B186-79D548C596F5" drawingBackgroundColor="0" serialization-array-index="10">
<cues containerClass="NSMutableArray" />
<displayElements containerClass="NSMutableArray">
<RVTextElement displayDelay="0" locked="0" persistent="0" typeID="0" fromTemplate="0" bezelRadius="0" drawingFill="0" drawingShadow="1" drawingStroke="0" fillColor="0 0 0 0" rotation="0" source="" displayName="" adjustsHeightToFit="0" verticalAlignment="0" RTFData="e1xydGYxXHByb3J0ZjFcYW5zaVxhbnNpY3BnMTI1Mlx1YzFcaHRtYXV0c3BcZGVmZjJ7XGZvbnR0Ymx7XGYwXGZjaGFyc2V0MCBUaW1lcyBOZXcgUm9tYW47fXtcZjJcZmNoYXJzZXQwIEdlb3JnaWE7fXtcZjNcZmNoYXJzZXQwIEhlbHZldGljYTt9fXtcY29sb3J0Ymw7XHJlZDBcZ3JlZW4wXGJsdWUwO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTt9XGxvY2hcaGljaFxkYmNoXHBhcmRcc2xsZWFkaW5nMFxwbGFpblxsdHJwYXJcaXRhcDB7XGxhbmcxMDMzXGZzMTIwXGYzXGNmMSBcY2YxXHFse1xmMyB7XGNmMlxsdHJjaCAnVGlzIGdyYWNlIGhhdGggYnJvdWdodCBtZSBzYWZlIHRodXMgZmFyLH1cbGkwXHNhMFxzYjBcZmkwXHFjXHBhcn0NCn0NCn0=" serialization-array-index="0">
<_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" />
<_-D-_serializedShadow containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="shadowColor" />
<NSMutableString serialization-native-value="{0, 0}" serialization-dictionary-key="shadowOffset" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="shadowBlurRadius" />
</_-D-_serializedShadow>
<stroke containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="RVShapeElementStrokeColorKey" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="RVShapeElementStrokeWidthKey" />
</stroke>
</RVTextElement>
</displayElements>
</RVDisplaySlide>
<RVDisplaySlide backgroundColor="0 0 0 0" enabled="1" highlightColor="0 0 0 0" hotKey="" label="" notes="" slideType="1" sort_index="11" UUID="D4E43B14-8E57-44AA-BCD3-0B1F1401A829" drawingBackgroundColor="0" serialization-array-index="11">
<cues containerClass="NSMutableArray" />
<displayElements containerClass="NSMutableArray">
<RVTextElement displayDelay="0" locked="0" persistent="0" typeID="0" fromTemplate="0" bezelRadius="0" drawingFill="0" drawingShadow="1" drawingStroke="0" fillColor="0 0 0 0" rotation="0" source="" displayName="" adjustsHeightToFit="0" verticalAlignment="0" RTFData="e1xydGYxXHByb3J0ZjFcYW5zaVxhbnNpY3BnMTI1Mlx1YzFcaHRtYXV0c3BcZGVmZjJ7XGZvbnR0Ymx7XGYwXGZjaGFyc2V0MCBUaW1lcyBOZXcgUm9tYW47fXtcZjJcZmNoYXJzZXQwIEdlb3JnaWE7fXtcZjNcZmNoYXJzZXQwIEhlbHZldGljYTt9fXtcY29sb3J0Ymw7XHJlZDBcZ3JlZW4wXGJsdWUwO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTt9XGxvY2hcaGljaFxkYmNoXHBhcmRcc2xsZWFkaW5nMFxwbGFpblxsdHJwYXJcaXRhcDB7XGxhbmcxMDMzXGZzMTIwXGYzXGNmMSBcY2YxXHFse1xmMyB7XGNmMlxsdHJjaCBBbmQgZ3JhY2Ugd2lsbCBsZWFkIG1lIGhvbWUufVxsaTBcc2EwXHNiMFxmaTBccWNccGFyfQ0KfQ0KfQ==" serialization-array-index="0">
<_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" />
<_-D-_serializedShadow containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="shadowColor" />
<NSMutableString serialization-native-value="{0, 0}" serialization-dictionary-key="shadowOffset" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="shadowBlurRadius" />
</_-D-_serializedShadow>
<stroke containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="RVShapeElementStrokeColorKey" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="RVShapeElementStrokeWidthKey" />
</stroke>
</RVTextElement>
</displayElements>
</RVDisplaySlide>
<RVDisplaySlide backgroundColor="0 0 0 0" enabled="1" highlightColor="0 0 0 0" hotKey="" label="" notes="" slideType="1" sort_index="12" UUID="D5B3DFC2-6C99-447B-A1E9-815F1550397F" drawingBackgroundColor="0" serialization-array-index="12">
<cues containerClass="NSMutableArray" />
<displayElements containerClass="NSMutableArray">
<RVTextElement displayDelay="0" locked="0" persistent="0" typeID="0" fromTemplate="0" bezelRadius="0" drawingFill="0" drawingShadow="1" drawingStroke="0" fillColor="0 0 0 0" rotation="0" source="" displayName="" adjustsHeightToFit="0" verticalAlignment="0" RTFData="e1xydGYxXHByb3J0ZjFcYW5zaVxhbnNpY3BnMTI1Mlx1YzFcaHRtYXV0c3BcZGVmZjJ7XGZvbnR0Ymx7XGYwXGZjaGFyc2V0MCBUaW1lcyBOZXcgUm9tYW47fXtcZjJcZmNoYXJzZXQwIEdlb3JnaWE7fXtcZjNcZmNoYXJzZXQwIEhlbHZldGljYTt9fXtcY29sb3J0Ymw7XHJlZDBcZ3JlZW4wXGJsdWUwO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTt9XGxvY2hcaGljaFxkYmNoXHBhcmRcc2xsZWFkaW5nMFxwbGFpblxsdHJwYXJcaXRhcDB7XGxhbmcxMDMzXGZzMTIwXGYzXGNmMSBcY2YxXHFse1xmMyB7XGNmMlxsdHJjaCBUaGUgTG9yZCBoYXMgcHJvbWlzZWQgZ29vZCB0byBtZSx9XGxpMFxzYTBcc2IwXGZpMFxxY1xwYXJ9DQp9DQp9" serialization-array-index="0">
<_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" />
<_-D-_serializedShadow containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="shadowColor" />
<NSMutableString serialization-native-value="{0, 0}" serialization-dictionary-key="shadowOffset" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="shadowBlurRadius" />
</_-D-_serializedShadow>
<stroke containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="RVShapeElementStrokeColorKey" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="RVShapeElementStrokeWidthKey" />
</stroke>
</RVTextElement>
</displayElements>
</RVDisplaySlide>
<RVDisplaySlide backgroundColor="0 0 0 0" enabled="1" highlightColor="0 0 0 0" hotKey="" label="" notes="" slideType="1" sort_index="13" UUID="E1573057-DB07-4442-BDCD-1C44B5B62A60" drawingBackgroundColor="0" serialization-array-index="13">
<cues containerClass="NSMutableArray" />
<displayElements containerClass="NSMutableArray">
<RVTextElement displayDelay="0" locked="0" persistent="0" typeID="0" fromTemplate="0" bezelRadius="0" drawingFill="0" drawingShadow="1" drawingStroke="0" fillColor="0 0 0 0" rotation="0" source="" displayName="" adjustsHeightToFit="0" verticalAlignment="0" RTFData="e1xydGYxXHByb3J0ZjFcYW5zaVxhbnNpY3BnMTI1Mlx1YzFcaHRtYXV0c3BcZGVmZjJ7XGZvbnR0Ymx7XGYwXGZjaGFyc2V0MCBUaW1lcyBOZXcgUm9tYW47fXtcZjJcZmNoYXJzZXQwIEdlb3JnaWE7fXtcZjNcZmNoYXJzZXQwIEhlbHZldGljYTt9fXtcY29sb3J0Ymw7XHJlZDBcZ3JlZW4wXGJsdWUwO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTt9XGxvY2hcaGljaFxkYmNoXHBhcmRcc2xsZWFkaW5nMFxwbGFpblxsdHJwYXJcaXRhcDB7XGxhbmcxMDMzXGZzMTIwXGYzXGNmMSBcY2YxXHFse1xmMyB7XGNmMlxsdHJjaCBIaXMgV29yZCBteSBob3BlIHNlY3VyZXM7fVxsaTBcc2EwXHNiMFxmaTBccWNccGFyfQ0KfQ0KfQ==" serialization-array-index="0">
<_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" />
<_-D-_serializedShadow containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="shadowColor" />
<NSMutableString serialization-native-value="{0, 0}" serialization-dictionary-key="shadowOffset" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="shadowBlurRadius" />
</_-D-_serializedShadow>
<stroke containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="RVShapeElementStrokeColorKey" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="RVShapeElementStrokeWidthKey" />
</stroke>
</RVTextElement>
</displayElements>
</RVDisplaySlide>
<RVDisplaySlide backgroundColor="0 0 0 0" enabled="1" highlightColor="0 0 0 0" hotKey="" label="" notes="" slideType="1" sort_index="14" UUID="D07DB5AA-710D-40F5-8FF0-C336B7C99B29" drawingBackgroundColor="0" serialization-array-index="14">
<cues containerClass="NSMutableArray" />
<displayElements containerClass="NSMutableArray">
<RVTextElement displayDelay="0" locked="0" persistent="0" typeID="0" fromTemplate="0" bezelRadius="0" drawingFill="0" drawingShadow="1" drawingStroke="0" fillColor="0 0 0 0" rotation="0" source="" displayName="" adjustsHeightToFit="0" verticalAlignment="0" RTFData="e1xydGYxXHByb3J0ZjFcYW5zaVxhbnNpY3BnMTI1Mlx1YzFcaHRtYXV0c3BcZGVmZjJ7XGZvbnR0Ymx7XGYwXGZjaGFyc2V0MCBUaW1lcyBOZXcgUm9tYW47fXtcZjJcZmNoYXJzZXQwIEdlb3JnaWE7fXtcZjNcZmNoYXJzZXQwIEhlbHZldGljYTt9fXtcY29sb3J0Ymw7XHJlZDBcZ3JlZW4wXGJsdWUwO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTt9XGxvY2hcaGljaFxkYmNoXHBhcmRcc2xsZWFkaW5nMFxwbGFpblxsdHJwYXJcaXRhcDB7XGxhbmcxMDMzXGZzMTIwXGYzXGNmMSBcY2YxXHFse1xmMyB7XGNmMlxsdHJjaCBIZSB3aWxsIG15IFNoaWVsZCBhbmQgUG9ydGlvbiBiZSx9XGxpMFxzYTBcc2IwXGZpMFxxY1xwYXJ9DQp9DQp9" serialization-array-index="0">
<_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" />
<_-D-_serializedShadow containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="shadowColor" />
<NSMutableString serialization-native-value="{0, 0}" serialization-dictionary-key="shadowOffset" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="shadowBlurRadius" />
</_-D-_serializedShadow>
<stroke containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="RVShapeElementStrokeColorKey" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="RVShapeElementStrokeWidthKey" />
</stroke>
</RVTextElement>
</displayElements>
</RVDisplaySlide>
<RVDisplaySlide backgroundColor="0 0 0 0" enabled="1" highlightColor="0 0 0 0" hotKey="" label="" notes="" slideType="1" sort_index="15" UUID="0D91E879-8C6A-4C68-A098-80E95E89F4F7" drawingBackgroundColor="0" serialization-array-index="15">
<cues containerClass="NSMutableArray" />
<displayElements containerClass="NSMutableArray">
<RVTextElement displayDelay="0" locked="0" persistent="0" typeID="0" fromTemplate="0" bezelRadius="0" drawingFill="0" drawingShadow="1" drawingStroke="0" fillColor="0 0 0 0" rotation="0" source="" displayName="" adjustsHeightToFit="0" verticalAlignment="0" RTFData="e1xydGYxXHByb3J0ZjFcYW5zaVxhbnNpY3BnMTI1Mlx1YzFcaHRtYXV0c3BcZGVmZjJ7XGZvbnR0Ymx7XGYwXGZjaGFyc2V0MCBUaW1lcyBOZXcgUm9tYW47fXtcZjJcZmNoYXJzZXQwIEdlb3JnaWE7fXtcZjNcZmNoYXJzZXQwIEhlbHZldGljYTt9fXtcY29sb3J0Ymw7XHJlZDBcZ3JlZW4wXGJsdWUwO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTt9XGxvY2hcaGljaFxkYmNoXHBhcmRcc2xsZWFkaW5nMFxwbGFpblxsdHJwYXJcaXRhcDB7XGxhbmcxMDMzXGZzMTIwXGYzXGNmMSBcY2YxXHFse1xmMyB7XGNmMlxsdHJjaCBBcyBsb25nIGFzIGxpZmUgZW5kdXJlcy59XGxpMFxzYTBcc2IwXGZpMFxxY1xwYXJ9DQp9DQp9" serialization-array-index="0">
<_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" />
<_-D-_serializedShadow containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="shadowColor" />
<NSMutableString serialization-native-value="{0, 0}" serialization-dictionary-key="shadowOffset" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="shadowBlurRadius" />
</_-D-_serializedShadow>
<stroke containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="RVShapeElementStrokeColorKey" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="RVShapeElementStrokeWidthKey" />
</stroke>
</RVTextElement>
</displayElements>
</RVDisplaySlide>
<RVDisplaySlide backgroundColor="0 0 0 0" enabled="1" highlightColor="0 0 0 0" hotKey="" label="" notes="" slideType="1" sort_index="16" UUID="5105264F-D786-453E-8D29-3ADC8D84C53F" drawingBackgroundColor="0" serialization-array-index="16">
<cues containerClass="NSMutableArray" />
<displayElements containerClass="NSMutableArray">
<RVTextElement displayDelay="0" locked="0" persistent="0" typeID="0" fromTemplate="0" bezelRadius="0" drawingFill="0" drawingShadow="1" drawingStroke="0" fillColor="0 0 0 0" rotation="0" source="" displayName="" adjustsHeightToFit="0" verticalAlignment="0" RTFData="e1xydGYxXHByb3J0ZjFcYW5zaVxhbnNpY3BnMTI1Mlx1YzFcaHRtYXV0c3BcZGVmZjJ7XGZvbnR0Ymx7XGYwXGZjaGFyc2V0MCBUaW1lcyBOZXcgUm9tYW47fXtcZjJcZmNoYXJzZXQwIEdlb3JnaWE7fXtcZjNcZmNoYXJzZXQwIEhlbHZldGljYTt9fXtcY29sb3J0Ymw7XHJlZDBcZ3JlZW4wXGJsdWUwO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTt9XGxvY2hcaGljaFxkYmNoXHBhcmRcc2xsZWFkaW5nMFxwbGFpblxsdHJwYXJcaXRhcDB7XGxhbmcxMDMzXGZzMTIwXGYzXGNmMSBcY2YxXHFse1xmMyB7XGNmMlxsdHJjaCBZZWEsIHdoZW4gdGhpcyBmbGVzaCBhbmQgaGVhcnQgc2hhbGwgZmFpbCx9XGxpMFxzYTBcc2IwXGZpMFxxY1xwYXJ9DQp9DQp9" serialization-array-index="0">
<_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" />
<_-D-_serializedShadow containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="shadowColor" />
<NSMutableString serialization-native-value="{0, 0}" serialization-dictionary-key="shadowOffset" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="shadowBlurRadius" />
</_-D-_serializedShadow>
<stroke containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="RVShapeElementStrokeColorKey" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="RVShapeElementStrokeWidthKey" />
</stroke>
</RVTextElement>
</displayElements>
</RVDisplaySlide>
<RVDisplaySlide backgroundColor="0 0 0 0" enabled="1" highlightColor="0 0 0 0" hotKey="" label="" notes="" slideType="1" sort_index="17" UUID="46D46358-A465-425E-866D-1D5E215952E4" drawingBackgroundColor="0" serialization-array-index="17">
<cues containerClass="NSMutableArray" />
<displayElements containerClass="NSMutableArray">
<RVTextElement displayDelay="0" locked="0" persistent="0" typeID="0" fromTemplate="0" bezelRadius="0" drawingFill="0" drawingShadow="1" drawingStroke="0" fillColor="0 0 0 0" rotation="0" source="" displayName="" adjustsHeightToFit="0" verticalAlignment="0" RTFData="e1xydGYxXHByb3J0ZjFcYW5zaVxhbnNpY3BnMTI1Mlx1YzFcaHRtYXV0c3BcZGVmZjJ7XGZvbnR0Ymx7XGYwXGZjaGFyc2V0MCBUaW1lcyBOZXcgUm9tYW47fXtcZjJcZmNoYXJzZXQwIEdlb3JnaWE7fXtcZjNcZmNoYXJzZXQwIEhlbHZldGljYTt9fXtcY29sb3J0Ymw7XHJlZDBcZ3JlZW4wXGJsdWUwO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTt9XGxvY2hcaGljaFxkYmNoXHBhcmRcc2xsZWFkaW5nMFxwbGFpblxsdHJwYXJcaXRhcDB7XGxhbmcxMDMzXGZzMTIwXGYzXGNmMSBcY2YxXHFse1xmMyB7XGNmMlxsdHJjaCBBbmQgbW9ydGFsIGxpZmUgc2hhbGwgY2Vhc2UsfVxsaTBcc2EwXHNiMFxmaTBccWNccGFyfQ0KfQ0KfQ==" serialization-array-index="0">
<_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" />
<_-D-_serializedShadow containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="shadowColor" />
<NSMutableString serialization-native-value="{0, 0}" serialization-dictionary-key="shadowOffset" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="shadowBlurRadius" />
</_-D-_serializedShadow>
<stroke containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="RVShapeElementStrokeColorKey" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="RVShapeElementStrokeWidthKey" />
</stroke>
</RVTextElement>
</displayElements>
</RVDisplaySlide>
<RVDisplaySlide backgroundColor="0 0 0 0" enabled="1" highlightColor="0 0 0 0" hotKey="" label="" notes="" slideType="1" sort_index="18" UUID="8B92AC4F-1470-416E-80CA-E088EF6F3AC4" drawingBackgroundColor="0" serialization-array-index="18">
<cues containerClass="NSMutableArray" />
<displayElements containerClass="NSMutableArray">
<RVTextElement displayDelay="0" locked="0" persistent="0" typeID="0" fromTemplate="0" bezelRadius="0" drawingFill="0" drawingShadow="1" drawingStroke="0" fillColor="0 0 0 0" rotation="0" source="" displayName="" adjustsHeightToFit="0" verticalAlignment="0" RTFData="e1xydGYxXHByb3J0ZjFcYW5zaVxhbnNpY3BnMTI1Mlx1YzFcaHRtYXV0c3BcZGVmZjJ7XGZvbnR0Ymx7XGYwXGZjaGFyc2V0MCBUaW1lcyBOZXcgUm9tYW47fXtcZjJcZmNoYXJzZXQwIEdlb3JnaWE7fXtcZjNcZmNoYXJzZXQwIEhlbHZldGljYTt9fXtcY29sb3J0Ymw7XHJlZDBcZ3JlZW4wXGJsdWUwO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTt9XGxvY2hcaGljaFxkYmNoXHBhcmRcc2xsZWFkaW5nMFxwbGFpblxsdHJwYXJcaXRhcDB7XGxhbmcxMDMzXGZzMTIwXGYzXGNmMSBcY2YxXHFse1xmMyB7XGNmMlxsdHJjaCBJIHNoYWxsIHBvc3Nlc3MsIHdpdGhpbiB0aGUgdmVpbCx9XGxpMFxzYTBcc2IwXGZpMFxxY1xwYXJ9DQp9DQp9" serialization-array-index="0">
<_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" />
<_-D-_serializedShadow containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="shadowColor" />
<NSMutableString serialization-native-value="{0, 0}" serialization-dictionary-key="shadowOffset" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="shadowBlurRadius" />
</_-D-_serializedShadow>
<stroke containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="RVShapeElementStrokeColorKey" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="RVShapeElementStrokeWidthKey" />
</stroke>
</RVTextElement>
</displayElements>
</RVDisplaySlide>
<RVDisplaySlide backgroundColor="0 0 0 0" enabled="1" highlightColor="0 0 0 0" hotKey="" label="" notes="" slideType="1" sort_index="19" UUID="D0865E31-B064-4817-B027-A35C231D4946" drawingBackgroundColor="0" serialization-array-index="19">
<cues containerClass="NSMutableArray" />
<displayElements containerClass="NSMutableArray">
<RVTextElement displayDelay="0" locked="0" persistent="0" typeID="0" fromTemplate="0" bezelRadius="0" drawingFill="0" drawingShadow="1" drawingStroke="0" fillColor="0 0 0 0" rotation="0" source="" displayName="" adjustsHeightToFit="0" verticalAlignment="0" RTFData="e1xydGYxXHByb3J0ZjFcYW5zaVxhbnNpY3BnMTI1Mlx1YzFcaHRtYXV0c3BcZGVmZjJ7XGZvbnR0Ymx7XGYwXGZjaGFyc2V0MCBUaW1lcyBOZXcgUm9tYW47fXtcZjJcZmNoYXJzZXQwIEdlb3JnaWE7fXtcZjNcZmNoYXJzZXQwIEhlbHZldGljYTt9fXtcY29sb3J0Ymw7XHJlZDBcZ3JlZW4wXGJsdWUwO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTt9XGxvY2hcaGljaFxkYmNoXHBhcmRcc2xsZWFkaW5nMFxwbGFpblxsdHJwYXJcaXRhcDB7XGxhbmcxMDMzXGZzMTIwXGYzXGNmMSBcY2YxXHFse1xmMyB7XGNmMlxsdHJjaCBBIGxpZmUgb2Ygam95IGFuZCBwZWFjZS59XGxpMFxzYTBcc2IwXGZpMFxxY1xwYXJ9DQp9DQp9" serialization-array-index="0">
<_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" />
<_-D-_serializedShadow containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="shadowColor" />
<NSMutableString serialization-native-value="{0, 0}" serialization-dictionary-key="shadowOffset" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="shadowBlurRadius" />
</_-D-_serializedShadow>
<stroke containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="RVShapeElementStrokeColorKey" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="RVShapeElementStrokeWidthKey" />
</stroke>
</RVTextElement>
</displayElements>
</RVDisplaySlide>
<RVDisplaySlide backgroundColor="0 0 0 0" enabled="1" highlightColor="0 0 0 0" hotKey="" label="" notes="" slideType="1" sort_index="20" UUID="28AC1D6E-1F79-4E88-80A4-CA318A634046" drawingBackgroundColor="0" serialization-array-index="20">
<cues containerClass="NSMutableArray" />
<displayElements containerClass="NSMutableArray">
<RVTextElement displayDelay="0" locked="0" persistent="0" typeID="0" fromTemplate="0" bezelRadius="0" drawingFill="0" drawingShadow="1" drawingStroke="0" fillColor="0 0 0 0" rotation="0" source="" displayName="" adjustsHeightToFit="0" verticalAlignment="0" RTFData="e1xydGYxXHByb3J0ZjFcYW5zaVxhbnNpY3BnMTI1Mlx1YzFcaHRtYXV0c3BcZGVmZjJ7XGZvbnR0Ymx7XGYwXGZjaGFyc2V0MCBUaW1lcyBOZXcgUm9tYW47fXtcZjJcZmNoYXJzZXQwIEdlb3JnaWE7fXtcZjNcZmNoYXJzZXQwIEhlbHZldGljYTt9fXtcY29sb3J0Ymw7XHJlZDBcZ3JlZW4wXGJsdWUwO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTt9XGxvY2hcaGljaFxkYmNoXHBhcmRcc2xsZWFkaW5nMFxwbGFpblxsdHJwYXJcaXRhcDB7XGxhbmcxMDMzXGZzMTIwXGYzXGNmMSBcY2YxXHFse1xmMyB7XGNmMlxsdHJjaCBUaGUgZWFydGggc2hhbGwgc29vbiBkaXNzb2x2ZSBsaWtlIHNub3csfVxsaTBcc2EwXHNiMFxmaTBccWNccGFyfQ0KfQ0KfQ==" serialization-array-index="0">
<_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" />
<_-D-_serializedShadow containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="shadowColor" />
<NSMutableString serialization-native-value="{0, 0}" serialization-dictionary-key="shadowOffset" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="shadowBlurRadius" />
</_-D-_serializedShadow>
<stroke containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="RVShapeElementStrokeColorKey" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="RVShapeElementStrokeWidthKey" />
</stroke>
</RVTextElement>
</displayElements>
</RVDisplaySlide>
<RVDisplaySlide backgroundColor="0 0 0 0" enabled="1" highlightColor="0 0 0 0" hotKey="" label="" notes="" slideType="1" sort_index="21" UUID="3E97F36D-D9CE-4C2B-AE1A-D337A43FD7F5" drawingBackgroundColor="0" serialization-array-index="21">
<cues containerClass="NSMutableArray" />
<displayElements containerClass="NSMutableArray">
<RVTextElement displayDelay="0" locked="0" persistent="0" typeID="0" fromTemplate="0" bezelRadius="0" drawingFill="0" drawingShadow="1" drawingStroke="0" fillColor="0 0 0 0" rotation="0" source="" displayName="" adjustsHeightToFit="0" verticalAlignment="0" RTFData="e1xydGYxXHByb3J0ZjFcYW5zaVxhbnNpY3BnMTI1Mlx1YzFcaHRtYXV0c3BcZGVmZjJ7XGZvbnR0Ymx7XGYwXGZjaGFyc2V0MCBUaW1lcyBOZXcgUm9tYW47fXtcZjJcZmNoYXJzZXQwIEdlb3JnaWE7fXtcZjNcZmNoYXJzZXQwIEhlbHZldGljYTt9fXtcY29sb3J0Ymw7XHJlZDBcZ3JlZW4wXGJsdWUwO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTt9XGxvY2hcaGljaFxkYmNoXHBhcmRcc2xsZWFkaW5nMFxwbGFpblxsdHJwYXJcaXRhcDB7XGxhbmcxMDMzXGZzMTIwXGYzXGNmMSBcY2YxXHFse1xmMyB7XGNmMlxsdHJjaCBUaGUgc3VuIGZvcmJlYXIgdG8gc2hpbmU7fVxsaTBcc2EwXHNiMFxmaTBccWNccGFyfQ0KfQ0KfQ==" serialization-array-index="0">
<_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" />
<_-D-_serializedShadow containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="shadowColor" />
<NSMutableString serialization-native-value="{0, 0}" serialization-dictionary-key="shadowOffset" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="shadowBlurRadius" />
</_-D-_serializedShadow>
<stroke containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="RVShapeElementStrokeColorKey" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="RVShapeElementStrokeWidthKey" />
</stroke>
</RVTextElement>
</displayElements>
</RVDisplaySlide>
<RVDisplaySlide backgroundColor="0 0 0 0" enabled="1" highlightColor="0 0 0 0" hotKey="" label="" notes="" slideType="1" sort_index="22" UUID="81E336F4-7170-47B0-B829-4E4A945A755F" drawingBackgroundColor="0" serialization-array-index="22">
<cues containerClass="NSMutableArray" />
<displayElements containerClass="NSMutableArray">
<RVTextElement displayDelay="0" locked="0" persistent="0" typeID="0" fromTemplate="0" bezelRadius="0" drawingFill="0" drawingShadow="1" drawingStroke="0" fillColor="0 0 0 0" rotation="0" source="" displayName="" adjustsHeightToFit="0" verticalAlignment="0" RTFData="e1xydGYxXHByb3J0ZjFcYW5zaVxhbnNpY3BnMTI1Mlx1YzFcaHRtYXV0c3BcZGVmZjJ7XGZvbnR0Ymx7XGYwXGZjaGFyc2V0MCBUaW1lcyBOZXcgUm9tYW47fXtcZjJcZmNoYXJzZXQwIEdlb3JnaWE7fXtcZjNcZmNoYXJzZXQwIEhlbHZldGljYTt9fXtcY29sb3J0Ymw7XHJlZDBcZ3JlZW4wXGJsdWUwO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTt9XGxvY2hcaGljaFxkYmNoXHBhcmRcc2xsZWFkaW5nMFxwbGFpblxsdHJwYXJcaXRhcDB7XGxhbmcxMDMzXGZzMTIwXGYzXGNmMSBcY2YxXHFse1xmMyB7XGNmMlxsdHJjaCBCdXQgR29kLCBXaG8gY2FsbGVkIG1lIGhlcmUgYmVsb3csfVxsaTBcc2EwXHNiMFxmaTBccWNccGFyfQ0KfQ0KfQ==" serialization-array-index="0">
<_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" />
<_-D-_serializedShadow containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="shadowColor" />
<NSMutableString serialization-native-value="{0, 0}" serialization-dictionary-key="shadowOffset" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="shadowBlurRadius" />
</_-D-_serializedShadow>
<stroke containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="RVShapeElementStrokeColorKey" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="RVShapeElementStrokeWidthKey" />
</stroke>
</RVTextElement>
</displayElements>
</RVDisplaySlide>
<RVDisplaySlide backgroundColor="0 0 0 0" enabled="1" highlightColor="0 0 0 0" hotKey="" label="" notes="" slideType="1" sort_index="23" UUID="4D669087-EB91-4972-A1F7-D866F339C502" drawingBackgroundColor="0" serialization-array-index="23">
<cues containerClass="NSMutableArray" />
<displayElements containerClass="NSMutableArray">
<RVTextElement displayDelay="0" locked="0" persistent="0" typeID="0" fromTemplate="0" bezelRadius="0" drawingFill="0" drawingShadow="1" drawingStroke="0" fillColor="0 0 0 0" rotation="0" source="" displayName="" adjustsHeightToFit="0" verticalAlignment="0" RTFData="e1xydGYxXHByb3J0ZjFcYW5zaVxhbnNpY3BnMTI1Mlx1YzFcaHRtYXV0c3BcZGVmZjJ7XGZvbnR0Ymx7XGYwXGZjaGFyc2V0MCBUaW1lcyBOZXcgUm9tYW47fXtcZjJcZmNoYXJzZXQwIEdlb3JnaWE7fXtcZjNcZmNoYXJzZXQwIEhlbHZldGljYTt9fXtcY29sb3J0Ymw7XHJlZDBcZ3JlZW4wXGJsdWUwO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTt9XGxvY2hcaGljaFxkYmNoXHBhcmRcc2xsZWFkaW5nMFxwbGFpblxsdHJwYXJcaXRhcDB7XGxhbmcxMDMzXGZzMTIwXGYzXGNmMSBcY2YxXHFse1xmMyB7XGNmMlxsdHJjaCBTaGFsbCBiZSBmb3JldmVyIG1pbmUufVxsaTBcc2EwXHNiMFxmaTBccWNccGFyfQ0KfQ0KfQ==" serialization-array-index="0">
<_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" />
<_-D-_serializedShadow containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="shadowColor" />
<NSMutableString serialization-native-value="{0, 0}" serialization-dictionary-key="shadowOffset" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="shadowBlurRadius" />
</_-D-_serializedShadow>
<stroke containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="RVShapeElementStrokeColorKey" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="RVShapeElementStrokeWidthKey" />
</stroke>
</RVTextElement>
</displayElements>
</RVDisplaySlide>
<RVDisplaySlide backgroundColor="0 0 0 0" enabled="1" highlightColor="0 0 0 0" hotKey="" label="" notes="" slideType="1" sort_index="24" UUID="ADE5EE3D-56A6-4439-96DD-51C53337D4B1" drawingBackgroundColor="0" serialization-array-index="24">
<cues containerClass="NSMutableArray" />
<displayElements containerClass="NSMutableArray">
<RVTextElement displayDelay="0" locked="0" persistent="0" typeID="0" fromTemplate="0" bezelRadius="0" drawingFill="0" drawingShadow="1" drawingStroke="0" fillColor="0 0 0 0" rotation="0" source="" displayName="" adjustsHeightToFit="0" verticalAlignment="0" RTFData="e1xydGYxXHByb3J0ZjFcYW5zaVxhbnNpY3BnMTI1Mlx1YzFcaHRtYXV0c3BcZGVmZjJ7XGZvbnR0Ymx7XGYwXGZjaGFyc2V0MCBUaW1lcyBOZXcgUm9tYW47fXtcZjJcZmNoYXJzZXQwIEdlb3JnaWE7fXtcZjNcZmNoYXJzZXQwIEhlbHZldGljYTt9fXtcY29sb3J0Ymw7XHJlZDBcZ3JlZW4wXGJsdWUwO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTt9XGxvY2hcaGljaFxkYmNoXHBhcmRcc2xsZWFkaW5nMFxwbGFpblxsdHJwYXJcaXRhcDB7XGxhbmcxMDMzXGZzMTIwXGYzXGNmMSBcY2YxXHFse1xmMyB7XGNmMlxsdHJjaCBXaGVuIHdlJ3ZlIGJlZW4gdGhlcmUgdGVuIHRob3VzYW5kIHllYXJzLH1cbGkwXHNhMFxzYjBcZmkwXHFjXHBhcn0NCn0NCn0=" serialization-array-index="0">
<_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" />
<_-D-_serializedShadow containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="shadowColor" />
<NSMutableString serialization-native-value="{0, 0}" serialization-dictionary-key="shadowOffset" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="shadowBlurRadius" />
</_-D-_serializedShadow>
<stroke containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="RVShapeElementStrokeColorKey" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="RVShapeElementStrokeWidthKey" />
</stroke>
</RVTextElement>
</displayElements>
</RVDisplaySlide>
<RVDisplaySlide backgroundColor="0 0 0 0" enabled="1" highlightColor="0 0 0 0" hotKey="" label="" notes="" slideType="1" sort_index="25" UUID="BD68D875-5154-485A-AEF3-D48D9CE27C05" drawingBackgroundColor="0" serialization-array-index="25">
<cues containerClass="NSMutableArray" />
<displayElements containerClass="NSMutableArray">
<RVTextElement displayDelay="0" locked="0" persistent="0" typeID="0" fromTemplate="0" bezelRadius="0" drawingFill="0" drawingShadow="1" drawingStroke="0" fillColor="0 0 0 0" rotation="0" source="" displayName="" adjustsHeightToFit="0" verticalAlignment="0" RTFData="e1xydGYxXHByb3J0ZjFcYW5zaVxhbnNpY3BnMTI1Mlx1YzFcaHRtYXV0c3BcZGVmZjJ7XGZvbnR0Ymx7XGYwXGZjaGFyc2V0MCBUaW1lcyBOZXcgUm9tYW47fXtcZjJcZmNoYXJzZXQwIEdlb3JnaWE7fXtcZjNcZmNoYXJzZXQwIEhlbHZldGljYTt9fXtcY29sb3J0Ymw7XHJlZDBcZ3JlZW4wXGJsdWUwO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTt9XGxvY2hcaGljaFxkYmNoXHBhcmRcc2xsZWFkaW5nMFxwbGFpblxsdHJwYXJcaXRhcDB7XGxhbmcxMDMzXGZzMTIwXGYzXGNmMSBcY2YxXHFse1xmMyB7XGNmMlxsdHJjaCBCcmlnaHQgc2hpbmluZyBhcyB0aGUgc3VuLH1cbGkwXHNhMFxzYjBcZmkwXHFjXHBhcn0NCn0NCn0=" serialization-array-index="0">
<_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" />
<_-D-_serializedShadow containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="shadowColor" />
<NSMutableString serialization-native-value="{0, 0}" serialization-dictionary-key="shadowOffset" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="shadowBlurRadius" />
</_-D-_serializedShadow>
<stroke containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="RVShapeElementStrokeColorKey" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="RVShapeElementStrokeWidthKey" />
</stroke>
</RVTextElement>
</displayElements>
</RVDisplaySlide>
<RVDisplaySlide backgroundColor="0 0 0 0" enabled="1" highlightColor="0 0 0 0" hotKey="" label="" notes="" slideType="1" sort_index="26" UUID="030026EE-A25C-4781-883D-0CAC394ADC93" drawingBackgroundColor="0" serialization-array-index="26">
<cues containerClass="NSMutableArray" />
<displayElements containerClass="NSMutableArray">
<RVTextElement displayDelay="0" locked="0" persistent="0" typeID="0" fromTemplate="0" bezelRadius="0" drawingFill="0" drawingShadow="1" drawingStroke="0" fillColor="0 0 0 0" rotation="0" source="" displayName="" adjustsHeightToFit="0" verticalAlignment="0" RTFData="e1xydGYxXHByb3J0ZjFcYW5zaVxhbnNpY3BnMTI1Mlx1YzFcaHRtYXV0c3BcZGVmZjJ7XGZvbnR0Ymx7XGYwXGZjaGFyc2V0MCBUaW1lcyBOZXcgUm9tYW47fXtcZjJcZmNoYXJzZXQwIEdlb3JnaWE7fXtcZjNcZmNoYXJzZXQwIEhlbHZldGljYTt9fXtcY29sb3J0Ymw7XHJlZDBcZ3JlZW4wXGJsdWUwO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTt9XGxvY2hcaGljaFxkYmNoXHBhcmRcc2xsZWFkaW5nMFxwbGFpblxsdHJwYXJcaXRhcDB7XGxhbmcxMDMzXGZzMTIwXGYzXGNmMSBcY2YxXHFse1xmMyB7XGNmMlxsdHJjaCBXZSd2ZSBubyBsZXNzIGRheXMgdG8gc2luZyBHb2QncyBwcmFpc2V9XGxpMFxzYTBcc2IwXGZpMFxxY1xwYXJ9DQp9DQp9" serialization-array-index="0">
<_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" />
<_-D-_serializedShadow containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="shadowColor" />
<NSMutableString serialization-native-value="{0, 0}" serialization-dictionary-key="shadowOffset" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="shadowBlurRadius" />
</_-D-_serializedShadow>
<stroke containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="RVShapeElementStrokeColorKey" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="RVShapeElementStrokeWidthKey" />
</stroke>
</RVTextElement>
</displayElements>
</RVDisplaySlide>
<RVDisplaySlide backgroundColor="0 0 0 0" enabled="1" highlightColor="0 0 0 0" hotKey="" label="" notes="" slideType="1" sort_index="27" UUID="50648C63-C1FC-40E0-8928-FEDFFCDA9689" drawingBackgroundColor="0" serialization-array-index="27">
<cues containerClass="NSMutableArray" />
<displayElements containerClass="NSMutableArray">
<RVTextElement displayDelay="0" locked="0" persistent="0" typeID="0" fromTemplate="0" bezelRadius="0" drawingFill="0" drawingShadow="1" drawingStroke="0" fillColor="0 0 0 0" rotation="0" source="" displayName="" adjustsHeightToFit="0" verticalAlignment="0" RTFData="e1xydGYxXHByb3J0ZjFcYW5zaVxhbnNpY3BnMTI1Mlx1YzFcaHRtYXV0c3BcZGVmZjJ7XGZvbnR0Ymx7XGYwXGZjaGFyc2V0MCBUaW1lcyBOZXcgUm9tYW47fXtcZjJcZmNoYXJzZXQwIEdlb3JnaWE7fXtcZjNcZmNoYXJzZXQwIEhlbHZldGljYTt9fXtcY29sb3J0Ymw7XHJlZDBcZ3JlZW4wXGJsdWUwO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTt9XGxvY2hcaGljaFxkYmNoXHBhcmRcc2xsZWFkaW5nMFxwbGFpblxsdHJwYXJcaXRhcDB7XGxhbmcxMDMzXGZzMTIwXGYzXGNmMSBcY2YxXHFse1xmMyB7XGNmMlxsdHJjaCBUaGFuIHdoZW4gd2UnZCBmaXJzdCBiZWd1bi59XGxpMFxzYTBcc2IwXGZpMFxxY1xwYXJ9DQp9DQp9" serialization-array-index="0">
<_-RVRect3D-_position x="10" y="10" z="0" width="1004" height="748" />
<_-D-_serializedShadow containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="shadowColor" />
<NSMutableString serialization-native-value="{0, 0}" serialization-dictionary-key="shadowOffset" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="shadowBlurRadius" />
</_-D-_serializedShadow>
<stroke containerClass="NSMutableDictionary">
<NSCalibratedRGBColor serialization-native-value="0 0 0 0" serialization-dictionary-key="RVShapeElementStrokeColorKey" />
<NSNumber serialization-native-value="0" serialization-dictionary-key="RVShapeElementStrokeWidthKey" />
</stroke>
</RVTextElement>
</displayElements>
</RVDisplaySlide>
</slides>
<timeline timeOffSet="0" selectedMediaTrackIndex="-1" unitOfMeasure="60" duration="0" loop="0">
<timeCues containerClass="NSMutableArray" />
<mediaTracks containerClass="NSMutableArray" />
</timeline>
<bibleReference containerClass="NSMutableDictionary" />
</RVPresentationDocument>