This commit is contained in:
Tomas Groth 2014-08-21 11:24:40 +02:00
commit 3b092319c9
87 changed files with 2231 additions and 357 deletions

View File

@ -286,6 +286,7 @@ class Settings(QtCore.QSettings):
'themes/last directory export': '',
'themes/last directory import': '',
'themes/theme level': ThemeLevel.Song,
'themes/wrap footer': False,
'user interface/live panel': True,
'user interface/live splitter geometry': QtCore.QByteArray(),
'user interface/lock panel': False,

View File

@ -96,9 +96,10 @@ def upgrade_db(url, upgrade):
mapper(Metadata, metadata_table)
version_meta = session.query(Metadata).get('version')
if version_meta is None:
version_meta = Metadata.populate(key='version', value='0')
# Tables have just been created - fill the version field with the most recent version
version = upgrade.__version__
version_meta = Metadata.populate(key='version', value=version)
session.add(version_meta)
version = 0
else:
version = int(version_meta.value)
if version > upgrade.__version__:

View File

@ -398,6 +398,7 @@ import logging
from PyQt4 import QtWebKit
from openlp.core.common import Settings
from openlp.core.lib.theme import BackgroundType, BackgroundGradientType, VerticalType, HorizontalType
log = logging.getLogger(__name__)
@ -750,12 +751,13 @@ def build_footer_css(item, height):
font-size: %spt;
color: %s;
text-align: left;
white-space: nowrap;
white-space: %s;
"""
theme = item.theme_data
if not theme or not item.footer:
return ''
bottom = height - int(item.footer.y()) - int(item.footer.height())
whitespace = 'normal' if Settings().value('themes/wrap footer') else 'nowrap'
lyrics_html = style % (item.footer.x(), bottom, item.footer.width(),
theme.font_footer_name, theme.font_footer_size, theme.font_footer_color)
theme.font_footer_name, theme.font_footer_size, theme.font_footer_color, whitespace)
return lyrics_html

View File

@ -59,7 +59,6 @@ class Renderer(OpenLPMixin, RegistryMixin, RegistryProperties):
"""
super(Renderer, self).__init__(None)
# Need live behaviour if this is also working as a pseudo MainDisplay.
self.is_live = True
self.screens = ScreenList()
self.theme_level = ThemeLevel.Global
self.global_theme_name = ''

View File

@ -66,11 +66,8 @@ class Display(QtGui.QGraphicsView):
if hasattr(parent, 'is_live') and parent.is_live:
self.is_live = True
if self.is_live:
super(Display, self).__init__()
# Overwrite the parent() method.
self.parent = lambda: parent
else:
super(Display, self).__init__(parent)
super(Display, self).__init__()
self.controller = parent
self.screen = {}
# FIXME: On Mac OS X (tested on 10.7) the display screen is corrupt with

View File

@ -1103,7 +1103,7 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ServiceManage
Moves the cursor selection up the window. Called by the up arrow.
"""
item = self.service_manager_list.currentItem()
item_before = self.service_manager_list.item_above(item)
item_before = self.service_manager_list.itemAbove(item)
if item_before is None:
return
self.service_manager_list.setCurrentItem(item_before)

View File

@ -96,6 +96,8 @@ class DisplayController(QtGui.QWidget):
"""
This is the generic function to send signal for control widgets, created from within other plugins
This function is needed to catch the current controller
:param args: Arguments to send to the plugins
"""
sender = self.sender().objectName() if self.sender().objectName() else self.sender().text()
controller = self
@ -143,11 +145,19 @@ class SlideController(DisplayController, RegistryProperties):
self.panel_layout = QtGui.QVBoxLayout(self.panel)
self.panel_layout.setSpacing(0)
self.panel_layout.setMargin(0)
# Type label for the top of the slide controller
# Type label at the top of the slide controller
self.type_label = QtGui.QLabel(self.panel)
self.type_label.setStyleSheet('font-weight: bold; font-size: 12pt;')
self.type_label.setAlignment(QtCore.Qt.AlignCenter)
if self.is_live:
self.type_label.setText(UiStrings().Live)
else:
self.type_label.setText(UiStrings().Preview)
self.panel_layout.addWidget(self.type_label)
# Info label for the title of the current item, at the top of the slide controller
self.info_label = QtGui.QLabel(self.panel)
self.info_label.setAlignment(QtCore.Qt.AlignCenter)
self.panel_layout.addWidget(self.info_label)
# Splitter
self.splitter = QtGui.QSplitter(self.panel)
self.splitter.setOrientation(QtCore.Qt.Vertical)
@ -402,12 +412,17 @@ class SlideController(DisplayController, RegistryProperties):
"""
try:
from openlp.plugins.songs.lib import VerseType
SONGS_PLUGIN_AVAILABLE = True
is_songs_plugin_available = True
except ImportError:
SONGS_PLUGIN_AVAILABLE = False
class VerseType(object):
"""
This empty class is mostly just to satisfy Python, PEP8 and PyCharm
"""
pass
is_songs_plugin_available = False
sender_name = self.sender().objectName()
verse_type = sender_name[15:] if sender_name[:15] == 'shortcutAction_' else ''
if SONGS_PLUGIN_AVAILABLE:
if is_songs_plugin_available:
if verse_type == 'V':
self.current_shortcut = VerseType.translated_tags[VerseType.Verse]
elif verse_type == 'C':
@ -777,6 +792,7 @@ class SlideController(DisplayController, RegistryProperties):
if service_item.is_command():
Registry().execute(
'%s_start' % service_item.name.lower(), [self.service_item, self.is_live, self.hide_mode(), slide_no])
self.info_label.setText(self.service_item.title)
self.slide_list = {}
if self.is_live:
self.song_menu.menu().clear()

View File

@ -384,16 +384,8 @@ class ThemeManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ThemeManager, R
self.application.set_busy_cursor()
if path:
Settings().setValue(self.settings_section + '/last directory export', path)
theme_path = os.path.join(path, theme + '.otz')
theme_zip = None
try:
theme_zip = zipfile.ZipFile(theme_path, 'w')
source = os.path.join(self.path, theme)
for files in os.walk(source):
for name in files[2]:
theme_zip.write(
os.path.join(source, name).encode('utf-8'), os.path.join(theme, name).encode('utf-8')
)
self._export_theme(path, theme)
QtGui.QMessageBox.information(self,
translate('OpenLP.ThemeManager', 'Theme Exported'),
translate('OpenLP.ThemeManager',
@ -403,11 +395,29 @@ class ThemeManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ThemeManager, R
critical_error_message_box(translate('OpenLP.ThemeManager', 'Theme Export Failed'),
translate('OpenLP.ThemeManager',
'Your theme could not be exported due to an error.'))
finally:
if theme_zip:
theme_zip.close()
self.application.set_normal_cursor()
def _export_theme(self, path, theme):
"""
Create the zipfile with the theme contents.
:param path: Location where the zip file will be placed
:param theme: The name of the theme to be exported
"""
theme_path = os.path.join(path, theme + '.otz')
try:
theme_zip = zipfile.ZipFile(theme_path, 'w')
source = os.path.join(self.path, theme)
for files in os.walk(source):
for name in files[2]:
theme_zip.write(os.path.join(source, name), os.path.join(theme, name))
except (IOError, OSError):
if theme_zip:
theme_zip.close()
shutil.rmtree(theme_path, True)
raise
else:
theme_zip.close()
def on_import_theme(self, field=None):
"""
Opens a file dialog to select the theme file(s) to import before attempting to extract OpenLP themes from

View File

@ -69,6 +69,14 @@ class ThemesTab(SettingsTab):
self.default_list_view.setObjectName('default_list_view')
self.global_group_box_layout.addWidget(self.default_list_view)
self.left_layout.addWidget(self.global_group_box)
self.universal_group_box = QtGui.QGroupBox(self.left_column)
self.universal_group_box.setObjectName('universal_group_box')
self.universal_group_box_layout = QtGui.QVBoxLayout(self.universal_group_box)
self.universal_group_box_layout.setObjectName('universal_group_box_layout')
self.wrap_footer_check_box = QtGui.QCheckBox(self.universal_group_box)
self.wrap_footer_check_box.setObjectName('wrap_footer_check_box')
self.universal_group_box_layout.addWidget(self.wrap_footer_check_box)
self.left_layout.addWidget(self.universal_group_box)
self.left_layout.addStretch()
self.level_group_box = QtGui.QGroupBox(self.right_column)
self.level_group_box.setObjectName('level_group_box')
@ -112,6 +120,8 @@ class ThemesTab(SettingsTab):
"""
self.tab_title_visible = UiStrings().Themes
self.global_group_box.setTitle(translate('OpenLP.ThemesTab', 'Global Theme'))
self.universal_group_box.setTitle(translate('OpenLP.ThemesTab', 'Universal Settings'))
self.wrap_footer_check_box.setText(translate('OpenLP.ThemesTab', '&Wrap footer text'))
self.level_group_box.setTitle(translate('OpenLP.ThemesTab', 'Theme Level'))
self.song_level_radio_button.setText(translate('OpenLP.ThemesTab', 'S&ong Level'))
self.song_level_label.setText(
@ -136,6 +146,7 @@ class ThemesTab(SettingsTab):
settings.beginGroup(self.settings_section)
self.theme_level = settings.value('theme level')
self.global_theme = settings.value('global theme')
self.wrap_footer_check_box.setChecked(settings.value('wrap footer'))
settings.endGroup()
if self.theme_level == ThemeLevel.Global:
self.global_level_radio_button.setChecked(True)
@ -152,6 +163,7 @@ class ThemesTab(SettingsTab):
settings.beginGroup(self.settings_section)
settings.setValue('theme level', self.theme_level)
settings.setValue('global theme', self.global_theme)
settings.setValue('wrap footer', self.wrap_footer_check_box.isChecked())
settings.endGroup()
self.renderer.set_theme_level(self.theme_level)
if self.tab_visited:

View File

@ -149,9 +149,9 @@ def get_application_version():
# If they are equal, then this tree is tarball with the source for the release. We do not want the revision
# number in the full version.
if tree_revision == tag_revision:
full_version = tag_version
full_version = tag_version.decode('utf-8')
else:
full_version = '%s-bzr%s' % (tag_version, tree_revision)
full_version = '%s-bzr%s' % (tag_version.decode('utf-8'), tree_revision.decode('utf-8'))
else:
# We're not running the development version, let's use the file.
filepath = AppLocation.get_directory(AppLocation.VersionDir)

View File

@ -225,7 +225,7 @@ class BGExtract(RegistryProperties):
url_book_name = urllib.parse.quote(book_name.encode("utf-8"))
url_params = 'search=%s+%s&version=%s' % (url_book_name, chapter, version)
soup = get_soup_for_bible_ref(
'http://www.biblegateway.com/passage/?%s' % url_params,
'http://legacy.biblegateway.com/passage/?%s' % url_params,
pre_parse_regex=r'<meta name.*?/>', pre_parse_substitute='')
if not soup:
return None
@ -252,7 +252,7 @@ class BGExtract(RegistryProperties):
"""
log.debug('BGExtract.get_books_from_http("%s")', version)
url_params = urllib.parse.urlencode({'action': 'getVersionInfo', 'vid': '%s' % version})
reference_url = 'http://www.biblegateway.com/versions/?%s#books' % url_params
reference_url = 'http://legacy.biblegateway.com/versions/?%s#books' % url_params
page = get_web_page(reference_url)
if not page:
send_error_message('download')

View File

@ -40,6 +40,8 @@ if os.name == 'nt':
import pywintypes
from openlp.core.lib import ScreenList
from openlp.core.lib.ui import UiStrings, critical_error_message_box, translate
from openlp.core.common import trace_error_handler
from .presentationcontroller import PresentationController, PresentationDocument
@ -99,7 +101,7 @@ class PowerpointController(PresentationController):
if self.process.Presentations.Count > 0:
return
self.process.Quit()
except pywintypes.com_error:
except (AttributeError, pywintypes.com_error):
pass
self.process = None
@ -126,16 +128,24 @@ class PowerpointDocument(PresentationDocument):
earlier.
"""
log.debug('load_presentation')
if not self.controller.process or not self.controller.process.Visible:
self.controller.start_process()
try:
if not self.controller.process or not self.controller.process.Visible:
self.controller.start_process()
self.controller.process.Presentations.Open(self.file_path, False, False, True)
self.presentation = self.controller.process.Presentations(self.controller.process.Presentations.Count)
self.create_thumbnails()
# Powerpoint 2013 pops up when loading a file, so we minimize it again
if self.presentation.Application.Version == u'15.0':
try:
self.presentation.Application.WindowState = 2
except:
log.error('Failed to minimize main powerpoint window')
trace_error_handler(log)
return True
except pywintypes.com_error:
log.debug('PPT open failed')
log.error('PPT open failed')
trace_error_handler(log)
return False
self.presentation = self.controller.process.Presentations(self.controller.process.Presentations.Count)
self.create_thumbnails()
return True
def create_thumbnails(self):
"""
@ -206,23 +216,33 @@ class PowerpointDocument(PresentationDocument):
Unblanks (restores) the presentation.
"""
log.debug('unblank_screen')
self.presentation.SlideShowSettings.Run()
self.presentation.SlideShowWindow.View.State = 1
self.presentation.SlideShowWindow.Activate()
if self.presentation.Application.Version == '14.0':
# Unblanking is broken in PowerPoint 2010, need to redisplay
slide = self.presentation.SlideShowWindow.View.CurrentShowPosition
click = self.presentation.SlideShowWindow.View.GetClickIndex()
self.presentation.SlideShowWindow.View.GotoSlide(slide)
if click:
self.presentation.SlideShowWindow.View.GotoClick(click)
try:
self.presentation.SlideShowSettings.Run()
self.presentation.SlideShowWindow.View.State = 1
self.presentation.SlideShowWindow.Activate()
if self.presentation.Application.Version == '14.0':
# Unblanking is broken in PowerPoint 2010, need to redisplay
slide = self.presentation.SlideShowWindow.View.CurrentShowPosition
click = self.presentation.SlideShowWindow.View.GetClickIndex()
self.presentation.SlideShowWindow.View.GotoSlide(slide)
if click:
self.presentation.SlideShowWindow.View.GotoClick(click)
except pywintypes.com_error:
log.error('COM error while in unblank_screen')
trace_error_handler(log)
self.show_error_msg()
def blank_screen(self):
"""
Blanks the screen.
"""
log.debug('blank_screen')
self.presentation.SlideShowWindow.View.State = 3
try:
self.presentation.SlideShowWindow.View.State = 3
except pywintypes.com_error:
log.error('COM error while in blank_screen')
trace_error_handler(log)
self.show_error_msg()
def is_blank(self):
"""
@ -230,7 +250,12 @@ class PowerpointDocument(PresentationDocument):
"""
log.debug('is_blank')
if self.is_active():
return self.presentation.SlideShowWindow.View.State == 3
try:
return self.presentation.SlideShowWindow.View.State == 3
except pywintypes.com_error:
log.error('COM error while in is_blank')
trace_error_handler(log)
self.show_error_msg()
else:
return False
@ -239,7 +264,12 @@ class PowerpointDocument(PresentationDocument):
Stops the current presentation and hides the output.
"""
log.debug('stop_presentation')
self.presentation.SlideShowWindow.View.Exit()
try:
self.presentation.SlideShowWindow.View.Exit()
except pywintypes.com_error:
log.error('COM error while in stop_presentation')
trace_error_handler(log)
self.show_error_msg()
if os.name == 'nt':
def start_presentation(self):
@ -259,24 +289,49 @@ class PowerpointDocument(PresentationDocument):
ppt_window = self.presentation.SlideShowSettings.Run()
if not ppt_window:
return
ppt_window.Top = size.y() * 72 / dpi
ppt_window.Height = size.height() * 72 / dpi
ppt_window.Left = size.x() * 72 / dpi
ppt_window.Width = size.width() * 72 / dpi
try:
ppt_window.Top = size.y() * 72 / dpi
ppt_window.Height = size.height() * 72 / dpi
ppt_window.Left = size.x() * 72 / dpi
ppt_window.Width = size.width() * 72 / dpi
except AttributeError as e:
log.error('AttributeError while in start_presentation')
log.error(e)
# Powerpoint 2013 pops up when starting a file, so we minimize it again
if self.presentation.Application.Version == u'15.0':
try:
self.presentation.Application.WindowState = 2
except:
log.error('Failed to minimize main powerpoint window')
trace_error_handler(log)
def get_slide_number(self):
"""
Returns the current slide number.
"""
log.debug('get_slide_number')
return self.presentation.SlideShowWindow.View.CurrentShowPosition
ret = 0
try:
ret = self.presentation.SlideShowWindow.View.CurrentShowPosition
except pywintypes.com_error:
log.error('COM error while in get_slide_number')
trace_error_handler(log)
self.show_error_msg()
return ret
def get_slide_count(self):
"""
Returns total number of slides.
"""
log.debug('get_slide_count')
return self.presentation.Slides.Count
ret = 0
try:
ret = self.presentation.Slides.Count
except pywintypes.com_error:
log.error('COM error while in get_slide_count')
trace_error_handler(log)
self.show_error_msg()
return ret
def goto_slide(self, slide_no):
"""
@ -285,14 +340,25 @@ class PowerpointDocument(PresentationDocument):
:param slide_no: The slide the text is required for, starting at 1
"""
log.debug('goto_slide')
self.presentation.SlideShowWindow.View.GotoSlide(slide_no)
try:
self.presentation.SlideShowWindow.View.GotoSlide(slide_no)
except pywintypes.com_error:
log.error('COM error while in goto_slide')
trace_error_handler(log)
self.show_error_msg()
def next_step(self):
"""
Triggers the next effect of slide on the running presentation.
"""
log.debug('next_step')
self.presentation.SlideShowWindow.View.Next()
try:
self.presentation.SlideShowWindow.View.Next()
except pywintypes.com_error:
log.error('COM error while in next_step')
trace_error_handler(log)
self.show_error_msg()
return
if self.get_slide_number() > self.get_slide_count():
self.previous_step()
@ -301,7 +367,12 @@ class PowerpointDocument(PresentationDocument):
Triggers the previous slide on the running presentation.
"""
log.debug('previous_step')
self.presentation.SlideShowWindow.View.Previous()
try:
self.presentation.SlideShowWindow.View.Previous()
except pywintypes.com_error:
log.error('COM error while in previous_step')
trace_error_handler(log)
self.show_error_msg()
def get_slide_text(self, slide_no):
"""
@ -319,6 +390,16 @@ class PowerpointDocument(PresentationDocument):
"""
return _get_text_from_shapes(self.presentation.Slides(slide_no).NotesPage.Shapes)
def show_error_msg(self):
"""
Stop presentation and display an error message.
"""
self.stop_presentation()
critical_error_message_box(UiStrings().Error, translate('PresentationPlugin.PowerpointDocument',
'An error occurred in the Powerpoint integration '
'and the presentation will be stopped. '
'Restart the presentation if you wish to present it.'))
def _get_text_from_shapes(shapes):
"""

View File

@ -138,6 +138,9 @@ class Ui_EditSongDialog(object):
self.author_remove_layout = QtGui.QHBoxLayout()
self.author_remove_layout.setObjectName('author_remove_layout')
self.author_remove_layout.addStretch()
self.author_edit_button = QtGui.QPushButton(self.authors_group_box)
self.author_edit_button.setObjectName('author_edit_button')
self.author_remove_layout.addWidget(self.author_edit_button)
self.author_remove_button = QtGui.QPushButton(self.authors_group_box)
self.author_remove_button.setObjectName('author_remove_button')
self.author_remove_layout.addWidget(self.author_remove_button)
@ -305,6 +308,7 @@ class Ui_EditSongDialog(object):
translate('SongsPlugin.EditSongForm', 'Title && Lyrics'))
self.authors_group_box.setTitle(SongStrings.Authors)
self.author_add_button.setText(translate('SongsPlugin.EditSongForm', '&Add to Song'))
self.author_edit_button.setText(translate('SongsPlugin.EditSongForm', '&Edit Author Type'))
self.author_remove_button.setText(translate('SongsPlugin.EditSongForm', '&Remove'))
self.maintenance_button.setText(translate('SongsPlugin.EditSongForm', '&Manage Authors, Topics, Song Books'))
self.topics_group_box.setTitle(SongStrings.Topic)

View File

@ -44,7 +44,7 @@ from openlp.core.lib.ui import set_case_insensitive_completer, critical_error_me
from openlp.plugins.songs.lib import VerseType, clean_song
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.lib.openlyricsxml import SongXML
from openlp.plugins.songs.forms.editsongdialog import Ui_EditSongDialog
from openlp.plugins.songs.forms.editverseform import EditVerseForm
from openlp.plugins.songs.forms.mediafilesform import MediaFilesForm
@ -70,6 +70,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties):
self.setupUi(self)
# Connecting signals and slots
self.author_add_button.clicked.connect(self.on_author_add_button_clicked)
self.author_edit_button.clicked.connect(self.on_author_edit_button_clicked)
self.author_remove_button.clicked.connect(self.on_author_remove_button_clicked)
self.authors_list_view.itemClicked.connect(self.on_authors_list_view_clicked)
self.topic_add_button.clicked.connect(self.on_topic_add_button_clicked)
@ -334,6 +335,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties):
"""
self.verse_edit_button.setEnabled(False)
self.verse_delete_button.setEnabled(False)
self.author_edit_button.setEnabled(False)
self.author_remove_button.setEnabled(False)
self.topic_remove_button.setEnabled(False)
@ -354,12 +356,9 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties):
# Types
self.author_types_combo_box.clear()
self.author_types_combo_box.addItem('')
# Don't iterate over the dictionary to give them this specific order
self.author_types_combo_box.addItem(AuthorType.Types[AuthorType.Words], AuthorType.Words)
self.author_types_combo_box.addItem(AuthorType.Types[AuthorType.Music], AuthorType.Music)
self.author_types_combo_box.addItem(AuthorType.Types[AuthorType.WordsAndMusic], AuthorType.WordsAndMusic)
self.author_types_combo_box.addItem(AuthorType.Types[AuthorType.Translation], AuthorType.Translation)
for author_type in AuthorType.SortedTypes:
self.author_types_combo_box.addItem(AuthorType.Types[author_type], author_type)
def load_topics(self):
"""
@ -596,9 +595,32 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties):
"""
Run a set of actions when an author in the list is selected (mainly enable the delete button).
"""
if self.authors_list_view.count() > 1:
count = self.authors_list_view.count()
if count > 0:
self.author_edit_button.setEnabled(True)
if count > 1:
# There must be at least one author
self.author_remove_button.setEnabled(True)
def on_author_edit_button_clicked(self):
"""
Show a dialog to change the type of an author when the edit button is clicked
"""
self.author_edit_button.setEnabled(False)
item = self.authors_list_view.currentItem()
author_id, author_type = item.data(QtCore.Qt.UserRole)
choice, ok = QtGui.QInputDialog.getItem(self, translate('SongsPlugin.EditSongForm', 'Edit Author Type'),
translate('SongsPlugin.EditSongForm', 'Choose type for this author'),
AuthorType.TranslatedTypes,
current=AuthorType.SortedTypes.index(author_type),
editable=False)
if not ok:
return
author = self.manager.get_object(Author, author_id)
author_type = AuthorType.from_translated_text(choice)
item.setData(QtCore.Qt.UserRole, (author_id, author_type))
item.setText(author.get_display_name(author_type))
def on_author_remove_button_clicked(self):
"""
Remove the author from the list when the delete button is clicked.

View File

@ -33,7 +33,7 @@ from PyQt4 import QtCore, QtGui
from openlp.core.lib import build_icon
from openlp.plugins.songs.lib import VerseType
from openlp.plugins.songs.lib.xml import SongXML
from openlp.plugins.songs.lib.openlyricsxml import SongXML
class SongReviewWidget(QtGui.QWidget):

View File

@ -374,7 +374,7 @@ def clean_song(manager, song):
:param manager: The song database manager object.
:param song: The song object.
"""
from .xml import SongXML
from .openlyricsxml import SongXML
if song.title:
song.title = clean_title(song.title)

View File

@ -69,17 +69,42 @@ class AuthorType(object):
The 'words+music' type is not an official type, but is provided for convenience.
"""
NoType = ''
Words = 'words'
Music = 'music'
WordsAndMusic = 'words+music'
Translation = 'translation'
Types = {
NoType: '',
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')
}
SortedTypes = [
NoType,
Words,
Music,
WordsAndMusic
]
TranslatedTypes = [
Types[NoType],
Types[Words],
Types[Music],
Types[WordsAndMusic]
]
@staticmethod
def from_translated_text(translated_type):
"""
Get the AuthorType from a translated string.
:param translated_type: Translated Author type.
"""
for key, value in AuthorType.Types.items():
if value == translated_type:
return key
return AuthorType.NoType
class Book(BaseModel):

View File

@ -34,36 +34,37 @@ import logging
from openlp.core.common import translate, UiStrings
from openlp.core.ui.wizard import WizardStrings
from .opensongimport import OpenSongImport
from .easyslidesimport import EasySlidesImport
from .olpimport import OpenLPSongImport
from .openlyricsimport import OpenLyricsImport
from .wowimport import WowImport
from .cclifileimport import CCLIFileImport
from .dreambeamimport import DreamBeamImport
from .powersongimport import PowerSongImport
from .ewimport import EasyWorshipSongImport
from .songbeamerimport import SongBeamerImport
from .songshowplusimport import SongShowPlusImport
from .songproimport import SongProImport
from .sundayplusimport import SundayPlusImport
from .foilpresenterimport import FoilPresenterImport
from .zionworximport import ZionWorxImport
from .propresenterimport import ProPresenterImport
# Imports that might fail
from .importers.opensong import OpenSongImport
from .importers.easyslides import EasySlidesImport
from .importers.openlp import OpenLPSongImport
from .importers.openlyrics import OpenLyricsImport
from .importers.wordsofworship import WordsOfWorshipImport
from .importers.cclifile import CCLIFileImport
from .importers.dreambeam import DreamBeamImport
from .importers.powersong import PowerSongImport
from .importers.easyworship import EasyWorshipSongImport
from .importers.songbeamer import SongBeamerImport
from .importers.songshowplus import SongShowPlusImport
from .importers.songpro import SongProImport
from .importers.sundayplus import SundayPlusImport
from .importers.foilpresenter import FoilPresenterImport
from .importers.zionworx import ZionWorxImport
from .importers.propresenter import ProPresenterImport
from .importers.worshipassistant import WorshipAssistantImport
from .importers.powerpraise import PowerPraiseImport
from .importers.presentationmanager import PresentationManagerImport
log = logging.getLogger(__name__)
# Imports that might fail
try:
from .sofimport import SofImport
from .importers.songsoffellowship import SongsOfFellowshipImport
HAS_SOF = True
except ImportError:
log.exception('Error importing %s', 'SofImport')
log.exception('Error importing %s', 'SongsOfFellowshipImport')
HAS_SOF = False
try:
from .oooimport import OooImport
from .importers.openoffice import OpenOfficeImport
HAS_OOO = True
except ImportError:
log.exception('Error importing %s', 'OooImport')
@ -71,14 +72,14 @@ except ImportError:
HAS_MEDIASHOUT = False
if os.name == 'nt':
try:
from .mediashoutimport import MediaShoutImport
from .importers.mediashout import MediaShoutImport
HAS_MEDIASHOUT = True
except ImportError:
log.exception('Error importing %s', 'MediaShoutImport')
HAS_WORSHIPCENTERPRO = False
if os.name == 'nt':
try:
from .worshipcenterproimport import WorshipCenterProImport
from .importers.worshipcenterpro import WorshipCenterProImport
HAS_WORSHIPCENTERPRO = True
except ImportError:
log.exception('Error importing %s', 'WorshipCenterProImport')
@ -108,7 +109,7 @@ class SongFormat(object):
Name of the format, e.g. ``'OpenLyrics'``
``'prefix'``
Prefix for Qt objects. Use mixedCase, e.g. ``'open_lyrics'``
Prefix for Qt objects. Use mixedCase, e.g. ``'openLyrics'``
See ``SongImportForm.add_file_select_item()``
Optional attributes for each song format:
@ -159,16 +160,19 @@ class SongFormat(object):
FoilPresenter = 8
MediaShout = 9
OpenSong = 10
PowerSong = 11
ProPresenter = 12
SongBeamer = 13
SongPro = 14
SongShowPlus = 15
SongsOfFellowship = 16
SundayPlus = 17
WordsOfWorship = 18
WorshipCenterPro = 19
ZionWorx = 20
PowerPraise = 11
PowerSong = 12
PresentationManager = 13
ProPresenter = 14
SongBeamer = 15
SongPro = 16
SongShowPlus = 17
SongsOfFellowship = 18
SundayPlus = 19
WordsOfWorship = 20
WorshipAssistant = 21
WorshipCenterPro = 22
ZionWorx = 23
# Set optional attribute defaults
__defaults__ = {
@ -188,7 +192,7 @@ class SongFormat(object):
OpenLyrics: {
'class': OpenLyricsImport,
'name': 'OpenLyrics',
'prefix': 'open_lyrics',
'prefix': 'openLyrics',
'filter': '%s (*.xml)' % translate('SongsPlugin.ImportWizardForm', 'OpenLyrics Files'),
'comboBoxText': translate('SongsPlugin.ImportWizardForm', 'OpenLyrics or OpenLP 2.0 Exported Song')
},
@ -264,6 +268,12 @@ class SongFormat(object):
'name': WizardStrings.OS,
'prefix': 'openSong'
},
PowerPraise: {
'class': PowerPraiseImport,
'name': 'PowerPraise',
'prefix': 'powerPraise',
'filter': '%s (*.ppl)' % translate('SongsPlugin.ImportWizardForm', 'PowerPraise Song Files')
},
PowerSong: {
'class': PowerSongImport,
'name': 'PowerSong 1.0',
@ -272,6 +282,12 @@ class SongFormat(object):
'invalidSourceMsg': translate('SongsPlugin.ImportWizardForm', 'You need to specify a valid PowerSong 1.0 '
'database folder.')
},
PresentationManager: {
'class': PresentationManagerImport,
'name': 'PresentationManager',
'prefix': 'presentationManager',
'filter': '%s (*.sng)' % translate('SongsPlugin.ImportWizardForm', 'PresentationManager Song Files')
},
ProPresenter: {
'class': ProPresenterImport,
'name': 'ProPresenter',
@ -316,11 +332,21 @@ class SongFormat(object):
'filter': '%s (*.ptf)' % translate('SongsPlugin.ImportWizardForm', 'SundayPlus Song Files')
},
WordsOfWorship: {
'class': WowImport,
'class': WordsOfWorshipImport,
'name': 'Words of Worship',
'prefix': 'wordsOfWorship',
'filter': '%s (*.wsg *.wow-song)' % translate('SongsPlugin.ImportWizardForm', 'Words Of Worship Song Files')
},
WorshipAssistant: {
'class': WorshipAssistantImport,
'name': 'Worship Assistant 0',
'prefix': 'worshipAssistant',
'selectMode': SongFormatSelect.SingleFile,
'filter': '%s (*.csv)' % translate('SongsPlugin.ImportWizardForm', 'Worship Assistant Files'),
'comboBoxText': translate('SongsPlugin.ImportWizardForm', 'Worship Assistant (CSV)'),
'descriptionText': translate('SongsPlugin.ImportWizardForm',
'In Worship Assistant, export your Database to a CSV file.')
},
WorshipCenterPro: {
'name': 'WorshipCenter Pro',
'prefix': 'worshipCenterPro',
@ -362,7 +388,9 @@ class SongFormat(object):
SongFormat.FoilPresenter,
SongFormat.MediaShout,
SongFormat.OpenSong,
SongFormat.PowerPraise,
SongFormat.PowerSong,
SongFormat.PresentationManager,
SongFormat.ProPresenter,
SongFormat.SongBeamer,
SongFormat.SongPro,
@ -370,16 +398,17 @@ class SongFormat(object):
SongFormat.SongsOfFellowship,
SongFormat.SundayPlus,
SongFormat.WordsOfWorship,
SongFormat.WorshipAssistant,
SongFormat.WorshipCenterPro,
SongFormat.ZionWorx
]
@staticmethod
def get(format, *attributes):
def get(song_format, *attributes):
"""
Return requested song format attribute(s).
:param format: A song format from SongFormat.
:param song_format: A song format from SongFormat.
:param attributes: Zero or more song format attributes from SongFormat.
Return type depends on number of supplied attributes:
@ -389,31 +418,31 @@ class SongFormat(object):
:>1: Return tuple of requested attribute values.
"""
if not attributes:
return SongFormat.__attributes__.get(format)
return SongFormat.__attributes__.get(song_format)
elif len(attributes) == 1:
default = SongFormat.__defaults__.get(attributes[0])
return SongFormat.__attributes__[format].get(attributes[0], default)
return SongFormat.__attributes__[song_format].get(attributes[0], default)
else:
values = []
for attr in attributes:
default = SongFormat.__defaults__.get(attr)
values.append(SongFormat.__attributes__[format].get(attr, default))
values.append(SongFormat.__attributes__[song_format].get(attr, default))
return tuple(values)
@staticmethod
def set(format, attribute, value):
def set(song_format, attribute, value):
"""
Set specified song format attribute to the supplied value.
"""
SongFormat.__attributes__[format][attribute] = value
SongFormat.__attributes__[song_format][attribute] = value
SongFormat.set(SongFormat.SongsOfFellowship, 'availability', HAS_SOF)
if HAS_SOF:
SongFormat.set(SongFormat.SongsOfFellowship, 'class', SofImport)
SongFormat.set(SongFormat.SongsOfFellowship, 'class', SongsOfFellowshipImport)
SongFormat.set(SongFormat.Generic, 'availability', HAS_OOO)
if HAS_OOO:
SongFormat.set(SongFormat.Generic, 'class', OooImport)
SongFormat.set(SongFormat.Generic, 'class', OpenOfficeImport)
SongFormat.set(SongFormat.MediaShout, 'availability', HAS_MEDIASHOUT)
if HAS_MEDIASHOUT:
SongFormat.set(SongFormat.MediaShout, 'class', MediaShoutImport)

View File

@ -0,0 +1,31 @@
# -*- 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 #
###############################################################################
"""
The :mod:`~openlp.plugins.songs.lib.import` module contains importers for the Songs plugin.
"""

View File

@ -64,7 +64,7 @@ class CCLIFileImport(SongImport):
filename = str(filename)
log.debug('Importing CCLI File: %s', filename)
if os.path.isfile(filename):
detect_file = open(filename, 'r')
detect_file = open(filename, 'rb')
detect_content = detect_file.read(2048)
try:
str(detect_content, 'utf-8')

View File

@ -27,15 +27,14 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
The :mod:`dreambeamimport` module provides the functionality for importing
DreamBeam songs into the OpenLP database.
The :mod:`dreambeam` module provides the functionality for importing DreamBeam songs into the OpenLP database.
"""
import logging
from lxml import etree, objectify
from openlp.core.lib import translate
from openlp.plugins.songs.lib.songimport import SongImport
from openlp.plugins.songs.lib.importers.songimport import SongImport
from openlp.plugins.songs.lib.ui import SongStrings
log = logging.getLogger(__name__)

View File

@ -33,7 +33,7 @@ import re
from lxml import etree, objectify
from openlp.plugins.songs.lib import VerseType
from openlp.plugins.songs.lib.songimport import SongImport
from openlp.plugins.songs.lib.importers.songimport import SongImport
log = logging.getLogger(__name__)

View File

@ -27,8 +27,7 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
The :mod:`ewimport` module provides the functionality for importing
EasyWorship song databases into the current installation database.
The :mod:`easyworship` module provides the functionality for importing EasyWorship song databases into OpenLP.
"""
import os

View File

@ -99,10 +99,10 @@ from lxml import etree, objectify
from openlp.core.lib import translate
from openlp.core.ui.wizard import WizardStrings
from openlp.plugins.songs.lib import clean_song, VerseType
from openlp.plugins.songs.lib.songimport import SongImport
from openlp.plugins.songs.lib.importers.songimport import SongImport
from openlp.plugins.songs.lib.db import Author, Book, Song, Topic
from openlp.plugins.songs.lib.ui import SongStrings
from openlp.plugins.songs.lib.xml import SongXML
from openlp.plugins.songs.lib.openlyricsxml import SongXML
log = logging.getLogger(__name__)

View File

@ -27,13 +27,13 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
The :mod:`mediashoutimport` module provides the functionality for importing
The :mod:`mediashout` module provides the functionality for importing
a MediaShout database into the OpenLP database.
"""
import pyodbc
from openlp.core.lib import translate
from openlp.plugins.songs.lib.songimport import SongImport
from openlp.plugins.songs.lib.importers.songimport import SongImport
VERSE_TAGS = ['V', 'C', 'B', 'O', 'P', 'I', 'E']

View File

@ -27,7 +27,7 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
The :mod:`olpimport` module provides the functionality for importing OpenLP
The :mod:`openlp` module provides the functionality for importing OpenLP
song databases into the current installation database.
"""
import logging

View File

@ -27,7 +27,7 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
The :mod:`openlyricsimport` module provides the functionality for importing
The :mod:`openlyrics` module provides the functionality for importing
songs which are saved as OpenLyrics files.
"""
@ -37,9 +37,9 @@ import os
from lxml import etree
from openlp.core.ui.wizard import WizardStrings
from openlp.plugins.songs.lib.songimport import SongImport
from openlp.plugins.songs.lib.importers.songimport import SongImport
from openlp.plugins.songs.lib.ui import SongStrings
from openlp.plugins.songs.lib.xml import OpenLyrics, OpenLyricsError
from openlp.plugins.songs.lib.openlyricsxml import OpenLyrics, OpenLyricsError
log = logging.getLogger(__name__)

View File

@ -52,7 +52,7 @@ except ImportError:
PAGE_BOTH = 6
class OooImport(SongImport):
class OpenOfficeImport(SongImport):
"""
Import songs from Impress/Powerpoint docs using Impress
"""

View File

@ -35,7 +35,7 @@ from lxml.etree import Error, LxmlError
from openlp.core.common import translate
from openlp.plugins.songs.lib import VerseType
from openlp.plugins.songs.lib.songimport import SongImport
from openlp.plugins.songs.lib.importers.songimport import SongImport
from openlp.plugins.songs.lib.ui import SongStrings
log = logging.getLogger(__name__)

View File

@ -0,0 +1,91 @@
# -*- 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:`powerpraiseimport` module provides the functionality for importing
Powerpraise song files into the current database.
"""
import os
from lxml import objectify
from openlp.core.ui.wizard import WizardStrings
from .songimport import SongImport
class PowerPraiseImport(SongImport):
"""
The :class:`PowerpraiseImport` class provides OpenLP with the
ability to import Powerpraise 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 = str(root.general.title)
verse_order_list = []
verse_count = {}
for item in root.order.item:
verse_order_list.append(str(item))
for part in root.songtext.part:
original_verse_def = part.get('caption')
# There are some predefined verse defitions in PowerPraise, try to parse these
if original_verse_def.startswith("Strophe") or original_verse_def.startswith("Teil"):
verse_def = 'v'
elif original_verse_def.startswith("Refrain"):
verse_def = 'c'
elif original_verse_def.startswith("Bridge"):
verse_def = 'b'
elif original_verse_def.startswith("Schluss"):
verse_def = 'e'
else:
verse_def = 'o'
verse_count[verse_def] = verse_count.get(verse_def, 0) + 1
verse_def = '%s%d' % (verse_def, verse_count[verse_def])
verse_text = []
for slide in part.slide:
if not hasattr(slide, 'line'):
continue # No content
for line in slide.line:
verse_text.append(str(line))
self.add_verse('\n'.join(verse_text), verse_def)
# Update verse name in verse order list
for i in range(len(verse_order_list)):
if verse_order_list[i].lower() == original_verse_def.lower():
verse_order_list[i] = verse_def
self.verse_order_list = verse_order_list
if not self.finish():
self.log_error(self.import_source)

View File

@ -27,7 +27,7 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
The :mod:`powersongimport` module provides the functionality for importing
The :mod:`powersong` module provides the functionality for importing
PowerSong songs into the OpenLP database.
"""
import logging
@ -35,7 +35,7 @@ import fnmatch
import os
from openlp.core.common import translate
from openlp.plugins.songs.lib.songimport import SongImport
from openlp.plugins.songs.lib.importers.songimport import SongImport
log = logging.getLogger(__name__)
@ -90,7 +90,7 @@ class PowerSongImport(SongImport):
"""
Receive either a list of files or a folder (unicode) to import.
"""
from .importer import SongFormat
from openlp.plugins.songs.lib.importer import SongFormat
ps_string = SongFormat.get(SongFormat.PowerSong, 'name')
if isinstance(self.import_source, str):
if os.path.isdir(self.import_source):

View File

@ -0,0 +1,93 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-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:`presentationmanager` module provides the functionality for importing
Presentationmanager song files into the current database.
"""
import os
from lxml import objectify
from openlp.core.ui.wizard import WizardStrings
from .songimport import SongImport
class PresentationManagerImport(SongImport):
"""
The :class:`PresentationManagerImport` class provides OpenLP with the
ability to import Presentationmanager 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 = str(root.attributes.title)
self.add_author(str(root.attributes.author))
self.copyright = str(root.attributes.copyright)
self.ccli_number = str(root.attributes.ccli_number)
self.comments = str(root.attributes.comments)
verse_order_list = []
verse_count = {}
duplicates = []
for verse in root.verses.verse:
original_verse_def = verse.get('id')
# Presentation Manager stores duplicate verses instead of a verse order.
# We need to create the verse order from that.
is_duplicate = False
if original_verse_def in duplicates:
is_duplicate = True
else:
duplicates.append(original_verse_def)
if original_verse_def.startswith("Verse"):
verse_def = 'v'
elif original_verse_def.startswith("Chorus") or original_verse_def.startswith("Refrain"):
verse_def = 'c'
elif original_verse_def.startswith("Bridge"):
verse_def = 'b'
elif original_verse_def.startswith("End"):
verse_def = 'e'
else:
verse_def = 'o'
if not is_duplicate: # Only increment verse number if no duplicate
verse_count[verse_def] = verse_count.get(verse_def, 0) + 1
verse_def = '%s%d' % (verse_def, verse_count[verse_def])
if not is_duplicate: # Only add verse if no duplicate
self.add_verse(str(verse).strip(), verse_def)
verse_order_list.append(verse_def)
self.verse_order_list = verse_order_list
if not self.finish():
self.log_error(self.import_source)

View File

@ -27,7 +27,7 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
The :mod:`propresenterimport` module provides the functionality for importing
The :mod:`propresenter` module provides the functionality for importing
ProPresenter song files into the current installation database.
"""

View File

@ -27,7 +27,7 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
The :mod:`songbeamerimport` module provides the functionality for importing SongBeamer songs into the OpenLP database.
The :mod:`songbeamer` module provides the functionality for importing SongBeamer songs into the OpenLP database.
"""
import chardet
import codecs
@ -36,7 +36,7 @@ import os
import re
from openlp.plugins.songs.lib import VerseType
from openlp.plugins.songs.lib.songimport import SongImport
from openlp.plugins.songs.lib.importers.songimport import SongImport
log = logging.getLogger(__name__)

View File

@ -39,7 +39,7 @@ from openlp.core.ui.wizard import WizardStrings
from openlp.plugins.songs.lib import clean_song, VerseType
from openlp.plugins.songs.lib.db import Song, Author, Topic, Book, MediaFile
from openlp.plugins.songs.lib.ui import SongStrings
from openlp.plugins.songs.lib.xml import SongXML
from openlp.plugins.songs.lib.openlyricsxml import SongXML
log = logging.getLogger(__name__)

View File

@ -27,13 +27,13 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
The :mod:`songproimport` module provides the functionality for importing SongPro
The :mod:`songpro` module provides the functionality for importing SongPro
songs into the OpenLP database.
"""
import re
from openlp.plugins.songs.lib import strip_rtf
from openlp.plugins.songs.lib.songimport import SongImport
from openlp.plugins.songs.lib.importers.songimport import SongImport
class SongProImport(SongImport):

View File

@ -27,7 +27,7 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
The :mod:`songshowplusimport` module provides the functionality for importing SongShow Plus songs into the OpenLP
The :mod:`songshowplus` module provides the functionality for importing SongShow Plus songs into the OpenLP
database.
"""
import chardet
@ -38,7 +38,7 @@ import struct
from openlp.core.ui.wizard import WizardStrings
from openlp.plugins.songs.lib import VerseType, retrieve_windows_encoding
from openlp.plugins.songs.lib.songimport import SongImport
from openlp.plugins.songs.lib.importers.songimport import SongImport
TITLE = 1
AUTHOR = 2

View File

@ -37,13 +37,13 @@ import logging
import os
import re
from .oooimport import OooImport
from .openoffice import OpenOfficeImport
log = logging.getLogger(__name__)
if os.name == 'nt':
from .oooimport import PAGE_BEFORE, PAGE_AFTER, PAGE_BOTH
from .openoffice import PAGE_BEFORE, PAGE_AFTER, PAGE_BOTH
RuntimeException = Exception
else:
try:
@ -62,7 +62,7 @@ except ImportError:
ITALIC = 2
class SofImport(OooImport):
class SongsOfFellowshipImport(OpenOfficeImport):
"""
Import songs provided on disks with the Songs of Fellowship music books
VOLS1_2.RTF, sof3words.rtf and sof4words.rtf
@ -83,7 +83,7 @@ class SofImport(OooImport):
Initialise the class. Requires a songmanager class which is passed
to SongImport for writing song to disk
"""
OooImport.__init__(self, manager, **kwargs)
OpenOfficeImport.__init__(self, manager, **kwargs)
self.song = False
def process_ooo_document(self):

View File

@ -32,7 +32,7 @@ import re
from openlp.plugins.songs.lib import VerseType, retrieve_windows_encoding
from openlp.plugins.songs.lib import strip_rtf
from openlp.plugins.songs.lib.songimport import SongImport
from openlp.plugins.songs.lib.importers.songimport import SongImport
HOTKEY_TO_VERSE_TYPE = {
'1': 'v1',

View File

@ -27,24 +27,23 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
The :mod:`wowimport` module provides the functionality for importing Words of
The :mod:`wordsofworship` module provides the functionality for importing Words of
Worship songs into the OpenLP database.
"""
import os
import logging
from openlp.core.common import translate
from openlp.plugins.songs.lib.songimport import SongImport
from openlp.plugins.songs.lib.importers.songimport import SongImport
BLOCK_TYPES = ('V', 'C', 'B')
log = logging.getLogger(__name__)
class WowImport(SongImport):
class WordsOfWorshipImport(SongImport):
"""
The :class:`WowImport` class provides the ability to import song files from
Words of Worship.
The :class:`WordsOfWorshipImport` class provides the ability to import song files from Words of Worship.
**Words Of Worship Song File Format:**

View File

@ -0,0 +1,171 @@
# -*- 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 #
###############################################################################
"""
The :mod:`worshipassistant` module provides the functionality for importing
Worship Assistant songs into the OpenLP database.
"""
import chardet
import csv
import logging
import re
from openlp.core.common import translate
from openlp.plugins.songs.lib import VerseType
from openlp.plugins.songs.lib.importers.songimport import SongImport
log = logging.getLogger(__name__)
EMPTY_STR = 'NULL'
class WorshipAssistantImport(SongImport):
"""
The :class:`WorshipAssistantImport` class provides the ability to import songs
from Worship Assistant, via a dump of the database to a CSV file.
The following fields are in the exported CSV file:
* ``SONGNR`` Song ID (Discarded by importer)
* ``TITLE`` Song title
* ``AUTHOR`` Song author.
* ``COPYRIGHT`` Copyright information
* ``FIRSTLINE`` Unknown (Discarded by importer)
* ``PRIKEY`` Primary chord key (Discarded by importer)
* ``ALTKEY`` Alternate chord key (Discarded by importer)
* ``TEMPO`` Tempo (Discarded by importer)
* ``FOCUS`` Unknown (Discarded by importer)
* ``THEME`` Theme (Discarded by importer)
* ``SCRIPTURE`` Associated scripture (Discarded by importer)
* ``ACTIVE`` Boolean value (Discarded by importer)
* ``SONGBOOK`` Boolean value (Discarded by importer)
* ``TIMESIG`` Unknown (Discarded by importer)
* ``INTRODUCED`` Date the song was created (Discarded by importer)
* ``LASTUSED`` Date the song was last used (Discarded by importer)
* ``TIMESUSED`` How many times the song was used (Discarded by importer)
* ``CCLINR`` CCLI Number
* ``USER1`` User Field 1 (Discarded by importer)
* ``USER2`` User Field 2 (Discarded by importer)
* ``USER3`` User Field 3 (Discarded by importer)
* ``USER4`` User Field 4 (Discarded by importer)
* ``USER5`` User Field 5 (Discarded by importer)
* ``ROADMAP`` Verse order used for the presentation
* ``FILELINK1`` Associated file 1 (Discarded by importer)
* ``OVERMAP`` Verse order used for printing (Discarded by importer)
* ``FILELINK2`` Associated file 2 (Discarded by importer)
* ``LYRICS`` The song lyrics used for printing (Discarded by importer, LYRICS2 is used instead)
* ``INFO`` Unknown (Discarded by importer)
* ``LYRICS2`` The song lyrics used for the presentation
* ``BACKGROUND`` Custom background (Discarded by importer)
"""
def do_import(self):
"""
Receive a CSV file to import.
"""
# Get encoding
detect_file = open(self.import_source, 'rb')
detect_content = detect_file.read()
details = chardet.detect(detect_content)
detect_file.close()
songs_file = open(self.import_source, 'r', encoding=details['encoding'])
songs_reader = csv.DictReader(songs_file)
try:
records = list(songs_reader)
except csv.Error as e:
self.log_error(translate('SongsPlugin.WorshipAssistantImport', 'Error reading CSV file.'),
translate('SongsPlugin.WorshipAssistantImport', 'Line %d: %s') %
(songs_reader.line_num, e))
return
num_records = len(records)
log.info('%s records found in CSV file' % num_records)
self.import_wizard.progress_bar.setMaximum(num_records)
for index, record in enumerate(records, 1):
if self.stop_import_flag:
return
# Ensure that all keys are uppercase
record = dict((field.upper(), value) for field, value in record.items())
# The CSV file has a line in the middle of the file where the headers are repeated.
# We need to skip this line.
if record['TITLE'] == "TITLE" and record['AUTHOR'] == 'AUTHOR' and record['LYRICS2'] == 'LYRICS2':
continue
self.set_defaults()
verse_order_list = []
try:
self.title = record['TITLE']
if record['AUTHOR'] != EMPTY_STR:
self.parse_author(record['AUTHOR'])
print(record['AUTHOR'])
if record['COPYRIGHT'] != EMPTY_STR:
self.add_copyright(record['COPYRIGHT'])
if record['CCLINR'] != EMPTY_STR:
self.ccli_number = record['CCLINR']
if record['ROADMAP'] != EMPTY_STR:
verse_order_list = record['ROADMAP'].split(',')
lyrics = record['LYRICS2']
except UnicodeDecodeError as e:
self.log_error(translate('SongsPlugin.WorshipAssistantImport', 'Record %d' % index),
translate('SongsPlugin.WorshipAssistantImport', 'Decoding error: %s') % e)
continue
except TypeError as e:
self.log_error(translate('SongsPlugin.WorshipAssistantImport',
'File not valid WorshipAssistant CSV format.'), 'TypeError: %s' % e)
return
verse = ''
for line in lyrics.splitlines():
if line.startswith('['): # verse marker
# drop the square brackets
right_bracket = line.find(']')
content = line[1:right_bracket].lower()
match = re.match('(\D*)(\d+)', content)
if match is not None:
verse_tag = match.group(1)
verse_num = match.group(2)
else:
# otherwise we assume number 1 and take the whole prefix as the verse tag
verse_tag = content
verse_num = '1'
verse_index = VerseType.from_loose_input(verse_tag) if verse_tag else 0
verse_tag = VerseType.tags[verse_index]
# Update verse order when the verse name has changed
if content != verse_tag + verse_num:
for i in range(len(verse_order_list)):
if verse_order_list[i].lower() == content.lower():
verse_order_list[i] = verse_tag + verse_num
elif line and not line.isspace():
verse += line + '\n'
elif verse:
self.add_verse(verse, verse_tag+verse_num)
verse = ''
if verse:
self.add_verse(verse, verse_tag+verse_num)
if verse_order_list:
self.verse_order_list = verse_order_list
if not self.finish():
self.log_error(translate('SongsPlugin.WorshipAssistantImport', 'Record %d') % index
+ (': "' + self.title + '"' if self.title else ''))
songs_file.close()

View File

@ -35,7 +35,7 @@ import logging
import pyodbc
from openlp.core.common import translate
from openlp.plugins.songs.lib.songimport import SongImport
from openlp.plugins.songs.lib.importers.songimport import SongImport
log = logging.getLogger(__name__)

View File

@ -27,14 +27,13 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
The :mod:`zionworximport` module provides the functionality for importing
ZionWorx songs into the OpenLP database.
The :mod:`zionworx` module provides the functionality for importing ZionWorx songs into the OpenLP database.
"""
import csv
import logging
from openlp.core.common import translate
from openlp.plugins.songs.lib.songimport import SongImport
from openlp.plugins.songs.lib.importers.songimport import SongImport
log = logging.getLogger(__name__)

View File

@ -36,7 +36,7 @@ from PyQt4 import QtCore, QtGui
from sqlalchemy.sql import or_
from openlp.core.common import Registry, AppLocation, Settings, check_directory_exists, UiStrings, translate
from openlp.core.lib import MediaManagerItem, ItemCapabilities, PluginStatus, ServiceItem, ServiceItemContext, \
from openlp.core.lib import MediaManagerItem, ItemCapabilities, PluginStatus, ServiceItemContext, \
check_item_selected, create_separated_list
from openlp.core.lib.ui import create_widget_action
from openlp.plugins.songs.forms.editsongform import EditSongForm
@ -46,7 +46,7 @@ from openlp.plugins.songs.forms.songexportform import SongExportForm
from openlp.plugins.songs.lib import VerseType, clean_string, delete_song
from openlp.plugins.songs.lib.db import Author, AuthorType, Song, Book, MediaFile
from openlp.plugins.songs.lib.ui import SongStrings
from openlp.plugins.songs.lib.xml import OpenLyrics, SongXML
from openlp.plugins.songs.lib.openlyricsxml import OpenLyrics, SongXML
log = logging.getLogger(__name__)
@ -126,6 +126,7 @@ class SongMediaItem(MediaManagerItem):
self.update_service_on_edit = Settings().value(self.settings_section + '/update service on edit')
self.add_song_from_service = Settings().value(self.settings_section + '/add song from service')
self.display_songbook = Settings().value(self.settings_section + '/display songbook')
self.display_copyright_symbol = Settings().value(self.settings_section + '/display copyright symbol')
def retranslateUi(self):
self.search_text_label.setText('%s:' % UiStrings().Search)
@ -506,7 +507,11 @@ class SongMediaItem(MediaManagerItem):
if authors_translation:
item.raw_footer.append("%s: %s" % (AuthorType.Types[AuthorType.Translation],
create_separated_list(authors_translation)))
item.raw_footer.append(song.copyright)
if song.copyright:
if self.display_copyright_symbol:
item.raw_footer.append("%s %s" % (SongStrings.CopyrightSymbol, song.copyright))
else:
item.raw_footer.append(song.copyright)
if self.display_songbook and song.book:
item.raw_footer.append("%s #%s" % (song.book.name, song.song_number))
if Settings().value('core/ccli number'):

View File

@ -37,7 +37,7 @@ from lxml import etree
from openlp.core.common import RegistryProperties, check_directory_exists, translate
from openlp.core.utils import clean_filename
from openlp.plugins.songs.lib.xml import OpenLyrics
from openlp.plugins.songs.lib.openlyricsxml import OpenLyrics
log = logging.getLogger(__name__)

View File

@ -38,7 +38,7 @@ from html.parser import HTMLParser
from bs4 import BeautifulSoup, NavigableString
from openlp.plugins.songs.lib import Song, VerseType, clean_song, Author
from openlp.plugins.songs.lib.xml import SongXML
from openlp.plugins.songs.lib.openlyricsxml import SongXML
USER_AGENT = 'Mozilla/5.0 (Linux; U; Android 4.0.3; en-us; GT-I9000 ' \
'Build/IML74K) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 ' \

View File

@ -31,6 +31,7 @@ from PyQt4 import QtCore, QtGui
from openlp.core.common import Settings, translate
from openlp.core.lib import SettingsTab
from openlp.plugins.songs.lib.ui import SongStrings
class SongsTab(SettingsTab):
@ -62,6 +63,9 @@ class SongsTab(SettingsTab):
self.display_songbook_check_box = QtGui.QCheckBox(self.mode_group_box)
self.display_songbook_check_box.setObjectName('songbook_check_box')
self.mode_layout.addWidget(self.display_songbook_check_box)
self.display_copyright_check_box = QtGui.QCheckBox(self.mode_group_box)
self.display_copyright_check_box.setObjectName('copyright_check_box')
self.mode_layout.addWidget(self.display_copyright_check_box)
self.left_layout.addWidget(self.mode_group_box)
self.left_layout.addStretch()
self.right_layout.addStretch()
@ -70,6 +74,7 @@ class SongsTab(SettingsTab):
self.update_on_edit_check_box.stateChanged.connect(self.on_update_on_edit_check_box_changed)
self.add_from_service_check_box.stateChanged.connect(self.on_add_from_service_check_box_changed)
self.display_songbook_check_box.stateChanged.connect(self.on_songbook_check_box_changed)
self.display_copyright_check_box.stateChanged.connect(self.on_copyright_check_box_changed)
def retranslateUi(self):
self.mode_group_box.setTitle(translate('SongsPlugin.SongsTab', 'Songs Mode'))
@ -80,6 +85,9 @@ class SongsTab(SettingsTab):
self.add_from_service_check_box.setText(translate('SongsPlugin.SongsTab',
'Import missing songs from service files'))
self.display_songbook_check_box.setText(translate('SongsPlugin.SongsTab', 'Display songbook in footer'))
self.display_copyright_check_box.setText(translate('SongsPlugin.SongsTab',
'Display "%s" symbol before copyright info' %
SongStrings.CopyrightSymbol))
def on_search_as_type_check_box_changed(self, check_state):
self.song_search = (check_state == QtCore.Qt.Checked)
@ -96,6 +104,9 @@ class SongsTab(SettingsTab):
def on_songbook_check_box_changed(self, check_state):
self.display_songbook = (check_state == QtCore.Qt.Checked)
def on_copyright_check_box_changed(self, check_state):
self.display_copyright_symbol = (check_state == QtCore.Qt.Checked)
def load(self):
settings = Settings()
settings.beginGroup(self.settings_section)
@ -104,11 +115,13 @@ class SongsTab(SettingsTab):
self.update_edit = settings.value('update service on edit')
self.update_load = settings.value('add song from service')
self.display_songbook = settings.value('display songbook')
self.display_copyright_symbol = settings.value('display copyright symbol')
self.search_as_type_check_box.setChecked(self.song_search)
self.tool_bar_active_check_box.setChecked(self.tool_bar)
self.update_on_edit_check_box.setChecked(self.update_edit)
self.add_from_service_check_box.setChecked(self.update_load)
self.display_songbook_check_box.setChecked(self.display_songbook)
self.display_copyright_check_box.setChecked(self.display_copyright_symbol)
settings.endGroup()
def save(self):
@ -119,6 +132,7 @@ class SongsTab(SettingsTab):
settings.setValue('update service on edit', self.update_edit)
settings.setValue('add song from service', self.update_load)
settings.setValue('display songbook', self.display_songbook)
settings.setValue('display copyright symbol', self.display_copyright_symbol)
settings.endGroup()
if self.tab_visited:
self.settings_form.register_post_process('songs_config_updated')

View File

@ -33,7 +33,6 @@ backend for the Songs plugin
import logging
from sqlalchemy import Column, ForeignKey, types
from sqlalchemy.exc import OperationalError
from sqlalchemy.sql.expression import func, false, null, text
from openlp.core.lib.db import get_upgrade_op
@ -57,16 +56,13 @@ def upgrade_1(session, metadata):
:param session:
:param metadata:
"""
try:
op = get_upgrade_op(session)
op.drop_table('media_files_songs')
op.add_column('media_files', Column('song_id', types.Integer(), server_default=null()))
op.add_column('media_files', Column('weight', types.Integer(), server_default=text('0')))
if metadata.bind.url.get_dialect().name != 'sqlite':
# SQLite doesn't support ALTER TABLE ADD CONSTRAINT
op.create_foreign_key('fk_media_files_song_id', 'media_files', 'songs', ['song_id', 'id'])
except OperationalError:
log.info('Upgrade 1 has already been run')
op = get_upgrade_op(session)
op.drop_table('media_files_songs')
op.add_column('media_files', Column('song_id', types.Integer(), server_default=null()))
op.add_column('media_files', Column('weight', types.Integer(), server_default=text('0')))
if metadata.bind.url.get_dialect().name != 'sqlite':
# SQLite doesn't support ALTER TABLE ADD CONSTRAINT
op.create_foreign_key('fk_media_files_song_id', 'media_files', 'songs', ['song_id', 'id'])
def upgrade_2(session, metadata):
@ -75,12 +71,9 @@ def upgrade_2(session, metadata):
This upgrade adds a create_date and last_modified date to the songs table
"""
try:
op = get_upgrade_op(session)
op.add_column('songs', Column('create_date', types.DateTime(), default=func.now()))
op.add_column('songs', Column('last_modified', types.DateTime(), default=func.now()))
except OperationalError:
log.info('Upgrade 2 has already been run')
op = get_upgrade_op(session)
op.add_column('songs', Column('create_date', types.DateTime(), default=func.now()))
op.add_column('songs', Column('last_modified', types.DateTime(), default=func.now()))
def upgrade_3(session, metadata):
@ -89,14 +82,11 @@ def upgrade_3(session, metadata):
This upgrade adds a temporary song flag to the songs table
"""
try:
op = get_upgrade_op(session)
if metadata.bind.url.get_dialect().name == 'sqlite':
op.add_column('songs', Column('temporary', types.Boolean(create_constraint=False), server_default=false()))
else:
op.add_column('songs', Column('temporary', types.Boolean(), server_default=false()))
except OperationalError:
log.info('Upgrade 3 has already been run')
op = get_upgrade_op(session)
if metadata.bind.url.get_dialect().name == 'sqlite':
op.add_column('songs', Column('temporary', types.Boolean(create_constraint=False), server_default=false()))
else:
op.add_column('songs', Column('temporary', types.Boolean(), server_default=false()))
def upgrade_4(session, metadata):
@ -105,17 +95,14 @@ def upgrade_4(session, metadata):
This upgrade adds a column for author type to the authors_songs table
"""
try:
# Since SQLite doesn't support changing the primary key of a table, we need to recreate the table
# and copy the old values
op = get_upgrade_op(session)
op.create_table('authors_songs_tmp',
Column('author_id', types.Integer(), ForeignKey('authors.id'), primary_key=True),
Column('song_id', types.Integer(), ForeignKey('songs.id'), primary_key=True),
Column('author_type', types.String(), primary_key=True,
nullable=False, server_default=text('""')))
op.execute('INSERT INTO authors_songs_tmp SELECT author_id, song_id, "" FROM authors_songs')
op.drop_table('authors_songs')
op.rename_table('authors_songs_tmp', 'authors_songs')
except OperationalError:
log.info('Upgrade 4 has already been run')
# Since SQLite doesn't support changing the primary key of a table, we need to recreate the table
# and copy the old values
op = get_upgrade_op(session)
op.create_table('authors_songs_tmp',
Column('author_id', types.Integer(), ForeignKey('authors.id'), primary_key=True),
Column('song_id', types.Integer(), ForeignKey('songs.id'), primary_key=True),
Column('author_type', types.String(), primary_key=True,
nullable=False, server_default=text('""')))
op.execute('INSERT INTO authors_songs_tmp SELECT author_id, song_id, "" FROM authors_songs')
op.drop_table('authors_songs')
op.rename_table('authors_songs_tmp', 'authors_songs')

View File

@ -49,7 +49,7 @@ from openlp.plugins.songs.lib import clean_song, upgrade
from openlp.plugins.songs.lib.db import init_schema, Song
from openlp.plugins.songs.lib.mediaitem import SongSearch
from openlp.plugins.songs.lib.importer import SongFormat
from openlp.plugins.songs.lib.olpimport import OpenLPSongImport
from openlp.plugins.songs.lib.importers.openlp import OpenLPSongImport
from openlp.plugins.songs.lib.mediaitem import SongMediaItem
from openlp.plugins.songs.lib.songstab import SongsTab
@ -64,6 +64,7 @@ __default_settings__ = {
'songs/add song from service': True,
'songs/display songbar': True,
'songs/display songbook': False,
'songs/display copyright symbol': False,
'songs/last directory import': '',
'songs/last directory export': '',
'songs/songselect username': '',

View File

@ -32,7 +32,6 @@ backend for the SongsUsage plugin
"""
import logging
from sqlalchemy.exc import OperationalError
from sqlalchemy import Column, types
from openlp.core.lib.db import get_upgrade_op
@ -50,9 +49,6 @@ def upgrade_1(session, metadata):
:param session: SQLAlchemy Session object
:param metadata: SQLAlchemy MetaData object
"""
try:
op = get_upgrade_op(session)
op.add_column('songusage_data', Column('plugin_name', types.Unicode(20), server_default=''))
op.add_column('songusage_data', Column('source', types.Unicode(10), server_default=''))
except OperationalError:
log.info('Upgrade 1 has already taken place')
op = get_upgrade_op(session)
op.add_column('songusage_data', Column('plugin_name', types.Unicode(20), server_default=''))
op.add_column('songusage_data', Column('source', types.Unicode(10), server_default=''))

View File

@ -1,19 +1,11 @@
[Desktop Entry]
Categories=AudioVideo;
Comment[de]=
Comment=
Exec=openlp %F
GenericName[de]=Church lyrics projection
GenericName=Church lyrics projection
Icon=openlp
MimeType=application/x-openlp-service;
Name[de]=OpenLP
Name=OpenLP
Path=
StartupNotify=true
Terminal=false
Type=Application
X-DBUS-ServiceName=
X-DBUS-StartupType=
X-KDE-SubstituteUID=false
X-KDE-Username=

View File

@ -62,11 +62,12 @@ class OpenLPJobs(object):
Branch_Pull = 'Branch-01-Pull'
Branch_Functional = 'Branch-02-Functional-Tests'
Branch_Interface = 'Branch-03-Interface-Tests'
Branch_Windows = 'Branch-04-Windows_Tests'
Branch_Windows_Functional = 'Branch-04a-Windows_Functional_Tests'
Branch_Windows_Interface = 'Branch-04b-Windows_Interface_Tests'
Branch_PEP = 'Branch-05a-Code_Analysis'
Branch_Coverage = 'Branch-05b-Test_Coverage'
Jobs = [Branch_Pull, Branch_Functional, Branch_Interface, Branch_Windows, Branch_PEP, Branch_Coverage]
Jobs = [Branch_Pull, Branch_Functional, Branch_Interface, Branch_Windows_Functional, Branch_Windows_Interface, Branch_PEP, Branch_Coverage]
class Colour(object):
@ -114,7 +115,9 @@ class JenkinsTrigger(object):
print('%s (revision %s)' % (get_repo_name(), revno))
for job in OpenLPJobs.Jobs:
self.__print_build_info(job)
if not self.__print_build_info(job):
print('Stopping after failure')
break
def open_browser(self):
"""
@ -131,6 +134,7 @@ class JenkinsTrigger(object):
:param job_name: The name of the job we want the information from. For example *Branch-01-Pull*. Use the class
variables from the :class:`OpenLPJobs` class.
"""
is_success = False
job = self.jenkins_instance.job(job_name)
while job.info['inQueue']:
time.sleep(1)
@ -139,11 +143,13 @@ class JenkinsTrigger(object):
if build.info['result'] == 'SUCCESS':
# Make 'SUCCESS' green.
result_string = '%s%s%s' % (Colour.GREEN_START, build.info['result'], Colour.GREEN_END)
is_success = True
else:
# Make 'FAILURE' red.
result_string = '%s%s%s' % (Colour.RED_START, build.info['result'], Colour.RED_END)
url = build.info['url']
print('[%s] %s' % (result_string, url))
return is_success
def get_repo_name():

View File

@ -105,10 +105,12 @@ try:
tag_version, tag_revision = tags[-1].split()
# If they are equal, then this tree is tarball with the source for the release. We do not want the revision number
# in the version string.
tree_revision = tree_revision.strip()
tag_revision = tag_revision.strip()
if tree_revision == tag_revision:
version_string = tag_version
version_string = tag_version.decode('utf-8')
else:
version_string = '%s-bzr%s' % (tag_version, tree_revision)
version_string = '%s-bzr%s' % (tag_version.decode('utf-8'), tree_revision.decode('utf-8'))
ver_file = open(VERSION_FILE, 'w')
ver_file.write(version_string)
except:
@ -152,7 +154,7 @@ using a computer and a data projector.""",
'Operating System :: POSIX :: BSD :: FreeBSD',
'Operating System :: POSIX :: Linux',
'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 3',
'Topic :: Desktop Environment :: Gnome',
'Topic :: Desktop Environment :: K Desktop Environment (KDE)',
'Topic :: Multimedia',

View File

@ -0,0 +1,76 @@
# -*- 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 #
###############################################################################
"""
Package to test the openlp.core.common package.
"""
import os
from unittest import TestCase
from openlp.core.common import RegistryMixin, Registry
TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '../', '..', 'resources'))
class TestRegistryMixin(TestCase):
def registry_mixin_missing_test(self):
"""
Test the registry creation and its usage
"""
# GIVEN: A new registry
Registry.create()
# WHEN: I create a new class
mock_1 = Test1()
# THEN: The following methods are missing
self.assertEqual(len(Registry().functions_list), 0), 'The function should not be in the dict anymore.'
def registry_mixin_present_test(self):
"""
Test the registry creation and its usage
"""
# GIVEN: A new registry
Registry.create()
# WHEN: I create a new class
mock_2 = Test2()
# THEN: The following bootstrap methods should be present
self.assertEqual(len(Registry().functions_list), 2), 'The bootstrap functions should be in the dict.'
class Test1(object):
def __init__(self):
pass
class Test2(RegistryMixin):
def __init__(self):
super(Test2, self).__init__(None)

View File

@ -6,11 +6,12 @@ from unittest import TestCase
from PyQt4 import QtCore
from openlp.core.common import Settings
from openlp.core.lib.htmlbuilder import build_html, build_background_css, build_lyrics_css, build_lyrics_outline_css, \
build_lyrics_format_css, build_footer_css
from openlp.core.lib.theme import HorizontalType, VerticalType
from tests.functional import MagicMock, patch
from tests.helpers.testmixin import TestMixin
HTML = """
<!DOCTYPE html>
@ -184,7 +185,7 @@ LYRICS_OUTLINE_CSS = ' -webkit-text-stroke: 0.125em #000000; -webkit-text-fill-c
LYRICS_FORMAT_CSS = ' word-wrap: break-word; text-align: justify; vertical-align: bottom; ' + \
'font-family: Arial; font-size: 40pt; color: #FFFFFF; line-height: 108%; margin: 0;padding: 0; ' + \
'padding-bottom: 0.5em; padding-left: 2px; width: 1580px; height: 810px; font-style:italic; font-weight:bold; '
FOOTER_CSS = """
FOOTER_CSS_BASE = """
left: 10px;
bottom: 0px;
width: 1260px;
@ -192,11 +193,28 @@ FOOTER_CSS = """
font-size: 12pt;
color: #FFFFFF;
text-align: left;
white-space: nowrap;
white-space: %s;
"""
FOOTER_CSS = FOOTER_CSS_BASE % ('nowrap')
FOOTER_CSS_WRAP = FOOTER_CSS_BASE % ('normal')
class Htmbuilder(TestCase):
class Htmbuilder(TestCase, TestMixin):
"""
Test the functions in the Htmlbuilder module
"""
def setUp(self):
"""
Create the UI
"""
self.build_settings()
def tearDown(self):
"""
Delete all the C++ objects at the end so that we don't have a segfault
"""
self.destroy_settings()
def build_html_test(self):
"""
Test the build_html() function
@ -225,7 +243,7 @@ class Htmbuilder(TestCase):
html = build_html(item, screen, is_live, background, plugins=plugins)
# THEN: The returned html should match.
assert html == HTML
self.assertEqual(html, HTML, 'The returned html should match')
def build_background_css_radial_test(self):
"""
@ -241,7 +259,7 @@ class Htmbuilder(TestCase):
css = build_background_css(item, width)
# THEN: The returned css should match.
assert BACKGROUND_CSS_RADIAL == css, 'The background css should be equal.'
self.assertEqual(BACKGROUND_CSS_RADIAL, css, 'The background css should be equal.')
def build_lyrics_css_test(self):
"""
@ -262,7 +280,7 @@ class Htmbuilder(TestCase):
css = build_lyrics_css(item)
# THEN: The css should be equal.
assert LYRICS_CSS == css, 'The lyrics css should be equal.'
self.assertEqual(LYRICS_CSS, css, 'The lyrics css should be equal.')
def build_lyrics_outline_css_test(self):
"""
@ -279,7 +297,7 @@ class Htmbuilder(TestCase):
css = build_lyrics_outline_css(theme_data)
# THEN: The css should be equal.
assert LYRICS_OUTLINE_CSS == css, 'The outline css should be equal.'
self.assertEqual(LYRICS_OUTLINE_CSS, css, 'The outline css should be equal.')
def build_lyrics_format_css_test(self):
"""
@ -302,7 +320,7 @@ class Htmbuilder(TestCase):
css = build_lyrics_format_css(theme_data, width, height)
# THEN: They should be equal.
assert LYRICS_FORMAT_CSS == css, 'The lyrics format css should be equal.'
self.assertEqual(LYRICS_FORMAT_CSS, css, 'The lyrics format css should be equal.')
def build_footer_css_test(self):
"""
@ -316,8 +334,27 @@ class Htmbuilder(TestCase):
item.theme_data.font_footer_color = '#FFFFFF'
height = 1024
# WHEN: create the css.
# WHEN: create the css with default settings.
css = build_footer_css(item, height)
# THEN: THE css should be the same.
assert FOOTER_CSS == css, 'The footer strings should be equal.'
self.assertEqual(FOOTER_CSS, css, 'The footer strings should be equal.')
def build_footer_css_wrap_test(self):
"""
Test the build_footer_css() function
"""
# GIVEN: Create a theme.
item = MagicMock()
item.footer = QtCore.QRect(10, 921, 1260, 103)
item.theme_data.font_footer_name = 'Arial'
item.theme_data.font_footer_size = 12
item.theme_data.font_footer_color = '#FFFFFF'
height = 1024
# WHEN: Settings say that footer should wrap
Settings().setValue('themes/wrap footer', True)
css = build_footer_css(item, height)
# THEN: Footer should wrap
self.assertEqual(FOOTER_CSS_WRAP, css, 'The footer strings should be equal.')

View File

@ -65,16 +65,6 @@ class TestRenderer(TestCase):
"""
del self.screens
def initial_renderer_test(self):
"""
Test the initial renderer state
"""
# GIVEN: A new renderer instance.
renderer = Renderer()
# WHEN: the default renderer is built.
# THEN: The renderer should be a live controller.
self.assertEqual(renderer.is_live, True, 'The base renderer should be a live controller')
def default_screen_layout_test(self):
"""
Test the default layout calculations

View File

@ -30,10 +30,13 @@
Package to test the openlp.core.ui.slidecontroller package.
"""
from unittest import TestCase
from openlp.core import Registry
from openlp.core.lib import ServiceItemAction
from openlp.core.ui import SlideController
from openlp.core.ui.slidecontroller import WIDE_MENU, NON_TEXT_MENU
from tests.interfaces import MagicMock
from tests.interfaces import MagicMock, patch
class TestSlideController(TestCase):
@ -42,37 +45,512 @@ class TestSlideController(TestCase):
"""
Test the initial slide controller state .
"""
# GIVEN: A new slideController instance.
# GIVEN: A new SlideController instance.
slide_controller = SlideController(None)
# WHEN: the default controller is built.
# THEN: The controller should not be a live controller.
self.assertEqual(slide_controller.is_live, False, 'The base slide controller should not be a live controller')
def toggle_blank_test(self):
def text_service_item_blank_test(self):
"""
Test the setting of the display blank icons by display type.
Test that loading a text-based service item into the slide controller sets the correct blank menu
"""
# GIVEN: A new slideController instance.
# GIVEN: A new SlideController instance.
slide_controller = SlideController(None)
service_item = MagicMock()
toolbar = MagicMock()
toolbar.set_widget_visible = self.dummy_widget_visible
toolbar.set_widget_visible = MagicMock()
slide_controller.toolbar = toolbar
slide_controller.service_item = service_item
# WHEN a text based service item is used
# WHEN: a text based service item is used
slide_controller.service_item.is_text = MagicMock(return_value=True)
slide_controller.set_blank_menu()
# THEN: then call set up the toolbar to blank the display screen.
self.assertEqual(len(self.test_widget), 3, 'There should be three icons to display on the screen')
# THEN: the call to set the visible items on the toolbar should be correct
toolbar.set_widget_visible.assert_called_with(WIDE_MENU, True)
def non_text_service_item_blank_test(self):
"""
Test that loading a non-text service item into the slide controller sets the correct blank menu
"""
# GIVEN: A new SlideController instance.
slide_controller = SlideController(None)
service_item = MagicMock()
toolbar = MagicMock()
toolbar.set_widget_visible = MagicMock()
slide_controller.toolbar = toolbar
slide_controller.service_item = service_item
# WHEN a non text based service item is used
slide_controller.service_item.is_text = MagicMock(return_value=False)
slide_controller.set_blank_menu()
# THEN: then call set up the toolbar to blank the display screen.
self.assertEqual(len(self.test_widget), 2, 'There should be only two icons to display on the screen')
toolbar.set_widget_visible.assert_called_with(NON_TEXT_MENU, True)
def dummy_widget_visible(self, widget, visible=True):
self.test_widget = widget
def receive_spin_delay_test(self):
"""
Test that the spin box is updated accordingly after a call to receive_spin_delay()
"""
with patch('openlp.core.ui.slidecontroller.Settings') as MockedSettings:
# GIVEN: A new SlideController instance.
mocked_value = MagicMock(return_value=1)
MockedSettings.return_value = MagicMock(value=mocked_value)
mocked_delay_spin_box = MagicMock()
slide_controller = SlideController(None)
slide_controller.delay_spin_box = mocked_delay_spin_box
# WHEN: The receive_spin_delay() method is called
slide_controller.receive_spin_delay()
# THEN: The Settings()value() and delay_spin_box.setValue() methods should have been called correctly
mocked_value.assert_called_with('core/loop delay')
mocked_delay_spin_box.setValue.assert_called_with(1)
def toggle_display_blank_test(self):
"""
Check that the toggle_display('blank') method calls the on_blank_display() method
"""
# GIVEN: A new SlideController instance.
mocked_on_blank_display = MagicMock()
mocked_on_theme_display = MagicMock()
mocked_on_hide_display = MagicMock()
slide_controller = SlideController(None)
slide_controller.on_blank_display = mocked_on_blank_display
slide_controller.on_theme_display = mocked_on_theme_display
slide_controller.on_hide_display = mocked_on_hide_display
# WHEN: toggle_display() is called with an argument of "blank"
slide_controller.toggle_display('blank')
# THEN: Only on_blank_display() should have been called with an argument of True
mocked_on_blank_display.assert_called_once_with(True)
self.assertEqual(0, mocked_on_theme_display.call_count, 'on_theme_display should not have been called')
self.assertEqual(0, mocked_on_hide_display.call_count, 'on_hide_display should not have been called')
def toggle_display_hide_test(self):
"""
Check that the toggle_display('hide') method calls the on_blank_display() method
"""
# GIVEN: A new SlideController instance.
mocked_on_blank_display = MagicMock()
mocked_on_theme_display = MagicMock()
mocked_on_hide_display = MagicMock()
slide_controller = SlideController(None)
slide_controller.on_blank_display = mocked_on_blank_display
slide_controller.on_theme_display = mocked_on_theme_display
slide_controller.on_hide_display = mocked_on_hide_display
# WHEN: toggle_display() is called with an argument of "hide"
slide_controller.toggle_display('hide')
# THEN: Only on_blank_display() should have been called with an argument of True
mocked_on_blank_display.assert_called_once_with(True)
self.assertEqual(0, mocked_on_theme_display.call_count, 'on_theme_display should not have been called')
self.assertEqual(0, mocked_on_hide_display.call_count, 'on_hide_display should not have been called')
def toggle_display_theme_test(self):
"""
Check that the toggle_display('theme') method calls the on_theme_display() method
"""
# GIVEN: A new SlideController instance.
mocked_on_blank_display = MagicMock()
mocked_on_theme_display = MagicMock()
mocked_on_hide_display = MagicMock()
slide_controller = SlideController(None)
slide_controller.on_blank_display = mocked_on_blank_display
slide_controller.on_theme_display = mocked_on_theme_display
slide_controller.on_hide_display = mocked_on_hide_display
# WHEN: toggle_display() is called with an argument of "theme"
slide_controller.toggle_display('theme')
# THEN: Only on_theme_display() should have been called with an argument of True
mocked_on_theme_display.assert_called_once_with(True)
self.assertEqual(0, mocked_on_blank_display.call_count, 'on_blank_display should not have been called')
self.assertEqual(0, mocked_on_hide_display.call_count, 'on_hide_display should not have been called')
def toggle_display_desktop_test(self):
"""
Check that the toggle_display('desktop') method calls the on_hide_display() method
"""
# GIVEN: A new SlideController instance.
mocked_on_blank_display = MagicMock()
mocked_on_theme_display = MagicMock()
mocked_on_hide_display = MagicMock()
slide_controller = SlideController(None)
slide_controller.on_blank_display = mocked_on_blank_display
slide_controller.on_theme_display = mocked_on_theme_display
slide_controller.on_hide_display = mocked_on_hide_display
# WHEN: toggle_display() is called with an argument of "desktop"
slide_controller.toggle_display('desktop')
# THEN: Only on_hide_display() should have been called with an argument of True
mocked_on_hide_display.assert_called_once_with(True)
self.assertEqual(0, mocked_on_blank_display.call_count, 'on_blank_display should not have been called')
self.assertEqual(0, mocked_on_theme_display.call_count, 'on_theme_display should not have been called')
def toggle_display_show_test(self):
"""
Check that the toggle_display('show') method calls all the on_X_display() methods
"""
# GIVEN: A new SlideController instance.
mocked_on_blank_display = MagicMock()
mocked_on_theme_display = MagicMock()
mocked_on_hide_display = MagicMock()
slide_controller = SlideController(None)
slide_controller.on_blank_display = mocked_on_blank_display
slide_controller.on_theme_display = mocked_on_theme_display
slide_controller.on_hide_display = mocked_on_hide_display
# WHEN: toggle_display() is called with an argument of "show"
slide_controller.toggle_display('show')
# THEN: All the on_X_display() methods should have been called with an argument of False
mocked_on_blank_display.assert_called_once_with(False)
mocked_on_theme_display.assert_called_once_with(False)
mocked_on_hide_display.assert_called_once_with(False)
def live_escape_test(self):
"""
Test that when the live_escape() method is called, the display is set to invisible and any media is stopped
"""
# GIVEN: A new SlideController instance and mocked out display and media_controller
mocked_display = MagicMock()
mocked_media_controller = MagicMock()
Registry.create()
Registry().register('media_controller', mocked_media_controller)
slide_controller = SlideController(None)
slide_controller.display = mocked_display
# WHEN: live_escape() is called
slide_controller.live_escape()
# THEN: the display should be set to invisible and the media controller stopped
mocked_display.setVisible.assert_called_once_with(False)
mocked_media_controller.media_stop.assert_called_once_with(slide_controller)
def service_previous_test(self):
"""
Check that calling the service_previous() method adds the previous key to the queue and processes the queue
"""
# GIVEN: A new SlideController instance.
mocked_keypress_queue = MagicMock()
mocked_process_queue = MagicMock()
slide_controller = SlideController(None)
slide_controller.keypress_queue = mocked_keypress_queue
slide_controller._process_queue = mocked_process_queue
# WHEN: The service_previous() method is called
slide_controller.service_previous()
# THEN: The keypress is added to the queue and the queue is processed
mocked_keypress_queue.append.assert_called_once_with(ServiceItemAction.Previous)
mocked_process_queue.assert_called_once_with()
def service_next_test(self):
"""
Check that calling the service_next() method adds the next key to the queue and processes the queue
"""
# GIVEN: A new SlideController instance and mocked out methods
mocked_keypress_queue = MagicMock()
mocked_process_queue = MagicMock()
slide_controller = SlideController(None)
slide_controller.keypress_queue = mocked_keypress_queue
slide_controller._process_queue = mocked_process_queue
# WHEN: The service_next() method is called
slide_controller.service_next()
# THEN: The keypress is added to the queue and the queue is processed
mocked_keypress_queue.append.assert_called_once_with(ServiceItemAction.Next)
mocked_process_queue.assert_called_once_with()
def update_slide_limits_test(self):
"""
Test that calling the update_slide_limits() method updates the slide limits
"""
# GIVEN: A mocked out Settings object, a new SlideController and a mocked out main_window
with patch('openlp.core.ui.slidecontroller.Settings') as MockedSettings:
mocked_value = MagicMock(return_value=10)
MockedSettings.return_value = MagicMock(value=mocked_value)
mocked_main_window = MagicMock(advanced_settings_section='advanced')
Registry.create()
Registry().register('main_window', mocked_main_window)
slide_controller = SlideController(None)
# WHEN: update_slide_limits() is called
slide_controller.update_slide_limits()
# THEN: The value of slide_limits should be 10
mocked_value.assert_called_once_with('advanced/slide limits')
self.assertEqual(10, slide_controller.slide_limits, 'Slide limits should have been updated to 10')
def enable_tool_bar_live_test(self):
"""
Check that when enable_tool_bar on a live slide controller is called, enable_live_tool_bar is called
"""
# GIVEN: Mocked out enable methods and a real slide controller which is set to live
mocked_enable_live_tool_bar = MagicMock()
mocked_enable_preview_tool_bar = MagicMock()
slide_controller = SlideController(None)
slide_controller.is_live = True
slide_controller.enable_live_tool_bar = mocked_enable_live_tool_bar
slide_controller.enable_preview_tool_bar = mocked_enable_preview_tool_bar
mocked_service_item = MagicMock()
# WHEN: enable_tool_bar() is called
slide_controller.enable_tool_bar(mocked_service_item)
# THEN: The enable_live_tool_bar() method is called, not enable_preview_tool_bar()
mocked_enable_live_tool_bar.assert_called_once_with(mocked_service_item)
self.assertEqual(0, mocked_enable_preview_tool_bar.call_count, 'The preview method should not have been called')
def enable_tool_bar_preview_test(self):
"""
Check that when enable_tool_bar on a preview slide controller is called, enable_preview_tool_bar is called
"""
# GIVEN: Mocked out enable methods and a real slide controller which is set to live
mocked_enable_live_tool_bar = MagicMock()
mocked_enable_preview_tool_bar = MagicMock()
slide_controller = SlideController(None)
slide_controller.is_live = False
slide_controller.enable_live_tool_bar = mocked_enable_live_tool_bar
slide_controller.enable_preview_tool_bar = mocked_enable_preview_tool_bar
mocked_service_item = MagicMock()
# WHEN: enable_tool_bar() is called
slide_controller.enable_tool_bar(mocked_service_item)
# THEN: The enable_preview_tool_bar() method is called, not enable_live_tool_bar()
mocked_enable_preview_tool_bar.assert_called_once_with(mocked_service_item)
self.assertEqual(0, mocked_enable_live_tool_bar.call_count, 'The live method should not have been called')
def refresh_service_item_text_test(self):
"""
Test that the refresh_service_item() method refreshes a text service item
"""
# GIVEN: A mock service item and a fresh slide controller
mocked_service_item = MagicMock()
mocked_service_item.is_text.return_value = True
mocked_service_item.is_image.return_value = False
mocked_process_item = MagicMock()
slide_controller = SlideController(None)
slide_controller.service_item = mocked_service_item
slide_controller._process_item = mocked_process_item
slide_controller.selected_row = 5
# WHEN: The refresh_service_item method() is called
slide_controller.refresh_service_item()
# THEN: The item should be re-processed
mocked_service_item.is_text.assert_called_once_with()
self.assertEqual(0, mocked_service_item.is_image.call_count, 'is_image should not have been called')
mocked_service_item.render.assert_called_once_with()
mocked_process_item.assert_called_once_with(mocked_service_item, 5)
def refresh_service_item_image_test(self):
"""
Test that the refresh_service_item() method refreshes a image service item
"""
# GIVEN: A mock service item and a fresh slide controller
mocked_service_item = MagicMock()
mocked_service_item.is_text.return_value = False
mocked_service_item.is_image.return_value = True
mocked_process_item = MagicMock()
slide_controller = SlideController(None)
slide_controller.service_item = mocked_service_item
slide_controller._process_item = mocked_process_item
slide_controller.selected_row = 5
# WHEN: The refresh_service_item method() is called
slide_controller.refresh_service_item()
# THEN: The item should be re-processed
mocked_service_item.is_text.assert_called_once_with()
mocked_service_item.is_image.assert_called_once_with()
mocked_service_item.render.assert_called_once_with()
mocked_process_item.assert_called_once_with(mocked_service_item, 5)
def refresh_service_item_not_image_or_text_test(self):
"""
Test that the refresh_service_item() method does not refresh a service item if it's neither text or an image
"""
# GIVEN: A mock service item and a fresh slide controller
mocked_service_item = MagicMock()
mocked_service_item.is_text.return_value = False
mocked_service_item.is_image.return_value = False
mocked_process_item = MagicMock()
slide_controller = SlideController(None)
slide_controller.service_item = mocked_service_item
slide_controller._process_item = mocked_process_item
slide_controller.selected_row = 5
# WHEN: The refresh_service_item method() is called
slide_controller.refresh_service_item()
# THEN: The item should be re-processed
mocked_service_item.is_text.assert_called_once_with()
mocked_service_item.is_image.assert_called_once_with()
self.assertEqual(0, mocked_service_item.render.call_count, 'The render() method should not have been called')
self.assertEqual(0, mocked_process_item.call_count,
'The mocked_process_item() method should not have been called')
def add_service_item_with_song_edit_test(self):
"""
Test the add_service_item() method when song_edit is True
"""
# GIVEN: A slide controller and a new item to add
mocked_item = MagicMock()
mocked_process_item = MagicMock()
slide_controller = SlideController(None)
slide_controller._process_item = mocked_process_item
slide_controller.song_edit = True
slide_controller.selected_row = 2
# WHEN: The item is added to the service
slide_controller.add_service_item(mocked_item)
# THEN: The item is processed, the slide number is correct, and the song is not editable (or something)
mocked_item.render.assert_called_once_with()
self.assertFalse(slide_controller.song_edit, 'song_edit should be False')
mocked_process_item.assert_called_once_with(mocked_item, 2)
def add_service_item_without_song_edit_test(self):
"""
Test the add_service_item() method when song_edit is False
"""
# GIVEN: A slide controller and a new item to add
mocked_item = MagicMock()
mocked_process_item = MagicMock()
slide_controller = SlideController(None)
slide_controller._process_item = mocked_process_item
slide_controller.song_edit = False
slide_controller.selected_row = 2
# WHEN: The item is added to the service
slide_controller.add_service_item(mocked_item)
# THEN: The item is processed, the slide number is correct, and the song is not editable (or something)
mocked_item.render.assert_called_once_with()
self.assertFalse(slide_controller.song_edit, 'song_edit should be False')
mocked_process_item.assert_called_once_with(mocked_item, 0)
def replace_service_manager_item_different_items_test(self):
"""
Test that when the service items are not the same, nothing happens
"""
# GIVEN: A slide controller and a new item to add
mocked_item = MagicMock()
mocked_preview_widget = MagicMock()
mocked_process_item = MagicMock()
slide_controller = SlideController(None)
slide_controller.preview_widget = mocked_preview_widget
slide_controller._process_item = mocked_process_item
slide_controller.service_item = None
# WHEN: The service item is replaced
slide_controller.replace_service_manager_item(mocked_item)
# THEN: The service item should not be processed
self.assertEqual(0, mocked_process_item.call_count, 'The _process_item() method should not have been called')
self.assertEqual(0, mocked_preview_widget.current_slide_number.call_count,
'The preview_widgetcurrent_slide_number.() method should not have been called')
def replace_service_manager_item_same_item_test(self):
"""
Test that when the service item is the same, the service item is reprocessed
"""
# GIVEN: A slide controller and a new item to add
mocked_item = MagicMock()
mocked_preview_widget = MagicMock()
mocked_preview_widget.current_slide_number.return_value = 7
mocked_process_item = MagicMock()
slide_controller = SlideController(None)
slide_controller.preview_widget = mocked_preview_widget
slide_controller._process_item = mocked_process_item
slide_controller.service_item = mocked_item
# WHEN: The service item is replaced
slide_controller.replace_service_manager_item(mocked_item)
# THEN: The service item should not be processed
mocked_preview_widget.current_slide_number.assert_called_with()
mocked_process_item.assert_called_once_with(mocked_item, 7)
def on_slide_selected_index_no_service_item_test(self):
"""
Test that when there is no service item, the on_slide_selected_index() method returns immediately
"""
# GIVEN: A mocked service item and a slide controller without a service item
mocked_item = MagicMock()
slide_controller = SlideController(None)
slide_controller.service_item = None
# WHEN: The method is called
slide_controller.on_slide_selected_index([10])
# THEN: It should have exited early
self.assertEqual(0, mocked_item.is_command.call_count, 'The service item should have not been called')
def on_slide_selected_index_service_item_command_test(self):
"""
Test that when there is a command service item, the command is executed
"""
# GIVEN: A mocked service item and a slide controller with a service item
mocked_item = MagicMock()
mocked_item.is_command.return_value = True
mocked_item.name = 'Mocked Item'
mocked_update_preview = MagicMock()
mocked_preview_widget = MagicMock()
mocked_slide_selected = MagicMock()
with patch.object(Registry, 'execute') as mocked_execute:
Registry.create()
slide_controller = SlideController(None)
slide_controller.service_item = mocked_item
slide_controller.update_preview = mocked_update_preview
slide_controller.preview_widget = mocked_preview_widget
slide_controller.slide_selected = mocked_slide_selected
slide_controller.is_live = True
# WHEN: The method is called
slide_controller.on_slide_selected_index([9])
# THEN: It should have sent a notification
mocked_item.is_command.assert_called_once_with()
mocked_execute.assert_called_once_with('mocked item_slide', [mocked_item, True, 9])
mocked_update_preview.assert_called_once_with()
self.assertEqual(0, mocked_preview_widget.change_slide.call_count, 'Change slide should not have been called')
self.assertEqual(0, mocked_slide_selected.call_count, 'slide_selected should not have been called')
def on_slide_selected_index_service_item_not_command_test(self):
"""
Test that when there is a service item but it's not a command, the preview widget is updated
"""
# GIVEN: A mocked service item and a slide controller with a service item
mocked_item = MagicMock()
mocked_item.is_command.return_value = False
mocked_item.name = 'Mocked Item'
mocked_update_preview = MagicMock()
mocked_preview_widget = MagicMock()
mocked_slide_selected = MagicMock()
with patch.object(Registry, 'execute') as mocked_execute:
Registry.create()
slide_controller = SlideController(None)
slide_controller.service_item = mocked_item
slide_controller.update_preview = mocked_update_preview
slide_controller.preview_widget = mocked_preview_widget
slide_controller.slide_selected = mocked_slide_selected
# WHEN: The method is called
slide_controller.on_slide_selected_index([7])
# THEN: It should have sent a notification
mocked_item.is_command.assert_called_once_with()
self.assertEqual(0, mocked_execute.call_count, 'Execute should not have been called')
self.assertEqual(0, mocked_update_preview.call_count, 'Update preview should not have been called')
mocked_preview_widget.change_slide.assert_called_once_with(7)
mocked_slide_selected.assert_called_once_with()

View File

@ -0,0 +1,62 @@
# -*- 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 #
###############################################################################
"""
Package to test the openlp.core.ui.thememanager package.
"""
import zipfile
import os
from unittest import TestCase
from tests.interfaces import MagicMock
from openlp.core.ui import ThemeManager
RESOURCES_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'resources', 'themes'))
class TestThemeManager(TestCase):
def export_theme_test(self):
"""
Test exporting a theme .
"""
# GIVEN: A new ThemeManager instance.
theme_manager = ThemeManager()
theme_manager.path = RESOURCES_PATH
zipfile.ZipFile.__init__ = MagicMock()
zipfile.ZipFile.__init__.return_value = None
zipfile.ZipFile.write = MagicMock()
# WHEN: The theme is exported
theme_manager._export_theme('/some/path', 'Default')
# THEN: The zipfile should be created at the given path
zipfile.ZipFile.__init__.assert_called_with('/some/path/Default.otz', 'w')
zipfile.ZipFile.write.assert_called_with(os.path.join(RESOURCES_PATH, 'Default', 'Default.xml'),
'Default/Default.xml')

View File

@ -0,0 +1,131 @@
# -*- 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 #
###############################################################################
"""
Functional tests to test the PowerPointController class and related methods.
"""
import os
if os.name == 'nt':
import pywintypes
import shutil
from unittest import TestCase
from tempfile import mkdtemp
from tests.functional import patch, MagicMock
from tests.helpers.testmixin import TestMixin
from openlp.plugins.presentations.lib.powerpointcontroller import PowerpointController, PowerpointDocument
class TestPowerpointController(TestCase, TestMixin):
"""
Test the PowerpointController Class
"""
def setUp(self):
"""
Set up the patches and mocks need for all tests.
"""
self.get_application()
self.build_settings()
self.mock_plugin = MagicMock()
self.temp_folder = mkdtemp()
self.mock_plugin.settings_section = self.temp_folder
def tearDown(self):
"""
Stop the patches
"""
self.destroy_settings()
shutil.rmtree(self.temp_folder)
def constructor_test(self):
"""
Test the Constructor from the PowerpointController
"""
# GIVEN: No presentation controller
controller = None
# WHEN: The presentation controller object is created
controller = PowerpointController(plugin=self.mock_plugin)
# THEN: The name of the presentation controller should be correct
self.assertEqual('Powerpoint', controller.name,
'The name of the presentation controller should be correct')
class TestPowerpointDocument(TestCase):
"""
Test the PowerpointDocument Class
"""
def setUp(self):
"""
Set up the patches and mocks need for all tests.
"""
self.powerpoint_document_stop_presentation_patcher = patch(
'openlp.plugins.presentations.lib.powerpointcontroller.PowerpointDocument.stop_presentation')
self.presentation_document_get_temp_folder_patcher = patch(
'openlp.plugins.presentations.lib.powerpointcontroller.PresentationDocument.get_temp_folder')
self.presentation_document_setup_patcher = patch(
'openlp.plugins.presentations.lib.powerpointcontroller.PresentationDocument._setup')
self.mock_powerpoint_document_stop_presentation = self.powerpoint_document_stop_presentation_patcher.start()
self.mock_presentation_document_get_temp_folder = self.presentation_document_get_temp_folder_patcher.start()
self.mock_presentation_document_setup = self.presentation_document_setup_patcher.start()
self.mock_controller = MagicMock()
self.mock_presentation = MagicMock()
self.mock_presentation_document_get_temp_folder.return_value = 'temp folder'
def tearDown(self):
"""
Stop the patches
"""
self.powerpoint_document_stop_presentation_patcher.stop()
self.presentation_document_get_temp_folder_patcher.stop()
self.presentation_document_setup_patcher.stop()
def show_error_msg_test(self):
"""
Test the PowerpointDocument.show_error_msg() method gets called on com exception
"""
if os.name == 'nt':
# GIVEN: A PowerpointDocument with mocked controller and presentation
with patch('openlp.plugins.presentations.lib.powerpointcontroller.critical_error_message_box') as \
mocked_critical_error_message_box:
instance = PowerpointDocument(self.mock_controller, self.mock_presentation)
instance.presentation = MagicMock()
instance.presentation.SlideShowWindow.View.GotoSlide = MagicMock(side_effect=pywintypes.com_error('1'))
# WHEN: Calling goto_slide which will throw an exception
instance.goto_slide(42)
# THEN: mocked_critical_error_message_box should have been called
mocked_critical_error_message_box.assert_called_with('Error', 'An error occurred in the Powerpoint '
'integration and the presentation will be stopped.'
' Restart the presentation if you wish to '
'present it.')

View File

@ -112,3 +112,44 @@ class TestDB(TestCase):
# 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)
def test_get_author_type_from_translated_text(self):
"""
Test getting an author type from translated text
"""
# GIVEN: A string with an author type
author_type_name = AuthorType.Types[AuthorType.Words]
# WHEN: We call the method
author_type = AuthorType.from_translated_text(author_type_name)
# THEN: The type should be correct
self.assertEqual(author_type, AuthorType.Words)
def test_author_get_display_name(self):
"""
Test that the display name of an author is correct
"""
# GIVEN: An author
author = Author()
author.display_name = "John Doe"
# WHEN: We call the get_display_name() function
display_name = author.get_display_name()
# THEN: It should return only the name
self.assertEqual("John Doe", display_name)
def test_author_get_display_name_with_type(self):
"""
Test that the display name of an author with a type is correct
"""
# GIVEN: An author
author = Author()
author.display_name = "John Doe"
# WHEN: We call the get_display_name() function
display_name = author.get_display_name(AuthorType.Words)
# THEN: It should return the name with the type in brackets
self.assertEqual("John Doe (Words)", display_name)

View File

@ -35,7 +35,7 @@ from unittest import TestCase
from tests.functional import MagicMock, patch
from openlp.plugins.songs.lib.ewimport import EasyWorshipSongImport, FieldDescEntry, FieldType
from openlp.plugins.songs.lib.importers.easyworship import EasyWorshipSongImport, FieldDescEntry, FieldType
TEST_PATH = os.path.abspath(
os.path.join(os.path.dirname(__file__), '..', '..', '..', 'resources', 'easyworshipsongs'))
@ -178,7 +178,7 @@ class TestEasyWorshipSongImport(TestCase):
Test creating an instance of the EasyWorship file importer
"""
# GIVEN: A mocked out SongImport class, and a mocked out "manager"
with patch('openlp.plugins.songs.lib.ewimport.SongImport'):
with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'):
mocked_manager = MagicMock()
# WHEN: An importer object is created
@ -192,7 +192,7 @@ class TestEasyWorshipSongImport(TestCase):
Test finding an existing field in a given list using the :mod:`find_field`
"""
# GIVEN: A mocked out SongImport class, a mocked out "manager" and a list of field descriptions.
with patch('openlp.plugins.songs.lib.ewimport.SongImport'):
with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'):
mocked_manager = MagicMock()
importer = EasyWorshipSongImport(mocked_manager, filenames=[])
importer.field_descriptions = TEST_FIELD_DESCS
@ -210,7 +210,7 @@ class TestEasyWorshipSongImport(TestCase):
Test finding an non-existing field in a given list using the :mod:`find_field`
"""
# GIVEN: A mocked out SongImport class, a mocked out "manager" and a list of field descriptions
with patch('openlp.plugins.songs.lib.ewimport.SongImport'):
with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'):
mocked_manager = MagicMock()
importer = EasyWorshipSongImport(mocked_manager, filenames=[])
importer.field_descriptions = TEST_FIELD_DESCS
@ -228,8 +228,8 @@ class TestEasyWorshipSongImport(TestCase):
"""
# GIVEN: A mocked out SongImport class, a mocked out struct class, and a mocked out "manager" and a list of
# field descriptions
with patch('openlp.plugins.songs.lib.ewimport.SongImport'), \
patch('openlp.plugins.songs.lib.ewimport.struct') as mocked_struct:
with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'), \
patch('openlp.plugins.songs.lib.importers.easyworship.struct') as mocked_struct:
mocked_manager = MagicMock()
importer = EasyWorshipSongImport(mocked_manager, filenames=[])
@ -246,7 +246,7 @@ class TestEasyWorshipSongImport(TestCase):
Test the :mod:`get_field` module
"""
# GIVEN: A mocked out SongImport class, a mocked out "manager", an encoding and some test data and known results
with patch('openlp.plugins.songs.lib.ewimport.SongImport'):
with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'):
mocked_manager = MagicMock()
importer = EasyWorshipSongImport(mocked_manager, filenames=[])
importer.encoding = TEST_DATA_ENCODING
@ -269,7 +269,7 @@ class TestEasyWorshipSongImport(TestCase):
"""
for test_results in GET_MEMO_FIELD_TEST_RESULTS:
# GIVEN: A mocked out SongImport class, a mocked out "manager", a mocked out memo_file and an encoding
with patch('openlp.plugins.songs.lib.ewimport.SongImport'):
with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'):
mocked_manager = MagicMock()
mocked_memo_file = MagicMock()
importer = EasyWorshipSongImport(mocked_manager, filenames=[])
@ -300,8 +300,8 @@ class TestEasyWorshipSongImport(TestCase):
Test the :mod:`do_import` module opens the correct files
"""
# 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:
with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'), \
patch('openlp.plugins.songs.lib.importers.easyworship.os.path') as mocked_os_path:
mocked_manager = MagicMock()
importer = EasyWorshipSongImport(mocked_manager, filenames=[])
mocked_os_path.isfile.side_effect = [True, False]
@ -319,8 +319,8 @@ class TestEasyWorshipSongImport(TestCase):
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:
with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'), \
patch('openlp.plugins.songs.lib.importers.easyworship.os.path') as mocked_os_path:
mocked_manager = MagicMock()
importer = EasyWorshipSongImport(mocked_manager, filenames=[])
importer.log_error = MagicMock()
@ -339,8 +339,8 @@ class TestEasyWorshipSongImport(TestCase):
Test the :mod:`do_import` module handles invalid database files correctly
"""
# GIVEN: A mocked out SongImport class, os.path and a mocked out "manager"
with patch('openlp.plugins.songs.lib.ewimport.SongImport'), \
patch('openlp.plugins.songs.lib.ewimport.os.path') as mocked_os_path:
with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'), \
patch('openlp.plugins.songs.lib.importers.easyworship.os.path') as mocked_os_path:
mocked_manager = MagicMock()
importer = EasyWorshipSongImport(mocked_manager, filenames=[])
mocked_os_path.isfile.return_value = True
@ -358,10 +358,10 @@ class TestEasyWorshipSongImport(TestCase):
Test the :mod:`do_import` module handles invalid memo files correctly
"""
# 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, \
with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'), \
patch('openlp.plugins.songs.lib.importers.easyworship.os.path') as mocked_os_path, \
patch('builtins.open') as mocked_open, \
patch('openlp.plugins.songs.lib.ewimport.struct') as mocked_struct:
patch('openlp.plugins.songs.lib.importers.easyworship.struct') as mocked_struct:
mocked_manager = MagicMock()
importer = EasyWorshipSongImport(mocked_manager, filenames=[])
mocked_os_path.isfile.return_value = True
@ -385,10 +385,10 @@ class TestEasyWorshipSongImport(TestCase):
Test the :mod:`do_import` converts the code page to the encoding correctly
"""
# 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, \
patch('builtins.open'), patch('openlp.plugins.songs.lib.ewimport.struct') as mocked_struct, \
patch('openlp.plugins.songs.lib.ewimport.retrieve_windows_encoding') as \
with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'), \
patch('openlp.plugins.songs.lib.importers.easyworship.os.path') as mocked_os_path, \
patch('builtins.open'), patch('openlp.plugins.songs.lib.importers.easyworship.struct') as mocked_struct, \
patch('openlp.plugins.songs.lib.importers.easyworship.retrieve_windows_encoding') as \
mocked_retrieve_windows_encoding:
mocked_manager = MagicMock()
importer = EasyWorshipSongImport(mocked_manager, filenames=[])
@ -413,8 +413,8 @@ class TestEasyWorshipSongImport(TestCase):
# GIVEN: Test files with a mocked out SongImport class, a mocked out "manager", a mocked out "import_wizard",
# and mocked out "author", "add_copyright", "add_verse", "finish" methods.
with patch('openlp.plugins.songs.lib.ewimport.SongImport'), \
patch('openlp.plugins.songs.lib.ewimport.retrieve_windows_encoding') as \
with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'), \
patch('openlp.plugins.songs.lib.importers.easyworship.retrieve_windows_encoding') as \
mocked_retrieve_windows_encoding:
mocked_retrieve_windows_encoding.return_value = 'cp1252'
mocked_manager = MagicMock()
@ -469,8 +469,8 @@ class TestEasyWorshipSongImport(TestCase):
# GIVEN: Test files with a mocked out SongImport class, a mocked out "manager", a mocked out "import_wizard",
# and mocked out "author", "add_copyright", "add_verse", "finish" methods.
with patch('openlp.plugins.songs.lib.ewimport.SongImport'), \
patch('openlp.plugins.songs.lib.ewimport.retrieve_windows_encoding') \
with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'), \
patch('openlp.plugins.songs.lib.importers.easyworship.retrieve_windows_encoding') \
as mocked_retrieve_windows_encoding:
mocked_retrieve_windows_encoding.return_value = 'cp1252'
mocked_manager = MagicMock()
@ -509,7 +509,7 @@ class TestEasyWorshipSongImport(TestCase):
"""
# GIVEN: A mocked out SongImport class, a mocked out "manager" and mocked out "author" method.
with patch('openlp.plugins.songs.lib.ewimport.SongImport'):
with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'):
mocked_manager = MagicMock()
mocked_add_author = MagicMock()
importer = EasyWorshipSongImportLogger(mocked_manager)

View File

@ -34,7 +34,7 @@ import os
from unittest import TestCase
from tests.functional import patch, MagicMock
from openlp.plugins.songs.lib.foilpresenterimport import FoilPresenter
from openlp.plugins.songs.lib.importers.foilpresenter import FoilPresenter
TEST_PATH = os.path.abspath(
os.path.join(os.path.dirname(__file__), '..', '..', '..', '/resources/foilpresentersongs'))
@ -57,27 +57,27 @@ class TestFoilPresenter(TestCase):
# _process_topics
def setUp(self):
self.child_patcher = patch('openlp.plugins.songs.lib.foilpresenterimport.FoilPresenter._child')
self.clean_song_patcher = patch('openlp.plugins.songs.lib.foilpresenterimport.clean_song')
self.objectify_patcher = patch('openlp.plugins.songs.lib.foilpresenterimport.objectify')
self.child_patcher = patch('openlp.plugins.songs.lib.importers.foilpresenter.FoilPresenter._child')
self.clean_song_patcher = patch('openlp.plugins.songs.lib.importers.foilpresenter.clean_song')
self.objectify_patcher = patch('openlp.plugins.songs.lib.importers.foilpresenter.objectify')
self.process_authors_patcher = \
patch('openlp.plugins.songs.lib.foilpresenterimport.FoilPresenter._process_authors')
patch('openlp.plugins.songs.lib.importers.foilpresenter.FoilPresenter._process_authors')
self.process_cclinumber_patcher = \
patch('openlp.plugins.songs.lib.foilpresenterimport.FoilPresenter._process_cclinumber')
patch('openlp.plugins.songs.lib.importers.foilpresenter.FoilPresenter._process_cclinumber')
self.process_comments_patcher = \
patch('openlp.plugins.songs.lib.foilpresenterimport.FoilPresenter._process_comments')
patch('openlp.plugins.songs.lib.importers.foilpresenter.FoilPresenter._process_comments')
self.process_lyrics_patcher = \
patch('openlp.plugins.songs.lib.foilpresenterimport.FoilPresenter._process_lyrics')
patch('openlp.plugins.songs.lib.importers.foilpresenter.FoilPresenter._process_lyrics')
self.process_songbooks_patcher = \
patch('openlp.plugins.songs.lib.foilpresenterimport.FoilPresenter._process_songbooks')
patch('openlp.plugins.songs.lib.importers.foilpresenter.FoilPresenter._process_songbooks')
self.process_titles_patcher = \
patch('openlp.plugins.songs.lib.foilpresenterimport.FoilPresenter._process_titles')
patch('openlp.plugins.songs.lib.importers.foilpresenter.FoilPresenter._process_titles')
self.process_topics_patcher = \
patch('openlp.plugins.songs.lib.foilpresenterimport.FoilPresenter._process_topics')
self.re_patcher = patch('openlp.plugins.songs.lib.foilpresenterimport.re')
self.song_patcher = patch('openlp.plugins.songs.lib.foilpresenterimport.Song')
self.song_xml_patcher = patch('openlp.plugins.songs.lib.foilpresenterimport.SongXML')
self.translate_patcher = patch('openlp.plugins.songs.lib.foilpresenterimport.translate')
patch('openlp.plugins.songs.lib.importers.foilpresenter.FoilPresenter._process_topics')
self.re_patcher = patch('openlp.plugins.songs.lib.importers.foilpresenter.re')
self.song_patcher = patch('openlp.plugins.songs.lib.importers.foilpresenter.Song')
self.song_xml_patcher = patch('openlp.plugins.songs.lib.importers.foilpresenter.SongXML')
self.translate_patcher = patch('openlp.plugins.songs.lib.importers.foilpresenter.translate')
self.mocked_child = self.child_patcher.start()
self.mocked_clean_song = self.clean_song_patcher.start()

View File

@ -28,6 +28,7 @@ class TestMediaItem(TestCase, TestMixin):
patch('openlp.plugins.songs.forms.editsongform.EditSongForm.__init__'):
self.media_item = SongMediaItem(None, MagicMock())
self.media_item.display_songbook = False
self.media_item.display_copyright_symbol = False
self.get_application()
self.build_settings()
QtCore.QLocale.setDefault(QtCore.QLocale('en_GB'))
@ -154,6 +155,39 @@ 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 build_song_footer_copyright_enabled_test(self):
"""
Test building song footer with displaying the copyright symbol
"""
# GIVEN: A Song and a Service Item; displaying the copyright symbol is enabled
self.media_item.display_copyright_symbol = True
mock_song = MagicMock()
mock_song.title = 'My Song'
mock_song.copyright = 'My copyright'
service_item = ServiceItem(None)
# WHEN: I generate the Footer with default settings
self.media_item.generate_footer(service_item, mock_song)
# THEN: The copyright symbol should be in the footer
self.assertEqual(service_item.raw_footer, ['My Song', '© My copyright'])
def build_song_footer_copyright_disabled_test(self):
"""
Test building song footer without displaying the copyright symbol
"""
# GIVEN: A Song and a Service Item; displaying the copyright symbol should be disabled by default
mock_song = MagicMock()
mock_song.title = 'My Song'
mock_song.copyright = 'My copyright'
service_item = ServiceItem(None)
# WHEN: I generate the Footer with default settings
self.media_item.generate_footer(service_item, mock_song)
# THEN: The copyright symbol should not be in the footer
self.assertEqual(service_item.raw_footer, ['My Song', 'My copyright'])
def authors_match_test(self):
"""
Test the author matching when importing a song from a service

View File

@ -34,8 +34,8 @@ import os
from unittest import TestCase
from tests.functional import MagicMock, patch
from openlp.plugins.songs.lib.openlyricsimport import OpenLyricsImport
from openlp.plugins.songs.lib.songimport import SongImport
from openlp.plugins.songs.lib.importers.openlyrics import OpenLyricsImport
from openlp.plugins.songs.lib.importers.songimport import SongImport
TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__),
'..', '..', '..', 'resources', 'openlyricssongs'))
@ -69,7 +69,7 @@ class TestOpenLyricsImport(TestCase):
Test creating an instance of the OpenLyrics 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.importers.openlyrics.SongImport'):
mocked_manager = MagicMock()
# WHEN: An importer object is created

View File

@ -34,7 +34,7 @@ import os
from unittest import TestCase
from tests.helpers.songfileimport import SongImportTestHelper
from openlp.plugins.songs.lib.opensongimport import OpenSongImport
from openlp.plugins.songs.lib.importers.opensong import OpenSongImport
from tests.functional import patch, MagicMock
TEST_PATH = os.path.abspath(
@ -45,18 +45,18 @@ class TestOpenSongFileImport(SongImportTestHelper):
def __init__(self, *args, **kwargs):
self.importer_class_name = 'OpenSongImport'
self.importer_module_name = 'opensongimport'
self.importer_module_name = 'opensong'
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.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.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.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')))
@ -69,7 +69,7 @@ class TestOpenSongImport(TestCase):
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'):
with patch('openlp.plugins.songs.lib.importers.opensong.SongImport'):
mocked_manager = MagicMock()
# WHEN: An importer object is created
@ -83,7 +83,7 @@ class TestOpenSongImport(TestCase):
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'):
with patch('openlp.plugins.songs.lib.importers.opensong.SongImport'):
mocked_manager = MagicMock()
mocked_import_wizard = MagicMock()
importer = OpenSongImport(mocked_manager, filenames=[])
@ -104,7 +104,7 @@ class TestOpenSongImport(TestCase):
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'):
with patch('openlp.plugins.songs.lib.importers.opensong.SongImport'):
mocked_manager = MagicMock()
mocked_import_wizard = MagicMock()
importer = OpenSongImport(mocked_manager, filenames=[])

View File

@ -0,0 +1,56 @@
# -*- 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:`powerpraiseimport` 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', 'powerpraisesongs'))
class TestPowerPraiseFileImport(SongImportTestHelper):
def __init__(self, *args, **kwargs):
self.importer_class_name = 'PowerPraiseImport'
self.importer_module_name = 'powerpraise'
super(TestPowerPraiseFileImport, self).__init__(*args, **kwargs)
def test_song_import(self):
"""
Test that loading a PowerPraise file works correctly
"""
self.file_import([os.path.join(TEST_PATH, 'Näher, mein Gott zu Dir.ppl')],
self.load_external_result_data(os.path.join(TEST_PATH, 'Näher, mein Gott zu Dir.json')))
self.file_import([os.path.join(TEST_PATH, 'You are so faithful.ppl')],
self.load_external_result_data(os.path.join(TEST_PATH, 'You are so faithful.json')))

View File

@ -0,0 +1,53 @@
# -*- 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 PresentationManager song importer.
"""
import os
from tests.helpers.songfileimport import SongImportTestHelper
TEST_PATH = os.path.abspath(
os.path.join(os.path.dirname(__file__), '..', '..', '..', 'resources', 'presentationmanagersongs'))
class TestSongShowPlusFileImport(SongImportTestHelper):
def __init__(self, *args, **kwargs):
self.importer_class_name = 'PresentationManagerImport'
self.importer_module_name = 'presentationmanager'
super(TestSongShowPlusFileImport, self).__init__(*args, **kwargs)
def test_song_import(self):
"""
Test that loading a PresentationManager file works correctly
"""
self.file_import([os.path.join(TEST_PATH, 'Great Is Thy Faithfulness.sng')],
self.load_external_result_data(os.path.join(TEST_PATH, 'Great Is Thy Faithfulness.json')))

View File

@ -43,12 +43,12 @@ class TestProPresenterFileImport(SongImportTestHelper):
def __init__(self, *args, **kwargs):
self.importer_class_name = 'ProPresenterImport'
self.importer_module_name = 'propresenterimport'
self.importer_module_name = 'propresenter'
super(TestProPresenterFileImport, self).__init__(*args, **kwargs)
def test_song_import(self):
"""
Test that loading an ProPresenter file works correctly
Test that loading a ProPresenter file works correctly
"""
self.file_import(os.path.join(TEST_PATH, 'Amazing Grace.pro4'),
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

@ -34,7 +34,7 @@ import os
from unittest import TestCase
from tests.functional import MagicMock, patch
from openlp.plugins.songs.lib.songbeamerimport import SongBeamerImport
from openlp.plugins.songs.lib.importers.songbeamer import SongBeamerImport
from openlp.plugins.songs.lib import VerseType
TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__),
@ -64,7 +64,7 @@ class TestSongBeamerImport(TestCase):
Test creating an instance of the SongBeamer 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.importers.songbeamer.SongImport'):
mocked_manager = MagicMock()
# WHEN: An importer object is created
@ -78,7 +78,7 @@ class TestSongBeamerImport(TestCase):
Test SongBeamerImport.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.songbeamerimport.SongImport'):
with patch('openlp.plugins.songs.lib.importers.songbeamer.SongImport'):
mocked_manager = MagicMock()
mocked_import_wizard = MagicMock()
importer = SongBeamerImport(mocked_manager, filenames=[])
@ -99,7 +99,7 @@ class TestSongBeamerImport(TestCase):
Test SongBeamerImport.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.songbeamerimport.SongImport'):
with patch('openlp.plugins.songs.lib.importers.songbeamer.SongImport'):
mocked_manager = MagicMock()
mocked_import_wizard = MagicMock()
importer = SongBeamerImport(mocked_manager, filenames=[])
@ -122,7 +122,7 @@ class TestSongBeamerImport(TestCase):
# GIVEN: Test files with a mocked out SongImport class, a mocked out "manager", a mocked out "import_wizard",
# and mocked out "author", "add_copyright", "add_verse", "finish" methods.
with patch('openlp.plugins.songs.lib.songbeamerimport.SongImport'):
with patch('openlp.plugins.songs.lib.importers.songbeamer.SongImport'):
for song_file in SONG_TEST_DATA:
mocked_manager = MagicMock()
mocked_import_wizard = MagicMock()

View File

@ -35,7 +35,7 @@ from unittest import TestCase
from tests.helpers.songfileimport import SongImportTestHelper
from openlp.plugins.songs.lib import VerseType
from openlp.plugins.songs.lib.songshowplusimport import SongShowPlusImport
from openlp.plugins.songs.lib.importers.songshowplus import SongShowPlusImport
from tests.functional import patch, MagicMock
TEST_PATH = os.path.abspath(
@ -46,18 +46,18 @@ class TestSongShowPlusFileImport(SongImportTestHelper):
def __init__(self, *args, **kwargs):
self.importer_class_name = 'SongShowPlusImport'
self.importer_module_name = 'songshowplusimport'
self.importer_module_name = 'songshowplus'
super(TestSongShowPlusFileImport, self).__init__(*args, **kwargs)
def test_song_import(self):
"""
Test that loading a SongShow Plus file works correctly on various files
"""
self.file_import(os.path.join(TEST_PATH, 'Amazing Grace.sbsong'),
self.file_import([os.path.join(TEST_PATH, 'Amazing Grace.sbsong')],
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.sbsong'),
self.file_import([os.path.join(TEST_PATH, 'Beautiful Garden Of Prayer.sbsong')],
self.load_external_result_data(os.path.join(TEST_PATH, 'Beautiful Garden Of Prayer.json')))
self.file_import(os.path.join(TEST_PATH, 'a mighty fortress is our god.sbsong'),
self.file_import([os.path.join(TEST_PATH, 'a mighty fortress is our god.sbsong')],
self.load_external_result_data(os.path.join(TEST_PATH, 'a mighty fortress is our god.json')))
@ -70,7 +70,7 @@ class TestSongShowPlusImport(TestCase):
Test creating an instance of the SongShow Plus file importer
"""
# GIVEN: A mocked out SongImport class, and a mocked out "manager"
with patch('openlp.plugins.songs.lib.songshowplusimport.SongImport'):
with patch('openlp.plugins.songs.lib.importers.songshowplus.SongImport'):
mocked_manager = MagicMock()
# WHEN: An importer object is created
@ -84,7 +84,7 @@ class TestSongShowPlusImport(TestCase):
Test SongShowPlusImport.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.songshowplusimport.SongImport'):
with patch('openlp.plugins.songs.lib.importers.songshowplus.SongImport'):
mocked_manager = MagicMock()
mocked_import_wizard = MagicMock()
importer = SongShowPlusImport(mocked_manager, filenames=[])
@ -105,7 +105,7 @@ class TestSongShowPlusImport(TestCase):
Test SongShowPlusImport.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.songshowplusimport.SongImport'):
with patch('openlp.plugins.songs.lib.importers.songshowplus.SongImport'):
mocked_manager = MagicMock()
mocked_import_wizard = MagicMock()
importer = SongShowPlusImport(mocked_manager, filenames=[])
@ -126,7 +126,7 @@ class TestSongShowPlusImport(TestCase):
Test to_openlp_verse_tag method by simulating adding a verse
"""
# GIVEN: A mocked out SongImport class, and a mocked out "manager"
with patch('openlp.plugins.songs.lib.songshowplusimport.SongImport'):
with patch('openlp.plugins.songs.lib.importers.songshowplus.SongImport'):
mocked_manager = MagicMock()
importer = SongShowPlusImport(mocked_manager, filenames=[])
@ -154,7 +154,7 @@ class TestSongShowPlusImport(TestCase):
Test to_openlp_verse_tag method by simulating adding a verse to the verse order
"""
# GIVEN: A mocked out SongImport class, and a mocked out "manager"
with patch('openlp.plugins.songs.lib.songshowplusimport.SongImport'):
with patch('openlp.plugins.songs.lib.importers.songshowplus.SongImport'):
mocked_manager = MagicMock()
importer = SongShowPlusImport(mocked_manager, filenames=[])

View File

@ -0,0 +1,56 @@
# -*- 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:`worshipassistantimport` module provides the functionality for importing
WorshipAssistant 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', 'worshipassistantsongs'))
class TestWorshipAssistantFileImport(SongImportTestHelper):
def __init__(self, *args, **kwargs):
self.importer_class_name = 'WorshipAssistantImport'
self.importer_module_name = 'worshipassistant'
super(TestWorshipAssistantFileImport, self).__init__(*args, **kwargs)
def test_song_import(self):
"""
Test that loading an Worship Assistant file works correctly
"""
self.file_import(os.path.join(TEST_PATH, 'du_herr.csv'),
self.load_external_result_data(os.path.join(TEST_PATH, 'du_herr.json')))
self.file_import(os.path.join(TEST_PATH, 'would_you_be_free.csv'),
self.load_external_result_data(os.path.join(TEST_PATH, 'would_you_be_free.json')))

View File

@ -37,7 +37,7 @@ if os.name != 'nt':
import pyodbc
from openlp.plugins.songs.lib.worshipcenterproimport import WorshipCenterProImport
from openlp.plugins.songs.lib.importers.worshipcenterpro import WorshipCenterProImport
from tests.functional import patch, MagicMock
@ -141,7 +141,7 @@ class TestWorshipCenterProSongImport(TestCase):
Test creating an instance of the WorshipCenter Pro file importer
"""
# GIVEN: A mocked out SongImport class, and a mocked out "manager"
with patch('openlp.plugins.songs.lib.worshipcenterproimport.SongImport'):
with patch('openlp.plugins.songs.lib.importers.worshipcenterpro.SongImport'):
mocked_manager = MagicMock()
# WHEN: An importer object is created
@ -156,9 +156,10 @@ class TestWorshipCenterProSongImport(TestCase):
"""
# GIVEN: A mocked out SongImport class, a mocked out pyodbc module, a mocked out translate method,
# a mocked "manager" and a mocked out log_error method.
with patch('openlp.plugins.songs.lib.worshipcenterproimport.SongImport'), \
patch('openlp.plugins.songs.lib.worshipcenterproimport.pyodbc.connect') as mocked_pyodbc_connect, \
patch('openlp.plugins.songs.lib.worshipcenterproimport.translate') as mocked_translate:
with patch('openlp.plugins.songs.lib.importers.worshipcenterpro.SongImport'), \
patch('openlp.plugins.songs.lib.importers.worshipcenterpro.pyodbc.connect') \
as mocked_pyodbc_connect, \
patch('openlp.plugins.songs.lib.importers.worshipcenterpro.translate') as mocked_translate:
mocked_manager = MagicMock()
mocked_log_error = MagicMock()
mocked_translate.return_value = 'Translated Text'
@ -185,9 +186,9 @@ class TestWorshipCenterProSongImport(TestCase):
"""
# GIVEN: A mocked out SongImport class, a mocked out pyodbc module with a simulated recordset, a mocked out
# translate method, a mocked "manager", add_verse method & mocked_finish method.
with patch('openlp.plugins.songs.lib.worshipcenterproimport.SongImport'), \
patch('openlp.plugins.songs.lib.worshipcenterproimport.pyodbc') as mocked_pyodbc, \
patch('openlp.plugins.songs.lib.worshipcenterproimport.translate') as mocked_translate:
with patch('openlp.plugins.songs.lib.importers.worshipcenterpro.SongImport'), \
patch('openlp.plugins.songs.lib.importers.worshipcenterpro.pyodbc') as mocked_pyodbc, \
patch('openlp.plugins.songs.lib.importers.worshipcenterpro.translate') as mocked_translate:
mocked_manager = MagicMock()
mocked_import_wizard = MagicMock()
mocked_add_verse = MagicMock()

View File

@ -33,8 +33,8 @@ This module contains tests for the ZionWorx song importer.
from unittest import TestCase
from tests.functional import MagicMock, patch
from openlp.plugins.songs.lib.zionworximport import ZionWorxImport
from openlp.plugins.songs.lib.songimport import SongImport
from openlp.plugins.songs.lib.importers.zionworx import ZionWorxImport
from openlp.plugins.songs.lib.importers.songimport import SongImport
class TestZionWorxImport(TestCase):
@ -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.zionworximport.SongImport'):
with patch('openlp.plugins.songs.lib.importers.zionworx.SongImport'):
mocked_manager = MagicMock()
# WHEN: An importer object is created

View File

@ -31,10 +31,13 @@ The :mod:`songfileimporthelper` modules provides a helper class and methods to e
song files from third party applications.
"""
import json
import logging
from unittest import TestCase
from tests.functional import patch, MagicMock, call
log = logging.getLogger(__name__)
class SongImportTestHelper(TestCase):
"""
@ -42,23 +45,24 @@ class SongImportTestHelper(TestCase):
"""
def __init__(self, *args, **kwargs):
super(SongImportTestHelper, self).__init__(*args, **kwargs)
self.importer_module = __import__(
'openlp.plugins.songs.lib.%s' % self.importer_module_name, fromlist=[self.importer_class_name])
self.importer_module = __import__('openlp.plugins.songs.lib.importers.%s' %
self.importer_module_name, fromlist=[self.importer_class_name])
self.importer_class = getattr(self.importer_module, self.importer_class_name)
def setUp(self):
"""
Patch and set up the mocks required.
"""
self.add_copyright_patcher = patch(
'openlp.plugins.songs.lib.%s.%s.add_copyright' % (self.importer_module_name, self.importer_class_name))
self.add_verse_patcher = patch(
'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.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.add_copyright_patcher = patch('openlp.plugins.songs.lib.importers.%s.%s.add_copyright' %
(self.importer_module_name, self.importer_class_name))
self.add_verse_patcher = patch('openlp.plugins.songs.lib.importers.%s.%s.add_verse' %
(self.importer_module_name, self.importer_class_name))
self.finish_patcher = patch('openlp.plugins.songs.lib.importers.%s.%s.finish' %
(self.importer_module_name, self.importer_class_name))
self.add_author_patcher = patch('openlp.plugins.songs.lib.importers.%s.%s.add_author' %
(self.importer_module_name, self.importer_class_name))
self.song_import_patcher = patch('openlp.plugins.songs.lib.importers.%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()
@ -95,7 +99,7 @@ class SongImportTestHelper(TestCase):
importer.topics = []
# WHEN: Importing the source file
importer.import_source = [source_file_name]
importer.import_source = source_file_name
add_verse_calls = self._get_data(result_data, 'verses')
author_calls = self._get_data(result_data, 'authors')
ccli_number = self._get_data(result_data, 'ccli_number')
@ -107,9 +111,21 @@ class SongImportTestHelper(TestCase):
topics = self._get_data(result_data, 'topics')
verse_order_list = self._get_data(result_data, 'verse_order_list')
# THEN: do_import should return none, the song data should be as expected, and finish should have been
# called.
# THEN: do_import should return none, the song data should be as expected, and finish should have been called.
self.assertIsNone(importer.do_import(), 'do_import should return None when it has completed')
# Debug information - will be displayed when the test fails
log.debug("Title imported: %s" % importer.title)
log.debug("Verses imported: %s" % self.mocked_add_verse.mock_calls)
log.debug("Verse order imported: %s" % importer.verse_order_list)
log.debug("Authors imported: %s" % self.mocked_add_author.mock_calls)
log.debug("CCLI No. imported: %s" % importer.ccli_number)
log.debug("Comments imported: %s" % importer.comments)
log.debug("Songbook imported: %s" % importer.song_book_name)
log.debug("Song number imported: %s" % importer.song_number)
log.debug("Song copyright imported: %s" % importer.song_number)
log.debug("Topics imported: %s" % importer.topics)
self.assertEqual(importer.title, title, 'title for %s should be "%s"' % (source_file_name, title))
for author in author_calls:
self.mocked_add_author.assert_any_call(author)

View File

@ -0,0 +1,18 @@
{
"title": "Näher, mein Gott, zu Dir",
"verse_order_list": ["v1", "v2", "v3"],
"verses": [
[
"Näher, mein Gott, zu Dir,\nsei meine Bitt'!\nNäher, o Herr, zu Dir\nmit jedem Schritt.\nNur an dem Herzen Dein\nkann ich geborgen sein;\ndeshalb die Bitte mein:\nNäher zu Dir!",
"v1"
],
[
"Näher, mein Gott, zu Dir!\nEin jeder Tag\nsoll es neu zeigen mir,\nwas er vermag:\nWie seiner Gnade Macht,\nErlösung hat gebracht,\nin uns're Sündennacht.\nNäher zu Dir!",
"v2"
],
[
"Näher, mein Gott, zu Dir!\nDich bet' ich an.\nWie vieles hast an mir,\nDu doch getan!\nVon Banden frei und los,\nruh' ich in Deinem Schoss.\nJa, Deine Gnad' ist gross!\nNäher zu Dir!",
"v3"
]
]
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<ppl version="3.0"><general><title>Näher, mein Gott, zu Dir</title><category>Anbetung</category><language>Deutsch</language></general><songtext><part caption="Teil 1"><slide mainsize="42" backgroundnr="0"><line>Näher, mein Gott, zu Dir,</line><line>sei meine Bitt'!</line><line>Näher, o Herr, zu Dir</line><line>mit jedem Schritt.</line></slide><slide mainsize="44" backgroundnr="0"><line>Nur an dem Herzen Dein</line><line>kann ich geborgen sein;</line><line>deshalb die Bitte mein:</line><line>Näher zu Dir!</line></slide></part><part caption="Teil 2"><slide mainsize="42" backgroundnr="0"><line>Näher, mein Gott, zu Dir!</line><line>Ein jeder Tag</line><line>soll es neu zeigen mir,</line><line>was er vermag:</line></slide><slide mainsize="42" backgroundnr="0"><line>Wie seiner Gnade Macht,</line><line>Erlösung hat gebracht,</line><line>in uns're Sündennacht.</line><line>Näher zu Dir!</line></slide></part><part caption="Teil 3"><slide mainsize="42" backgroundnr="0"><line>Näher, mein Gott, zu Dir!</line><line>Dich bet' ich an.</line><line>Wie vieles hast an mir,</line><line>Du doch getan!</line></slide><slide mainsize="42" backgroundnr="0"><line>Von Banden frei und los,</line><line>ruh' ich in Deinem Schoss.</line><line>Ja, Deine Gnad' ist gross!</line><line>Näher zu Dir!</line></slide></part></songtext><order><item>Teil 1</item><item>Teil 2</item><item>Teil 3</item></order><information><copyright><position>lastslide</position><text><line>Text und Musik: Lowell Mason, 1792-1872</line></text></copyright><source><position>firstslide</position><text><line>grünes Buch 339</line></text></source></information><formatting><font><maintext><name>Times New Roman</name><size>44</size><bold>true</bold><italic>true</italic><color>16777215</color><outline>30</outline><shadow>15</shadow></maintext><translationtext><name>Times New Roman</name><size>20</size><bold>false</bold><italic>false</italic><color>16777215</color><outline>30</outline><shadow>20</shadow></translationtext><copyrighttext><name>Times New Roman</name><size>14</size><bold>false</bold><italic>false</italic><color>16777215</color><outline>30</outline><shadow>20</shadow></copyrighttext><sourcetext><name>Times New Roman</name><size>30</size><bold>false</bold><italic>false</italic><color>16777215</color><outline>30</outline><shadow>20</shadow></sourcetext><outline><enabled>false</enabled><color>0</color></outline><shadow><enabled>true</enabled><color>0</color><direction>125</direction></shadow></font><background><file>Blumen\Blume 3.jpg</file></background><linespacing><main>30</main><translation>20</translation></linespacing><textorientation><horizontal>left</horizontal><vertical>center</vertical><transpos>inline</transpos></textorientation><borders><mainleft>50</mainleft><maintop>40</maintop><mainright>60</mainright><mainbottom>70</mainbottom><copyrightbottom>30</copyrightbottom><sourcetop>20</sourcetop><sourceright>40</sourceright></borders></formatting></ppl>

View File

@ -0,0 +1,26 @@
{
"title": "You are so faithful",
"verse_order_list": ["v1", "c1", "v2", "c1", "v3", "c1", "v4"],
"verses": [
[
"You are so faithful\nso faithful, so faithful.\nYou are so faithful\nso faithful, so faithful.",
"v1"
],
[
"That's why I praise you\nin the morning\nThat's why I praise you\nin the noontime.\nThat's why I praise you\nin the evening\nThat's why I praise you\nall the time.",
"c1"
],
[
"You are so loving\nso loving, so loving.\nYou are so loving\nso loving, so loving.",
"v2"
],
[
"You are so caring\nso caring, so caring.\nYou are so caring\nso caring, so caring.",
"v3"
],
[
"You are so mighty\nso mighty, so mighty.\nYou are so mighty\nso mighty, so mighty.",
"v4"
]
]
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<ppl version="3.0"><general><title>You are so faithful</title><category>Lobpreis</category><language>Englisch</language></general><songtext><part caption="Strophe 1"><slide mainsize="30" backgroundnr="0"><line>You are so faithful</line><line>so faithful, so faithful.</line><translation>Du bist so treu</translation><translation>so treu, so treu.</translation></slide><slide mainsize="30" backgroundnr="0"><line>You are so faithful</line><line>so faithful, so faithful.</line><translation>Du bist so treu</translation><translation>so treu, so treu.</translation></slide></part><part caption="Refrain"><slide mainsize="30" backgroundnr="0"><line>That's why I praise you</line><line>in the morning</line><line>That's why I praise you</line><line>in the noontime.</line><translation>Deshalb preise ich Dich</translation><translation>am Morgen</translation><translation>Deshalb preise ich Dich</translation><translation>am Mittag.</translation></slide><slide mainsize="30" backgroundnr="0"><line>That's why I praise you</line><line>in the evening</line><line>That's why I praise you</line><line>all the time.</line><translation>Deshalb preise ich Dich</translation><translation>am Abend</translation><translation>Deshalb preise ich Dich</translation><translation>allezeit.</translation></slide></part><part caption="Strophe 2"><slide mainsize="30" backgroundnr="0"><line>You are so loving</line><line>so loving, so loving.</line><translation>Du bist so liebevoll</translation><translation>so liebevoll, so liebevoll.</translation></slide><slide mainsize="30" backgroundnr="0"><line>You are so loving</line><line>so loving, so loving.</line><translation>Du bist so liebevoll</translation><translation>so liebevoll, so liebevoll.</translation></slide></part><part caption="Strophe 3"><slide mainsize="30" backgroundnr="0"><line>You are so caring</line><line>so caring, so caring.</line><translation>Du sorgst so gut</translation><translation>Du kümmerst dich um uns.</translation></slide><slide mainsize="30" backgroundnr="0"><line>You are so caring</line><line>so caring, so caring.</line><translation>Du sorgst so gut</translation><translation>Du kümmerst dich um uns.</translation></slide></part><part caption="Strophe 4"><slide mainsize="30" backgroundnr="0"><line>You are so mighty</line><line>so mighty, so mighty.</line><translation>Du bist so mächtig</translation><translation>so mächtig, so mächtig.</translation></slide><slide mainsize="30" backgroundnr="0"><line>You are so mighty</line><line>so mighty, so mighty.</line><translation>Du bist so mächtig</translation><translation>so mächtig, so mächtig.</translation></slide></part></songtext><order><item>Strophe 1</item><item>Refrain</item><item>Strophe 2</item><item>Refrain</item><item>Strophe 3</item><item>Refrain</item><item>Strophe 4</item></order><information><copyright><position>lastslide</position><text><line>Musik &amp; Copyright unbekannt</line></text></copyright><source><position>firstslide</position><text/></source></information><formatting><font><maintext><name>Tahoma</name><size>30</size><bold>true</bold><italic>false</italic><color>16777215</color><outline>30</outline><shadow>20</shadow></maintext><translationtext><name>Tahoma</name><size>20</size><bold>false</bold><italic>false</italic><color>16777215</color><outline>30</outline><shadow>20</shadow></translationtext><copyrighttext><name>Tahoma</name><size>14</size><bold>false</bold><italic>false</italic><color>16777215</color><outline>30</outline><shadow>20</shadow></copyrighttext><sourcetext><name>Tahoma</name><size>30</size><bold>false</bold><italic>false</italic><color>16777215</color><outline>30</outline><shadow>20</shadow></sourcetext><outline><enabled>true</enabled><color>0</color></outline><shadow><enabled>true</enabled><color>0</color><direction>125</direction></shadow></font><background><file>Blumen\Blume 6.jpg</file></background><linespacing><main>30</main><translation>20</translation></linespacing><textorientation><horizontal>center</horizontal><vertical>center</vertical><transpos>inline</transpos></textorientation><borders><mainleft>50</mainleft><maintop>40</maintop><mainright>60</mainright><mainbottom>70</mainbottom><copyrightbottom>30</copyrightbottom><sourcetop>20</sourcetop><sourceright>40</sourceright></borders></formatting></ppl>

View File

@ -0,0 +1,25 @@
{
"title": "Great Is Thy Faithfulness",
"authors": [
"Thomas O. Chisholm (1866-1960)"
],
"verse_order_list": ["v1", "c1", "v2", "c1", "v3", "c1"],
"verses": [
[
"\"Great is Thy faithfulness\", O God my Father.\nThere is no shadow of turning with Thee;\nThou changest not, Thy compassions they fail not,\nAs Thou hast been Thou forever shall be.",
"v1"
],
[
"Great is Thy faithfulness!\nGreat is Thy faithfulness!\nMorning by morning new mercies I see!\nAll I have needed Thy hand hath provided -\n\"Great is Thy faithfulness\", Lord, unto me!",
"c1"
],
[
"Summer and winter, and springtime and harvest,\nSun, moon, and stars in their courses above,\nJoin with all nature in manifold witness,\nTo Thy great faithfulness, mercy and love.",
"v2"
],
[
"Pardon for sin and a peace that endureth,\nThine own dear presence to cheer and to guide,\nStrength for today and bright hope for tomorrow,\nBlessings all mine, with ten thousand beside!",
"v3"
]
]
}

View File

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<song xmlns="creativelifestyles/song">
<attributes>
<title>Great Is Thy Faithfulness</title>
<author>Thomas O. Chisholm (1866-1960)</author>
<copyright></copyright>
<ccli_number></ccli_number>
<comments></comments>
</attributes>
<verses>
<verse id="Verse 1">
"Great is Thy faithfulness", O God my Father.
There is no shadow of turning with Thee;
Thou changest not, Thy compassions they fail not,
As Thou hast been Thou forever shall be.
</verse>
<verse id="Chorus">
Great is Thy faithfulness!
Great is Thy faithfulness!
Morning by morning new mercies I see!
All I have needed Thy hand hath provided -
"Great is Thy faithfulness", Lord, unto me!
</verse>
<verse id="Verse 2">
Summer and winter, and springtime and harvest,
Sun, moon, and stars in their courses above,
Join with all nature in manifold witness,
To Thy great faithfulness, mercy and love.
</verse>
<verse id="Chorus">
Great is Thy faithfulness!
Great is Thy faithfulness!
Morning by morning new mercies I see!
All I have needed Thy hand hath provided -
"Great is Thy faithfulness", Lord, unto me!
</verse>
<verse id="Verse 3">
Pardon for sin and a peace that endureth,
Thine own dear presence to cheer and to guide,
Strength for today and bright hope for tomorrow,
Blessings all mine, with ten thousand beside!
</verse>
<verse id="Chorus">
Great is Thy faithfulness!
Great is Thy faithfulness!
Morning by morning new mercies I see!
All I have needed Thy hand hath provided -
"Great is Thy faithfulness", Lord, unto me!
</verse>
</verses>
</song>

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<theme version="2.0">
<name>Default</name>
<background type="solid">
<color>#000000</color>
</background>
<font type="main">
<name>Arial</name>
<color>#FFFFFF</color>
<size>40</size>
<bold>False</bold>
<italics>False</italics>
<line_adjustment>0</line_adjustment>
<location height="690" override="False" width="1004" x="10" y="10"/>
<shadow shadowColor="#000000" shadowSize="5">True</shadow>
<outline outlineColor="#000000" outlineSize="2">False</outline>
</font>
<font type="footer">
<name>Arial</name>
<color>#FFFFFF</color>
<size>12</size>
<bold>False</bold>
<italics>False</italics>
<line_adjustment>0</line_adjustment>
<location height="78" override="False" width="1004" x="10" y="690"/>
<shadow shadowColor="#000000" shadowSize="5">True</shadow>
<outline outlineColor="#000000" outlineSize="2">False</outline>
</font>
<display>
<horizontalAlign>0</horizontalAlign>
<verticalAlign>0</verticalAlign>
<slideTransition>False</slideTransition>
</display>
</theme>

View File

@ -0,0 +1,30 @@
"SongID","SongNr","Title","Author","Copyright","FirstLine","PriKey","AltKey","Tempo","Focus","Theme","Scripture","Active","Songbook","TimeSig","Introduced","LastUsed","TimesUsed","CCLINr","User1","User2","User3","User4","User5","Roadmap","Overmap","FileLink1","FileLink2","Updated","Lyrics","Info","Lyrics2","Background"
"4ee399dc-edda-4aa9-891e-a859ca093c78","NULL","Du, Herr, verläßt mich nicht","Carl Brockhaus / Johann Georg Bäßler 1806","NULL","NULL","NULL","NULL","NULL","NULL","NULL","NULL","1","1","NULL","NULL","NULL","NULL","NULL","NULL","NULL","NULL","NULL","NULL","NULL","NULL","NULL","NULL","2014-06-25 12:15:28.317","","NULL","[1]
Du, Herr, verläßt mich nicht.
Auf Dich mein Herz allein vertraut,
Mein Auge glaubend auf Dich schaut.
Du bist mein Heil, mein Licht,
Mein Fels, mein sichrer Hort.
Bin ich versucht, gibt's Not und Leid,
Du bleibst mein Trost, mein Arm im Streit,
Mein Licht am dunklen Ort.
[2]
Ich weiß, daß Du mich liebst.
Bist mir in jeder Lage nah',
Wohin ich gehe Du bist da,
Ja, Du mir alles gibst.
Ich überlaß mich Dir;
Denn Du, Herr, kennst mich ganz und gar
Und führst mich sicher, wunderbar,
Und bist selbst alles mir.
[3]
In dieser Wüste hier
Find't nirgend meine Seele Ruh',
Denn meine Ruh' bist, Jesu, Du.
Wohl mir, ich geh' zu Dir!
Bald werd' ich bei Dir sein,
Bald mit den Deinen ewiglich
Anbeten, loben, preisen Dich,
Mich Deiner stets erfreun.","NULL"
1 SongID SongNr Title Author Copyright FirstLine PriKey AltKey Tempo Focus Theme Scripture Active Songbook TimeSig Introduced LastUsed TimesUsed CCLINr User1 User2 User3 User4 User5 Roadmap Overmap FileLink1 FileLink2 Updated Lyrics Info Lyrics2 Background
2 4ee399dc-edda-4aa9-891e-a859ca093c78 NULL Du, Herr, verläßt mich nicht Carl Brockhaus / Johann Georg Bäßler 1806 NULL NULL NULL NULL NULL NULL NULL NULL 1 1 NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL 2014-06-25 12:15:28.317 NULL [1] Du, Herr, verläßt mich nicht. Auf Dich mein Herz allein vertraut, Mein Auge glaubend auf Dich schaut. Du bist mein Heil, mein Licht, Mein Fels, mein sichrer Hort. Bin ich versucht, gibt's Not und Leid, Du bleibst mein Trost, mein Arm im Streit, Mein Licht am dunklen Ort. [2] Ich weiß, daß Du mich liebst. Bist mir in jeder Lage nah', Wohin ich gehe – Du bist da, Ja, Du mir alles gibst. Ich überlaß mich Dir; Denn Du, Herr, kennst mich ganz und gar Und führst mich sicher, wunderbar, Und bist selbst alles mir. [3] In dieser Wüste hier Find't nirgend meine Seele Ruh', Denn meine Ruh' bist, Jesu, Du. Wohl mir, ich geh' zu Dir! Bald werd' ich bei Dir sein, Bald mit den Deinen ewiglich Anbeten, loben, preisen Dich, Mich Deiner stets erfreun. NULL

View File

@ -0,0 +1,21 @@
{
"authors": [
"Carl Brockhaus / Johann Georg Bäßler 1806"
],
"title": "Du, Herr, verläßt mich nicht",
"verse_order_list": [],
"verses": [
[
"Du, Herr, verläßt mich nicht.\nAuf Dich mein Herz allein vertraut,\nMein Auge glaubend auf Dich schaut.\nDu bist mein Heil, mein Licht,\nMein Fels, mein sichrer Hort.\nBin ich versucht, gibt's Not und Leid,\nDu bleibst mein Trost, mein Arm im Streit,\nMein Licht am dunklen Ort.\n",
"v1"
],
[
"Ich weiß, daß Du mich liebst.\nBist mir in jeder Lage nah',\nWohin ich gehe Du bist da,\nJa, Du mir alles gibst.\nIch überlaß mich Dir;\nDenn Du, Herr, kennst mich ganz und gar\nUnd führst mich sicher, wunderbar,\nUnd bist selbst alles mir.\n",
"v2"
],
[
"In dieser Wüste hier\nFind't nirgend meine Seele Ruh',\nDenn meine Ruh' bist, Jesu, Du.\nWohl mir, ich geh' zu Dir!\nBald werd' ich bei Dir sein,\nBald mit den Deinen ewiglich\nAnbeten, loben, preisen Dich,\nMich Deiner stets erfreun.\n",
"v3"
]
]
}

View File

@ -0,0 +1,30 @@
SONGNR,TITLE,AUTHOR,COPYRIGHT,FIRSTLINE,PRIKEY,ALTKEY,TEMPO,FOCUS,THEME,SCRIPTURE,ACTIVE,SONGBOOK,TIMESIG,INTRODUCED,LASTUSED,TIMESUSED,CCLINR,USER1,USER2,USER3,USER4,USER5,ROADMAP,FILELINK1,OVERMAP,FILELINK2,LYRICS,INFO,LYRICS2,Background
"7","Would You Be Free","Jones, Lewis E.","Public Domain","Would you be free from your burden of sin?","G","","Moderate","Only To Others","","","N","Y","","1899-12-30","1899-12-30","","","","","","","","1,C,1","","","",".G C G
Would you be free from your burden of sin?
. D D7 G
There's power in the blood, power in the blood
. C G
Would you o'er evil a victory win?
. D D7 G
There's wonderful power in the blood
.G C G
There is power, power, wonder working power
.D G
In the blood of the Lamb
. C G
There is power, power, wonder working power
. D G
In the precious blood of the Lamb
","","[1]
Would you be free from your burden of sin?
There's power in the blood, power in the blood
Would you o'er evil a victory win?
There's wonderful power in the blood
[C]
There is power, power, wonder working power
In the blood of the Lamb
There is power, power, wonder working power
In the precious blood of the Lamb
",""
1 SONGNR TITLE AUTHOR COPYRIGHT FIRSTLINE PRIKEY ALTKEY TEMPO FOCUS THEME SCRIPTURE ACTIVE SONGBOOK TIMESIG INTRODUCED LASTUSED TIMESUSED CCLINR USER1 USER2 USER3 USER4 USER5 ROADMAP FILELINK1 OVERMAP FILELINK2 LYRICS INFO LYRICS2 Background
2 7 Would You Be Free Jones, Lewis E. Public Domain Would you be free from your burden of sin? G Moderate Only To Others N Y 1899-12-30 1899-12-30 1,C,1 .G C G Would you be free from your burden of sin? . D D7 G There's power in the blood, power in the blood . C G Would you o'er evil a victory win? . D D7 G There's wonderful power in the blood .G C G There is power, power, wonder working power .D G In the blood of the Lamb . C G There is power, power, wonder working power . D G In the precious blood of the Lamb [1] Would you be free from your burden of sin? There's power in the blood, power in the blood Would you o'er evil a victory win? There's wonderful power in the blood [C] There is power, power, wonder working power In the blood of the Lamb There is power, power, wonder working power In the precious blood of the Lamb

View File

@ -0,0 +1,19 @@
{
"authors": [
"Jones",
"Lewis E"
],
"title": "Would You Be Free",
"verse_order_list": ["v1", "c1", "v1"],
"copyright": "Public Domain",
"verses": [
[
"Would you be free from your burden of sin? \nThere's power in the blood, power in the blood \nWould you o'er evil a victory win? \nThere's wonderful power in the blood \n",
"v1"
],
[
"There is power, power, wonder working power \nIn the blood of the Lamb \nThere is power, power, wonder working power \nIn the precious blood of the Lamb \n",
"c1"
]
]
}

View File

@ -30,7 +30,7 @@
Package to test for proper bzr tags.
"""
import os
import re
from unittest import TestCase
from subprocess import Popen, PIPE
@ -50,12 +50,12 @@ TAGS = [
['1.9.11', '2039'],
['1.9.12', '2063'],
['2.0', '2118'],
['2.0.1', '?'],
['2.0.2', '?'],
['2.0.3', '?'],
['2.0.4', '?'],
['2.1.0', '2119']
]
# Depending on the repository, we sometimes have the 2.0.x tags in the repo too. They come up with a revision number of
# "?", which I suspect is due to the fact that we're using shared repositories. This regular expression matches all
# 2.0.x tags.
TAG_SEARCH = re.compile('2\.0\.\d')
class TestBzrTags(TestCase):
@ -69,8 +69,9 @@ class TestBzrTags(TestCase):
# WHEN getting the branches tags
bzr = Popen(('bzr', 'tags', '--directory=' + path), stdout=PIPE)
stdout = bzr.communicate()[0]
tags = [line.decode('utf-8').split() for line in stdout.splitlines()]
std_out = bzr.communicate()[0]
tags = [line.decode('utf-8').split() for line in std_out.splitlines()]
tags = [t_r for t_r in tags if t_r[1] != '?' or not (t_r[1] == '?' and TAG_SEARCH.search(t_r[0]))]
# THEN the tags should match the accepted tags
self.assertEqual(TAGS, tags, 'List of tags should match')