From 93bd032cd92b8bbf2d54bbe0937f9951d2be5e13 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 1 Jul 2013 08:47:20 +0200 Subject: [PATCH 01/93] html_builder clean up --- openlp/core/lib/htmlbuilder.py | 74 +++------------------------------- openlp/core/ui/maindisplay.py | 2 - 2 files changed, 5 insertions(+), 71 deletions(-) diff --git a/openlp/core/lib/htmlbuilder.py b/openlp/core/lib/htmlbuilder.py index d4e22b0dd..dbfb8537d 100644 --- a/openlp/core/lib/htmlbuilder.py +++ b/openlp/core/lib/htmlbuilder.py @@ -114,12 +114,6 @@ sup { document.getElementById('black').style.display = black; document.getElementById('lyricsmain').style.visibility = lyrics; document.getElementById('image').style.visibility = lyrics; - outline = document.getElementById('lyricsoutline') - if(outline != null) - outline.style.visibility = lyrics; - shadow = document.getElementById('lyricsshadow') - if(shadow != null) - shadow.style.visibility = lyrics; document.getElementById('footer').style.visibility = lyrics; } @@ -138,9 +132,6 @@ sup { */ var txt = document.getElementById('lyricsmain'); if(window.getComputedStyle(txt).textAlign == 'justify'){ - var outline = document.getElementById('lyricsoutline'); - if(outline != null) - txt = outline; if(window.getComputedStyle(txt).webkitTextStrokeWidth != '0px'){ new_text = new_text.replace(/(\s| )+(?![^<]*>)/g, function(match) { @@ -150,8 +141,6 @@ sup { } } text_fade('lyricsmain', new_text); - text_fade('lyricsoutline', new_text); - text_fade('lyricsshadow', new_text.replace(match, '')); } function text_fade(id, new_text){ @@ -190,7 +179,7 @@ sup { %s -%s +
@@ -251,8 +240,7 @@ def build_html(item, screen, is_live, background, image=None, plugins=None): u'true' if theme and theme.display_slide_transition and is_live else u'false', js_additions, bgimage_src, image_src, - html_additions, - build_lyrics_html(item, webkit_ver) + html_additions ) return html @@ -328,40 +316,17 @@ def build_lyrics_css(item, webkit_ver): %s } .lyricsmain { -%s -} -.lyricsoutline { -%s -} -.lyricsshadow { -%s + %s } """ theme = item.themedata lyricstable = u'' lyrics = u'' lyricsmain = u'' - outline = u'' - shadow = u'' if theme and item.main: lyricstable = u'left: %spx; top: %spx;' % (item.main.x(), item.main.y()) lyrics = build_lyrics_format_css(theme, item.main.width(), item.main.height()) - # For performance reasons we want to show as few DIV's as possible, especially when animating/transitions. - # However some bugs in older versions of qtwebkit mean we need to perform workarounds and add extra divs. Only - # do these when needed. - # - # Before 533.3 the webkit-text-fill colour wasn't displayed, only the stroke (outline) color. So put stroke - # layer underneath the main text. - # - # Up to 534.3 the webkit-text-stroke was sometimes out of alignment with the fill, or normal text. - # letter-spacing=1 is workaround https://bugs.webkit.org/show_bug.cgi?id=44403 - # - # Up to 534.3 the text-shadow didn't get displayed when webkit-text-stroke was used. So use an offset text - # layer underneath. https://bugs.webkit.org/show_bug.cgi?id=19728 - if webkit_ver >= 533.3: - lyricsmain += build_lyrics_outline_css(theme) - else: - outline = build_lyrics_outline_css(theme) + lyricsmain += build_lyrics_outline_css(theme) if theme.font_main_shadow: if theme.font_main_outline and webkit_ver <= 534.3: shadow = u'padding-left: %spx; padding-top: %spx;' % \ @@ -371,7 +336,7 @@ def build_lyrics_css(item, webkit_ver): else: lyricsmain += u' text-shadow: %s %spx %spx;' % \ (theme.font_main_shadow_color, theme.font_main_shadow_size, theme.font_main_shadow_size) - lyrics_css = style % (lyricstable, lyrics, lyricsmain, outline, shadow) + lyrics_css = style % (lyricstable, lyrics, lyricsmain) return lyrics_css @@ -431,9 +396,6 @@ def build_lyrics_format_css(theme, width, height): 'padding: 0; padding-bottom: %s; padding-left: %spx; width: %spx; height: %spx; ' % \ (justify, align, valign, theme.font_main_name, theme.font_main_size, theme.font_main_color, 100 + int(theme.font_main_line_adjustment), padding_bottom, left_margin, width, height) - if theme.font_main_outline: - if webkit_version() <= 534.3: - lyrics += u' letter-spacing: 1px;' if theme.font_main_italics: lyrics += u' font-style:italic; ' if theme.font_main_bold: @@ -441,32 +403,6 @@ def build_lyrics_format_css(theme, width, height): return lyrics -def build_lyrics_html(item, webkitvers): - """ - Build the HTML required to show the lyrics - - ``item`` - Service Item containing theme and location information - - ``webkitvers`` - The version of qtwebkit we're using - """ - # Bugs in some versions of QtWebKit mean we sometimes need additional divs for outline and shadow, since the CSS - # doesn't work. To support vertical alignment middle and bottom, nested div's using display:table/display:table-cell - # are required for each lyric block. - lyrics = u'' - theme = item.themedata - if webkitvers <= 534.3 and theme and theme.font_main_outline: - lyrics += u'
' - if webkitvers < 533.3: - lyrics += u'
' - lyrics += u'
' - return lyrics - - def build_footer_css(item, height): """ Build the display of the item footer diff --git a/openlp/core/ui/maindisplay.py b/openlp/core/ui/maindisplay.py index 7069cb9b7..91cd801e2 100644 --- a/openlp/core/ui/maindisplay.py +++ b/openlp/core/ui/maindisplay.py @@ -242,8 +242,6 @@ class MainDisplay(Display): # Windows if there are many items in the service to re-render. # Setting the div elements direct seems to solve the issue self.frame.findFirstElement("#lyricsmain").setInnerXml(slide) - self.frame.findFirstElement("#lyricsoutline").setInnerXml(slide) - self.frame.findFirstElement("#lyricsshadow").setInnerXml(slide) def alert(self, text, location): """ From 8ee3e7265c8c9f5d78fc6701172d23eb6c4d96b4 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 1 Jul 2013 08:57:13 +0200 Subject: [PATCH 02/93] renames --- openlp/core/lib/htmlbuilder.py | 82 ++++++++++++++-------------------- 1 file changed, 33 insertions(+), 49 deletions(-) diff --git a/openlp/core/lib/htmlbuilder.py b/openlp/core/lib/htmlbuilder.py index dbfb8537d..0606b84d0 100644 --- a/openlp/core/lib/htmlbuilder.py +++ b/openlp/core/lib/htmlbuilder.py @@ -211,8 +211,7 @@ def build_html(item, screen, is_live, background, image=None, plugins=None): """ width = screen[u'size'].width() height = screen[u'size'].height() - theme = item.themedata - webkit_ver = webkit_version() + theme_data = item.themedata # Image generated and poked in if background: bgimage_src = u'src="data:image/png;base64,%s"' % background @@ -236,10 +235,11 @@ def build_html(item, screen, is_live, background, image=None, plugins=None): build_background_css(item, width), css_additions, build_footer_css(item, height), - build_lyrics_css(item, webkit_ver), - u'true' if theme and theme.display_slide_transition and is_live else u'false', + build_lyrics_css(item), + u'true' if theme_data and theme_data.display_slide_transition and is_live else u'false', js_additions, - bgimage_src, image_src, + bgimage_src, + image_src, html_additions ) return html @@ -291,16 +291,13 @@ def build_background_css(item, width): return background -def build_lyrics_css(item, webkit_ver): +def build_lyrics_css(item): """ Build the lyrics display css ``item`` Service Item containing theme and location information - ``webkitvers`` - The version of qtwebkit we're using - """ style = u""" .lyricstable { @@ -319,55 +316,41 @@ def build_lyrics_css(item, webkit_ver): %s } """ - theme = item.themedata + theme_data = item.themedata lyricstable = u'' lyrics = u'' lyricsmain = u'' - if theme and item.main: + if theme_data and item.main: lyricstable = u'left: %spx; top: %spx;' % (item.main.x(), item.main.y()) - lyrics = build_lyrics_format_css(theme, item.main.width(), item.main.height()) - lyricsmain += build_lyrics_outline_css(theme) - if theme.font_main_shadow: - if theme.font_main_outline and webkit_ver <= 534.3: - shadow = u'padding-left: %spx; padding-top: %spx;' % \ - (int(theme.font_main_shadow_size) + (int(theme.font_main_outline_size) * 2), - theme.font_main_shadow_size) - shadow += build_lyrics_outline_css(theme, True) - else: - lyricsmain += u' text-shadow: %s %spx %spx;' % \ - (theme.font_main_shadow_color, theme.font_main_shadow_size, theme.font_main_shadow_size) + lyrics = build_lyrics_format_css(theme_data, item.main.width(), item.main.height()) + lyricsmain += build_lyrics_outline_css(theme_data) + if theme_data.font_main_shadow: + lyricsmain += u' text-shadow: %s %spx %spx;' % \ + (theme_data.font_main_shadow_color, theme_data.font_main_shadow_size, theme_data.font_main_shadow_size) lyrics_css = style % (lyricstable, lyrics, lyricsmain) return lyrics_css -def build_lyrics_outline_css(theme, is_shadow=False): +def build_lyrics_outline_css(theme_data): """ Build the css which controls the theme outline. Also used by renderer for splitting verses - ``theme`` + ``theme_data`` Object containing theme information - - ``is_shadow`` - If true, use the shadow colors instead """ - if theme.font_main_outline: - size = float(theme.font_main_outline_size) / 16 - if is_shadow: - fill_color = theme.font_main_shadow_color - outline_color = theme.font_main_shadow_color - else: - fill_color = theme.font_main_color - outline_color = theme.font_main_outline_color + if theme_data.font_main_outline: + size = float(theme_data.font_main_outline_size) / 16 + fill_color = theme_data.font_main_color + outline_color = theme_data.font_main_outline_color return u' -webkit-text-stroke: %sem %s; -webkit-text-fill-color: %s; ' % (size, outline_color, fill_color) - else: - return u'' + return u'' -def build_lyrics_format_css(theme, width, height): +def build_lyrics_format_css(theme_data, width, height): """ Build the css which controls the theme format. Also used by renderer for splitting verses - ``theme`` + ``theme_data`` Object containing theme information ``width`` @@ -376,17 +359,17 @@ def build_lyrics_format_css(theme, width, height): ``height`` Height of the lyrics block """ - align = HorizontalType.Names[theme.display_horizontal_align] - valign = VerticalType.Names[theme.display_vertical_align] - if theme.font_main_outline: - left_margin = int(theme.font_main_outline_size) * 2 + align = HorizontalType.Names[theme_data.display_horizontal_align] + valign = VerticalType.Names[theme_data.display_vertical_align] + if theme_data.font_main_outline: + left_margin = int(theme_data.font_main_outline_size) * 2 else: left_margin = 0 justify = u'white-space:pre-wrap;' # fix tag incompatibilities - if theme.display_horizontal_align == HorizontalType.Justify: + if theme_data.display_horizontal_align == HorizontalType.Justify: justify = u'' - if theme.display_vertical_align == VerticalType.Bottom: + if theme_data.display_vertical_align == VerticalType.Bottom: padding_bottom = u'0.5em' else: padding_bottom = u'0' @@ -394,11 +377,12 @@ def build_lyrics_format_css(theme, width, height): 'text-align: %s; vertical-align: %s; font-family: %s; ' \ 'font-size: %spt; color: %s; line-height: %d%%; margin: 0;' \ 'padding: 0; padding-bottom: %s; padding-left: %spx; width: %spx; height: %spx; ' % \ - (justify, align, valign, theme.font_main_name, theme.font_main_size, - theme.font_main_color, 100 + int(theme.font_main_line_adjustment), padding_bottom, left_margin, width, height) - if theme.font_main_italics: + (justify, align, valign, theme_data.font_main_name, theme_data.font_main_size, + theme_data.font_main_color, 100 + int(theme_data.font_main_line_adjustment), padding_bottom, + left_margin, width, height) + if theme_data.font_main_italics: lyrics += u' font-style:italic; ' - if theme.font_main_bold: + if theme_data.font_main_bold: lyrics += u' font-weight:bold; ' return lyrics From 6bba69bf880a0956d0efe2ac2eb85a9cf615c57e Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Fri, 5 Jul 2013 20:35:27 +0200 Subject: [PATCH 03/93] removed duplicate css --- openlp/core/ui/media/phononplayer.py | 47 ++++++++++------------------ 1 file changed, 16 insertions(+), 31 deletions(-) diff --git a/openlp/core/ui/media/phononplayer.py b/openlp/core/ui/media/phononplayer.py index 2a6eb77ba..35de76298 100644 --- a/openlp/core/ui/media/phononplayer.py +++ b/openlp/core/ui/media/phononplayer.py @@ -45,36 +45,22 @@ from openlp.core.ui.media.mediaplayer import MediaPlayer log = logging.getLogger(__name__) ADDITIONAL_EXT = { - u'audio/ac3': [u'.ac3'], - u'audio/flac': [u'.flac'], - u'audio/x-m4a': [u'.m4a'], - u'audio/midi': [u'.mid', u'.midi'], - u'audio/x-mp3': [u'.mp3'], - u'audio/mpeg': [u'.mp3', u'.mp2', u'.mpga', u'.mpega', u'.m4a'], - u'audio/qcelp': [u'.qcp'], - u'audio/x-wma': [u'.wma'], - u'audio/x-ms-wma': [u'.wma'], - u'video/x-flv': [u'.flv'], - u'video/x-matroska': [u'.mpv', u'.mkv'], - u'video/x-wmv': [u'.wmv'], - u'video/x-mpg': [u'.mpg'], - u'video/mpeg': [u'.mp4', u'.mts', u'.mov'], - u'video/x-ms-wmv': [u'.wmv']} - -VIDEO_CSS = u""" -#videobackboard { - z-index:3; - background-color: %(bgcolor)s; + u'audio/ac3': [u'.ac3'], + u'audio/flac': [u'.flac'], + u'audio/x-m4a': [u'.m4a'], + u'audio/midi': [u'.mid', u'.midi'], + u'audio/x-mp3': [u'.mp3'], + u'audio/mpeg': [u'.mp3', u'.mp2', u'.mpga', u'.mpega', u'.m4a'], + u'audio/qcelp': [u'.qcp'], + u'audio/x-wma': [u'.wma'], + u'audio/x-ms-wma': [u'.wma'], + u'video/x-flv': [u'.flv'], + u'video/x-matroska': [u'.mpv', u'.mkv'], + u'video/x-wmv': [u'.wmv'], + u'video/x-mpg': [u'.mpg'], + u'video/mpeg': [u'.mp4', u'.mts', u'.mov'], + u'video/x-ms-wmv': [u'.wmv'] } -#video1 { - background-color: %(bgcolor)s; - z-index:4; -} -#video2 { - background-color: %(bgcolor)s; - z-index:4; -} -""" class PhononPlayer(MediaPlayer): @@ -268,8 +254,7 @@ class PhononPlayer(MediaPlayer): """ Add css style sheets to htmlbuilder """ - background = QtGui.QColor(Settings().value(u'players/background color')).name() - return VIDEO_CSS % {u'bgcolor': background} + return u'' def get_info(self): """ From 961d70b836d135ffb1837f86da941875dc4b319e Mon Sep 17 00:00:00 2001 From: Philip Ridout Date: Sat, 3 Aug 2013 20:15:59 +0100 Subject: [PATCH 04/93] Fixed bug #1194610 By detecting the encoding ranther than assuming. --- .../plugins/songs/lib/songshowplusimport.py | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/openlp/plugins/songs/lib/songshowplusimport.py b/openlp/plugins/songs/lib/songshowplusimport.py index a72f83c4f..ca08e9002 100644 --- a/openlp/plugins/songs/lib/songshowplusimport.py +++ b/openlp/plugins/songs/lib/songshowplusimport.py @@ -30,6 +30,7 @@ The :mod:`songshowplusimport` module provides the functionality for importing SongShow Plus songs into the OpenLP database. """ +import chardet import os import logging import re @@ -134,41 +135,41 @@ class SongShowPlusImport(SongImport): log.debug(length_descriptor_size) data = song_data.read(length_descriptor) if block_key == TITLE: - self.title = unicode(data, u'cp1252') + self.title = unicode(data, chardet.detect(data)['encoding']) elif block_key == AUTHOR: authors = data.split(" / ") for author in authors: if author.find(",") !=-1: authorParts = author.split(", ") author = authorParts[1] + " " + authorParts[0] - self.parse_author(unicode(author, u'cp1252')) + self.parse_author(unicode(author, chardet.detect(data)['encoding'])) elif block_key == COPYRIGHT: - self.addCopyright(unicode(data, u'cp1252')) + self.addCopyright(unicode(data, chardet.detect(data)['encoding'])) elif block_key == CCLI_NO: self.ccliNumber = int(data) elif block_key == VERSE: - self.addVerse(unicode(data, u'cp1252'), "%s%s" % (VerseType.tags[VerseType.Verse], verse_no)) + self.addVerse(unicode(data, chardet.detect(data)['encoding']), "%s%s" % (VerseType.tags[VerseType.Verse], verse_no)) elif block_key == CHORUS: - self.addVerse(unicode(data, u'cp1252'), "%s%s" % (VerseType.tags[VerseType.Chorus], verse_no)) + self.addVerse(unicode(data, chardet.detect(data)['encoding']), "%s%s" % (VerseType.tags[VerseType.Chorus], verse_no)) elif block_key == BRIDGE: - self.addVerse(unicode(data, u'cp1252'), "%s%s" % (VerseType.tags[VerseType.Bridge], verse_no)) + self.addVerse(unicode(data, chardet.detect(data)['encoding']), "%s%s" % (VerseType.tags[VerseType.Bridge], verse_no)) elif block_key == TOPIC: - self.topics.append(unicode(data, u'cp1252')) + self.topics.append(unicode(data, chardet.detect(data)['encoding'])) elif block_key == COMMENTS: - self.comments = unicode(data, u'cp1252') + self.comments = unicode(data, chardet.detect(data)['encoding']) elif block_key == VERSE_ORDER: verse_tag = self.to_openlp_verse_tag(data, True) if verse_tag: if not isinstance(verse_tag, unicode): - verse_tag = unicode(verse_tag, u'cp1252') + verse_tag = unicode(verse_tag, chardet.detect(data)['encoding']) self.ssp_verse_order_list.append(verse_tag) elif block_key == SONG_BOOK: - self.songBookName = unicode(data, u'cp1252') + self.songBookName = unicode(data, chardet.detect(data)['encoding']) elif block_key == SONG_NUMBER: self.songNumber = ord(data) elif block_key == CUSTOM_VERSE: verse_tag = self.to_openlp_verse_tag(verse_name) - self.addVerse(unicode(data, u'cp1252'), verse_tag) + self.addVerse(unicode(data, chardet.detect(data)['encoding']), verse_tag) else: log.debug("Unrecognised blockKey: %s, data: %s" % (block_key, data)) song_data.seek(next_block_starts) From 57bab854641a64db58d85becf5d3116fc268da42 Mon Sep 17 00:00:00 2001 From: Oliver Wieland Date: Fri, 9 Aug 2013 21:31:59 +0200 Subject: [PATCH 05/93] fixes bug #1051699 'Bibles add option to not display chapter and verse numbers' Fixes: https://launchpad.net/bugs/1051699 --- openlp/plugins/bibles/lib/__init__.py | 1 + openlp/plugins/bibles/lib/biblestab.py | 4 +++- openlp/plugins/bibles/lib/mediaitem.py | 6 ++++-- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/openlp/plugins/bibles/lib/__init__.py b/openlp/plugins/bibles/lib/__init__.py index 371632d41..1935b53f2 100644 --- a/openlp/plugins/bibles/lib/__init__.py +++ b/openlp/plugins/bibles/lib/__init__.py @@ -60,6 +60,7 @@ class DisplayStyle(object): Round = 1 Curly = 2 Square = 3 + NoDisplay = 4 class LanguageSelection(object): diff --git a/openlp/plugins/bibles/lib/biblestab.py b/openlp/plugins/bibles/lib/biblestab.py index 17182d7e2..6dc831602 100644 --- a/openlp/plugins/bibles/lib/biblestab.py +++ b/openlp/plugins/bibles/lib/biblestab.py @@ -64,7 +64,7 @@ class BiblesTab(SettingsTab): self.display_style_label = QtGui.QLabel(self.verse_display_group_box) self.display_style_label.setObjectName(u'display_style_label') self.display_style_combo_box = QtGui.QComboBox(self.verse_display_group_box) - self.display_style_combo_box.addItems([u'', u'', u'', u'']) + self.display_style_combo_box.addItems([u'', u'', u'', u'', u'']) self.display_style_combo_box.setObjectName(u'display_style_combo_box') self.verse_display_layout.addRow(self.display_style_label, self.display_style_combo_box) self.layout_style_label = QtGui.QLabel(self.verse_display_group_box) @@ -171,6 +171,8 @@ class BiblesTab(SettingsTab): translate('BiblesPlugin.BiblesTab', '{ And }')) self.display_style_combo_box.setItemText(DisplayStyle.Square, translate('BiblesPlugin.BiblesTab', '[ And ]')) + self.display_style_combo_box.setItemText(DisplayStyle.NoDisplay, + translate('BiblesPlugin.BiblesTab', 'Hide verse numbers')) self.change_note_label.setText(translate('BiblesPlugin.BiblesTab', 'Note:\nChanges do not affect verses already in the service.')) self.bible_second_check_box.setText(translate('BiblesPlugin.BiblesTab', 'Display second Bible verses')) diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index b67bb99b2..ba7129a99 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -949,10 +949,12 @@ class BibleMediaItem(MediaManagerItem): verse_text = unicode(verse) if self.settings.display_style == DisplayStyle.Round: return u'{su}(%s){/su}' % verse_text - if self.settings.display_style == DisplayStyle.Curly: + elif self.settings.display_style == DisplayStyle.Curly: return u'{su}{%s}{/su}' % verse_text - if self.settings.display_style == DisplayStyle.Square: + elif self.settings.display_style == DisplayStyle.Square: return u'{su}[%s]{/su}' % verse_text + else: + return u'{su}{/su}' return u'{su}%s{/su}' % verse_text def search(self, string, showError): From 77a4b0986a33246b1b791d49a4b0a66e62077cfa Mon Sep 17 00:00:00 2001 From: Oliver Wieland Date: Sat, 10 Aug 2013 11:17:44 +0200 Subject: [PATCH 06/93] 'formatVerse' now returns an empty string instead of '{su}{/su}' --- openlp/.version | 2 +- openlp/plugins/bibles/lib/mediaitem.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openlp/.version b/openlp/.version index 3245d0c77..8b707d904 100644 --- a/openlp/.version +++ b/openlp/.version @@ -1 +1 @@ -2.1.0-bzr2234 +2.2.2-bzr2285 diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index ba7129a99..1af97b5df 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -954,7 +954,7 @@ class BibleMediaItem(MediaManagerItem): elif self.settings.display_style == DisplayStyle.Square: return u'{su}[%s]{/su}' % verse_text else: - return u'{su}{/su}' + return u'' return u'{su}%s{/su}' % verse_text def search(self, string, showError): From 6bebf6a4076abc3c49856f96e1506ed22f1fef87 Mon Sep 17 00:00:00 2001 From: Oliver Wieland Date: Sat, 10 Aug 2013 16:51:01 +0200 Subject: [PATCH 07/93] Option "No brackets" didn't work anymore - fixed --- openlp/.version | 2 +- openlp/plugins/bibles/lib/mediaitem.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openlp/.version b/openlp/.version index 8b707d904..3245d0c77 100644 --- a/openlp/.version +++ b/openlp/.version @@ -1 +1 @@ -2.2.2-bzr2285 +2.1.0-bzr2234 diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index 1af97b5df..837e5062b 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -953,7 +953,7 @@ class BibleMediaItem(MediaManagerItem): return u'{su}{%s}{/su}' % verse_text elif self.settings.display_style == DisplayStyle.Square: return u'{su}[%s]{/su}' % verse_text - else: + elif self.settings.display_style == DisplayStyle.NoDisplay: return u'' return u'{su}%s{/su}' % verse_text From e474a3ce7d7c777f18f0b4fc00a321eac3396306 Mon Sep 17 00:00:00 2001 From: Oliver Wieland Date: Sun, 11 Aug 2013 16:43:27 +0200 Subject: [PATCH 08/93] new checkbox "Display verse numbers" instead of option "No Display" in display_style_combo_box --- openlp/plugins/bibles/bibleplugin.py | 1 + openlp/plugins/bibles/lib/__init__.py | 1 - openlp/plugins/bibles/lib/biblestab.py | 30 +++++++++++++++++++++++--- openlp/plugins/bibles/lib/mediaitem.py | 27 ++++++++++++----------- 4 files changed, 42 insertions(+), 17 deletions(-) diff --git a/openlp/plugins/bibles/bibleplugin.py b/openlp/plugins/bibles/bibleplugin.py index e2888d67a..db1a47a42 100644 --- a/openlp/plugins/bibles/bibleplugin.py +++ b/openlp/plugins/bibles/bibleplugin.py @@ -48,6 +48,7 @@ __default_settings__ = { u'bibles/verse layout style': LayoutStyle.VersePerSlide, u'bibles/book name language': LanguageSelection.Bible, u'bibles/display brackets': DisplayStyle.NoBrackets, + u'bibles/display verse': True, u'bibles/display new chapter': False, u'bibles/second bibles': True, u'bibles/advanced bible': u'', diff --git a/openlp/plugins/bibles/lib/__init__.py b/openlp/plugins/bibles/lib/__init__.py index 1935b53f2..371632d41 100644 --- a/openlp/plugins/bibles/lib/__init__.py +++ b/openlp/plugins/bibles/lib/__init__.py @@ -60,7 +60,6 @@ class DisplayStyle(object): Round = 1 Curly = 2 Square = 3 - NoDisplay = 4 class LanguageSelection(object): diff --git a/openlp/plugins/bibles/lib/biblestab.py b/openlp/plugins/bibles/lib/biblestab.py index 6dc831602..190b0d639 100644 --- a/openlp/plugins/bibles/lib/biblestab.py +++ b/openlp/plugins/bibles/lib/biblestab.py @@ -58,13 +58,16 @@ class BiblesTab(SettingsTab): self.verse_display_group_box.setObjectName(u'verse_display_group_box') self.verse_display_layout = QtGui.QFormLayout(self.verse_display_group_box) self.verse_display_layout.setObjectName(u'verse_display_layout') + self.display_verse_check_box = QtGui.QCheckBox(self.verse_display_group_box) + self.display_verse_check_box.setObjectName(u'verse_display_check_box') + self.verse_display_layout.addRow(self.display_verse_check_box) self.new_chapters_check_box = QtGui.QCheckBox(self.verse_display_group_box) self.new_chapters_check_box.setObjectName(u'new_chapters_check_box') self.verse_display_layout.addRow(self.new_chapters_check_box) self.display_style_label = QtGui.QLabel(self.verse_display_group_box) self.display_style_label.setObjectName(u'display_style_label') self.display_style_combo_box = QtGui.QComboBox(self.verse_display_group_box) - self.display_style_combo_box.addItems([u'', u'', u'', u'', u'']) + self.display_style_combo_box.addItems([u'', u'', u'', u'']) self.display_style_combo_box.setObjectName(u'display_style_combo_box') self.verse_display_layout.addRow(self.display_style_label, self.display_style_combo_box) self.layout_style_label = QtGui.QLabel(self.verse_display_group_box) @@ -134,6 +137,7 @@ class BiblesTab(SettingsTab): self.left_layout.addStretch() self.right_layout.addStretch() # Signals and slots + self.display_verse_check_box.stateChanged.connect(self.on_display_verse_check_box_changed) self.new_chapters_check_box.stateChanged.connect(self.on_new_chapters_check_box_changed) self.display_style_combo_box.activated.connect(self.on_display_style_combo_box_changed) self.bible_theme_combo_box.activated.connect(self.on_bible_theme_combo_box_changed) @@ -156,6 +160,7 @@ class BiblesTab(SettingsTab): def retranslateUi(self): self.verse_display_group_box.setTitle(translate('BiblesPlugin.BiblesTab', 'Verse Display')) + self.display_verse_check_box.setText(translate('BiblesPlugin.BiblesTab', 'Display verse numbers')) self.new_chapters_check_box.setText(translate('BiblesPlugin.BiblesTab', 'Only show new chapter numbers')) self.layout_style_label.setText(UiStrings().LayoutStyle) self.display_style_label.setText(UiStrings().DisplayStyle) @@ -171,8 +176,6 @@ class BiblesTab(SettingsTab): translate('BiblesPlugin.BiblesTab', '{ And }')) self.display_style_combo_box.setItemText(DisplayStyle.Square, translate('BiblesPlugin.BiblesTab', '[ And ]')) - self.display_style_combo_box.setItemText(DisplayStyle.NoDisplay, - translate('BiblesPlugin.BiblesTab', 'Hide verse numbers')) self.change_note_label.setText(translate('BiblesPlugin.BiblesTab', 'Note:\nChanges do not affect verses already in the service.')) self.bible_second_check_box.setText(translate('BiblesPlugin.BiblesTab', 'Display second Bible verses')) @@ -210,6 +213,14 @@ class BiblesTab(SettingsTab): def on_language_selection_combo_box_changed(self): self.language_selection = self.language_selection_combo_box.currentIndex() + def on_display_verse_check_box_changed(self, check_state): + self.display_verse = False + # We have a set value convert to True/False. + if check_state == QtCore.Qt.Checked: + self.display_verse = True + + self.check_display_verse() + def on_new_chapters_check_box_changed(self, check_state): self.show_new_chapters = False # We have a set value convert to True/False. @@ -301,11 +312,14 @@ class BiblesTab(SettingsTab): def load(self): settings = Settings() settings.beginGroup(self.settings_section) + self.display_verse = settings.value(u'display verse') self.show_new_chapters = settings.value(u'display new chapter') self.display_style = settings.value(u'display brackets') self.layout_style = settings.value(u'verse layout style') self.bible_theme = settings.value(u'bible theme') self.second_bibles = settings.value(u'second bibles') + self.display_verse_check_box.setChecked(self.display_verse) + self.check_display_verse() self.new_chapters_check_box.setChecked(self.show_new_chapters) self.display_style_combo_box.setCurrentIndex(self.display_style) self.layout_style_combo_box.setCurrentIndex(self.layout_style) @@ -353,6 +367,7 @@ class BiblesTab(SettingsTab): def save(self): settings = Settings() settings.beginGroup(self.settings_section) + settings.setValue(u'display verse', self.display_verse) settings.setValue(u'display new chapter', self.show_new_chapters) settings.setValue(u'display brackets', self.display_style) settings.setValue(u'verse layout style', self.layout_style) @@ -407,3 +422,12 @@ class BiblesTab(SettingsTab): color.setAlpha(128) palette.setColor(QtGui.QPalette.Active, QtGui.QPalette.Text, color) return palette + + def check_display_verse(self): + """ + Enables / Disables verse settings dependent on display_verse + """ + self.new_chapters_check_box.setEnabled(self.display_verse) + self.display_style_label.setEnabled(self.display_verse) + self.display_style_combo_box.setEnabled(self.display_verse) + diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index 837e5062b..217a7487f 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -943,19 +943,20 @@ class BibleMediaItem(MediaManagerItem): The verse number (int). """ verse_separator = get_reference_separator(u'sep_v_display') - if not self.settings.show_new_chapters or old_chapter != chapter: - verse_text = unicode(chapter) + verse_separator + unicode(verse) - else: - verse_text = unicode(verse) - if self.settings.display_style == DisplayStyle.Round: - return u'{su}(%s){/su}' % verse_text - elif self.settings.display_style == DisplayStyle.Curly: - return u'{su}{%s}{/su}' % verse_text - elif self.settings.display_style == DisplayStyle.Square: - return u'{su}[%s]{/su}' % verse_text - elif self.settings.display_style == DisplayStyle.NoDisplay: - return u'' - return u'{su}%s{/su}' % verse_text + + if self.settings.display_verse: + if not self.settings.show_new_chapters or old_chapter != chapter: + verse_text = unicode(chapter) + verse_separator + unicode(verse) + else: + verse_text = unicode(verse) + if self.settings.display_style == DisplayStyle.Round: + return u'{su}(%s){/su}' % verse_text + elif self.settings.display_style == DisplayStyle.Curly: + return u'{su}{%s}{/su}' % verse_text + elif self.settings.display_style == DisplayStyle.Square: + return u'{su}[%s]{/su}' % verse_text + return u'{su}%s{/su}' % verse_text + return u'' def search(self, string, showError): """ From 1fb0048def598c580b85fd6a695eba84e0f82719 Mon Sep 17 00:00:00 2001 From: Philip Ridout Date: Tue, 13 Aug 2013 21:51:52 +0100 Subject: [PATCH 09/93] added fallback to retieve_windows encoding --- .../plugins/songs/lib/songshowplusimport.py | 32 ++++++++++++------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/openlp/plugins/songs/lib/songshowplusimport.py b/openlp/plugins/songs/lib/songshowplusimport.py index ca08e9002..c9ca2a345 100644 --- a/openlp/plugins/songs/lib/songshowplusimport.py +++ b/openlp/plugins/songs/lib/songshowplusimport.py @@ -135,41 +135,41 @@ class SongShowPlusImport(SongImport): log.debug(length_descriptor_size) data = song_data.read(length_descriptor) if block_key == TITLE: - self.title = unicode(data, chardet.detect(data)['encoding']) + self.title = self.decode(data) elif block_key == AUTHOR: authors = data.split(" / ") for author in authors: if author.find(",") !=-1: authorParts = author.split(", ") author = authorParts[1] + " " + authorParts[0] - self.parse_author(unicode(author, chardet.detect(data)['encoding'])) + self.parse_author(self.decode(author)) elif block_key == COPYRIGHT: - self.addCopyright(unicode(data, chardet.detect(data)['encoding'])) + self.addCopyright(self.decode(data)) elif block_key == CCLI_NO: self.ccliNumber = int(data) elif block_key == VERSE: - self.addVerse(unicode(data, chardet.detect(data)['encoding']), "%s%s" % (VerseType.tags[VerseType.Verse], verse_no)) + self.addVerse(self.decode(data), "%s%s" % (VerseType.tags[VerseType.Verse], verse_no)) elif block_key == CHORUS: - self.addVerse(unicode(data, chardet.detect(data)['encoding']), "%s%s" % (VerseType.tags[VerseType.Chorus], verse_no)) + self.addVerse(self.decode(data), "%s%s" % (VerseType.tags[VerseType.Chorus], verse_no)) elif block_key == BRIDGE: - self.addVerse(unicode(data, chardet.detect(data)['encoding']), "%s%s" % (VerseType.tags[VerseType.Bridge], verse_no)) + self.addVerse(self.decode(data), "%s%s" % (VerseType.tags[VerseType.Bridge], verse_no)) elif block_key == TOPIC: - self.topics.append(unicode(data, chardet.detect(data)['encoding'])) + self.topics.append(self.decode(data)) elif block_key == COMMENTS: - self.comments = unicode(data, chardet.detect(data)['encoding']) + self.comments = self.decode(data) elif block_key == VERSE_ORDER: verse_tag = self.to_openlp_verse_tag(data, True) if verse_tag: if not isinstance(verse_tag, unicode): - verse_tag = unicode(verse_tag, chardet.detect(data)['encoding']) + verse_tag = self.decode(verse_tag) self.ssp_verse_order_list.append(verse_tag) elif block_key == SONG_BOOK: - self.songBookName = unicode(data, chardet.detect(data)['encoding']) + self.songBookName = self.decode(data) elif block_key == SONG_NUMBER: self.songNumber = ord(data) elif block_key == CUSTOM_VERSE: verse_tag = self.to_openlp_verse_tag(verse_name) - self.addVerse(unicode(data, chardet.detect(data)['encoding']), verse_tag) + self.addVerse(self.decode(data), verse_tag) else: log.debug("Unrecognised blockKey: %s, data: %s" % (block_key, data)) song_data.seek(next_block_starts) @@ -207,3 +207,13 @@ class SongShowPlusImport(SongImport): verse_tag = VerseType.tags[VerseType.Other] verse_number = self.other_list[verse_name] return verse_tag + verse_number + + def decode(self, data): + try: + return unicode(data, chardet.detect(data)['encoding']) + except: + while True: + try: + return unicode(data, self.encoding) + except: + self.encoding = retrieve_windows_encoding() \ No newline at end of file From cd8f1e29b1b505c027962a5e4554c19a62cf73e9 Mon Sep 17 00:00:00 2001 From: Oliver Wieland Date: Fri, 16 Aug 2013 22:41:06 +0200 Subject: [PATCH 10/93] changed variable and property names for consistency added test functions for bibles/versereferencelist --- openlp/plugins/bibles/bibleplugin.py | 2 +- openlp/plugins/bibles/lib/biblestab.py | 37 ++++---- openlp/plugins/bibles/lib/mediaitem.py | 2 +- .../openlp_plugins/bibles/__init__.py | 0 .../bibles/test_versereferencelist.py | 84 +++++++++++++++++++ 5 files changed, 104 insertions(+), 21 deletions(-) create mode 100644 tests/functional/openlp_plugins/bibles/__init__.py create mode 100644 tests/functional/openlp_plugins/bibles/test_versereferencelist.py diff --git a/openlp/plugins/bibles/bibleplugin.py b/openlp/plugins/bibles/bibleplugin.py index db1a47a42..ac41e4468 100644 --- a/openlp/plugins/bibles/bibleplugin.py +++ b/openlp/plugins/bibles/bibleplugin.py @@ -48,7 +48,7 @@ __default_settings__ = { u'bibles/verse layout style': LayoutStyle.VersePerSlide, u'bibles/book name language': LanguageSelection.Bible, u'bibles/display brackets': DisplayStyle.NoBrackets, - u'bibles/display verse': True, + u'bibles/verse number display': True, u'bibles/display new chapter': False, u'bibles/second bibles': True, u'bibles/advanced bible': u'', diff --git a/openlp/plugins/bibles/lib/biblestab.py b/openlp/plugins/bibles/lib/biblestab.py index 190b0d639..ad7ef403c 100644 --- a/openlp/plugins/bibles/lib/biblestab.py +++ b/openlp/plugins/bibles/lib/biblestab.py @@ -58,9 +58,9 @@ class BiblesTab(SettingsTab): self.verse_display_group_box.setObjectName(u'verse_display_group_box') self.verse_display_layout = QtGui.QFormLayout(self.verse_display_group_box) self.verse_display_layout.setObjectName(u'verse_display_layout') - self.display_verse_check_box = QtGui.QCheckBox(self.verse_display_group_box) - self.display_verse_check_box.setObjectName(u'verse_display_check_box') - self.verse_display_layout.addRow(self.display_verse_check_box) + self.verse_number_display_check_box = QtGui.QCheckBox(self.verse_display_group_box) + self.verse_number_display_check_box.setObjectName(u'verse_display_check_box') + self.verse_display_layout.addRow(self.verse_number_display_check_box) self.new_chapters_check_box = QtGui.QCheckBox(self.verse_display_group_box) self.new_chapters_check_box.setObjectName(u'new_chapters_check_box') self.verse_display_layout.addRow(self.new_chapters_check_box) @@ -137,7 +137,7 @@ class BiblesTab(SettingsTab): self.left_layout.addStretch() self.right_layout.addStretch() # Signals and slots - self.display_verse_check_box.stateChanged.connect(self.on_display_verse_check_box_changed) + self.verse_number_display_check_box.stateChanged.connect(self.on_verse_number_display_check_box_changed) self.new_chapters_check_box.stateChanged.connect(self.on_new_chapters_check_box_changed) self.display_style_combo_box.activated.connect(self.on_display_style_combo_box_changed) self.bible_theme_combo_box.activated.connect(self.on_bible_theme_combo_box_changed) @@ -160,7 +160,7 @@ class BiblesTab(SettingsTab): def retranslateUi(self): self.verse_display_group_box.setTitle(translate('BiblesPlugin.BiblesTab', 'Verse Display')) - self.display_verse_check_box.setText(translate('BiblesPlugin.BiblesTab', 'Display verse numbers')) + self.verse_number_display_check_box.setText(translate('BiblesPlugin.BiblesTab', 'Display verse numbers')) self.new_chapters_check_box.setText(translate('BiblesPlugin.BiblesTab', 'Only show new chapter numbers')) self.layout_style_label.setText(UiStrings().LayoutStyle) self.display_style_label.setText(UiStrings().DisplayStyle) @@ -213,13 +213,12 @@ class BiblesTab(SettingsTab): def on_language_selection_combo_box_changed(self): self.language_selection = self.language_selection_combo_box.currentIndex() - def on_display_verse_check_box_changed(self, check_state): - self.display_verse = False + def on_verse_number_display_check_box_changed(self, check_state): + self.verse_number_display = False # We have a set value convert to True/False. if check_state == QtCore.Qt.Checked: - self.display_verse = True - - self.check_display_verse() + self.verse_number_display = True + self.check_verse_number_display() def on_new_chapters_check_box_changed(self, check_state): self.show_new_chapters = False @@ -312,14 +311,14 @@ class BiblesTab(SettingsTab): def load(self): settings = Settings() settings.beginGroup(self.settings_section) - self.display_verse = settings.value(u'display verse') + self.verse_number_display = settings.value(u'verse number display') self.show_new_chapters = settings.value(u'display new chapter') self.display_style = settings.value(u'display brackets') self.layout_style = settings.value(u'verse layout style') self.bible_theme = settings.value(u'bible theme') self.second_bibles = settings.value(u'second bibles') - self.display_verse_check_box.setChecked(self.display_verse) - self.check_display_verse() + self.verse_number_display_check_box.setChecked(self.verse_number_display) + self.check_verse_number_display() self.new_chapters_check_box.setChecked(self.show_new_chapters) self.display_style_combo_box.setCurrentIndex(self.display_style) self.layout_style_combo_box.setCurrentIndex(self.layout_style) @@ -367,7 +366,7 @@ class BiblesTab(SettingsTab): def save(self): settings = Settings() settings.beginGroup(self.settings_section) - settings.setValue(u'display verse', self.display_verse) + settings.setValue(u'verse number display', self.verse_number_display) settings.setValue(u'display new chapter', self.show_new_chapters) settings.setValue(u'display brackets', self.display_style) settings.setValue(u'verse layout style', self.layout_style) @@ -423,11 +422,11 @@ class BiblesTab(SettingsTab): palette.setColor(QtGui.QPalette.Active, QtGui.QPalette.Text, color) return palette - def check_display_verse(self): + def check_verse_number_display(self): """ - Enables / Disables verse settings dependent on display_verse + Enables / Disables verse settings dependent on verse_number_display """ - self.new_chapters_check_box.setEnabled(self.display_verse) - self.display_style_label.setEnabled(self.display_verse) - self.display_style_combo_box.setEnabled(self.display_verse) + self.new_chapters_check_box.setEnabled(self.verse_number_display) + self.display_style_label.setEnabled(self.verse_number_display) + self.display_style_combo_box.setEnabled(self.verse_number_display) diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index 217a7487f..805160c28 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -944,7 +944,7 @@ class BibleMediaItem(MediaManagerItem): """ verse_separator = get_reference_separator(u'sep_v_display') - if self.settings.display_verse: + if self.settings.verse_number_display: if not self.settings.show_new_chapters or old_chapter != chapter: verse_text = unicode(chapter) + verse_separator + unicode(verse) else: diff --git a/tests/functional/openlp_plugins/bibles/__init__.py b/tests/functional/openlp_plugins/bibles/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/functional/openlp_plugins/bibles/test_versereferencelist.py b/tests/functional/openlp_plugins/bibles/test_versereferencelist.py new file mode 100644 index 000000000..9ab36e571 --- /dev/null +++ b/tests/functional/openlp_plugins/bibles/test_versereferencelist.py @@ -0,0 +1,84 @@ +""" +This module contains tests for the versereferencelist submodule of the Bibles plugin. +""" + +from unittest import TestCase +from openlp.plugins.bibles.lib.versereferencelist import VerseReferenceList + +class TestVerseReferenceList(TestCase): + def setUp(self): + self.reference_list = VerseReferenceList() + + def add_test(self): + """ + Test the addition of verses to the list + """ + + #GIVEN: book, chapter, verse and version + book = u'testBook' + chapter = 1 + verse = 1 + version = u'testVersion' + copyright = u'testCopyright' + permission = u'testPermision' + + #WHEN: We add it to the verse list + self.reference_list.add(book, chapter, verse, version, copyright, permission) + + #THEN: The entries should be in the first entry of the list + self.assertEqual(self.reference_list.current_index, 0, u'The current index should be 0') + self.assertEqual(self.reference_list.verse_list[0][u'book'], book, u'The book in first entry should be %s' %book) + self.assertEqual(self.reference_list.verse_list[0][u'chapter'], chapter, u'The chapter in first entry should be %u' %chapter) + self.assertEqual(self.reference_list.verse_list[0][u'start'], verse, u'The start in first entry should be %u' %verse) + self.assertEqual(self.reference_list.verse_list[0][u'version'], version, u'The version in first entry should be %s' %version) + self.assertEqual(self.reference_list.verse_list[0][u'end'], verse, u'The end in first entry should be %u' %verse) + + #GIVEN: next verse + verse = 2 + + #WHEN: We add it to the verse list + self.reference_list.add(book, chapter, verse, version, copyright, permission) + + #THEN: The current index should be 0 and the end pointer of the entry should be '2' + self.assertEqual(self.reference_list.current_index, 0, u'The current index should be 0') + self.assertEqual(self.reference_list.verse_list[0][u'end'], verse, u'The end in first entry should be %u' %verse) + + #GIVEN: a verse in another book + book = u'testBook2' + chapter = 2 + verse = 5 + + #WHEN: We add it to the verse list + self.reference_list.add(book, chapter, verse, version, copyright, permission) + + #THEN: the current index should be 1 + self.assertEqual(self.reference_list.current_index, 1, u'The current index should be 1') + + def add_version_test(self): + """ + Test the addition of versions to the list + """ + #GIVEN: version, copyright and permission + version = u'testVersion' + copyright = u'testCopyright' + permission = u'testPermision' + + + #WHEN: a not existing version will be added + self.reference_list.add_version(version, copyright, permission) + + #THEN: the data will be appended to the list + self.assertEqual(self.reference_list.version_list[0], {u'version': version, u'copyright': copyright, u'permission': permission}, + u'The version data should be appended') + + + #GIVEN: old length of the array + oldLen = self.reference_list.version_list.__len__() + + #WHEN: an existing version will be added + self.reference_list.add_version(version, copyright, permission) + + #THEN: the data will not be appended to the list + self.assertEqual(self.reference_list.version_list.__len__(), oldLen, u'The version data should not be appended') + + From 74572b07fdd03eb2bd803067cae92f2576e4da75 Mon Sep 17 00:00:00 2001 From: Oliver Wieland Date: Sat, 17 Aug 2013 00:14:27 +0200 Subject: [PATCH 11/93] Changed variable names Doing some styling changes --- openlp/plugins/bibles/bibleplugin.py | 2 +- openlp/plugins/bibles/lib/biblestab.py | 36 ++--- openlp/plugins/bibles/lib/mediaitem.py | 3 +- .../bibles/test_versereferencelist.py | 126 +++++++++++------- 4 files changed, 99 insertions(+), 68 deletions(-) diff --git a/openlp/plugins/bibles/bibleplugin.py b/openlp/plugins/bibles/bibleplugin.py index ac41e4468..a638ecb12 100644 --- a/openlp/plugins/bibles/bibleplugin.py +++ b/openlp/plugins/bibles/bibleplugin.py @@ -48,7 +48,7 @@ __default_settings__ = { u'bibles/verse layout style': LayoutStyle.VersePerSlide, u'bibles/book name language': LanguageSelection.Bible, u'bibles/display brackets': DisplayStyle.NoBrackets, - u'bibles/verse number display': True, + u'bibles/is verse number visible': True, u'bibles/display new chapter': False, u'bibles/second bibles': True, u'bibles/advanced bible': u'', diff --git a/openlp/plugins/bibles/lib/biblestab.py b/openlp/plugins/bibles/lib/biblestab.py index ad7ef403c..72d222d8e 100644 --- a/openlp/plugins/bibles/lib/biblestab.py +++ b/openlp/plugins/bibles/lib/biblestab.py @@ -58,9 +58,9 @@ class BiblesTab(SettingsTab): self.verse_display_group_box.setObjectName(u'verse_display_group_box') self.verse_display_layout = QtGui.QFormLayout(self.verse_display_group_box) self.verse_display_layout.setObjectName(u'verse_display_layout') - self.verse_number_display_check_box = QtGui.QCheckBox(self.verse_display_group_box) - self.verse_number_display_check_box.setObjectName(u'verse_display_check_box') - self.verse_display_layout.addRow(self.verse_number_display_check_box) + self.is_verse_number_visible_check_box = QtGui.QCheckBox(self.verse_display_group_box) + self.is_verse_number_visible_check_box.setObjectName(u'verse_display_check_box') + self.verse_display_layout.addRow(self.is_verse_number_visible_check_box) self.new_chapters_check_box = QtGui.QCheckBox(self.verse_display_group_box) self.new_chapters_check_box.setObjectName(u'new_chapters_check_box') self.verse_display_layout.addRow(self.new_chapters_check_box) @@ -137,7 +137,7 @@ class BiblesTab(SettingsTab): self.left_layout.addStretch() self.right_layout.addStretch() # Signals and slots - self.verse_number_display_check_box.stateChanged.connect(self.on_verse_number_display_check_box_changed) + self.is_verse_number_visible_check_box.stateChanged.connect(self.on_is_verse_number_visible_check_box_changed) self.new_chapters_check_box.stateChanged.connect(self.on_new_chapters_check_box_changed) self.display_style_combo_box.activated.connect(self.on_display_style_combo_box_changed) self.bible_theme_combo_box.activated.connect(self.on_bible_theme_combo_box_changed) @@ -160,7 +160,7 @@ class BiblesTab(SettingsTab): def retranslateUi(self): self.verse_display_group_box.setTitle(translate('BiblesPlugin.BiblesTab', 'Verse Display')) - self.verse_number_display_check_box.setText(translate('BiblesPlugin.BiblesTab', 'Display verse numbers')) + self.is_verse_number_visible_check_box.setText(translate('BiblesPlugin.BiblesTab', 'Is verse number visible')) self.new_chapters_check_box.setText(translate('BiblesPlugin.BiblesTab', 'Only show new chapter numbers')) self.layout_style_label.setText(UiStrings().LayoutStyle) self.display_style_label.setText(UiStrings().DisplayStyle) @@ -213,12 +213,12 @@ class BiblesTab(SettingsTab): def on_language_selection_combo_box_changed(self): self.language_selection = self.language_selection_combo_box.currentIndex() - def on_verse_number_display_check_box_changed(self, check_state): - self.verse_number_display = False + def on_is_verse_number_visible_check_box_changed(self, check_state): + self.is_verse_number_visible = False # We have a set value convert to True/False. if check_state == QtCore.Qt.Checked: - self.verse_number_display = True - self.check_verse_number_display() + self.is_verse_number_visible = True + self.check_is_verse_number_visible() def on_new_chapters_check_box_changed(self, check_state): self.show_new_chapters = False @@ -311,14 +311,14 @@ class BiblesTab(SettingsTab): def load(self): settings = Settings() settings.beginGroup(self.settings_section) - self.verse_number_display = settings.value(u'verse number display') + self.is_verse_number_visible = settings.value(u'is verse number visible') self.show_new_chapters = settings.value(u'display new chapter') self.display_style = settings.value(u'display brackets') self.layout_style = settings.value(u'verse layout style') self.bible_theme = settings.value(u'bible theme') self.second_bibles = settings.value(u'second bibles') - self.verse_number_display_check_box.setChecked(self.verse_number_display) - self.check_verse_number_display() + self.is_verse_number_visible_check_box.setChecked(self.is_verse_number_visible) + self.check_is_verse_number_visible() self.new_chapters_check_box.setChecked(self.show_new_chapters) self.display_style_combo_box.setCurrentIndex(self.display_style) self.layout_style_combo_box.setCurrentIndex(self.layout_style) @@ -366,7 +366,7 @@ class BiblesTab(SettingsTab): def save(self): settings = Settings() settings.beginGroup(self.settings_section) - settings.setValue(u'verse number display', self.verse_number_display) + settings.setValue(u'is verse number visible', self.is_verse_number_visible) settings.setValue(u'display new chapter', self.show_new_chapters) settings.setValue(u'display brackets', self.display_style) settings.setValue(u'verse layout style', self.layout_style) @@ -422,11 +422,11 @@ class BiblesTab(SettingsTab): palette.setColor(QtGui.QPalette.Active, QtGui.QPalette.Text, color) return palette - def check_verse_number_display(self): + def check_is_verse_number_visible(self): """ - Enables / Disables verse settings dependent on verse_number_display + Enables / Disables verse settings dependent on is_verse_number_visible """ - self.new_chapters_check_box.setEnabled(self.verse_number_display) - self.display_style_label.setEnabled(self.verse_number_display) - self.display_style_combo_box.setEnabled(self.verse_number_display) + self.new_chapters_check_box.setEnabled(self.is_verse_number_visible) + self.display_style_label.setEnabled(self.is_verse_number_visible) + self.display_style_combo_box.setEnabled(self.is_verse_number_visible) diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index 805160c28..c2f444768 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -943,8 +943,7 @@ class BibleMediaItem(MediaManagerItem): The verse number (int). """ verse_separator = get_reference_separator(u'sep_v_display') - - if self.settings.verse_number_display: + if self.settings.is_verse_number_visible: if not self.settings.show_new_chapters or old_chapter != chapter: verse_text = unicode(chapter) + verse_separator + unicode(verse) else: diff --git a/tests/functional/openlp_plugins/bibles/test_versereferencelist.py b/tests/functional/openlp_plugins/bibles/test_versereferencelist.py index 9ab36e571..25b620964 100644 --- a/tests/functional/openlp_plugins/bibles/test_versereferencelist.py +++ b/tests/functional/openlp_plugins/bibles/test_versereferencelist.py @@ -1,20 +1,21 @@ """ This module contains tests for the versereferencelist submodule of the Bibles plugin. """ - from unittest import TestCase from openlp.plugins.bibles.lib.versereferencelist import VerseReferenceList class TestVerseReferenceList(TestCase): def setUp(self): - self.reference_list = VerseReferenceList() - - def add_test(self): """ - Test the addition of verses to the list + Initializes all we need """ - #GIVEN: book, chapter, verse and version + def add_first_verse_test(self): + """ + Test the addition of a verse to the empty list + """ + # GIVEN: an empty list + reference_list = VerseReferenceList() book = u'testBook' chapter = 1 verse = 1 @@ -22,63 +23,94 @@ class TestVerseReferenceList(TestCase): copyright = u'testCopyright' permission = u'testPermision' - #WHEN: We add it to the verse list - self.reference_list.add(book, chapter, verse, version, copyright, permission) + # WHEN: We add it to the verse list + reference_list.add(book, chapter, verse, version, copyright, permission) - #THEN: The entries should be in the first entry of the list - self.assertEqual(self.reference_list.current_index, 0, u'The current index should be 0') - self.assertEqual(self.reference_list.verse_list[0][u'book'], book, u'The book in first entry should be %s' %book) - self.assertEqual(self.reference_list.verse_list[0][u'chapter'], chapter, u'The chapter in first entry should be %u' %chapter) - self.assertEqual(self.reference_list.verse_list[0][u'start'], verse, u'The start in first entry should be %u' %verse) - self.assertEqual(self.reference_list.verse_list[0][u'version'], version, u'The version in first entry should be %s' %version) - self.assertEqual(self.reference_list.verse_list[0][u'end'], verse, u'The end in first entry should be %u' %verse) + # THEN: The entries should be in the first entry of the list + self.assertEqual(reference_list.current_index, 0, u'The current index should be 0') + self.assertEqual(reference_list.verse_list[0][u'book'], book, u'The book in first entry should be %s' % book) + self.assertEqual(reference_list.verse_list[0][u'chapter'], chapter, u'The chapter in first entry should be %u' % chapter) + self.assertEqual(reference_list.verse_list[0][u'start'], verse, u'The start in first entry should be %u' % verse) + self.assertEqual(reference_list.verse_list[0][u'version'], version, u'The version in first entry should be %s' % version) + self.assertEqual(reference_list.verse_list[0][u'end'], verse, u'The end in first entry should be %u' % verse) - #GIVEN: next verse - verse = 2 + def add_next_verse_test(self): + """ + Test the addition of the following verse + """ + # GIVEN: 1 line in the list of verses + book = u'testBook' + chapter = 1 + verse = 1 + next_verse = 2 + version = u'testVersion' + copyright = u'testCopyright' + permission = u'testPermision' + reference_list = VerseReferenceList() + reference_list.add(book, chapter, verse, version, copyright, permission) - #WHEN: We add it to the verse list - self.reference_list.add(book, chapter, verse, version, copyright, permission) + # WHEN: We add the following verse to the verse list + reference_list.add(book, chapter, next_verse, version, copyright, permission) - #THEN: The current index should be 0 and the end pointer of the entry should be '2' - self.assertEqual(self.reference_list.current_index, 0, u'The current index should be 0') - self.assertEqual(self.reference_list.verse_list[0][u'end'], verse, u'The end in first entry should be %u' %verse) + # THEN: The current index should be 0 and the end pointer of the entry should be '2' + self.assertEqual(reference_list.current_index, 0, u'The current index should be 0') + self.assertEqual(reference_list.verse_list[0][u'end'], next_verse, u'The end in first entry should be %u' % next_verse) - #GIVEN: a verse in another book - book = u'testBook2' - chapter = 2 - verse = 5 + def add_another_verse_test(self): + """ + Test the addition of a verse in another book + """ + # GIVEN: 1 line in the list of verses + book = u'testBook' + chapter = 1 + verse = 1 + next_verse = 2 + another_book = u'testBook2' + another_chapter = 2 + another_verse = 5 + version = u'testVersion' + copyright = u'testCopyright' + permission = u'testPermision' + reference_list = VerseReferenceList() + reference_list.add(book, chapter, verse, version, copyright, permission) - #WHEN: We add it to the verse list - self.reference_list.add(book, chapter, verse, version, copyright, permission) + # WHEN: We add a verse of another book to the verse list + reference_list.add(another_book, another_chapter, another_verse, version, copyright, permission) - #THEN: the current index should be 1 - self.assertEqual(self.reference_list.current_index, 1, u'The current index should be 1') + # THEN: the current index should be 1 + self.assertEqual(reference_list.current_index, 1, u'The current index should be 1') def add_version_test(self): """ - Test the addition of versions to the list + Test the addition of a version to the list """ - #GIVEN: version, copyright and permission + # GIVEN: version, copyright and permission + reference_list = VerseReferenceList() version = u'testVersion' copyright = u'testCopyright' permission = u'testPermision' - - #WHEN: a not existing version will be added - self.reference_list.add_version(version, copyright, permission) + # WHEN: a not existing version will be added + reference_list.add_version(version, copyright, permission) - #THEN: the data will be appended to the list - self.assertEqual(self.reference_list.version_list[0], {u'version': version, u'copyright': copyright, u'permission': permission}, - u'The version data should be appended') + # THEN: the data will be appended to the list + self.assertEqual(len(reference_list.version_list), 1, u'The version data should be appended') + self.assertEqual(reference_list.version_list[0], {u'version': version, u'copyright': copyright, u'permission': permission}, + u'The version data should be appended') - - #GIVEN: old length of the array - oldLen = self.reference_list.version_list.__len__() + def add_existing_version_test(self): + """ + Test the addition of an existing version to the list + """ + # GIVEN: version, copyright and permission, added to the version list + reference_list = VerseReferenceList() + version = u'testVersion' + copyright = u'testCopyright' + permission = u'testPermision' + reference_list.add_version(version, copyright, permission) - #WHEN: an existing version will be added - self.reference_list.add_version(version, copyright, permission) + # WHEN: an existing version will be added + reference_list.add_version(version, copyright, permission) - #THEN: the data will not be appended to the list - self.assertEqual(self.reference_list.version_list.__len__(), oldLen, u'The version data should not be appended') - - + # THEN: the data will not be appended to the list + self.assertEqual(len(reference_list.version_list), 1, u'The version data should not be appended') From b591a091fda5893fe0630ade3025fa84256bfbcc Mon Sep 17 00:00:00 2001 From: Oliver Wieland Date: Thu, 22 Aug 2013 20:56:11 +0200 Subject: [PATCH 12/93] Added doc string --- openlp/plugins/bibles/lib/biblestab.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/openlp/plugins/bibles/lib/biblestab.py b/openlp/plugins/bibles/lib/biblestab.py index 72d222d8e..ed3ccefa4 100644 --- a/openlp/plugins/bibles/lib/biblestab.py +++ b/openlp/plugins/bibles/lib/biblestab.py @@ -214,6 +214,9 @@ class BiblesTab(SettingsTab): self.language_selection = self.language_selection_combo_box.currentIndex() def on_is_verse_number_visible_check_box_changed(self, check_state): + """ + Event handler for the 'verse number visible' check box + """ self.is_verse_number_visible = False # We have a set value convert to True/False. if check_state == QtCore.Qt.Checked: From eb1703bcc996932b7a673c00c87079c257115e09 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Mon, 26 Aug 2013 09:11:20 +0100 Subject: [PATCH 13/93] fix up edit code --- openlp/core/ui/formattingtagform.py | 180 +++++++++------------------- 1 file changed, 55 insertions(+), 125 deletions(-) diff --git a/openlp/core/ui/formattingtagform.py b/openlp/core/ui/formattingtagform.py index df0fe5e8a..a9828d228 100644 --- a/openlp/core/ui/formattingtagform.py +++ b/openlp/core/ui/formattingtagform.py @@ -42,6 +42,16 @@ from openlp.core.lib.ui import critical_error_message_box from openlp.core.ui.formattingtagdialog import Ui_FormattingTagDialog +class EDITCOLUMN(object): + """ + Hides the magic numbers for the table columns + """ + Description = 0 + Tag = 1 + StartHtml = 2 + EndHtml = 3 + + class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog): """ The :class:`FormattingTagForm` manages the settings tab . @@ -58,7 +68,7 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog): r'|(?P\?(?:(?!\?>).)*\?)' r'|(?P!--(?:(?!-->).)*--))>', re.UNICODE) self.html_regex = re.compile(r'^(?:[^<>]*%s)*[^<>]*$' % self.html_tag_regex.pattern) - #self.tag_table_widget.itemSelectionChanged.connect(self.on_row_selected) + self.tag_table_widget.itemSelectionChanged.connect(self.on_row_selected) self.new_button.clicked.connect(self.on_new_clicked) #self.save_push_button.clicked.connect(self.on_saved_clicked) self.delete_button.clicked.connect(self.on_delete_clicked) @@ -66,7 +76,6 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog): self.button_box.rejected.connect(self.close) # Forces reloading of tags from openlp configuration. FormattingTags.load_tags() - self.pause_validation = False def exec_(self): """ @@ -80,76 +89,33 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog): """ Table Row selected so display items and set field state. """ - #self.save_push_button.setEnabled(False) - self.selected = self.tag_table_widget.currentRow() - html = FormattingTags.get_html_tags()[self.selected] - self.description_line_edit.setText(html[u'desc']) - self.tag_line_edit.setText(self._strip(html[u'start tag'])) - self.start_tag_line_edit.setText(html[u'start html']) - self.end_tag_line_edit.setText(html[u'end html']) - if html[u'protected']: - self.description_line_edit.setEnabled(False) - self.tag_line_edit.setEnabled(False) - self.start_tag_line_edit.setEnabled(False) - self.end_tag_line_edit.setEnabled(False) - self.delete_push_button.setEnabled(False) - else: - self.description_line_edit.setEnabled(True) - self.tag_line_edit.setEnabled(True) - self.start_tag_line_edit.setEnabled(True) - self.end_tag_line_edit.setEnabled(True) - self.delete_push_button.setEnabled(True) - - def on_text_edited(self, text): - """ - Enable the ``save_push_button`` when any of the selected tag's properties - has been changed. - """ - self.save_push_button.setEnabled(True) + self.delete_button.setEnabled(True) def on_new_clicked(self): """ - Add a new tag to list only if it is not a duplicate. + Add a new tag to edit list and select it for editing. """ - last_row = self.tag_table_widget.rowCount() - 1 - self.tag_table_widget.selectRow(last_row) - self.tag_table_widget.setCurrentCell(last_row, 0) - for html in FormattingTags.get_html_tags(): - if self._strip(html[u'start tag']) == u'n': - critical_error_message_box( - translate('OpenLP.FormattingTagForm', 'Update Error'), - translate('OpenLP.FormattingTagForm', 'Tag "n" already defined.')) - return - # Add new tag to list - tag = { - u'desc': translate('OpenLP.FormattingTagForm', 'New Tag'), - u'start tag': u'{n%s}' % unicode(last_row + 1), - u'start html': translate('OpenLP.FormattingTagForm', ''), - u'end tag': u'{/n%s}' % unicode(last_row + 1), - u'end html': translate('OpenLP.FormattingTagForm', ''), - u'protected': False, - u'temporary': False - } - #FormattingTags.add_html_tags([tag]) - #FormattingTags.save_html_tags() - #self._reloadTable() - # Highlight new row - #self.tag_table_widget.selectRow(self.tag_table_widget.rowCount() - 1) - #self.on_row_selected() + new_row = self.tag_table_widget.rowCount() + self.tag_table_widget.insertRow(new_row) + self.tag_table_widget.setItem(new_row, 0, + QtGui.QTableWidgetItem(translate('OpenLP.FormattingTagForm', 'New Tag'))) + self.tag_table_widget.setItem(new_row, 1, + QtGui.QTableWidgetItem('n%s' % unicode(new_row))) + self.tag_table_widget.setItem(new_row, 2, + QtGui.QTableWidgetItem(translate('OpenLP.FormattingTagForm', ''))) + self.tag_table_widget.setItem(new_row, 3, + QtGui.QTableWidgetItem(translate('OpenLP.FormattingTagForm', ''))) + self.tag_table_widget.resizeRowsToContents() self.tag_table_widget.scrollToBottom() + self.tag_table_widget.selectRow(new_row) def on_delete_clicked(self): """ - Delete selected custom tag. + Delete selected custom row. """ - if self.selected != -1: - FormattingTags.remove_html_tag(self.selected) - # As the first items are protected we should not have to take care - # of negative indexes causing tracebacks. - self.tag_table_widget.selectRow(self.selected - 1) - self.selected = -1 - FormattingTags.save_html_tags() - self._reloadTable() + selected = self.tag_table_widget.currentRow() + if selected != -1: + self.tag_table_widget.removeRow(selected) def on_saved_clicked(self): """ @@ -217,12 +183,10 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog): # only process for editable rows pre_row_item = self.tag_table_widget.item(pre_row, 0) edit_item = None - if pre_row_item and (pre_row_item.flags() & QtCore.Qt.ItemIsEditable) and not self.pause_validation: - data = self.item_to_data_dict(pre_row_item) + if pre_row_item and (pre_row_item.flags() & QtCore.Qt.ItemIsEditable): item = self.tag_table_widget.item(pre_row, pre_col) text = unicode(item.text()) - if pre_col is 0: - # Tag name edited + if pre_col is EDITCOLUMN.Tag: if text: for row in range(self.tag_table_widget.rowCount()): counting_item = self.tag_table_widget.item(row, 0) @@ -233,33 +197,10 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog): 'Tag %s is already defined. Please pick a different one.' % text), QtGui.QMessageBox.Discard|QtGui.QMessageBox.Ok) if answer == QtGui.QMessageBox.Discard: - item.setText(data.get(u'tag')) + break else: edit_item = item break - if not edit_item: - data[u'tag'] = text - data.setdefault(u'description', u'') - data.setdefault(u'html', u'') - pre_row_item.setData(QtCore.Qt.UserRole, data) - flags = self.tag_table_widget.item(pre_row, 1).flags() - if not (flags & QtCore.Qt.ItemIsEditable): - # if description cell is read only, the user is adding a new tag. - # So we add another empty row and enable editing for description and html. - new_row = self.tag_table_widget.rowCount() - self.tag_table_widget.insertRow(new_row) - for column in range(4): - new_item = QtGui.QTableWidgetItem(u'') - if column != 0: - new_item.setFlags(flags) - self.tag_table_widget.setItem(new_row, column, new_item) - for column in [1, 2]: - self.tag_table_widget.item(pre_row, column).setFlags(item.flags()) - # trigger edit as editing might have been enabled after selecting - if cur_row == pre_row and cur_col in [1, 2]: - cur_item = self.tag_table_widget.item(cur_row, cur_col) - self.tag_table_widget.editItem(cur_item) - self.tag_table_widget.resizeRowsToContents() else: answer = None if self.tag_table_widget.item(pre_row, 1).text() or self.tag_table_widget.item(pre_row, 2).text(): @@ -268,43 +209,32 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog): translate('OpenLP.FormattingTagForm', 'No tag name defined. Do you want to delete the whole tag?'), QtGui.QMessageBox.Yes|QtGui.QMessageBox.Discard|QtGui.QMessageBox.Cancel) - if answer == QtGui.QMessageBox.Discard: - item.setText(data.get(u'tag')) - if answer == QtGui.QMessageBox.Cancel: - edit_item = item + #if answer == QtGui.QMessageBox.Discard: + # item.setText(data.get(u'tag')) + #if answer == QtGui.QMessageBox.Cancel: + # edit_item = item elif pre_row < self.tag_table_widget.rowCount() - 1: self.tag_table_widget.removeRow(pre_row) - elif pre_col is 1: - # Description edited - data[u'description'] = text - pre_row_item.setData(QtCore.Qt.UserRole, data) - elif pre_col is 2: + #elif pre_col is EDITCOLUMN.StartHtml: # HTML edited - end_html = self.start_html_to_end_html(text) - if end_html is not None: - item.setToolTip(cgi.escape(text)) - if self.tag_table_widget.item(pre_row, 3) is None: - self.tag_table_widget.setItem(pre_row, 3, QtGui.QTableWidgetItem(end_html)) - else: - self.tag_table_widget.item(pre_row, 3).setText(end_html) - self.tag_table_widget.item(pre_row, 3).setToolTip(cgi.escape(end_html)) - data[u'html'] = text - pre_row_item.setData(QtCore.Qt.UserRole, data) - self.tag_table_widget.resizeRowsToContents() - elif QtGui.QMessageBox.question(self, - translate('OpenLP.FormattingTagForm', 'Validation Error'), - translate('OpenLP.FormattingTagForm', 'The entered HTML is not valid. Please enter valid HTML.'), - QtGui.QMessageBox.Ok | QtGui.QMessageBox.Cancel) == QtGui.QMessageBox.Cancel: - item.setText(data.get(u'html')) - else: - edit_item = item - if not edit_item: - # select the tag cell in a empty row - cur_row_item = self.tag_table_widget.item(cur_row, 0) - if cur_row_item and (cur_row_item.flags() & QtCore.Qt.ItemIsEditable) and cur_row_item.text().isEmpty(): - edit_item = cur_row_item - if edit_item: - self.tag_table_widget.setCurrentItem(edit_item) + #end_html = self.start_html_to_end_html(text) + #if end_html is not None: + # item.setToolTip(cgi.escape(text)) + ## if self.tag_table_widget.item(pre_row, 3) is None: + # self.tag_table_widget.setItem(pre_row, 3, QtGui.QTableWidgetItem(end_html)) + # else: + # self.tag_table_widget.item(pre_row, 3).setText(end_html) + # self.tag_table_widget.item(pre_row, 3).setToolTip(cgi.escape(end_html)) + # #data[u'html'] = text + # #pre_row_item.setData(QtCore.Qt.UserRole, data) + # # self.tag_table_widget.resizeRowsToContents() + #if not edit_item: + # # select the tag cell in a empty row + # cur_row_item = self.tag_table_widget.item(cur_row, 0) + # if cur_row_item and (cur_row_item.flags() & QtCore.Qt.ItemIsEditable) and cur_row_item.text().isEmpty(): + # edit_item = cur_row_item + #if edit_item: + # self.tag_table_widget.setCurrentItem(edit_item) # enable delete_button for editable rows cur_row = self.tag_table_widget.currentRow() cur_row_item = self.tag_table_widget.item(cur_row, 0) From 0a0d7dca84b5f578f5d566e993104230c2416097 Mon Sep 17 00:00:00 2001 From: Oliver Wieland Date: Mon, 26 Aug 2013 19:14:52 +0200 Subject: [PATCH 14/93] biblestab.py: minor changes in naming objects mediaitem.py: rearrange code for better readability --- openlp/plugins/bibles/lib/biblestab.py | 4 ++-- openlp/plugins/bibles/lib/mediaitem.py | 26 +++++++++++++------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/openlp/plugins/bibles/lib/biblestab.py b/openlp/plugins/bibles/lib/biblestab.py index ed3ccefa4..0b2d19615 100644 --- a/openlp/plugins/bibles/lib/biblestab.py +++ b/openlp/plugins/bibles/lib/biblestab.py @@ -59,7 +59,7 @@ class BiblesTab(SettingsTab): self.verse_display_layout = QtGui.QFormLayout(self.verse_display_group_box) self.verse_display_layout.setObjectName(u'verse_display_layout') self.is_verse_number_visible_check_box = QtGui.QCheckBox(self.verse_display_group_box) - self.is_verse_number_visible_check_box.setObjectName(u'verse_display_check_box') + self.is_verse_number_visible_check_box.setObjectName(u'is_verse_number_visible_check_box') self.verse_display_layout.addRow(self.is_verse_number_visible_check_box) self.new_chapters_check_box = QtGui.QCheckBox(self.verse_display_group_box) self.new_chapters_check_box.setObjectName(u'new_chapters_check_box') @@ -160,7 +160,7 @@ class BiblesTab(SettingsTab): def retranslateUi(self): self.verse_display_group_box.setTitle(translate('BiblesPlugin.BiblesTab', 'Verse Display')) - self.is_verse_number_visible_check_box.setText(translate('BiblesPlugin.BiblesTab', 'Is verse number visible')) + self.is_verse_number_visible_check_box.setText(translate('BiblesPlugin.BiblesTab', 'Show verse numbers')) self.new_chapters_check_box.setText(translate('BiblesPlugin.BiblesTab', 'Only show new chapter numbers')) self.layout_style_label.setText(UiStrings().LayoutStyle) self.display_style_label.setText(UiStrings().DisplayStyle) diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index c2f444768..682fdb7a3 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -943,19 +943,19 @@ class BibleMediaItem(MediaManagerItem): The verse number (int). """ verse_separator = get_reference_separator(u'sep_v_display') - if self.settings.is_verse_number_visible: - if not self.settings.show_new_chapters or old_chapter != chapter: - verse_text = unicode(chapter) + verse_separator + unicode(verse) - else: - verse_text = unicode(verse) - if self.settings.display_style == DisplayStyle.Round: - return u'{su}(%s){/su}' % verse_text - elif self.settings.display_style == DisplayStyle.Curly: - return u'{su}{%s}{/su}' % verse_text - elif self.settings.display_style == DisplayStyle.Square: - return u'{su}[%s]{/su}' % verse_text - return u'{su}%s{/su}' % verse_text - return u'' + if not self.settings.is_verse_number_visible: + return u'' + if not self.settings.show_new_chapters or old_chapter != chapter: + verse_text = unicode(chapter) + verse_separator + unicode(verse) + else: + verse_text = unicode(verse) + if self.settings.display_style == DisplayStyle.Round: + return u'{su}(%s){/su}' % verse_text + elif self.settings.display_style == DisplayStyle.Curly: + return u'{su}{%s}{/su}' % verse_text + elif self.settings.display_style == DisplayStyle.Square: + return u'{su}[%s]{/su}' % verse_text + return u'{su}%s{/su}' % verse_text def search(self, string, showError): """ From b3ab68b0f5e3a51fd729d60b3202b6f731f357c9 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sat, 31 Aug 2013 09:52:44 +0100 Subject: [PATCH 15/93] Validation in progress --- openlp/core/ui/formattingtagcontroller.py | 98 +++++++++++++++++++++ openlp/core/ui/formattingtagdialog.py | 66 -------------- openlp/core/ui/formattingtagform.py | 100 +++++++++------------- 3 files changed, 139 insertions(+), 125 deletions(-) create mode 100644 openlp/core/ui/formattingtagcontroller.py diff --git a/openlp/core/ui/formattingtagcontroller.py b/openlp/core/ui/formattingtagcontroller.py new file mode 100644 index 000000000..7e1131507 --- /dev/null +++ b/openlp/core/ui/formattingtagcontroller.py @@ -0,0 +1,98 @@ +# -*- 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:`formattingtagform` provides an Tag Edit facility. The Base set are protected and included each time loaded. +Custom tags can be defined and saved. The Custom Tag arrays are saved in a pickle so QSettings works on them. Base Tags +cannot be changed. +""" + +import re +import cgi + +from openlp.core.lib import translate + + +class FormattingTagController(object): + """ + The :class:`FormattingTagController` manages the non UI functions . + """ + def __init__(self): + """ + Initiator + """ + self.html_tag_regex = re.compile(r'<(?:(?P/(?=[^\s/>]+>))?' + r'(?P[^\s/!\?>]+)(?:\s+[^\s=]+="[^"]*")*\s*(?P/)?' + r'|(?P!\[CDATA\[(?:(?!\]\]>).)*\]\])' + r'|(?P\?(?:(?!\?>).)*\?)' + r'|(?P!--(?:(?!-->).)*--))>', re.UNICODE) + self.html_regex = re.compile(r'^(?:[^<>]*%s)*[^<>]*$' % self.html_tag_regex.pattern) + + def pre_save(self): + self.custom_tags = [] + + def validate_for_save(self, desc, tag, start_html, end_html): + if not desc: + pass + print desc + print self.start_html_to_end_html(start_html) + + def html_start_validate(self, start, end): + pass + + def _strip(self, tag): + """ + Remove tag wrappers for editing. + """ + tag = tag.replace(u'{', u'') + tag = tag.replace(u'}', u'') + return tag + + def start_html_to_end_html(self, start_html): + """ + Return the end HTML for a given start HTML or None if invalid. + """ + end_tags = [] + match = self.html_regex.match(start_html) + if match: + match = self.html_tag_regex.search(start_html) + while match: + if match.group(u'tag'): + tag = match.group(u'tag').lower() + if match.group(u'close'): + if match.group(u'empty') or not end_tags or end_tags.pop() != tag: + return + elif not match.group(u'empty'): + end_tags.append(tag) + match = self.html_tag_regex.search(start_html, match.end()) + return u''.join(map(lambda tag: u'' % tag, reversed(end_tags))) + + def start_tag_changed(self, start_html, end_html): + end = self.start_html_to_end_html(start_html) + if not end_html: + return None, end \ No newline at end of file diff --git a/openlp/core/ui/formattingtagdialog.py b/openlp/core/ui/formattingtagdialog.py index cdd1d6edc..20762895f 100644 --- a/openlp/core/ui/formattingtagdialog.py +++ b/openlp/core/ui/formattingtagdialog.py @@ -90,59 +90,6 @@ class Ui_FormattingTagDialog(object): item = QtGui.QTableWidgetItem() self.tag_table_widget.setHorizontalHeaderItem(3, item) self.list_data_grid_layout.addWidget(self.tag_table_widget) - - - #self.horizontal_layout = QtGui.QHBoxLayout() - #self.horizontal_layout.setObjectName(u'horizontal_layout') - #spacer_item = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) - #self.horizontal_layout.addItem(spacer_item) - #self.delete_push_button = QtGui.QPushButton(formatting_tag_dialog) - #self.delete_push_button.setObjectName(u'delete_push_button') - #self.horizontal_layout.addWidget(self.delete_push_button) - #self.list_data_grid_layout.addLayout(self.horizontal_layout, 1, 0, 1, 1) - #self.edit_group_box = QtGui.QGroupBox(formatting_tag_dialog) - #self.edit_group_box.setObjectName(u'edit_group_box') - #self.data_grid_layout = QtGui.QGridLayout(self.edit_group_box) - #self.data_grid_layout.setObjectName(u'data_grid_layout') - #self.description_label = QtGui.QLabel(self.edit_group_box) - #self.description_label.setAlignment(QtCore.Qt.AlignCenter) - #self.description_label.setObjectName(u'description_label') - #self.data_grid_layout.addWidget(self.description_label, 0, 0, 1, 1) - #self.description_line_edit = QtGui.QLineEdit(self.edit_group_box) - #self.description_line_edit.setObjectName(u'description_line_edit') - #self.data_grid_layout.addWidget(self.description_line_edit, 0, 1, 2, 1) - #self.new_push_button = QtGui.QPushButton(self.edit_group_box) - #self.new_push_button.setObjectName(u'new_push_button') - #self.data_grid_layout.addWidget(self.new_push_button, 0, 2, 2, 1) - #self.tag_label = QtGui.QLabel(self.edit_group_box) - #self.tag_label.setAlignment(QtCore.Qt.AlignCenter) - #self.tag_label.setObjectName(u'tag_label') - #self.data_grid_layout.addWidget(self.tag_label, 2, 0, 1, 1) - #self.tag_line_edit = QtGui.QLineEdit(self.edit_group_box) - #self.tag_line_edit.setMaximumSize(QtCore.QSize(50, 16777215)) - #self.tag_line_edit.setMaxLength(5) - #self.tag_line_edit.setObjectName(u'tag_line_edit') - #self.data_grid_layout.addWidget(self.tag_line_edit, 2, 1, 1, 1) - #self.start_tag_label = QtGui.QLabel(self.edit_group_box) - #self.start_tag_label.setAlignment(QtCore.Qt.AlignCenter) - #self.start_tag_label.setObjectName(u'start_tag_label') - #self.data_grid_layout.addWidget(self.start_tag_label, 3, 0, 1, 1) - #self.start_tag_line_edit = QtGui.QLineEdit(self.edit_group_box) - #self.start_tag_line_edit.setObjectName(u'start_tag_line_edit') - #self.data_grid_layout.addWidget(self.start_tag_line_edit, 3, 1, 1, 1) - #self.end_tag_label = QtGui.QLabel(self.edit_group_box) - #self.end_tag_label.setAlignment(QtCore.Qt.AlignCenter) - #self.end_tag_label.setObjectName(u'end_tag_label') - #self.data_grid_layout.addWidget(self.end_tag_label, 4, 0, 1, 1) - #self.end_tag_line_edit = QtGui.QLineEdit(self.edit_group_box) - #self.end_tag_line_edit.setObjectName(u'end_tag_line_edit') - #self.data_grid_layout.addWidget(self.end_tag_line_edit, 4, 1, 1, 1) - #self.save_push_button = QtGui.QPushButton(self.edit_group_box) - #self.save_push_button.setObjectName(u'save_push_button') - #self.data_grid_layout.addWidget(self.save_push_button, 4, 2, 1, 1) - #self.list_data_grid_layout.addWidget(self.edit_group_box, 2, 0, 1, 1) - - self.edit_button_layout = QtGui.QHBoxLayout() self.new_button = QtGui.QPushButton(formatting_tag_dialog) self.new_button.setIcon(build_icon(u':/general/general_new.png')) @@ -162,13 +109,6 @@ class Ui_FormattingTagDialog(object): self.restore_button.setIcon(build_icon(u':/general/general_revert.png')) self.restore_button.setObjectName(u'restore_button') self.list_data_grid_layout.addWidget(self.button_box) - - #self.button_box = create_button_box(formatting_tag_dialog, u'button_box', [u'close']) - #self.list_data_grid_layout.addWidget(self.button_box, 5, 0, 1, 1) - #self.delete_push_button = QtGui.QPushButton(formatting_tag_dialog) - #self.delete_push_button.setObjectName(u'delete_push_button') - #self.list_data_grid_layout.addWidget(self.delete_push_button, 5, 0, 1, 1) - self.retranslateUi(formatting_tag_dialog) def retranslateUi(self, formatting_tag_dialog): @@ -176,12 +116,6 @@ class Ui_FormattingTagDialog(object): Translate the UI on the fly """ formatting_tag_dialog.setWindowTitle(translate('OpenLP.FormattingTagDialog', 'Configure Formatting Tags')) - #self.edit_group_box.setTitle(translate('OpenLP.FormattingTagDialog', 'Edit Selection')) - #self.save_push_button.setText(translate('OpenLP.FormattingTagDialog', 'Save')) - #self.description_label.setText(translate('OpenLP.FormattingTagDialog', 'Description')) - #self.tag_label.setText(translate('OpenLP.FormattingTagDialog', 'Tag')) - #self.start_tag_label.setText(translate('OpenLP.FormattingTagDialog', 'Start HTML')) - #self.end_tag_label.setText(translate('OpenLP.FormattingTagDialog', 'End HTML')) self.delete_button.setText(UiStrings().Delete) self.new_button.setText(UiStrings().New) self.tag_table_widget_read_label.setText(translate('OpenLP.FormattingTagDialog', 'Static Formatting')) diff --git a/openlp/core/ui/formattingtagform.py b/openlp/core/ui/formattingtagform.py index a9828d228..5b65b48d4 100644 --- a/openlp/core/ui/formattingtagform.py +++ b/openlp/core/ui/formattingtagform.py @@ -32,7 +32,6 @@ Custom tags can be defined and saved. The Custom Tag arrays are saved in a pickl cannot be changed. """ -import re import cgi from PyQt4 import QtGui, QtCore @@ -40,6 +39,7 @@ from PyQt4 import QtGui, QtCore from openlp.core.lib import FormattingTags, translate from openlp.core.lib.ui import critical_error_message_box from openlp.core.ui.formattingtagdialog import Ui_FormattingTagDialog +from openlp.core.ui.formattingtagcontroller import FormattingTagController class EDITCOLUMN(object): @@ -52,7 +52,7 @@ class EDITCOLUMN(object): EndHtml = 3 -class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog): +class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog, FormattingTagController): """ The :class:`FormattingTagForm` manages the settings tab . """ @@ -60,17 +60,12 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog): """ Constructor """ - QtGui.QDialog.__init__(self, parent) + super(FormattingTagForm, self).__init__(parent) self.setupUi(self) - self.html_tag_regex = re.compile(r'<(?:(?P/(?=[^\s/>]+>))?' - r'(?P[^\s/!\?>]+)(?:\s+[^\s=]+="[^"]*")*\s*(?P/)?' - r'|(?P!\[CDATA\[(?:(?!\]\]>).)*\]\])' - r'|(?P\?(?:(?!\?>).)*\?)' - r'|(?P!--(?:(?!-->).)*--))>', re.UNICODE) - self.html_regex = re.compile(r'^(?:[^<>]*%s)*[^<>]*$' % self.html_tag_regex.pattern) + self.services = FormattingTagController() self.tag_table_widget.itemSelectionChanged.connect(self.on_row_selected) self.new_button.clicked.connect(self.on_new_clicked) - #self.save_push_button.clicked.connect(self.on_saved_clicked) + self.save_button.clicked.connect(self.on_saved_clicked) self.delete_button.clicked.connect(self.on_delete_clicked) self.tag_table_widget.currentCellChanged.connect(self.on_current_cell_changed) self.button_box.rejected.connect(self.close) @@ -103,8 +98,7 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog): QtGui.QTableWidgetItem('n%s' % unicode(new_row))) self.tag_table_widget.setItem(new_row, 2, QtGui.QTableWidgetItem(translate('OpenLP.FormattingTagForm', ''))) - self.tag_table_widget.setItem(new_row, 3, - QtGui.QTableWidgetItem(translate('OpenLP.FormattingTagForm', ''))) + self.tag_table_widget.setItem(new_row, 3, QtGui.QTableWidgetItem(u"")) self.tag_table_widget.resizeRowsToContents() self.tag_table_widget.scrollToBottom() self.tag_table_widget.selectRow(new_row) @@ -121,26 +115,34 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog): """ Update Custom Tag details if not duplicate and save the data. """ + count = 0 + self.services.pre_save() + while count < self.tag_table_widget.rowCount(): + result = self.services.validate_for_save(self.tag_table_widget.item(count, 0).text(), + self.tag_table_widget.item(count, 1).text(), self.tag_table_widget.item(count, 2).text(), + self.tag_table_widget.item(count, 3).text()) + count += 1 + html_expands = FormattingTags.get_html_tags() - if self.selected != -1: - html = html_expands[self.selected] - tag = self.tag_line_edit.text() - for linenumber, html1 in enumerate(html_expands): - if self._strip(html1[u'start tag']) == tag and linenumber != self.selected: - critical_error_message_box( - translate('OpenLP.FormattingTagForm', 'Update Error'), - translate('OpenLP.FormattingTagForm', 'Tag %s already defined.') % tag) - return - html[u'desc'] = self.description_line_edit.text() - html[u'start html'] = self.start_tag_line_edit.text() - html[u'end html'] = self.end_tag_line_edit.text() - html[u'start tag'] = u'{%s}' % tag - html[u'end tag'] = u'{/%s}' % tag - # Keep temporary tags when the user changes one. - html[u'temporary'] = False - self.selected = -1 - FormattingTags.save_html_tags() - self._reloadTable() + #if self.selected != -1: + # html = html_expands[self.selected] + # tag = self.tag_line_edit.text() + # for linenumber, html1 in enumerate(html_expands): + # if self._strip(html1[u'start tag']) == tag and linenumber != self.selected: + # critical_error_message_box( + # translate('OpenLP.FormattingTagForm', 'Update Error'), + # translate('OpenLP.FormattingTagForm', 'Tag %s already defined.') % tag) + # return + # html[u'desc'] = self.description_line_edit.text() + # html[u'start html'] = self.start_tag_line_edit.text() + # html[u'end html'] = self.end_tag_line_edit.text() + # html[u'start tag'] = u'{%s}' % tag + # html[u'end tag'] = u'{/%s}' % tag + # # Keep temporary tags when the user changes one. + # html[u'temporary'] = False + # self.selected = -1 + #FormattingTags.save_html_tags() + #self._reloadTable() def _reloadTable(self): """ @@ -166,7 +168,6 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog): print self.tag_table_widget.rowCount(), html line = self.tag_table_widget.rowCount() self.tag_table_widget.setRowCount(line + 1) - self.tag_table_widget.setRowCount(self.tag_table_widget.rowCount() + 1) self.tag_table_widget.setItem(line, 0, QtGui.QTableWidgetItem(html[u'desc'])) self.tag_table_widget.setItem(line, 1, QtGui.QTableWidgetItem(self._strip(html[u'start tag']))) self.tag_table_widget.setItem(line, 2, QtGui.QTableWidgetItem(html[u'start html'])) @@ -182,8 +183,7 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog): print cur_row, cur_col, pre_col, pre_col # only process for editable rows pre_row_item = self.tag_table_widget.item(pre_row, 0) - edit_item = None - if pre_row_item and (pre_row_item.flags() & QtCore.Qt.ItemIsEditable): + if pre_row_item: item = self.tag_table_widget.item(pre_row, pre_col) text = unicode(item.text()) if pre_col is EDITCOLUMN.Tag: @@ -209,25 +209,14 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog): translate('OpenLP.FormattingTagForm', 'No tag name defined. Do you want to delete the whole tag?'), QtGui.QMessageBox.Yes|QtGui.QMessageBox.Discard|QtGui.QMessageBox.Cancel) - #if answer == QtGui.QMessageBox.Discard: - # item.setText(data.get(u'tag')) - #if answer == QtGui.QMessageBox.Cancel: - # edit_item = item - elif pre_row < self.tag_table_widget.rowCount() - 1: - self.tag_table_widget.removeRow(pre_row) - #elif pre_col is EDITCOLUMN.StartHtml: + elif pre_col is EDITCOLUMN.StartHtml: # HTML edited - #end_html = self.start_html_to_end_html(text) - #if end_html is not None: - # item.setToolTip(cgi.escape(text)) - ## if self.tag_table_widget.item(pre_row, 3) is None: - # self.tag_table_widget.setItem(pre_row, 3, QtGui.QTableWidgetItem(end_html)) - # else: - # self.tag_table_widget.item(pre_row, 3).setText(end_html) - # self.tag_table_widget.item(pre_row, 3).setToolTip(cgi.escape(end_html)) - # #data[u'html'] = text - # #pre_row_item.setData(QtCore.Qt.UserRole, data) - # # self.tag_table_widget.resizeRowsToContents() + item = self.tag_table_widget.item(pre_row, 3) + end_html = unicode(item.text()) + errors, tag = self.services.start_tag_changed(text, end_html) + if tag: + self.tag_table_widget.setItem(pre_row, 3, QtGui.QTableWidgetItem(tag)) + self.tag_table_widget.resizeRowsToContents() #if not edit_item: # # select the tag cell in a empty row # cur_row_item = self.tag_table_widget.item(cur_row, 0) @@ -242,10 +231,3 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog): delete_enabled &= cur_row < self.tag_table_widget.rowCount() - 1 self.delete_button.setEnabled(delete_enabled) - def _strip(self, tag): - """ - Remove tag wrappers for editing. - """ - tag = tag.replace(u'{', u'') - tag = tag.replace(u'}', u'') - return tag From dceaaa6996d4e4148a7e8aec61eae3471c706bff Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sat, 31 Aug 2013 12:43:05 +0100 Subject: [PATCH 16/93] ore changes --- openlp/core/ui/formattingtagform.py | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/openlp/core/ui/formattingtagform.py b/openlp/core/ui/formattingtagform.py index 5b65b48d4..894c08038 100644 --- a/openlp/core/ui/formattingtagform.py +++ b/openlp/core/ui/formattingtagform.py @@ -185,7 +185,7 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog, FormattingTagCont pre_row_item = self.tag_table_widget.item(pre_row, 0) if pre_row_item: item = self.tag_table_widget.item(pre_row, pre_col) - text = unicode(item.text()) + text = item.text() if pre_col is EDITCOLUMN.Tag: if text: for row in range(self.tag_table_widget.rowCount()): @@ -198,9 +198,6 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog, FormattingTagCont QtGui.QMessageBox.Discard|QtGui.QMessageBox.Ok) if answer == QtGui.QMessageBox.Discard: break - else: - edit_item = item - break else: answer = None if self.tag_table_widget.item(pre_row, 1).text() or self.tag_table_widget.item(pre_row, 2).text(): @@ -212,7 +209,7 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog, FormattingTagCont elif pre_col is EDITCOLUMN.StartHtml: # HTML edited item = self.tag_table_widget.item(pre_row, 3) - end_html = unicode(item.text()) + end_html = item.text() errors, tag = self.services.start_tag_changed(text, end_html) if tag: self.tag_table_widget.setItem(pre_row, 3, QtGui.QTableWidgetItem(tag)) @@ -225,9 +222,3 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog, FormattingTagCont #if edit_item: # self.tag_table_widget.setCurrentItem(edit_item) # enable delete_button for editable rows - cur_row = self.tag_table_widget.currentRow() - cur_row_item = self.tag_table_widget.item(cur_row, 0) - delete_enabled = bool(cur_row_item) and bool(cur_row_item.flags() & QtCore.Qt.ItemIsEditable) - delete_enabled &= cur_row < self.tag_table_widget.rowCount() - 1 - self.delete_button.setEnabled(delete_enabled) - From e4ba5cb72a04d689ff2191ab31c5a616366c2cfd Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sat, 31 Aug 2013 18:20:43 +0100 Subject: [PATCH 17/93] add test files and ui validation sort of works --- openlp/core/lib/formattingtags.py | 2 +- openlp/core/ui/formattingtagcontroller.py | 64 +++++++++++++++++-- openlp/core/ui/formattingtagform.py | 46 +++++-------- .../tests.formattingcontroller.py | 25 ++++++++ .../tests_fromattingstagsform.py | 50 +++++++++++++++ 5 files changed, 149 insertions(+), 38 deletions(-) create mode 100644 tests/functional/openlp_core_ui/tests.formattingcontroller.py create mode 100644 tests/functional/openlp_core_ui/tests_fromattingstagsform.py diff --git a/openlp/core/lib/formattingtags.py b/openlp/core/lib/formattingtags.py index 1984b08ba..ebff708b4 100644 --- a/openlp/core/lib/formattingtags.py +++ b/openlp/core/lib/formattingtags.py @@ -36,7 +36,7 @@ from openlp.core.lib import Settings, translate class FormattingTags(object): """ - Static Class to HTML Tags to be access around the code the list is managed by the Options Tab. + Static Class for HTML Tags to be access around the code the list is managed by the Options Tab. """ html_expands = [] diff --git a/openlp/core/ui/formattingtagcontroller.py b/openlp/core/ui/formattingtagcontroller.py index 7e1131507..6fb75ade4 100644 --- a/openlp/core/ui/formattingtagcontroller.py +++ b/openlp/core/ui/formattingtagcontroller.py @@ -33,9 +33,8 @@ cannot be changed. """ import re -import cgi -from openlp.core.lib import translate +from openlp.core.lib import FormattingTags, translate class FormattingTagController(object): @@ -54,17 +53,34 @@ class FormattingTagController(object): self.html_regex = re.compile(r'^(?:[^<>]*%s)*[^<>]*$' % self.html_tag_regex.pattern) def pre_save(self): + """ + Cleanup the array before save validation runs + """ + self.protected_tags = [tag for tag in FormattingTags.html_expands if tag.get(u'protected')] self.custom_tags = [] def validate_for_save(self, desc, tag, start_html, end_html): + """ + Validate a custom tag and add to the tags array if valid.. + + `description` + Explanation of the tag. + + `tag` + The tag in the song used to mark the text. + + `start_html` + The start html tag. + + `end_html` + The end html tag. + + """ if not desc: pass print desc print self.start_html_to_end_html(start_html) - def html_start_validate(self, start, end): - pass - def _strip(self, tag): """ Remove tag wrappers for editing. @@ -76,6 +92,10 @@ class FormattingTagController(object): def start_html_to_end_html(self, start_html): """ Return the end HTML for a given start HTML or None if invalid. + + `start_html` + The start html tag. + """ end_tags = [] match = self.html_regex.match(start_html) @@ -93,6 +113,38 @@ class FormattingTagController(object): return u''.join(map(lambda tag: u'' % tag, reversed(end_tags))) def start_tag_changed(self, start_html, end_html): + """ + Validate the HTML tags when the start tag has been changed. + + `start_html` + The start html tag. + + `end_html` + The end html tag. + + """ end = self.start_html_to_end_html(start_html) if not end_html: - return None, end \ No newline at end of file + if not end: + return translate('OpenLP.FormattingTagForm', 'Start tag %s is not valid HTML' % start_html), None + return None, end + return None, None + + def end_tag_changed(self, start_html, end_html): + """ + Validate the HTML tags when the end tag has been changed. + + `start_html` + The start html tag. + + `end_html` + The end html tag. + + """ + end = self.start_html_to_end_html(start_html) + if not end_html: + return None, end + if end and end != end_html: + return translate('OpenLP.FormattingTagForm', + 'End tag %s does not match end tag for start tag %s' % (end, start_html)), None + return None, None \ No newline at end of file diff --git a/openlp/core/ui/formattingtagform.py b/openlp/core/ui/formattingtagform.py index 15a59ac52..1eaec7cc5 100644 --- a/openlp/core/ui/formattingtagform.py +++ b/openlp/core/ui/formattingtagform.py @@ -182,31 +182,11 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog, FormattingTagCont """ print cur_row, cur_col, pre_col, pre_col # only process for editable rows - pre_row_item = self.tag_table_widget.item(pre_row, 0) - if pre_row_item: + if self.tag_table_widget.item(pre_row, 0): item = self.tag_table_widget.item(pre_row, pre_col) text = item.text() - if pre_col is EDITCOLUMN.Tag: - if text: - for row in range(self.tag_table_widget.rowCount()): - counting_item = self.tag_table_widget.item(row, 0) - if row != pre_row and counting_item and counting_item.text() == text: - answer = QtGui.QMessageBox.warning(self, - translate('OpenLP.FormattingTagForm', 'Validation Error'), - translate('OpenLP.FormattingTagForm', - 'Tag %s is already defined. Please pick a different one.' % text), - QtGui.QMessageBox.Discard|QtGui.QMessageBox.Ok) - if answer == QtGui.QMessageBox.Discard: - break - else: - answer = None - if self.tag_table_widget.item(pre_row, 1).text() or self.tag_table_widget.item(pre_row, 2).text(): - answer = QtGui.QMessageBox.warning(self, - translate('OpenLP.FormattingTagForm', 'Validation Error'), - translate('OpenLP.FormattingTagForm', - 'No tag name defined. Do you want to delete the whole tag?'), - QtGui.QMessageBox.Yes|QtGui.QMessageBox.Discard|QtGui.QMessageBox.Cancel) - elif pre_col is EDITCOLUMN.StartHtml: + errors = None + if pre_col is EDITCOLUMN.StartHtml: # HTML edited item = self.tag_table_widget.item(pre_row, 3) end_html = item.text() @@ -214,11 +194,15 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog, FormattingTagCont if tag: self.tag_table_widget.setItem(pre_row, 3, QtGui.QTableWidgetItem(tag)) self.tag_table_widget.resizeRowsToContents() - #if not edit_item: - # # select the tag cell in a empty row - # cur_row_item = self.tag_table_widget.item(cur_row, 0) - # if cur_row_item and (cur_row_item.flags() & QtCore.Qt.ItemIsEditable) and cur_row_item.text().isEmpty(): - # edit_item = cur_row_item - #if edit_item: - # self.tag_table_widget.setCurrentItem(edit_item) - # enable delete_button for editable rows + elif pre_col is EDITCOLUMN.EndHtml: + # HTML edited + item = self.tag_table_widget.item(pre_row, 2) + start_html = item.text() + errors, tag = self.services.end_tag_changed(start_html, text) + if tag: + self.tag_table_widget.setItem(pre_row, 3, QtGui.QTableWidgetItem(tag)) + if errors: + QtGui.QMessageBox.warning(self, + translate('OpenLP.FormattingTagForm', 'Validation Error'), errors, + QtGui.QMessageBox.Yes|QtGui.QMessageBox.Discard|QtGui.QMessageBox.Cancel) + self.tag_table_widget.resizeRowsToContents() diff --git a/tests/functional/openlp_core_ui/tests.formattingcontroller.py b/tests/functional/openlp_core_ui/tests.formattingcontroller.py new file mode 100644 index 000000000..6f78a6ffa --- /dev/null +++ b/tests/functional/openlp_core_ui/tests.formattingcontroller.py @@ -0,0 +1,25 @@ +""" +Package to test the openlp.core.ui.formattingtagscontroller package. +""" +from unittest import TestCase + +from mock import MagicMock, patch + +from openlp.core.ui.formattingtagform import FormattingTagForm + +class TestFormattingTagForm(TestCase): + + def strip_test(self): + """ + Test that the _strip strips the correct chars + """ + + # GIVEN: An instance of the Formatting Tag Form and a string containing a tag + form = FormattingTagForm() + tag = u'{tag}' + + # WHEN: Calling _strip + result = form._strip(tag) + + # THEN: The tag should be returned with the wrappers removed. + self.assertEqual(result, u'tag', u'FormattingTagForm._strip should return u\'tag\' when called with u\'{tag}\'') \ No newline at end of file diff --git a/tests/functional/openlp_core_ui/tests_fromattingstagsform.py b/tests/functional/openlp_core_ui/tests_fromattingstagsform.py new file mode 100644 index 000000000..4e5a109cc --- /dev/null +++ b/tests/functional/openlp_core_ui/tests_fromattingstagsform.py @@ -0,0 +1,50 @@ +""" +Package to test the openlp.core.ui.formattingtagsform package. +""" +from unittest import TestCase + +from mock import MagicMock, patch + +from openlp.core.ui.formattingtagform import FormattingTagForm + +# TODO: Tests Still TODO +# __init__ +# exec_ +# on_new_clicked +# on_delete_clicked +# on_saved_clicked +# _reloadTable + + +class TestFormattingTagForm(TestCase): + + def setUp(self): + self.init_patcher = patch(u'openlp.core.ui.formattingtagform.FormattingTagForm.__init__') + self.qdialog_patcher = patch(u'openlp.core.ui.formattingtagform.QtGui.QDialog') + self.ui_formatting_tag_dialog_patcher = patch(u'openlp.core.ui.formattingtagform.Ui_FormattingTagDialog') + self.mocked_init = self.init_patcher.start() + self.mocked_qdialog = self.qdialog_patcher.start() + self.mocked_ui_formatting_tag_dialog = self.ui_formatting_tag_dialog_patcher.start() + self.mocked_init.return_value = None + + def tearDown(self): + self.init_patcher.stop() + self.qdialog_patcher.stop() + self.ui_formatting_tag_dialog_patcher.stop() + + def on_text_edited_test(self): + """ + Test that the appropriate actions are preformed when on_text_edited is called + """ + + # GIVEN: An instance of the Formatting Tag Form and a mocked save_push_button + form = FormattingTagForm() + form.save_push_button = MagicMock() + + # WHEN: on_text_edited is called with an arbitrary value + form.on_text_edited(u'text') + + # THEN: setEnabled and setDefault should have been called on save_push_button + form.save_push_button.setEnabled.assert_called_with(True) + form.save_push_button.setDefault.assert_called_with(True) + From 99a07ad91e40c80271197150f3255a1d8e5a38be Mon Sep 17 00:00:00 2001 From: Oliver Wieland Date: Sat, 31 Aug 2013 22:04:36 +0200 Subject: [PATCH 18/93] added tests --- openlp/plugins/bibles/lib/mediaitem.py | 4 +- .../bibles/test_versereferencelist.py | 116 ++++++++++++++++++ 2 files changed, 118 insertions(+), 2 deletions(-) create mode 100644 tests/functional/openlp_plugins/bibles/test_versereferencelist.py diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index 3d94c7e93..01ba525a8 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -944,7 +944,7 @@ class BibleMediaItem(MediaManagerItem): """ verse_separator = get_reference_separator('sep_v_display') if not self.settings.is_verse_number_visible: - return u'' + return '' if not self.settings.show_new_chapters or old_chapter != chapter: verse_text = str(chapter) + verse_separator + str(verse) else: @@ -955,7 +955,7 @@ class BibleMediaItem(MediaManagerItem): return '{su}{%s}{/su}' % verse_text if self.settings.display_style == DisplayStyle.Square: return '{su}[%s]{/su}' % verse_text - return u'{su}%s{/su}' % verse_text + return '{su}%s{/su}' % verse_text def search(self, string, showError): """ diff --git a/tests/functional/openlp_plugins/bibles/test_versereferencelist.py b/tests/functional/openlp_plugins/bibles/test_versereferencelist.py new file mode 100644 index 000000000..372264a76 --- /dev/null +++ b/tests/functional/openlp_plugins/bibles/test_versereferencelist.py @@ -0,0 +1,116 @@ +""" +This module contains tests for the versereferencelist submodule of the Bibles plugin. +""" +from unittest import TestCase +from openlp.plugins.bibles.lib.versereferencelist import VerseReferenceList + +class TestVerseReferenceList(TestCase): + def setUp(self): + """ + Initializes all we need + """ + + def add_first_verse_test(self): + """ + Test the addition of a verse to the empty list + """ + # GIVEN: an empty list + reference_list = VerseReferenceList() + book = 'testBook' + chapter = 1 + verse = 1 + version = 'testVersion' + copyright = 'testCopyright' + permission = 'testPermision' + + # WHEN: We add it to the verse list + reference_list.add(book, chapter, verse, version, copyright, permission) + + # THEN: The entries should be in the first entry of the list + self.assertEqual(reference_list.current_index, 0, 'The current index should be 0') + self.assertEqual(reference_list.verse_list[0]['book'], book, 'The book in first entry should be %s' % book) + self.assertEqual(reference_list.verse_list[0]['chapter'], chapter, 'The chapter in first entry should be %u' % chapter) + self.assertEqual(reference_list.verse_list[0]['start'], verse, 'The start in first entry should be %u' % verse) + self.assertEqual(reference_list.verse_list[0]['version'], version, 'The version in first entry should be %s' % version) + self.assertEqual(reference_list.verse_list[0]['end'], verse, 'The end in first entry should be %u' % verse) + + def add_next_verse_test(self): + """ + Test the addition of the following verse + """ + # GIVEN: 1 line in the list of verses + book = 'testBook' + chapter = 1 + verse = 1 + next_verse = 2 + version = 'testVersion' + copyright = 'testCopyright' + permission = 'testPermision' + reference_list = VerseReferenceList() + reference_list.add(book, chapter, verse, version, copyright, permission) + + # WHEN: We add the following verse to the verse list + reference_list.add(book, chapter, next_verse, version, copyright, permission) + + # THEN: The current index should be 0 and the end pointer of the entry should be '2' + self.assertEqual(reference_list.current_index, 0, 'The current index should be 0') + self.assertEqual(reference_list.verse_list[0]['end'], next_verse, 'The end in first entry should be %u' % next_verse) + + def add_another_verse_test(self): + """ + Test the addition of a verse in another book + """ + # GIVEN: 1 line in the list of verses + book = 'testBook' + chapter = 1 + verse = 1 + next_verse = 2 + another_book = 'testBook2' + another_chapter = 2 + another_verse = 5 + version = 'testVersion' + copyright = 'testCopyright' + permission = 'testPermision' + reference_list = VerseReferenceList() + reference_list.add(book, chapter, verse, version, copyright, permission) + + # WHEN: We add a verse of another book to the verse list + reference_list.add(another_book, another_chapter, another_verse, version, copyright, permission) + + # THEN: the current index should be 1 + self.assertEqual(reference_list.current_index, 1, 'The current index should be 1') + + def add_version_test(self): + """ + Test the addition of a version to the list + """ + # GIVEN: version, copyright and permission + reference_list = VerseReferenceList() + version = 'testVersion' + copyright = 'testCopyright' + permission = 'testPermision' + + # WHEN: a not existing version will be added + reference_list.add_version(version, copyright, permission) + + # THEN: the data will be appended to the list + self.assertEqual(len(reference_list.version_list), 1, 'The version data should be appended') + self.assertEqual(reference_list.version_list[0], {'version': version, 'copyright': copyright, 'permission': permission}, + 'The version data should be appended') + + def add_existing_version_test(self): + """ + Test the addition of an existing version to the list + """ + # GIVEN: version, copyright and permission, added to the version list + reference_list = VerseReferenceList() + version = 'testVersion' + copyright = 'testCopyright' + permission = 'testPermision' + reference_list.add_version(version, copyright, permission) + + # WHEN: an existing version will be added + reference_list.add_version(version, copyright, permission) + + # THEN: the data will not be appended to the list + self.assertEqual(len(reference_list.version_list), 1, 'The version data should not be appended') From ede6adeb48c8be7ecaaffae80142060688cbc035 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sun, 1 Sep 2013 07:16:27 +0100 Subject: [PATCH 19/93] Tests added --- openlp/core/ui/__init__.py | 2 +- .../openlp_core_ui/tests.formattingcontroller.py | 13 ++++++++----- .../openlp_core_ui/tests_fromattingstagsform.py | 2 +- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/openlp/core/ui/__init__.py b/openlp/core/ui/__init__.py index 49e59e4c1..4fa7539e4 100644 --- a/openlp/core/ui/__init__.py +++ b/openlp/core/ui/__init__.py @@ -104,4 +104,4 @@ __all__ = ['SplashScreen', 'AboutForm', 'SettingsForm', 'MainDisplay', 'SlideCon 'ThemeManager', 'MediaDockManager', 'ServiceItemEditForm', 'FirstTimeForm', 'FirstTimeLanguageForm', 'ThemeForm', 'ThemeLayoutForm', 'FileRenameForm', 'StartTimeForm', 'MainDisplay', 'Display', 'ServiceNoteForm', 'SlideController', 'DisplayController', 'GeneralTab', 'ThemesTab', 'AdvancedTab', 'PluginForm', - 'FormattingTagForm', 'ShortcutListForm'] + 'FormattingTagForm', 'ShortcutListForm', 'FormattingTagController'] diff --git a/tests/functional/openlp_core_ui/tests.formattingcontroller.py b/tests/functional/openlp_core_ui/tests.formattingcontroller.py index 6f78a6ffa..7959e82b5 100644 --- a/tests/functional/openlp_core_ui/tests.formattingcontroller.py +++ b/tests/functional/openlp_core_ui/tests.formattingcontroller.py @@ -5,21 +5,24 @@ from unittest import TestCase from mock import MagicMock, patch -from openlp.core.ui.formattingtagform import FormattingTagForm +from openlp.core.ui import FormattingTagController -class TestFormattingTagForm(TestCase): - def strip_test(self): +class TestFormattingTagController(TestCase): + + def setUp(self): + self.services = FormattingTagController() + + def test_strip(self): """ Test that the _strip strips the correct chars """ # GIVEN: An instance of the Formatting Tag Form and a string containing a tag - form = FormattingTagForm() tag = u'{tag}' # WHEN: Calling _strip - result = form._strip(tag) + result = self.services._strip(tag) # THEN: The tag should be returned with the wrappers removed. self.assertEqual(result, u'tag', u'FormattingTagForm._strip should return u\'tag\' when called with u\'{tag}\'') \ No newline at end of file diff --git a/tests/functional/openlp_core_ui/tests_fromattingstagsform.py b/tests/functional/openlp_core_ui/tests_fromattingstagsform.py index 4e5a109cc..2b31c0a62 100644 --- a/tests/functional/openlp_core_ui/tests_fromattingstagsform.py +++ b/tests/functional/openlp_core_ui/tests_fromattingstagsform.py @@ -32,7 +32,7 @@ class TestFormattingTagForm(TestCase): self.qdialog_patcher.stop() self.ui_formatting_tag_dialog_patcher.stop() - def on_text_edited_test(self): + def test_on_text_edited(self): """ Test that the appropriate actions are preformed when on_text_edited is called """ From 6282b4fcb68aed457a86c9140d8d5ecc513325c9 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sun, 1 Sep 2013 07:21:29 +0100 Subject: [PATCH 20/93] Tests and fixed --- openlp/core/ui/__init__.py | 1 + .../tests.formattingcontroller.py | 28 ----------- .../tests_fromattingstagsform.py | 50 ------------------- 3 files changed, 1 insertion(+), 78 deletions(-) delete mode 100644 tests/functional/openlp_core_ui/tests.formattingcontroller.py delete mode 100644 tests/functional/openlp_core_ui/tests_fromattingstagsform.py diff --git a/openlp/core/ui/__init__.py b/openlp/core/ui/__init__.py index 4fa7539e4..be44e549c 100644 --- a/openlp/core/ui/__init__.py +++ b/openlp/core/ui/__init__.py @@ -95,6 +95,7 @@ from aboutform import AboutForm from pluginform import PluginForm from settingsform import SettingsForm from formattingtagform import FormattingTagForm +from formattingtagcontroller import FormattingTagController from shortcutlistform import ShortcutListForm from mediadockmanager import MediaDockManager from servicemanager import ServiceManager diff --git a/tests/functional/openlp_core_ui/tests.formattingcontroller.py b/tests/functional/openlp_core_ui/tests.formattingcontroller.py deleted file mode 100644 index 7959e82b5..000000000 --- a/tests/functional/openlp_core_ui/tests.formattingcontroller.py +++ /dev/null @@ -1,28 +0,0 @@ -""" -Package to test the openlp.core.ui.formattingtagscontroller package. -""" -from unittest import TestCase - -from mock import MagicMock, patch - -from openlp.core.ui import FormattingTagController - - -class TestFormattingTagController(TestCase): - - def setUp(self): - self.services = FormattingTagController() - - def test_strip(self): - """ - Test that the _strip strips the correct chars - """ - - # GIVEN: An instance of the Formatting Tag Form and a string containing a tag - tag = u'{tag}' - - # WHEN: Calling _strip - result = self.services._strip(tag) - - # THEN: The tag should be returned with the wrappers removed. - self.assertEqual(result, u'tag', u'FormattingTagForm._strip should return u\'tag\' when called with u\'{tag}\'') \ No newline at end of file diff --git a/tests/functional/openlp_core_ui/tests_fromattingstagsform.py b/tests/functional/openlp_core_ui/tests_fromattingstagsform.py deleted file mode 100644 index 2b31c0a62..000000000 --- a/tests/functional/openlp_core_ui/tests_fromattingstagsform.py +++ /dev/null @@ -1,50 +0,0 @@ -""" -Package to test the openlp.core.ui.formattingtagsform package. -""" -from unittest import TestCase - -from mock import MagicMock, patch - -from openlp.core.ui.formattingtagform import FormattingTagForm - -# TODO: Tests Still TODO -# __init__ -# exec_ -# on_new_clicked -# on_delete_clicked -# on_saved_clicked -# _reloadTable - - -class TestFormattingTagForm(TestCase): - - def setUp(self): - self.init_patcher = patch(u'openlp.core.ui.formattingtagform.FormattingTagForm.__init__') - self.qdialog_patcher = patch(u'openlp.core.ui.formattingtagform.QtGui.QDialog') - self.ui_formatting_tag_dialog_patcher = patch(u'openlp.core.ui.formattingtagform.Ui_FormattingTagDialog') - self.mocked_init = self.init_patcher.start() - self.mocked_qdialog = self.qdialog_patcher.start() - self.mocked_ui_formatting_tag_dialog = self.ui_formatting_tag_dialog_patcher.start() - self.mocked_init.return_value = None - - def tearDown(self): - self.init_patcher.stop() - self.qdialog_patcher.stop() - self.ui_formatting_tag_dialog_patcher.stop() - - def test_on_text_edited(self): - """ - Test that the appropriate actions are preformed when on_text_edited is called - """ - - # GIVEN: An instance of the Formatting Tag Form and a mocked save_push_button - form = FormattingTagForm() - form.save_push_button = MagicMock() - - # WHEN: on_text_edited is called with an arbitrary value - form.on_text_edited(u'text') - - # THEN: setEnabled and setDefault should have been called on save_push_button - form.save_push_button.setEnabled.assert_called_with(True) - form.save_push_button.setDefault.assert_called_with(True) - From d55ac01a1ac98b01f7feb392c31fdd73d2798f84 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sun, 1 Sep 2013 07:22:06 +0100 Subject: [PATCH 21/93] Tests and fixed again --- .../tests_formattingtagscontroller.py | 28 +++++++++++ .../tests_formattingtagsform.py | 50 +++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 tests/functional/openlp_core_ui/tests_formattingtagscontroller.py create mode 100644 tests/functional/openlp_core_ui/tests_formattingtagsform.py diff --git a/tests/functional/openlp_core_ui/tests_formattingtagscontroller.py b/tests/functional/openlp_core_ui/tests_formattingtagscontroller.py new file mode 100644 index 000000000..7959e82b5 --- /dev/null +++ b/tests/functional/openlp_core_ui/tests_formattingtagscontroller.py @@ -0,0 +1,28 @@ +""" +Package to test the openlp.core.ui.formattingtagscontroller package. +""" +from unittest import TestCase + +from mock import MagicMock, patch + +from openlp.core.ui import FormattingTagController + + +class TestFormattingTagController(TestCase): + + def setUp(self): + self.services = FormattingTagController() + + def test_strip(self): + """ + Test that the _strip strips the correct chars + """ + + # GIVEN: An instance of the Formatting Tag Form and a string containing a tag + tag = u'{tag}' + + # WHEN: Calling _strip + result = self.services._strip(tag) + + # THEN: The tag should be returned with the wrappers removed. + self.assertEqual(result, u'tag', u'FormattingTagForm._strip should return u\'tag\' when called with u\'{tag}\'') \ No newline at end of file diff --git a/tests/functional/openlp_core_ui/tests_formattingtagsform.py b/tests/functional/openlp_core_ui/tests_formattingtagsform.py new file mode 100644 index 000000000..2b31c0a62 --- /dev/null +++ b/tests/functional/openlp_core_ui/tests_formattingtagsform.py @@ -0,0 +1,50 @@ +""" +Package to test the openlp.core.ui.formattingtagsform package. +""" +from unittest import TestCase + +from mock import MagicMock, patch + +from openlp.core.ui.formattingtagform import FormattingTagForm + +# TODO: Tests Still TODO +# __init__ +# exec_ +# on_new_clicked +# on_delete_clicked +# on_saved_clicked +# _reloadTable + + +class TestFormattingTagForm(TestCase): + + def setUp(self): + self.init_patcher = patch(u'openlp.core.ui.formattingtagform.FormattingTagForm.__init__') + self.qdialog_patcher = patch(u'openlp.core.ui.formattingtagform.QtGui.QDialog') + self.ui_formatting_tag_dialog_patcher = patch(u'openlp.core.ui.formattingtagform.Ui_FormattingTagDialog') + self.mocked_init = self.init_patcher.start() + self.mocked_qdialog = self.qdialog_patcher.start() + self.mocked_ui_formatting_tag_dialog = self.ui_formatting_tag_dialog_patcher.start() + self.mocked_init.return_value = None + + def tearDown(self): + self.init_patcher.stop() + self.qdialog_patcher.stop() + self.ui_formatting_tag_dialog_patcher.stop() + + def test_on_text_edited(self): + """ + Test that the appropriate actions are preformed when on_text_edited is called + """ + + # GIVEN: An instance of the Formatting Tag Form and a mocked save_push_button + form = FormattingTagForm() + form.save_push_button = MagicMock() + + # WHEN: on_text_edited is called with an arbitrary value + form.on_text_edited(u'text') + + # THEN: setEnabled and setDefault should have been called on save_push_button + form.save_push_button.setEnabled.assert_called_with(True) + form.save_push_button.setDefault.assert_called_with(True) + From 8ab834c18ebe4eb51b9dde52459f703e342fa731 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sun, 1 Sep 2013 21:43:22 +0100 Subject: [PATCH 22/93] Fix up save and load# --- openlp/core/lib/formattingtags.py | 14 +----- openlp/core/ui/formattingtagcontroller.py | 25 ++++++++++- openlp/core/ui/formattingtagform.py | 43 ++++++------------- openlp/plugins/songs/forms/editsongform.py | 1 - .../tests_formattingtagsform.py | 7 ++- 5 files changed, 41 insertions(+), 49 deletions(-) diff --git a/openlp/core/lib/formattingtags.py b/openlp/core/lib/formattingtags.py index 37d32e4dc..bbc912058 100644 --- a/openlp/core/lib/formattingtags.py +++ b/openlp/core/lib/formattingtags.py @@ -48,22 +48,12 @@ class FormattingTags(object): return FormattingTags.html_expands @staticmethod - def save_html_tags(): + def save_html_tags(new_tags): """ Saves all formatting tags except protected ones. """ - tags = [] - for tag in FormattingTags.html_expands: - if not tag['protected'] and not tag.get('temporary'): - # Using dict ensures that copy is made and encoding of values a little later does not affect tags in - # the original list - tags.append(dict(tag)) - tag = tags[-1] - # Remove key 'temporary' from tags. It is not needed to be saved. - if 'temporary' in tag: - del tag['temporary'] # Formatting Tags were also known as display tags. - Settings().setValue('formattingTags/html_tags', json.dumps(tags) if tags else '') + Settings().setValue('formattingTags/html_tags', json.dumps(new_tags) if new_tags else '') @staticmethod def load_tags(): diff --git a/openlp/core/ui/formattingtagcontroller.py b/openlp/core/ui/formattingtagcontroller.py index b1dcbcbdd..242b26e30 100644 --- a/openlp/core/ui/formattingtagcontroller.py +++ b/openlp/core/ui/formattingtagcontroller.py @@ -63,7 +63,7 @@ class FormattingTagController(object): """ Validate a custom tag and add to the tags array if valid.. - `description` + `desc` Explanation of the tag. `tag` @@ -78,6 +78,29 @@ class FormattingTagController(object): """ if not desc: pass + for linenumber, html1 in enumerate(self.protected_tags): + if self._strip(html1[u'start tag']) == tag: + return translate('OpenLP.FormattingTagForm', 'Tag %s already defined.') % tag + for linenumber, html1 in enumerate(self.custom_tags): + if self._strip(html1[u'start tag']) == tag: + return translate('OpenLP.FormattingTagForm', 'Tag %s already defined.') % tag + tag = { + 'desc': desc, + 'start tag': '{%s}' % tag, + 'start html': start_html, + 'end tag': '{/%s}' % tag, + 'end html': end_html, + 'protected': False, + 'temporary': False + } + self.custom_tags.append(tag) + + def save_tags(self): + """ + Save the new tags if they are valid. + """ + FormattingTags.save_html_tags(self.custom_tags) + FormattingTags.load_tags() def _strip(self, tag): """ diff --git a/openlp/core/ui/formattingtagform.py b/openlp/core/ui/formattingtagform.py index 307b51954..c2aeddc9a 100644 --- a/openlp/core/ui/formattingtagform.py +++ b/openlp/core/ui/formattingtagform.py @@ -32,12 +32,9 @@ Custom tags can be defined and saved. The Custom Tag arrays are saved in a json Base Tags cannot be changed. """ -import cgi - -from PyQt4 import QtGui, QtCore +from PyQt4 import QtGui from openlp.core.lib import FormattingTags, translate -from openlp.core.lib.ui import critical_error_message_box from openlp.core.ui.formattingtagdialog import Ui_FormattingTagDialog from openlp.core.ui.formattingtagcontroller import FormattingTagController @@ -65,7 +62,7 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog, FormattingTagCont self.services = FormattingTagController() self.tag_table_widget.itemSelectionChanged.connect(self.on_row_selected) self.new_button.clicked.connect(self.on_new_clicked) - self.save_button.clicked.connect(self.on_saved_clicked) + #self.save_button.clicked.connect(self.on_saved_clicked) self.delete_button.clicked.connect(self.on_delete_clicked) self.tag_table_widget.currentCellChanged.connect(self.on_current_cell_changed) self.button_box.rejected.connect(self.close) @@ -110,39 +107,24 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog, FormattingTagCont if selected != -1: self.tag_table_widget.removeRow(selected) - def on_saved_clicked(self): + def accept(self): """ Update Custom Tag details if not duplicate and save the data. """ count = 0 self.services.pre_save() while count < self.tag_table_widget.rowCount(): - result = self.services.validate_for_save(self.tag_table_widget.item(count, 0).text(), + error = self.services.validate_for_save(self.tag_table_widget.item(count, 0).text(), self.tag_table_widget.item(count, 1).text(), self.tag_table_widget.item(count, 2).text(), self.tag_table_widget.item(count, 3).text()) + if error: + QtGui.QMessageBox.warning(self, + translate('OpenLP.FormattingTagForm', 'Validation Error'), error, QtGui.QMessageBox.Ok) + self.tag_table_widget.selectRow(count) + return count += 1 - - html_expands = FormattingTags.get_html_tags() - #if self.selected != -1: - # html = html_expands[self.selected] - # tag = self.tag_line_edit.text() - # for linenumber, html1 in enumerate(html_expands): - # if self._strip(html1[u'start tag']) == tag and linenumber != self.selected: - # critical_error_message_box( - # translate('OpenLP.FormattingTagForm', 'Update Error'), - # translate('OpenLP.FormattingTagForm', 'Tag %s already defined.') % tag) - # return - # html[u'desc'] = self.description_line_edit.text() - # html[u'start html'] = self.start_tag_line_edit.text() - # html[u'end html'] = self.end_tag_line_edit.text() - # html[u'start tag'] = u'{%s}' % tag - # html[u'end tag'] = u'{/%s}' % tag - # # Keep temporary tags when the user changes one. - # html[u'temporary'] = False - # self.selected = -1 - #FormattingTags.save_html_tags() - #self._reloadTable() - + self.services.save_tags() + QtGui.QDialog.accept(self) def _reloadTable(self): """ @@ -201,7 +183,6 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog, FormattingTagCont self.tag_table_widget.setItem(pre_row, 3, QtGui.QTableWidgetItem(tag)) if errors: QtGui.QMessageBox.warning(self, - translate('OpenLP.FormattingTagForm', 'Validation Error'), errors, - QtGui.QMessageBox.Yes|QtGui.QMessageBox.Discard|QtGui.QMessageBox.Cancel) + translate('OpenLP.FormattingTagForm', 'Validation Error'), errors, QtGui.QMessageBox.Ok) self.tag_table_widget.resizeRowsToContents() diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index 747988583..66f71545f 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -692,7 +692,6 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): self.verse_edit_button.setEnabled(False) self.verse_delete_button.setEnabled(False) - def on_verse_order_text_changed(self, text): """ Checks if the verse order is complete or missing. Shows a error message according to the state of the verse diff --git a/tests/functional/openlp_core_ui/tests_formattingtagsform.py b/tests/functional/openlp_core_ui/tests_formattingtagsform.py index 2b31c0a62..7edd861d9 100644 --- a/tests/functional/openlp_core_ui/tests_formattingtagsform.py +++ b/tests/functional/openlp_core_ui/tests_formattingtagsform.py @@ -39,12 +39,11 @@ class TestFormattingTagForm(TestCase): # GIVEN: An instance of the Formatting Tag Form and a mocked save_push_button form = FormattingTagForm() - form.save_push_button = MagicMock() + form.save_button = MagicMock() # WHEN: on_text_edited is called with an arbitrary value - form.on_text_edited(u'text') + #form.on_text_edited('text') # THEN: setEnabled and setDefault should have been called on save_push_button - form.save_push_button.setEnabled.assert_called_with(True) - form.save_push_button.setDefault.assert_called_with(True) + #form.save_button.setEnabled.assert_called_with(True) From e98657d7041b25dc505eb433cb22bd6f4528b88b Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Mon, 2 Sep 2013 21:59:19 +0100 Subject: [PATCH 23/93] Fix up controller tests --- openlp/core/ui/formattingtagcontroller.py | 6 +- openlp/core/ui/formattingtagform.py | 10 ++- .../tests_formattingtagscontroller.py | 68 +++++++++++++++++-- 3 files changed, 76 insertions(+), 8 deletions(-) diff --git a/openlp/core/ui/formattingtagcontroller.py b/openlp/core/ui/formattingtagcontroller.py index 242b26e30..b0c10630a 100644 --- a/openlp/core/ui/formattingtagcontroller.py +++ b/openlp/core/ui/formattingtagcontroller.py @@ -76,14 +76,16 @@ class FormattingTagController(object): The end html tag. """ - if not desc: - pass for linenumber, html1 in enumerate(self.protected_tags): if self._strip(html1[u'start tag']) == tag: return translate('OpenLP.FormattingTagForm', 'Tag %s already defined.') % tag + if self._strip(html1[u'desc']) == desc: + return translate('OpenLP.FormattingTagForm', 'Description %s already defined.') % tag for linenumber, html1 in enumerate(self.custom_tags): if self._strip(html1[u'start tag']) == tag: return translate('OpenLP.FormattingTagForm', 'Tag %s already defined.') % tag + if self._strip(html1[u'desc']) == desc: + return translate('OpenLP.FormattingTagForm', 'Description %s already defined.') % tag tag = { 'desc': desc, 'start tag': '{%s}' % tag, diff --git a/openlp/core/ui/formattingtagform.py b/openlp/core/ui/formattingtagform.py index c2aeddc9a..05ffca57e 100644 --- a/openlp/core/ui/formattingtagform.py +++ b/openlp/core/ui/formattingtagform.py @@ -160,13 +160,18 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog, FormattingTagCont """ This function processes all user edits in the table. It is called on each cell change. """ - print (cur_row, cur_col, pre_col, pre_col) # only process for editable rows if self.tag_table_widget.item(pre_row, 0): item = self.tag_table_widget.item(pre_row, pre_col) text = item.text() errors = None - if pre_col is EDITCOLUMN.StartHtml: + if pre_col is EDITCOLUMN.Description: + if not text: + errors = translate('OpenLP.FormattingTagForm', 'Description is missing') + elif pre_col is EDITCOLUMN.Tag: + if not text: + errors = translate('OpenLP.FormattingTagForm', 'Tag is missing') + elif pre_col is EDITCOLUMN.StartHtml: # HTML edited item = self.tag_table_widget.item(pre_row, 3) end_html = item.text() @@ -184,5 +189,6 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog, FormattingTagCont if errors: QtGui.QMessageBox.warning(self, translate('OpenLP.FormattingTagForm', 'Validation Error'), errors, QtGui.QMessageBox.Ok) + #self.tag_table_widget.selectRow(pre_row - 1) self.tag_table_widget.resizeRowsToContents() diff --git a/tests/functional/openlp_core_ui/tests_formattingtagscontroller.py b/tests/functional/openlp_core_ui/tests_formattingtagscontroller.py index 7959e82b5..279dd43f1 100644 --- a/tests/functional/openlp_core_ui/tests_formattingtagscontroller.py +++ b/tests/functional/openlp_core_ui/tests_formattingtagscontroller.py @@ -3,8 +3,6 @@ Package to test the openlp.core.ui.formattingtagscontroller package. """ from unittest import TestCase -from mock import MagicMock, patch - from openlp.core.ui import FormattingTagController @@ -17,7 +15,6 @@ class TestFormattingTagController(TestCase): """ Test that the _strip strips the correct chars """ - # GIVEN: An instance of the Formatting Tag Form and a string containing a tag tag = u'{tag}' @@ -25,4 +22,67 @@ class TestFormattingTagController(TestCase): result = self.services._strip(tag) # THEN: The tag should be returned with the wrappers removed. - self.assertEqual(result, u'tag', u'FormattingTagForm._strip should return u\'tag\' when called with u\'{tag}\'') \ No newline at end of file + self.assertEqual(result, u'tag', u'FormattingTagForm._strip should return u\'tag\' when called with u\'{tag}\'') + + def test_end_tag_changed_processes_correctly(self): + """ + Test that the end html tags are generated correctly + """ + # GIVEN: A list of start , end tags and error messages + tests = [] + test = {'start': '', 'end': None, 'gen': '', 'valid': None} + tests.append(test) + test = {'start': '', 'end': '', 'gen': None, 'valid': None} + tests.append(test) + test = {'start': '', 'end': '', 'gen': None, + 'valid': 'End tag does not match end tag for start tag '} + tests.append(test) + + # WHEN: Testing each one of them in turn + for test in tests: + error, result = self.services.end_tag_changed(test['start'], test['end']) + + # THEN: The result should match the predetermined value. + self.assertTrue(result == test['gen'], + 'Function should handle end tag correctly : %s and %s for %s ' % (test['gen'], result, test['start'])) + self.assertTrue(error == test['valid'], + 'Function should not generate unexpected error messages : %s ' % error) + + def test_start_tag_changed_processes_correctly(self): + """ + Test that the end html tags are generated correctly + """ + # GIVEN: A list of start , end tags and error messages + tests = [] + test = {'start': '', 'end': '', 'gen': '', 'valid': None} + tests.append(test) + test = {'start': '', 'end': '', 'gen': None, 'valid': None} + tests.append(test) + test = {'start': 'superfly', 'end': '', 'gen': None, 'valid': 'Start tag superfly is not valid HTML'} + tests.append(test) + + # WHEN: Testing each one of them in turn + for test in tests: + error, result = self.services.start_tag_changed(test['start'], test['end']) + + # THEN: The result should match the predetermined value. + self.assertTrue(result == test['gen'], + 'Function should handle end tag correctly : %s and %s ' % (test['gen'], result)) + self.assertTrue(error == test['valid'], + 'Function should not generate unexpected error messages : %s ' % error) + + def test_start_html_to_end_html(self): + """ + Test that the end html tags are generated correctly + """ + # GIVEN: A list of valid and invalid tags + tests = {'': '', '': '', 'superfly': '', '': None, + '': ''} + + # WHEN: Testing each one of them + for test1, test2 in tests.items(): + result = self.services.start_html_to_end_html(test1) + + # THEN: The result should match the predetermined value. + self.assertTrue(result == test2, 'Calculated end tag should be valid: %s and %s = %s' + % (test1, test2, result)) \ No newline at end of file From 74e5bcf500b943d62c8ba6b4656e779cfe8e9c8d Mon Sep 17 00:00:00 2001 From: "Jeffrey S. Smith" Date: Fri, 6 Sep 2013 20:40:48 -0500 Subject: [PATCH 24/93] Update helper scripts to Python 3 --- scripts/check_dependencies.py | 6 +++--- scripts/translation_utils.py | 8 ++++---- setup.py | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/scripts/check_dependencies.py b/scripts/check_dependencies.py index 348a809eb..c37bc5821 100755 --- a/scripts/check_dependencies.py +++ b/scripts/check_dependencies.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 @@ -49,7 +49,7 @@ except ImportError: IS_WIN = sys.platform.startswith('win') VERS = { - 'Python': '2.6', + 'Python': '3.0', 'PyQt4': '4.6', 'Qt4': '4.6', 'sqlalchemy': '0.5', @@ -76,6 +76,7 @@ MODULES = [ 'PyQt4.QtWebKit', 'PyQt4.phonon', 'sqlalchemy', + 'alembic', 'sqlite3', 'lxml', 'chardet', @@ -85,7 +86,6 @@ MODULES = [ 'cherrypy', 'uno', 'icu', - 'bs4', ] diff --git a/scripts/translation_utils.py b/scripts/translation_utils.py index a9fa86db9..32f54a950 100755 --- a/scripts/translation_utils.py +++ b/scripts/translation_utils.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 @@ -102,7 +102,7 @@ class CommandStack(object): def __iter__(self): return self - def next(self): + def __next__(self): if self.current_index == len(self.data): raise StopIteration else: @@ -145,9 +145,9 @@ def print_quiet(text, linefeed=True): global quiet_mode if not quiet_mode: if linefeed: - print text + print (text) else: - print text, + print (text, end=' ') def print_verbose(text): """ diff --git a/setup.py b/setup.py index 0e8f02384..4e97683a9 100755 --- a/setup.py +++ b/setup.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 From 331bd1e9d4cc4edcb4e761de222c2fa7d05d2069 Mon Sep 17 00:00:00 2001 From: Oliver Wieland Date: Sat, 7 Sep 2013 07:30:08 +0200 Subject: [PATCH 25/93] Do not add a space on the front of each verse when no verse number is displayed --- openlp/plugins/bibles/lib/mediaitem.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index 01ba525a8..94f015fdb 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -803,20 +803,20 @@ class BibleMediaItem(MediaManagerItem): verses.add(book, chapter, verse, version, copyright, permissions) verse_text = self.formatVerse(old_chapter, chapter, verse) if second_bible: - bible_text = '%s %s\n\n%s %s' % (verse_text, text, verse_text, second_text) + bible_text = '%s%s\n\n%s %s' % (verse_text, text, verse_text, second_text) raw_slides.append(bible_text.rstrip()) bible_text = '' # If we are 'Verse Per Slide' then create a new slide. elif self.settings.layout_style == LayoutStyle.VersePerSlide: - bible_text = '%s %s' % (verse_text, text) + bible_text = '%s%s' % (verse_text, text) raw_slides.append(bible_text.rstrip()) bible_text = '' # If we are 'Verse Per Line' then force a new line. elif self.settings.layout_style == LayoutStyle.VersePerLine: - bible_text = '%s%s %s\n' % (bible_text, verse_text, text) + bible_text = '%s%s%s\n' % (bible_text, verse_text, text) # We have to be 'Continuous'. else: - bible_text = '%s %s %s\n' % (bible_text, verse_text, text) + bible_text = '%s %s%s\n' % (bible_text, verse_text, text) bible_text = bible_text.strip(' ') if not old_item: start_item = bitem @@ -950,12 +950,12 @@ class BibleMediaItem(MediaManagerItem): else: verse_text = str(verse) if self.settings.display_style == DisplayStyle.Round: - return '{su}(%s){/su}' % verse_text + return '{su}(%s){/su} ' % verse_text if self.settings.display_style == DisplayStyle.Curly: - return '{su}{%s}{/su}' % verse_text + return '{su}{%s}{/su} ' % verse_text if self.settings.display_style == DisplayStyle.Square: - return '{su}[%s]{/su}' % verse_text - return '{su}%s{/su}' % verse_text + return '{su}[%s]{/su} ' % verse_text + return '{su}%s{/su} ' % verse_text def search(self, string, showError): """ From ee6490dc2306143cf331e36357396fceeac25744 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sat, 7 Sep 2013 08:52:52 +0100 Subject: [PATCH 26/93] Fix missing () --- openlp/core/ui/servicemanager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index bc1409660..c00f4159b 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -1004,7 +1004,7 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog): """ Makes a specific item in the service live. """ - if index >= 0 and index < self.service_manager_list.topLevelItemCount: + if index >= 0 and index < self.service_manager_list.topLevelItemCount(): item = self.service_manager_list.topLevelItem(index) self.service_manager_list.setCurrentItem(item) self.make_live() From 87e7fcdbda62aedbe0855a97cff6a7fafa6af666 Mon Sep 17 00:00:00 2001 From: Philip Ridout Date: Sat, 7 Sep 2013 17:18:31 +0000 Subject: [PATCH 27/93] added fallback to retieve_windows encoding --- openlp/plugins/songs/lib/songshowplusimport.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/openlp/plugins/songs/lib/songshowplusimport.py b/openlp/plugins/songs/lib/songshowplusimport.py index c9ca2a345..a10ac7668 100644 --- a/openlp/plugins/songs/lib/songshowplusimport.py +++ b/openlp/plugins/songs/lib/songshowplusimport.py @@ -212,8 +212,4 @@ class SongShowPlusImport(SongImport): try: return unicode(data, chardet.detect(data)['encoding']) except: - while True: - try: - return unicode(data, self.encoding) - except: - self.encoding = retrieve_windows_encoding() \ No newline at end of file + return unicode(data, u'cp1252') \ No newline at end of file From a23022d84f31012327c817d49e4d2cc8f426203e Mon Sep 17 00:00:00 2001 From: "Jeffrey S. Smith" Date: Sat, 7 Sep 2013 12:33:36 -0500 Subject: [PATCH 28/93] Fix EasyWorship importer and its tests for py3 --- openlp/plugins/songs/lib/ewimport.py | 45 ++++++++++--------- .../openlp_plugins/songs/test_ewimport.py | 30 ++++++------- 2 files changed, 38 insertions(+), 37 deletions(-) diff --git a/openlp/plugins/songs/lib/ewimport.py b/openlp/plugins/songs/lib/ewimport.py index 380083aa2..622b7c39d 100644 --- a/openlp/plugins/songs/lib/ewimport.py +++ b/openlp/plugins/songs/lib/ewimport.py @@ -122,7 +122,7 @@ class EasyWorshipSongImport(SongImport): db_file.seek(120) field_info = db_file.read(num_fields * 2) db_file.seek(4 + (num_fields * 4) + 261, os.SEEK_CUR) - field_names = db_file.read(header_size - db_file.tell()).split('\0', num_fields) + field_names = db_file.read(header_size - db_file.tell()).split(b'\0', num_fields) field_names.pop() field_descs = [] for i, field_name in enumerate(field_names): @@ -132,12 +132,12 @@ class EasyWorshipSongImport(SongImport): # Pick out the field description indexes we will need try: success = True - fi_title = self.findField('Title') - fi_author = self.findField('Author') - fi_copy = self.findField('Copyright') - fi_admin = self.findField('Administrator') - fi_words = self.findField('Words') - fi_ccli = self.findField('Song Number') + fi_title = self.findField(b'Title') + fi_author = self.findField(b'Author') + fi_copy = self.findField(b'Copyright') + fi_admin = self.findField(b'Administrator') + fi_words = self.findField(b'Words') + fi_ccli = self.findField(b'Song Number') except IndexError: # This is the wrong table success = False @@ -150,7 +150,7 @@ class EasyWorshipSongImport(SongImport): cur_block_pos = header_size + ((cur_block - 1) * 1024 * block_size) db_file.seek(cur_block_pos) cur_block, rec_count = struct.unpack(' 63: - return '' + return b'' self.memoFile.seek(11 + (5 * sub_block), os.SEEK_CUR) sub_block_start, = struct.unpack('B', self.memoFile.read(1)) self.memoFile.seek(block_start + (sub_block_start * 16)) else: - return '' + return b'' return self.memoFile.read(blob_size) else: return 0 diff --git a/tests/functional/openlp_plugins/songs/test_ewimport.py b/tests/functional/openlp_plugins/songs/test_ewimport.py index 57959c8e2..68e934b6c 100644 --- a/tests/functional/openlp_plugins/songs/test_ewimport.py +++ b/tests/functional/openlp_plugins/songs/test_ewimport.py @@ -74,18 +74,18 @@ TEST_FIELD_DESCS = [TestFieldDesc('Title', FieldType.String, 50), TestFieldDesc('Default Background', FieldType.Logical, 1), TestFieldDesc('Words', FieldType.Memo, 250), TestFieldDesc('Words', FieldType.Memo, 250), TestFieldDesc('BK Bitmap', FieldType.Blob, 10), TestFieldDesc('Last Modified', FieldType.Timestamp, 10)] -TEST_FIELDS = ['A Heart Like Thine\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0', 32868, 2147483750, - 129, '{\\rtf1\\ansi\\deff0\\deftab254{\\fonttbl{\\f0\\fnil\\fcharset0 Arial;}{\\f1\\fnil\\fcharset0 Verdana;}}' - '{\\colortbl\\red0\\green0\\blue0;\\red255\\green0\\blue0;\\red0\\green128\\blue0;\\red0\\green0\\blue255;' - '\\red255\\green255\\blue0;\\red255\\green0\\blue255;\\red128\\g��\7\0f\r\0\0\1\0', - '{\\rtf1\\ansi\\deff0\\deftab254{\\fonttbl{\\f0\\fnil\\fcharset0 Arial;}{\\f1\\fnil\\fcharset0 Verdana;}}' - '{\\colortbl\\red0\\green0\\blue0;\\red255\\green0\\blue0;\\red0\\green128\\blue0;\\red0\\green0\\blue255;\\red255' - '\\green255\\blue0;\\red255\\green0\\blue255;\\red128\\g>�\6\0�\6\0\0\1\0', '\0\0\0\0\0\0\0\0\0\0', 0] +TEST_FIELDS = [b'A Heart Like Thine\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0', 32868, 2147483750, + 129, b'{\\rtf1\\ansi\\deff0\\deftab254{\\fonttbl{\\f0\\fnil\\fcharset0 Arial;}{\\f1\\fnil\\fcharset0 Verdana;}}' + b'{\\colortbl\\red0\\green0\\blue0;\\red255\\green0\\blue0;\\red0\\green128\\blue0;\\red0\\green0\\blue255;' + b'\\red255\\green255\\blue0;\\red255\\green0\\blue255;\\red128\\g\xBF\xBD\7\0f\r\0\0\1\0', + b'{\\rtf1\\ansi\\deff0\\deftab254{\\fonttbl{\\f0\\fnil\\fcharset0 Arial;}{\\f1\\fnil\\fcharset0 Verdana;}}' + b'{\\colortbl\\red0\\green0\\blue0;\\red255\\green0\\blue0;\\red0\\green128\\blue0;\\red0\\green0\\blue255;\\red255' + b'\\green255\\blue0;\\red255\\green0\\blue255;\\red128\\g\6\0\xEF\xBF\xBD\6\0\0\1\0', b'\0\0\0\0\0\0\0\0\0\0', 0] GET_MEMO_FIELD_TEST_RESULTS = [ - (4, '\2', {'return': '\2','read': (1, 3430), 'seek': (507136, (8, os.SEEK_CUR))}), - (4, '\3', {'return': '', 'read': (1, ), 'seek': (507136, )}), - (5, '\3', {'return': '\3', 'read': (1, 1725), 'seek': (3220111360, (41, os.SEEK_CUR), 3220111408)}), - (5, '\4', {'return': '', 'read': (), 'seek': ()})] + (4, b'\2', {'return': b'\2','read': (1, 3430), 'seek': (507136, (8, os.SEEK_CUR))}), + (4, b'\3', {'return': b'', 'read': (1, ), 'seek': (507136, )}), + (5, b'\3', {'return': b'\3', 'read': (1, 1725), 'seek': (3220111360, (41, os.SEEK_CUR), 3220111408)}), + (5, b'\4', {'return': b'', 'read': (), 'seek': ()})] class TestEasyWorshipSongImport(TestCase): """ @@ -189,7 +189,7 @@ class TestEasyWorshipSongImport(TestCase): importer.encoding = TEST_DATA_ENCODING importer.fields = TEST_FIELDS importer.fieldDescs = TEST_FIELD_DESCS - field_results = [(0, 'A Heart Like Thine'), (1, 100), (2, 102), (3, True), (6, None), (7, None)] + field_results = [(0, b'A Heart Like Thine'), (1, 100), (2, 102), (3, True), (6, None), (7, None)] # WHEN: Called with test data for field_index, result in field_results: @@ -276,7 +276,7 @@ class TestEasyWorshipSongImport(TestCase): # 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('__builtin__.open') as mocked_open, \ + patch('builtins.open') as mocked_open, \ patch('openlp.plugins.songs.lib.ewimport.struct') as mocked_struct: mocked_manager = MagicMock() importer = EasyWorshipSongImport(mocked_manager) @@ -303,7 +303,7 @@ class TestEasyWorshipSongImport(TestCase): # 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('__builtin__.open'), patch('openlp.plugins.songs.lib.ewimport.struct') as mocked_struct, \ + patch('builtins.open'), patch('openlp.plugins.songs.lib.ewimport.struct') as mocked_struct, \ patch('openlp.plugins.songs.lib.ewimport.retrieve_windows_encoding') as mocked_retrieve_windows_encoding: mocked_manager = MagicMock() importer = EasyWorshipSongImport(mocked_manager) @@ -354,7 +354,7 @@ class TestEasyWorshipSongImport(TestCase): # called. self.assertIsNone(importer.doImport(), 'doImport should return None when it has completed') for song_data in SONG_TEST_DATA: - print mocked_title.mocked_calls() + print (mocked_title.mocked_calls()) title = song_data['title'] author_calls = song_data['authors'] song_copyright = song_data['copyright'] From 3499f1ee65d78561ab768325098db9176fa4f396 Mon Sep 17 00:00:00 2001 From: Philip Ridout Date: Sat, 7 Sep 2013 17:37:40 +0000 Subject: [PATCH 29/93] missed some instances of unicode --- openlp/plugins/songs/lib/songshowplusimport.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openlp/plugins/songs/lib/songshowplusimport.py b/openlp/plugins/songs/lib/songshowplusimport.py index 315d3d18d..d1fa9dec3 100644 --- a/openlp/plugins/songs/lib/songshowplusimport.py +++ b/openlp/plugins/songs/lib/songshowplusimport.py @@ -160,7 +160,7 @@ class SongShowPlusImport(SongImport): elif block_key == VERSE_ORDER: verse_tag = self.to_openlp_verse_tag(data, True) if verse_tag: - if not isinstance(verse_tag, unicode): + if not isinstance(verse_tag, str): verse_tag = self.decode(verse_tag) self.ssp_verse_order_list.append(verse_tag) elif block_key == SONG_BOOK: @@ -210,6 +210,6 @@ class SongShowPlusImport(SongImport): def decode(self, data): try: - return unicode(data, chardet.detect(data)['encoding']) + return str(data, chardet.detect(data)['encoding']) except: - return unicode(data, 'cp1252') \ No newline at end of file + return str(data, 'cp1252') \ No newline at end of file From e0592a76e42930588e46c3f0f62349b2c956b900 Mon Sep 17 00:00:00 2001 From: "Jeffrey S. Smith" Date: Sat, 7 Sep 2013 13:36:33 -0500 Subject: [PATCH 30/93] Remove worthless print statement in EasyWorhip song import test --- tests/functional/openlp_plugins/songs/test_ewimport.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/functional/openlp_plugins/songs/test_ewimport.py b/tests/functional/openlp_plugins/songs/test_ewimport.py index 68e934b6c..3f1735a3c 100644 --- a/tests/functional/openlp_plugins/songs/test_ewimport.py +++ b/tests/functional/openlp_plugins/songs/test_ewimport.py @@ -354,7 +354,6 @@ class TestEasyWorshipSongImport(TestCase): # called. self.assertIsNone(importer.doImport(), 'doImport should return None when it has completed') for song_data in SONG_TEST_DATA: - print (mocked_title.mocked_calls()) title = song_data['title'] author_calls = song_data['authors'] song_copyright = song_data['copyright'] From c45d225b9dc453d829b5971a8eae22fb9be020b2 Mon Sep 17 00:00:00 2001 From: "Jeffrey S. Smith" Date: Sat, 7 Sep 2013 16:29:31 -0500 Subject: [PATCH 31/93] PyIcu is really a Windows-only dependency --- scripts/check_dependencies.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/check_dependencies.py b/scripts/check_dependencies.py index c37bc5821..07c84b76f 100755 --- a/scripts/check_dependencies.py +++ b/scripts/check_dependencies.py @@ -63,6 +63,7 @@ WIN32_MODULES = [ 'win32ui', 'pywintypes', 'pyodbc', + 'icu', ] MODULES = [ @@ -85,7 +86,6 @@ MODULES = [ 'mako', 'cherrypy', 'uno', - 'icu', ] From 53ac150337c10a4a65d3820bc612a6b117c7a9bd Mon Sep 17 00:00:00 2001 From: "Jeffrey S. Smith" Date: Sat, 7 Sep 2013 22:50:51 -0500 Subject: [PATCH 32/93] Fix strip_rtf to handle CJK encodings --- openlp/plugins/songs/lib/__init__.py | 115 ++++++++++-------- .../openlp_plugins/songs/test_lib.py | 34 +++++- 2 files changed, 99 insertions(+), 50 deletions(-) diff --git a/openlp/plugins/songs/lib/__init__.py b/openlp/plugins/songs/lib/__init__.py index 271a94710..ccbe35fd9 100644 --- a/openlp/plugins/songs/lib/__init__.py +++ b/openlp/plugins/songs/lib/__init__.py @@ -46,7 +46,7 @@ log = logging.getLogger(__name__) WHITESPACE = re.compile(r'[\W_]+', re.UNICODE) APOSTROPHE = re.compile('[\'`’ʻ′]', re.UNICODE) -PATTERN = re.compile(r"\\([a-z]{1,32})(-?\d{1,10})?[ ]?|\\'([0-9a-f]{2})|\\([^a-z])|([{}])|[\r\n]+|(.)", re.I) +PATTERN = re.compile(r"(\\\*)?\\([a-z]{1,32})(-?\d{1,10})?[ ]?|\\'([0-9a-f]{2})|\\([^a-z*])|([{}])|[\r\n]+|([^\\{}\r\n]+)", re.I) # RTF control words which specify a "destination" to be ignored. DESTINATIONS = frozenset(( 'aftncn', 'aftnsep', 'aftnsepc', 'annotation', 'atnauthor', @@ -57,8 +57,8 @@ DESTINATIONS = frozenset(( 'datafield', 'datastore', 'defchp', 'defpap', 'do', 'doccomm', 'docvar', 'dptxbxtext', 'ebcend', 'ebcstart', 'factoidname', 'falt', 'fchars', 'ffdeftext', 'ffentrymcr', 'ffexitmcr', - 'ffformat', 'ffhelptext', 'ffl', 'ffname', 'ffstattext', 'field', - 'file', 'filetbl', 'fldinst', 'fldrslt', 'fldtype', 'fname', + 'ffformat', 'ffhelptext', 'ffl', 'ffname', 'ffstattext', + 'file', 'filetbl', 'fldinst', 'fldtype', 'fname', 'fontemb', 'fontfile', 'footer', 'footerf', 'footerl', 'footerr', 'footnote', 'formfield', 'ftncn', 'ftnsep', 'ftnsepc', 'g', 'generator', 'gridtbl', 'header', 'headerf', 'headerl', @@ -106,6 +106,11 @@ DESTINATIONS = frozenset(( 'xmlclose', 'xmlname', 'xmlnstbl', 'xmlopen')) # Translation of some special characters. SPECIAL_CHARS = { + '\n': '\n', + '\r': '\n', + '~': '\u00A0', + '-': '\u00AD', + '_': '\u2011', 'par': '\n', 'sect': '\n\n', # Required page and column break. @@ -132,16 +137,19 @@ SPECIAL_CHARS = { 'zwj': '\u200D', 'zwnj': '\u200C'} CHARSET_MAPPING = { - 'fcharset0': 'cp1252', - 'fcharset161': 'cp1253', - 'fcharset162': 'cp1254', - 'fcharset163': 'cp1258', - 'fcharset177': 'cp1255', - 'fcharset178': 'cp1256', - 'fcharset186': 'cp1257', - 'fcharset204': 'cp1251', - 'fcharset222': 'cp874', - 'fcharset238': 'cp1250'} + '0': 'cp1252', + '128': 'cp932', + '129': 'cp949', + '134': 'cp936', + '161': 'cp1253', + '162': 'cp1254', + '163': 'cp1258', + '177': 'cp1255', + '178': 'cp1256', + '186': 'cp1257', + '204': 'cp1251', + '222': 'cp874', + '238': 'cp1250'} class VerseType(object): @@ -351,7 +359,7 @@ def retrieve_windows_encoding(recommendation=None): if recommendation == encodings[index][0]: recommended_index = index break - if recommended_index > 0: + if recommended_index > -1: choice = QtGui.QInputDialog.getItem(None, translate('SongsPlugin', 'Character Encoding'), translate('SongsPlugin', 'The codepage setting is responsible\n' @@ -365,7 +373,7 @@ def retrieve_windows_encoding(recommendation=None): [pair[1] for pair in encodings], 0, False) if not choice[1]: return None - return filter(lambda item: item[1] == choice[0], encodings)[0][0] + return next(filter(lambda item: item[1] == choice[0], encodings))[0] def clean_string(string): @@ -521,43 +529,59 @@ def strip_rtf(text, default_encoding=None): curskip = 0 # Output buffer. out = [] + # Encoded buffer. + ebytes = bytearray() for match in PATTERN.finditer(text): - word, arg, hex, char, brace, tchar = match.groups() + iinu, word, arg, hex, char, brace, tchar = match.groups() + # \x (non-alpha character) + if char: + if char in '\\{}': + tchar = char + else: + word = char + # Flush encoded buffer to output buffer + if ebytes and not hex and not tchar: + failed = False + while True: + try: + encoding, default_encoding = get_encoding(font, font_table, default_encoding, failed=failed) + if not encoding: + return None + dbytes = ebytes.decode(encoding) + # Code 5C is a peculiar case with Windows Codepage 932 + if encoding == 'cp932' and '\\' in dbytes: + dbytes = dbytes.replace('\\', '\u00A5') + out.append(dbytes) + ebytes.clear() + except UnicodeDecodeError: + failed = True + else: + break + # {} if brace: curskip = 0 if brace == '{': # Push state stack.append((ucskip, ignorable, font)) - elif brace == '}': + elif brace == '}' and len(stack) > 0: # Pop state ucskip, ignorable, font = stack.pop() - # \x (not a letter) - elif char: - curskip = 0 - if char == '~' and not ignorable: - out.append('\xA0') - elif char in '{}\\' and not ignorable: - out.append(char) - elif char == '-' and not ignorable: - out.append('\u00AD') - elif char == '_' and not ignorable: - out.append('\u2011') - elif char == '*': - ignorable = True # \command elif word: curskip = 0 if word in DESTINATIONS: ignorable = True elif word in SPECIAL_CHARS: - out.append(SPECIAL_CHARS[word]) + if not ignorable: + out.append(SPECIAL_CHARS[word]) elif word == 'uc': ucskip = int(arg) - elif word == ' ': + elif word == 'u': c = int(arg) if c < 0: c += 0x10000 - out.append(chr(c)) + if not ignorable: + out.append(chr(c)) curskip = ucskip elif word == 'fonttbl': ignorable = True @@ -565,31 +589,24 @@ def strip_rtf(text, default_encoding=None): font = arg elif word == 'ansicpg': font_table[font] = 'cp' + arg - elif word == 'fcharset' and font not in font_table and word + arg in CHARSET_MAPPING: - # \ansicpg overrides \fcharset, if present. - font_table[font] = CHARSET_MAPPING[word + arg] + elif word == 'fcharset' and font not in font_table and arg in CHARSET_MAPPING: + font_table[font] = CHARSET_MAPPING[arg] + elif word == 'fldrslt': + pass + # \* 'Ignore if not understood' marker + elif iinu: + ignorable = True # \'xx elif hex: if curskip > 0: curskip -= 1 elif not ignorable: - charcode = int(hex, 16) - failed = False - while True: - try: - encoding, default_encoding = get_encoding(font, font_table, default_encoding, failed=failed) - if not encoding: - return None - out.append(chr(charcode).decode(encoding)) - except UnicodeDecodeError: - failed = True - else: - break + ebytes.append(int(hex, 16)) elif tchar: if curskip > 0: curskip -= 1 elif not ignorable: - out.append(tchar) + ebytes += tchar.encode() text = ''.join(out) return text, default_encoding diff --git a/tests/functional/openlp_plugins/songs/test_lib.py b/tests/functional/openlp_plugins/songs/test_lib.py index ac22ae1ef..a9e64b5c9 100644 --- a/tests/functional/openlp_plugins/songs/test_lib.py +++ b/tests/functional/openlp_plugins/songs/test_lib.py @@ -6,7 +6,7 @@ from unittest import TestCase from mock import patch, MagicMock -from openlp.plugins.songs.lib import VerseType, clean_string, clean_title +from openlp.plugins.songs.lib import VerseType, clean_string, clean_title, strip_rtf from openlp.plugins.songs.lib.songcompare import songs_probably_equal, _remove_typos, _op_length @@ -215,6 +215,38 @@ class TestLib(TestCase): # THEN: The maximum length should be returned. assert result == 10, 'The length should be 10.' + def strip_rtf_charsets_test(self): + """ + Test that the strip_rtf() method properly decodes the supported charsets. + """ + test_charset_table = [ + ('0', 'weor\\\'F0-myndum \\\'FEah\\par ', 'weorð-myndum þah\n'), + ('128', '\\\'83C\\\'83G\\\'83X\\\'A5\\\'83L\\\'83\\\'8A\\\'83X\\\'83g\\\'A1 ' + '\\\\ \\\'95\\\\ \\\'8E\\} \\\'8E\\{ \\\'A1\\par ', 'イエス・キリスト。 ¥ 表 枝 施 。\n'), + ('129', '\\\'BF\\\'B9\\\'BC\\\'F6 \\\'B1\\\'D7\\\'B8\\\'AE\\\'BD\\\'BA\\\'B5\\\'B5\\par ', '예수 그리스도\n'), + ('134', '\\\'D2\\\'AE\\\'F6\\\'D5\\\'BB\\\'F9\\\'B6\\\'BD\\\'CA\\\'C7\\\'D6\\\'F7\\par ', '耶稣基督是主\n'), + ('161', '\\\'D7\\\'F1\\\'E9\\\'F3\\\'F4\\\'FC\\\'F2\\par ', 'Χριστός\n'), + ('162', 'Hazreti \\\'DDsa\\par ', 'Hazreti İsa\n'), + ('163', 'ph\\\'FD\\\'F5ng\\par ', 'phương\n'), + ('177', '\\\'E1\\\'F8\\\'E0\\\'F9\\\'E9\\\'FA\\par ', 'בראשית\n'), + ('178', '\\\'ED\\\'D3\\\'E6\\\'DA \\\'C7\\\'E1\\\'E3\\\'D3\\\'ED\\\'CD\\par ', 'يسوع المسيح\n'), + ('186', 'J\\\'EBzus Kristus yra Vie\\\'F0pats\\par ', 'Jėzus Kristus yra Viešpats\n'), + ('204', '\\\'D0\\\'EE\\\'F1\\\'F1\\\'E8\\\'FF\\par ', 'Россия\n'), + ('222', '\\\'A4\\\'C3\\\'D4\\\'CA\\\'B5\\\'EC\\par ', 'คริสต์\n'), + ('238', 'Z\\\'E1v\\\'ECre\\\'E8n\\\'E1 zkou\\\'9Aka\\par ', 'Závěrečná zkouška\n') + ] + + # GIVEN: For each character set and input + for charset, input, exp_result in test_charset_table: + + # WHEN: We call strip_rtf on the input RTF + result, result_enc = strip_rtf( + '{\\rtf1 \\ansi \\ansicpg1252 {\\fonttbl \\f0 \\fswiss \\fcharset%s Helvetica;}' \ + '{\\colortbl ;\\red0 \\green0 \\blue0 ;}\\pard \\f0 %s}' % (charset, input)) + + # THEN: The stripped text matches thed expected result + assert result == exp_result, 'The result should be %s' % exp_result + class TestVerseType(TestCase): """ From 5406a44d5977f45647bd02f01e736793b62acdef Mon Sep 17 00:00:00 2001 From: Oliver Wieland Date: Sun, 8 Sep 2013 11:29:36 +0200 Subject: [PATCH 33/93] fixes wrong url on the remotes tab --- openlp/plugins/remotes/lib/remotetab.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/plugins/remotes/lib/remotetab.py b/openlp/plugins/remotes/lib/remotetab.py index 338abc4a0..b9c71f338 100644 --- a/openlp/plugins/remotes/lib/remotetab.py +++ b/openlp/plugins/remotes/lib/remotetab.py @@ -209,7 +209,7 @@ class RemoteTab(SettingsTab): for address in interface.addressEntries(): ip = address.ip() if ip.protocol() == 0 and ip != QtNetwork.QHostAddress.LocalHost: - ip_address = ip + ip_address = ip.toString() break else: ip_address = self.address_edit.text() From a5b517fbf0c823992f1fa536b40a316716536268 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sun, 8 Sep 2013 12:30:14 +0200 Subject: [PATCH 34/93] started to write tests --- .../openlp_core_lib/test_htmlbuilder.py | 76 +++++++++++++++++++ .../openlp_core_lib/test_image_manager.py | 2 +- .../openlp_core_lib/test_serviceitem.py | 3 +- .../openlp_core_lib/test_settings.py | 6 +- 4 files changed, 82 insertions(+), 5 deletions(-) create mode 100644 tests/functional/openlp_core_lib/test_htmlbuilder.py diff --git a/tests/functional/openlp_core_lib/test_htmlbuilder.py b/tests/functional/openlp_core_lib/test_htmlbuilder.py new file mode 100644 index 000000000..0ddf2bd4b --- /dev/null +++ b/tests/functional/openlp_core_lib/test_htmlbuilder.py @@ -0,0 +1,76 @@ +""" +Package to test the openlp.core.lib.htmlbuilder module. +""" + +from unittest import TestCase +from mock import MagicMock + +from PyQt4 import QtCore + +from openlp.core.lib.htmlbuilder import build_footer_css, build_lyrics_outline_css + + +FOOTER_CSS = """ + left: 10px; + bottom: 0px; + width: 1260px; + font-family: Arial; + font-size: 12pt; + color: #FFFFFF; + text-align: left; + white-space: nowrap; + """ +OUTLINE_CSS = ' -webkit-text-stroke: 0.125em #000000; -webkit-text-fill-color: #FFFFFF; ' +LYRICS_CSS = 'white-space:pre-wrap; word-wrap: break-word; text-align: left; vertical-align: top; ' + \ + 'font-family: Arial; font-size: 40pt; color: #FFFFFF; line-height: 100%; margin: 0;padding: 0; ' + \ + 'padding-bottom: 0; padding-left: 0px; width: 1260px; height: 921px; ' + +class Htmbuilder(TestCase): + def build_html(self): + pass + + def build_background_css(self): + pass + + def build_lyrics_css_test(self): + """ + """ + item = MagicMock() + item.main = + item.themedata.font_main_shadow = + item.themedata.font_main_shadow_color = + item.themedata.font_main_shadow_size = + item.themedata.font_main_shadow = + assert LYRICS_CSS = build_lyrics_css(item), 'The lyrics css should be equal.' + + def build_lyrics_outline_css_test(self): + """ + Test the build_lyrics_outline_css() function + """ + theme_data = MagicMock() + theme_data.font_main_outline = True + theme_data.font_main_outline_size = 2 + theme_data.font_main_color = '#FFFFFF' + theme_data.font_main_outline_color = '#000000' + assert OUTLINE_CSS == build_lyrics_outline_css(theme_data), 'The outline css should be equal.' + + + def build_lyrics_format_css(self): + """ + Test the build_lyrics_format_css() function + """ + pass + + def build_footer_css_test(self): + """ + Test the build_footer_css() function + """ + # Create a theme. + item = MagicMock() + item.footer = QtCore.QRect(10, 921, 1260, 103) + item.themedata.font_footer_name = 'Arial' + item.themedata.font_footer_size = 12 + item.themedata.font_footer_color = '#FFFFFF' + height = 1024 + assert FOOTER_CSS == build_footer_css(item, height), 'The footer strings should be equal.' + diff --git a/tests/functional/openlp_core_lib/test_image_manager.py b/tests/functional/openlp_core_lib/test_image_manager.py index a1bc7624a..71adfe4e2 100644 --- a/tests/functional/openlp_core_lib/test_image_manager.py +++ b/tests/functional/openlp_core_lib/test_image_manager.py @@ -3,8 +3,8 @@ """ import os -from unittest import TestCase +from unittest import TestCase from PyQt4 import QtCore, QtGui from openlp.core.lib import Registry, ImageManager, ScreenList diff --git a/tests/functional/openlp_core_lib/test_serviceitem.py b/tests/functional/openlp_core_lib/test_serviceitem.py index c387d83ad..1db175713 100644 --- a/tests/functional/openlp_core_lib/test_serviceitem.py +++ b/tests/functional/openlp_core_lib/test_serviceitem.py @@ -6,11 +6,12 @@ import os import json import tempfile + from unittest import TestCase from mock import MagicMock, patch +from lxml import objectify, etree from openlp.core.lib import ItemCapabilities, ServiceItem, Registry -from lxml import objectify, etree VERSE = 'The Lord said to {r}Noah{/r}: \n'\ 'There\'s gonna be a {su}floody{/su}, {sb}floody{/sb}\n'\ diff --git a/tests/functional/openlp_core_lib/test_settings.py b/tests/functional/openlp_core_lib/test_settings.py index 444b206d6..30e8d960f 100644 --- a/tests/functional/openlp_core_lib/test_settings.py +++ b/tests/functional/openlp_core_lib/test_settings.py @@ -2,13 +2,13 @@ Package to test the openlp.core.lib.settings package. """ import os -from unittest import TestCase from tempfile import mkstemp -from openlp.core.lib import Settings - +from unittest import TestCase from PyQt4 import QtGui +from openlp.core.lib import Settings + class TestSettings(TestCase): """ From afb8f088a6484b51c5e120f927ea28ff50b4ad70 Mon Sep 17 00:00:00 2001 From: "Jeffrey S. Smith" Date: Sun, 8 Sep 2013 08:17:14 -0500 Subject: [PATCH 35/93] Remove extra whitespace --- scripts/translation_utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/translation_utils.py b/scripts/translation_utils.py index 32f54a950..0069d411a 100755 --- a/scripts/translation_utils.py +++ b/scripts/translation_utils.py @@ -145,9 +145,9 @@ def print_quiet(text, linefeed=True): global quiet_mode if not quiet_mode: if linefeed: - print (text) + print(text) else: - print (text, end=' ') + print(text, end=' ') def print_verbose(text): """ From 2aeaa24871416c0df9772ba8f245c6add38ff977 Mon Sep 17 00:00:00 2001 From: Oliver Wieland Date: Sun, 8 Sep 2013 18:16:18 +0200 Subject: [PATCH 36/93] remotetab.py: separate get ip address for metter testing test_remotetab.py:tests for get_ip_address --- openlp/plugins/remotes/lib/remotetab.py | 30 ++++++++--------- .../openlp_plugins/remotes/test_remotetab.py | 33 ++++++++++++++++++- 2 files changed, 47 insertions(+), 16 deletions(-) diff --git a/openlp/plugins/remotes/lib/remotetab.py b/openlp/plugins/remotes/lib/remotetab.py index b9c71f338..b84faf7bd 100644 --- a/openlp/plugins/remotes/lib/remotetab.py +++ b/openlp/plugins/remotes/lib/remotetab.py @@ -198,21 +198,7 @@ class RemoteTab(SettingsTab): """ Update the display based on the data input on the screen """ - ip_address = 'localhost' - if self.address_edit.text() == ZERO_URL: - interfaces = QtNetwork.QNetworkInterface.allInterfaces() - for interface in interfaces: - if not interface.isValid(): - continue - if not (interface.flags() & (QtNetwork.QNetworkInterface.IsUp | QtNetwork.QNetworkInterface.IsRunning)): - continue - for address in interface.addressEntries(): - ip = address.ip() - if ip.protocol() == 0 and ip != QtNetwork.QHostAddress.LocalHost: - ip_address = ip.toString() - break - else: - ip_address = self.address_edit.text() + ip_address = self.get_ip_address(self.address_edit.text()) http_url = 'http://%s:%s/' % (ip_address, self.port_spin_box.value()) https_url = 'https://%s:%s/' % (ip_address, self.https_port_spin_box.value()) self.remote_url.setText('%s' % (http_url, http_url)) @@ -226,6 +212,20 @@ class RemoteTab(SettingsTab): self.live_url.setText('%s' % (http_url_temp, http_url_temp)) self.live_https_url.setText('%s' % (https_url_temp, https_url_temp)) + def get_ip_address(self, ip): + if ip == ZERO_URL: + interfaces = QtNetwork.QNetworkInterface.allInterfaces() + for interface in interfaces: + if not interface.isValid(): + continue + if not (interface.flags() & (QtNetwork.QNetworkInterface.IsUp | QtNetwork.QNetworkInterface.IsRunning)): + continue + for address in interface.addressEntries(): + ip = address.ip() + if ip.protocol() == 0 and ip != QtNetwork.QHostAddress.LocalHost: + return ip.toString() + return ip + def load(self): """ Load the configuration and update the server configuration if necessary diff --git a/tests/functional/openlp_plugins/remotes/test_remotetab.py b/tests/functional/openlp_plugins/remotes/test_remotetab.py index 9ff795e73..81b67dbea 100644 --- a/tests/functional/openlp_plugins/remotes/test_remotetab.py +++ b/tests/functional/openlp_plugins/remotes/test_remotetab.py @@ -2,7 +2,7 @@ This module contains tests for the lib submodule of the Remotes plugin. """ import os - +import re from unittest import TestCase from tempfile import mkstemp from mock import patch @@ -52,6 +52,37 @@ class TestRemoteTab(TestCase): del self.form os.unlink(self.ini_file) + def get_ip_address_default_test(self): + """ + Test the get_ip_address function with ZERO_URL + """ + # GIVEN: A mocked location + with patch('openlp.core.utils.applocation.Settings') as mocked_class, \ + patch('openlp.core.utils.AppLocation.get_directory') as mocked_get_directory, \ + patch('openlp.core.utils.applocation.check_directory_exists') as mocked_check_directory_exists, \ + patch('openlp.core.utils.applocation.os') as mocked_os: + # GIVEN: A mocked out Settings class and a mocked out AppLocation.get_directory() + # WHEN: the default ip address is given + ip_address = self.form.get_ip_address(ZERO_URL) + # THEN: the default ip address will be returned + self.assertTrue(re.match('\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}', ip_address), 'The return value should be a valid ip address') + + def get_ip_address_with_ip_test(self): + """ + Test the get_ip_address function with given ip address + """ + # GIVEN: A mocked location + with patch('openlp.core.utils.applocation.Settings') as mocked_class, \ + patch('openlp.core.utils.AppLocation.get_directory') as mocked_get_directory, \ + patch('openlp.core.utils.applocation.check_directory_exists') as mocked_check_directory_exists, \ + patch('openlp.core.utils.applocation.os') as mocked_os: + # GIVEN: An ip address + given_ip = '192.168.1.1' + # WHEN: the default ip address is given + ip_address = self.form.get_ip_address(given_ip) + # THEN: the default ip address will be returned + self.assertEqual(ip_address, given_ip, 'The return value should be %s' % given_ip) + def set_basic_urls_test(self): """ Test the set_urls function with standard defaults From 7d4701030492143e6bc94629ddbd30ff226cf39b Mon Sep 17 00:00:00 2001 From: Oliver Wieland Date: Sun, 8 Sep 2013 18:24:21 +0200 Subject: [PATCH 37/93] cleaning tests --- .../openlp_plugins/remotes/test_remotetab.py | 30 +++++++------------ 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/tests/functional/openlp_plugins/remotes/test_remotetab.py b/tests/functional/openlp_plugins/remotes/test_remotetab.py index 81b67dbea..1ee9a3695 100644 --- a/tests/functional/openlp_plugins/remotes/test_remotetab.py +++ b/tests/functional/openlp_plugins/remotes/test_remotetab.py @@ -56,32 +56,22 @@ class TestRemoteTab(TestCase): """ Test the get_ip_address function with ZERO_URL """ - # GIVEN: A mocked location - with patch('openlp.core.utils.applocation.Settings') as mocked_class, \ - patch('openlp.core.utils.AppLocation.get_directory') as mocked_get_directory, \ - patch('openlp.core.utils.applocation.check_directory_exists') as mocked_check_directory_exists, \ - patch('openlp.core.utils.applocation.os') as mocked_os: - # GIVEN: A mocked out Settings class and a mocked out AppLocation.get_directory() - # WHEN: the default ip address is given - ip_address = self.form.get_ip_address(ZERO_URL) - # THEN: the default ip address will be returned - self.assertTrue(re.match('\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}', ip_address), 'The return value should be a valid ip address') + # WHEN: the default ip address is given + ip_address = self.form.get_ip_address(ZERO_URL) + # THEN: the default ip address will be returned + self.assertTrue(re.match('\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}', ip_address), 'The return value should be a valid ip address') def get_ip_address_with_ip_test(self): """ Test the get_ip_address function with given ip address """ # GIVEN: A mocked location - with patch('openlp.core.utils.applocation.Settings') as mocked_class, \ - patch('openlp.core.utils.AppLocation.get_directory') as mocked_get_directory, \ - patch('openlp.core.utils.applocation.check_directory_exists') as mocked_check_directory_exists, \ - patch('openlp.core.utils.applocation.os') as mocked_os: - # GIVEN: An ip address - given_ip = '192.168.1.1' - # WHEN: the default ip address is given - ip_address = self.form.get_ip_address(given_ip) - # THEN: the default ip address will be returned - self.assertEqual(ip_address, given_ip, 'The return value should be %s' % given_ip) + # GIVEN: An ip address + given_ip = '192.168.1.1' + # WHEN: the default ip address is given + ip_address = self.form.get_ip_address(given_ip) + # THEN: the default ip address will be returned + self.assertEqual(ip_address, given_ip, 'The return value should be %s' % given_ip) def set_basic_urls_test(self): """ From c0d19ec840fed8d666f76be1101b4a1321855063 Mon Sep 17 00:00:00 2001 From: Oliver Wieland Date: Sun, 8 Sep 2013 18:40:48 +0200 Subject: [PATCH 38/93] rename variable --- openlp/plugins/remotes/lib/remotetab.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openlp/plugins/remotes/lib/remotetab.py b/openlp/plugins/remotes/lib/remotetab.py index b84faf7bd..e30ad3ea8 100644 --- a/openlp/plugins/remotes/lib/remotetab.py +++ b/openlp/plugins/remotes/lib/remotetab.py @@ -212,8 +212,8 @@ class RemoteTab(SettingsTab): self.live_url.setText('%s' % (http_url_temp, http_url_temp)) self.live_https_url.setText('%s' % (https_url_temp, https_url_temp)) - def get_ip_address(self, ip): - if ip == ZERO_URL: + def get_ip_address(self, ip_address): + if ip_address == ZERO_URL: interfaces = QtNetwork.QNetworkInterface.allInterfaces() for interface in interfaces: if not interface.isValid(): @@ -224,7 +224,7 @@ class RemoteTab(SettingsTab): ip = address.ip() if ip.protocol() == 0 and ip != QtNetwork.QHostAddress.LocalHost: return ip.toString() - return ip + return ip_address def load(self): """ From 86c37481ab4a750207e2de58950e4489532f6158 Mon Sep 17 00:00:00 2001 From: Oliver Wieland Date: Sun, 8 Sep 2013 21:24:53 +0200 Subject: [PATCH 39/93] doc string added --- openlp/plugins/remotes/lib/remotetab.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/openlp/plugins/remotes/lib/remotetab.py b/openlp/plugins/remotes/lib/remotetab.py index e30ad3ea8..b1cf4a803 100644 --- a/openlp/plugins/remotes/lib/remotetab.py +++ b/openlp/plugins/remotes/lib/remotetab.py @@ -213,6 +213,11 @@ class RemoteTab(SettingsTab): self.live_https_url.setText('%s' % (https_url_temp, https_url_temp)) def get_ip_address(self, ip_address): + """ + returns the IP address in dependency of the passed address + ip_address == 0.0.0.0: return the IP address of the first valid interface + else: return ip_address + """ if ip_address == ZERO_URL: interfaces = QtNetwork.QNetworkInterface.allInterfaces() for interface in interfaces: From 71f74a53c2a7e3827503353f91d0f431a393d645 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sun, 8 Sep 2013 22:13:15 +0100 Subject: [PATCH 40/93] Rename class --- openlp/core/ui/formattingtagform.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/openlp/core/ui/formattingtagform.py b/openlp/core/ui/formattingtagform.py index 05ffca57e..5a2409322 100644 --- a/openlp/core/ui/formattingtagform.py +++ b/openlp/core/ui/formattingtagform.py @@ -39,7 +39,7 @@ from openlp.core.ui.formattingtagdialog import Ui_FormattingTagDialog from openlp.core.ui.formattingtagcontroller import FormattingTagController -class EDITCOLUMN(object): +class EditColumn(object): """ Hides the magic numbers for the table columns """ @@ -165,13 +165,13 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog, FormattingTagCont item = self.tag_table_widget.item(pre_row, pre_col) text = item.text() errors = None - if pre_col is EDITCOLUMN.Description: + if pre_col is EditColumn.Description: if not text: errors = translate('OpenLP.FormattingTagForm', 'Description is missing') - elif pre_col is EDITCOLUMN.Tag: + elif pre_col is EditColumn.Tag: if not text: errors = translate('OpenLP.FormattingTagForm', 'Tag is missing') - elif pre_col is EDITCOLUMN.StartHtml: + elif pre_col is EditColumn.StartHtml: # HTML edited item = self.tag_table_widget.item(pre_row, 3) end_html = item.text() @@ -179,7 +179,7 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog, FormattingTagCont if tag: self.tag_table_widget.setItem(pre_row, 3, QtGui.QTableWidgetItem(tag)) self.tag_table_widget.resizeRowsToContents() - elif pre_col is EDITCOLUMN.EndHtml: + elif pre_col is EditColumn.EndHtml: # HTML edited item = self.tag_table_widget.item(pre_row, 2) start_html = item.text() From e5849077188110b2c6d1112ad3277aa2a94658d2 Mon Sep 17 00:00:00 2001 From: Oliver Wieland Date: Mon, 9 Sep 2013 18:32:13 +0200 Subject: [PATCH 41/93] replaced constant value with enum --- openlp/plugins/remotes/lib/remotetab.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/plugins/remotes/lib/remotetab.py b/openlp/plugins/remotes/lib/remotetab.py index b1cf4a803..c06d71ee9 100644 --- a/openlp/plugins/remotes/lib/remotetab.py +++ b/openlp/plugins/remotes/lib/remotetab.py @@ -227,7 +227,7 @@ class RemoteTab(SettingsTab): continue for address in interface.addressEntries(): ip = address.ip() - if ip.protocol() == 0 and ip != QtNetwork.QHostAddress.LocalHost: + if ip.protocol() == QtNetwork.QAbstractSocket.IPv4Protocol and ip != QtNetwork.QHostAddress.LocalHost: return ip.toString() return ip_address From 334cef478da3d57fef89323919a7ec932ba20c3c Mon Sep 17 00:00:00 2001 From: "Jeffrey S. Smith" Date: Mon, 9 Sep 2013 16:10:40 -0500 Subject: [PATCH 42/93] Fix various tests and broken code revealed by tests --- openlp/core/ui/starttimeform.py | 4 ++-- openlp/core/utils/__init__.py | 2 +- openlp/plugins/bibles/lib/http.py | 2 +- tests/functional/openlp_core_lib/test_lib.py | 4 ++-- tests/functional/openlp_core_utils/test_utils.py | 4 ++++ tests/interfaces/openlp_plugins/bibles/test_lib_http.py | 4 ++-- tests/interfaces/openlp_plugins/remotes/test_server.py | 2 +- 7 files changed, 13 insertions(+), 9 deletions(-) diff --git a/openlp/core/ui/starttimeform.py b/openlp/core/ui/starttimeform.py index 4fddb2a90..0a0867b3f 100644 --- a/openlp/core/ui/starttimeform.py +++ b/openlp/core/ui/starttimeform.py @@ -88,9 +88,9 @@ class StartTimeForm(QtGui.QDialog, Ui_StartTimeDialog): """ Split time up into hours minutes and seconds from secongs """ - hours = seconds / 3600 + hours = seconds // 3600 seconds -= 3600 * hours - minutes = seconds / 60 + minutes = seconds // 60 seconds -= 60 * minutes return hours, minutes, seconds diff --git a/openlp/core/utils/__init__.py b/openlp/core/utils/__init__.py index 3bd052a0e..d2e664e75 100644 --- a/openlp/core/utils/__init__.py +++ b/openlp/core/utils/__init__.py @@ -256,7 +256,7 @@ def is_not_image_file(file_name): if not file_name: return True else: - formats = [str(fmt).lower() for fmt in QtGui.QImageReader.supportedImageFormats()] + formats = [bytes(fmt).decode().lower() for fmt in QtGui.QImageReader.supportedImageFormats()] file_part, file_extension = os.path.splitext(str(file_name)) if file_extension[1:].lower() in formats and os.path.exists(file_name): return False diff --git a/openlp/plugins/bibles/lib/http.py b/openlp/plugins/bibles/lib/http.py index 5d9e51c15..0fee09265 100644 --- a/openlp/plugins/bibles/lib/http.py +++ b/openlp/plugins/bibles/lib/http.py @@ -717,7 +717,7 @@ def get_soup_for_bible_ref(reference_url, header=None, pre_parse_regex=None, pre return None page_source = page.read() if pre_parse_regex and pre_parse_substitute is not None: - page_source = re.sub(pre_parse_regex, pre_parse_substitute, page_source) + page_source = re.sub(pre_parse_regex, pre_parse_substitute, page_source.decode()) soup = None try: soup = BeautifulSoup(page_source) diff --git a/tests/functional/openlp_core_lib/test_lib.py b/tests/functional/openlp_core_lib/test_lib.py index a7d7d5d88..fa7fe92e9 100644 --- a/tests/functional/openlp_core_lib/test_lib.py +++ b/tests/functional/openlp_core_lib/test_lib.py @@ -187,7 +187,7 @@ class TestLib(TestCase): """ Test the get_text_file_string() method when a read error happens """ - with patch('openlp.core.lib.os.path.isfile') as mocked_isfile, patch('builtins.open') as mocked_open: + with patch('openlp.core.lib.os.path.isfile') as mocked_isfile, patch('openlp.core.lib.open', create=True) as mocked_open: # GIVEN: A mocked-out open() which raises an exception and isfile returns True filename = 'testfile.txt' mocked_isfile.return_value = True @@ -252,7 +252,7 @@ class TestLib(TestCase): # GIVEN: A set of mocked-out Qt classes mocked_byte_array = MagicMock() MockedQtCore.QByteArray.return_value = mocked_byte_array - mocked_byte_array.toBase64.return_value = 'base64mock' + mocked_byte_array.toBase64.return_value = QtCore.QByteArray('base64mock') mocked_buffer = MagicMock() MockedQtCore.QBuffer.return_value = mocked_buffer MockedQtCore.QIODevice.WriteOnly = 'writeonly' diff --git a/tests/functional/openlp_core_utils/test_utils.py b/tests/functional/openlp_core_utils/test_utils.py index 6eacb2e48..2aa1f9611 100644 --- a/tests/functional/openlp_core_utils/test_utils.py +++ b/tests/functional/openlp_core_utils/test_utils.py @@ -8,6 +8,8 @@ from mock import patch from openlp.core.utils import clean_filename, get_filesystem_encoding, _get_frozen_path, get_locale_key, \ get_natural_key, split_filename +import os + class TestUtils(TestCase): """ @@ -120,6 +122,8 @@ class TestUtils(TestCase): # THEN: We get a properly sorted list test_passes = sorted(unsorted_list, key=get_locale_key) == ['Aushang', '\u00C4u\u00DFerung', 'Auszug'] assert test_passes, 'Strings should be sorted properly' + if os.name != 'nt': + del get_locale_key_windows_test def get_locale_key_linux_test(self): diff --git a/tests/interfaces/openlp_plugins/bibles/test_lib_http.py b/tests/interfaces/openlp_plugins/bibles/test_lib_http.py index f760996b5..bd645f1ff 100644 --- a/tests/interfaces/openlp_plugins/bibles/test_lib_http.py +++ b/tests/interfaces/openlp_plugins/bibles/test_lib_http.py @@ -43,7 +43,7 @@ class TestBibleHTTP(TestCase): results = handler.get_bible_chapter('NIV', 'John', 3) # THEN: We should get back a valid service item - assert len(results.verselist) == 36, 'The book of John should not have had any verses added or removed' + assert len(results.verse_list) == 36, 'The book of John should not have had any verses added or removed' def crosswalk_extract_books_test(self): """ @@ -69,5 +69,5 @@ class TestBibleHTTP(TestCase): results = handler.get_bible_chapter('niv', 'john', 3) # THEN: We should get back a valid service item - assert len(results.verselist) == 36, 'The book of John should not have had any verses added or removed' + assert len(results.verse_list) == 36, 'The book of John should not have had any verses added or removed' diff --git a/tests/interfaces/openlp_plugins/remotes/test_server.py b/tests/interfaces/openlp_plugins/remotes/test_server.py index 63975370f..6cb44a933 100644 --- a/tests/interfaces/openlp_plugins/remotes/test_server.py +++ b/tests/interfaces/openlp_plugins/remotes/test_server.py @@ -9,7 +9,7 @@ from mock import MagicMock import urllib.request, urllib.error, urllib.parse import cherrypy -from BeautifulSoup import BeautifulSoup +from bs4 import BeautifulSoup from openlp.core.lib import Settings from openlp.plugins.remotes.lib.httpserver import HttpServer From 1106bca319efca63027fcf07afd0d009535cec98 Mon Sep 17 00:00:00 2001 From: Oliver Wieland Date: Tue, 10 Sep 2013 20:18:53 +0200 Subject: [PATCH 43/93] added blank line --- tests/functional/openlp_plugins/remotes/test_remotetab.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/functional/openlp_plugins/remotes/test_remotetab.py b/tests/functional/openlp_plugins/remotes/test_remotetab.py index 1ee9a3695..e683699cd 100644 --- a/tests/functional/openlp_plugins/remotes/test_remotetab.py +++ b/tests/functional/openlp_plugins/remotes/test_remotetab.py @@ -3,6 +3,7 @@ This module contains tests for the lib submodule of the Remotes plugin. """ import os import re + from unittest import TestCase from tempfile import mkstemp from mock import patch From 30618ad60c44b2bf0c36d2c79622c91c7437900e Mon Sep 17 00:00:00 2001 From: "Jeffrey S. Smith" Date: Tue, 10 Sep 2013 15:36:46 -0500 Subject: [PATCH 44/93] Explain a complex regex used by strip_rtf --- openlp/plugins/songs/lib/__init__.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/openlp/plugins/songs/lib/__init__.py b/openlp/plugins/songs/lib/__init__.py index ccbe35fd9..12874aa89 100644 --- a/openlp/plugins/songs/lib/__init__.py +++ b/openlp/plugins/songs/lib/__init__.py @@ -46,6 +46,12 @@ log = logging.getLogger(__name__) WHITESPACE = re.compile(r'[\W_]+', re.UNICODE) APOSTROPHE = re.compile('[\'`’ʻ′]', re.UNICODE) +# PATTERN will look for the next occurence of one of these symbols: +# \controlword - optionally preceded by \*, optionally followed by a number +# \'## - where ## is a pair of hex digits, representing a single character +# \# - where # is a single non-alpha character, representing a special symbol +# { or } - marking the beginning/end of a group +# a run of characters without any \ { } or end-of-line PATTERN = re.compile(r"(\\\*)?\\([a-z]{1,32})(-?\d{1,10})?[ ]?|\\'([0-9a-f]{2})|\\([^a-z*])|([{}])|[\r\n]+|([^\\{}\r\n]+)", re.I) # RTF control words which specify a "destination" to be ignored. DESTINATIONS = frozenset(( From 0677e389c1fb151f4ed507bdf5007cf5d7054194 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Wed, 11 Sep 2013 15:00:34 +0200 Subject: [PATCH 45/93] doc --- openlp/core/lib/htmlbuilder.py | 366 ++++++++++++++++++ .../openlp_core_lib/test_htmlbuilder.py | 21 +- 2 files changed, 378 insertions(+), 9 deletions(-) diff --git a/openlp/core/lib/htmlbuilder.py b/openlp/core/lib/htmlbuilder.py index 4fbeab335..9cb69579b 100644 --- a/openlp/core/lib/htmlbuilder.py +++ b/openlp/core/lib/htmlbuilder.py @@ -26,7 +26,372 @@ # with this program; if not, write to the Free Software Foundation, Inc., 59 # # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### +""" +This module is responsible for generating the HTML for :class:`~openlp.core.ui.maindisplay`. The ``build_html`` function +is the function which has to be called from outside. The generated and returned HTML will look similar to this:: + + + + OpenLP Display + + + + + + + + + + + + + + +
+ +
+ + +""" import logging from PyQt4 import QtWebKit @@ -242,6 +607,7 @@ def build_html(item, screen, is_live, background, image=None, plugins=None): image_src, html_additions ) + print(html) return html diff --git a/tests/functional/openlp_core_lib/test_htmlbuilder.py b/tests/functional/openlp_core_lib/test_htmlbuilder.py index 0ddf2bd4b..ae6a96a23 100644 --- a/tests/functional/openlp_core_lib/test_htmlbuilder.py +++ b/tests/functional/openlp_core_lib/test_htmlbuilder.py @@ -3,11 +3,11 @@ Package to test the openlp.core.lib.htmlbuilder module. """ from unittest import TestCase -from mock import MagicMock +from mock import MagicMock, patch from PyQt4 import QtCore -from openlp.core.lib.htmlbuilder import build_footer_css, build_lyrics_outline_css +from openlp.core.lib.htmlbuilder import build_footer_css, build_lyrics_outline_css, build_lyrics_css FOOTER_CSS = """ @@ -35,13 +35,16 @@ class Htmbuilder(TestCase): def build_lyrics_css_test(self): """ """ - item = MagicMock() - item.main = - item.themedata.font_main_shadow = - item.themedata.font_main_shadow_color = - item.themedata.font_main_shadow_size = - item.themedata.font_main_shadow = - assert LYRICS_CSS = build_lyrics_css(item), 'The lyrics css should be equal.' + with patch('openlp.core.lib.htmlbuilder.build_lyrics_format_css') as mocked_method:# + mocked_method.return_value = '' + item = MagicMock() + item.main = + item.themedata.font_main_shadow = + item.themedata.font_main_shadow_color = + item.themedata.font_main_shadow_size = + item.themedata.font_main_shadow = + print(build_lyrics_css(item)) + assert LYRICS_CSS == build_lyrics_css(item), 'The lyrics css should be equal.' def build_lyrics_outline_css_test(self): """ From bf023ee20d79d34d979ed91b97c599db3f05b859 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Wed, 11 Sep 2013 15:30:23 +0200 Subject: [PATCH 46/93] more tests --- .../openlp_core_lib/test_htmlbuilder.py | 242 ++++++++++++++++-- 1 file changed, 220 insertions(+), 22 deletions(-) diff --git a/tests/functional/openlp_core_lib/test_htmlbuilder.py b/tests/functional/openlp_core_lib/test_htmlbuilder.py index ae6a96a23..5e2ed0fa8 100644 --- a/tests/functional/openlp_core_lib/test_htmlbuilder.py +++ b/tests/functional/openlp_core_lib/test_htmlbuilder.py @@ -7,9 +7,168 @@ from mock import MagicMock, patch from PyQt4 import QtCore -from openlp.core.lib.htmlbuilder import build_footer_css, build_lyrics_outline_css, build_lyrics_css +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 +BUILD_HTML = """ + + + +OpenLP Display + + + + + + +plugin HTML +
+ +
+ + +""" +BACKGROUND_CSS = """ +""" +LYRICS_CSS = 'white-space:pre-wrap; word-wrap: break-word; text-align: left; vertical-align: top; ' + \ + 'font-family: Arial; font-size: 40pt; color: #FFFFFF; line-height: 100%; margin: 0;padding: 0; ' + \ + 'padding-bottom: 0; padding-left: 0px; width: 1260px; height: 921px; ' +LYRICS_OUTLINE_CSS = ' -webkit-text-stroke: 0.125em #000000; -webkit-text-fill-color: #FFFFFF; ' +LYRICS_FORMAT_CSS = """ +""" FOOTER_CSS = """ left: 10px; bottom: 0px; @@ -20,45 +179,79 @@ FOOTER_CSS = """ text-align: left; white-space: nowrap; """ -OUTLINE_CSS = ' -webkit-text-stroke: 0.125em #000000; -webkit-text-fill-color: #FFFFFF; ' -LYRICS_CSS = 'white-space:pre-wrap; word-wrap: break-word; text-align: left; vertical-align: top; ' + \ - 'font-family: Arial; font-size: 40pt; color: #FFFFFF; line-height: 100%; margin: 0;padding: 0; ' + \ - 'padding-bottom: 0; padding-left: 0px; width: 1260px; height: 921px; ' + class Htmbuilder(TestCase): - def build_html(self): - pass - - def build_background_css(self): - pass - - def build_lyrics_css_test(self): + def build_html_test(self): """ + Test the build_html() function + """ + # GIVEN: Mocked arguments and function. + with patch('openlp.core.lib.htmlbuilder.build_background_css') as mocked_build_background_css, \ + patch('openlp.core.lib.htmlbuilder.build_footer_css') as mocked_build_footer_css, \ + patch('openlp.core.lib.htmlbuilder.build_lyrics_css') as mocked_build_lyrics_css: + # Mocked function. + mocked_build_background_css.return_value = '' + mocked_build_footer_css.return_value = 'dummy: dummy;' + mocked_build_lyrics_css.return_value = '' + # Mocked arguments. + item = MagicMock() + item.bg_image_bytes = None + screen = MagicMock() + is_live = False + background = None + plugin = MagicMock() + plugin.get_display_css = MagicMock(return_value='plugin CSS') + plugin.get_display_javascript = MagicMock(return_value='plugin JS') + plugin.get_display_html = MagicMock(return_value='plugin HTML') + plugins = [plugin] + + # WHEN: Create the html. + html = build_html(item, screen, is_live, background, plugins=plugins) + + # THEN: The returned html should match. + assert html == BUILD_HTML + + def build_background_css_tes(self): + """ + Test the build_background_css() function + """ + # THEN: The returned html should match. + pass + + def build_lyrics_css_tes(self): + """ + Test the build_lyrics_css() function """ with patch('openlp.core.lib.htmlbuilder.build_lyrics_format_css') as mocked_method:# mocked_method.return_value = '' item = MagicMock() - item.main = - item.themedata.font_main_shadow = - item.themedata.font_main_shadow_color = - item.themedata.font_main_shadow_size = - item.themedata.font_main_shadow = - print(build_lyrics_css(item)) +# item.main = +# item.themedata.font_main_shadow = +# item.themedata.font_main_shadow_color = +# item.themedata.font_main_shadow_size = +# item.themedata.font_main_shadow = + assert LYRICS_CSS == build_lyrics_css(item), 'The lyrics css should be equal.' def build_lyrics_outline_css_test(self): """ Test the build_lyrics_outline_css() function """ + # GIVEN: The mocked theme data. theme_data = MagicMock() theme_data.font_main_outline = True theme_data.font_main_outline_size = 2 theme_data.font_main_color = '#FFFFFF' theme_data.font_main_outline_color = '#000000' - assert OUTLINE_CSS == build_lyrics_outline_css(theme_data), 'The outline css should be equal.' + # WHEN: Create the css. + css = build_lyrics_outline_css(theme_data) - def build_lyrics_format_css(self): + # THEN: The css should be equal. + assert LYRICS_OUTLINE_CSS == css, 'The outline css should be equal.' + + def build_lyrics_format_css_tes(self): """ Test the build_lyrics_format_css() function """ @@ -68,12 +261,17 @@ class Htmbuilder(TestCase): """ Test the build_footer_css() function """ - # Create a theme. + # GIVEN: Create a theme. item = MagicMock() item.footer = QtCore.QRect(10, 921, 1260, 103) item.themedata.font_footer_name = 'Arial' item.themedata.font_footer_size = 12 item.themedata.font_footer_color = '#FFFFFF' height = 1024 - assert FOOTER_CSS == build_footer_css(item, height), 'The footer strings should be equal.' + + # WHEN: create the css. + css = build_footer_css(item, height) + + # THEN: THE css should be the same. + assert FOOTER_CSS == css,'The footer strings should be equal.' From fea4893f406a08d7d115c3072416cf0481e24076 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Wed, 11 Sep 2013 15:30:59 +0200 Subject: [PATCH 47/93] removed pritn --- openlp/core/lib/htmlbuilder.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openlp/core/lib/htmlbuilder.py b/openlp/core/lib/htmlbuilder.py index 9cb69579b..89aa8f43c 100644 --- a/openlp/core/lib/htmlbuilder.py +++ b/openlp/core/lib/htmlbuilder.py @@ -607,7 +607,6 @@ def build_html(item, screen, is_live, background, image=None, plugins=None): image_src, html_additions ) - print(html) return html From f24f3a751ffd6ed901b8869bb760b0d3560c544b Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Wed, 11 Sep 2013 15:51:49 +0200 Subject: [PATCH 48/93] more tests --- openlp/core/lib/htmlbuilder.py | 2 +- .../openlp_core_lib/test_htmlbuilder.py | 46 +++++++++++++------ 2 files changed, 34 insertions(+), 14 deletions(-) diff --git a/openlp/core/lib/htmlbuilder.py b/openlp/core/lib/htmlbuilder.py index 89aa8f43c..5b998ce92 100644 --- a/openlp/core/lib/htmlbuilder.py +++ b/openlp/core/lib/htmlbuilder.py @@ -680,7 +680,7 @@ def build_lyrics_css(item): .lyricsmain { %s } - """ +""" theme_data = item.themedata lyricstable = '' lyrics = '' diff --git a/tests/functional/openlp_core_lib/test_htmlbuilder.py b/tests/functional/openlp_core_lib/test_htmlbuilder.py index 5e2ed0fa8..f007665ad 100644 --- a/tests/functional/openlp_core_lib/test_htmlbuilder.py +++ b/tests/functional/openlp_core_lib/test_htmlbuilder.py @@ -163,9 +163,23 @@ plugin HTML """ BACKGROUND_CSS = """ """ -LYRICS_CSS = 'white-space:pre-wrap; word-wrap: break-word; text-align: left; vertical-align: top; ' + \ - 'font-family: Arial; font-size: 40pt; color: #FFFFFF; line-height: 100%; margin: 0;padding: 0; ' + \ - 'padding-bottom: 0; padding-left: 0px; width: 1260px; height: 921px; ' +LYRICS_CSS = """ +.lyricstable { + z-index: 5; + position: absolute; + display: table; + left: 10px; top: 20px; +} +.lyricscell { + display: table-cell; + word-wrap: break-word; + -webkit-transition: opacity 0.4s ease; + lyrics_format_css +} +.lyricsmain { + text-shadow: #000000 5px 5px; +} +""" LYRICS_OUTLINE_CSS = ' -webkit-text-stroke: 0.125em #000000; -webkit-text-fill-color: #FFFFFF; ' LYRICS_FORMAT_CSS = """ """ @@ -219,20 +233,26 @@ class Htmbuilder(TestCase): # THEN: The returned html should match. pass - def build_lyrics_css_tes(self): + def build_lyrics_css_test(self): """ Test the build_lyrics_css() function """ - with patch('openlp.core.lib.htmlbuilder.build_lyrics_format_css') as mocked_method:# - mocked_method.return_value = '' + # GIVEN: Mocked method and arguments. + with patch('openlp.core.lib.htmlbuilder.build_lyrics_format_css') as mocked_build_lyrics_format_css, \ + patch('openlp.core.lib.htmlbuilder.build_lyrics_outline_css') as mocked_build_lyrics_outline_css: + mocked_build_lyrics_format_css.return_value = 'lyrics_format_css' + mocked_build_lyrics_outline_css.return_value = '' item = MagicMock() -# item.main = -# item.themedata.font_main_shadow = -# item.themedata.font_main_shadow_color = -# item.themedata.font_main_shadow_size = -# item.themedata.font_main_shadow = + item.main = QtCore.QRect(10, 20, 10, 20) + item.themedata.font_main_shadow = True + item.themedata.font_main_shadow_color = '#000000' + item.themedata.font_main_shadow_size = 5 - assert LYRICS_CSS == build_lyrics_css(item), 'The lyrics css should be equal.' + # WHEN: Create the css. + css = build_lyrics_css(item) + + # THEN: The css should be equal. + assert LYRICS_CSS == css, 'The lyrics css should be equal.' def build_lyrics_outline_css_test(self): """ @@ -273,5 +293,5 @@ class Htmbuilder(TestCase): css = build_footer_css(item, height) # THEN: THE css should be the same. - assert FOOTER_CSS == css,'The footer strings should be equal.' + assert FOOTER_CSS == css, 'The footer strings should be equal.' From 1ca117532fd7823d1add1b95b221e0eeda319dcc Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Wed, 11 Sep 2013 16:02:17 +0200 Subject: [PATCH 49/93] more tests --- .../openlp_core_lib/test_htmlbuilder.py | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/tests/functional/openlp_core_lib/test_htmlbuilder.py b/tests/functional/openlp_core_lib/test_htmlbuilder.py index f007665ad..157bfca86 100644 --- a/tests/functional/openlp_core_lib/test_htmlbuilder.py +++ b/tests/functional/openlp_core_lib/test_htmlbuilder.py @@ -161,8 +161,7 @@ plugin HTML """ -BACKGROUND_CSS = """ -""" +BACKGROUND_CSS_RADIAL = 'background: -webkit-gradient(radial, 5 50%, 100, 5 50%, 5, from(#000000), to(#FFFFFF)) fixed' LYRICS_CSS = """ .lyricstable { z-index: 5; @@ -226,12 +225,21 @@ class Htmbuilder(TestCase): # THEN: The returned html should match. assert html == BUILD_HTML - def build_background_css_tes(self): + def build_background_css_radial_test(self): """ - Test the build_background_css() function + Test the build_background_css() function with a radial background """ - # THEN: The returned html should match. - pass + # GIVEN: Mocked arguments. + item = MagicMock() + item.themedata.background_start_color = '#000000' + item.themedata.background_end_color = '#FFFFFF' + width = 10 + + # WHEN: Create the css. + css = build_background_css(item, width) + + # THEN: The returned css should match. + assert BACKGROUND_CSS_RADIAL == css, 'The background css should be equal.' def build_lyrics_css_test(self): """ From 4999ecb6e6147cc0afb69ea0b2576c444344e9da Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Wed, 11 Sep 2013 16:22:28 +0200 Subject: [PATCH 50/93] last test --- openlp/core/lib/htmlbuilder.py | 4 +-- .../openlp_core_lib/test_htmlbuilder.py | 28 ++++++++++++++++--- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/openlp/core/lib/htmlbuilder.py b/openlp/core/lib/htmlbuilder.py index 5b998ce92..07a83f336 100644 --- a/openlp/core/lib/htmlbuilder.py +++ b/openlp/core/lib/htmlbuilder.py @@ -746,9 +746,9 @@ def build_lyrics_format_css(theme_data, width, height): theme_data.font_main_color, 100 + int(theme_data.font_main_line_adjustment), padding_bottom, left_margin, width, height) if theme_data.font_main_italics: - lyrics += ' font-style:italic; ' + lyrics += 'font-style:italic; ' if theme_data.font_main_bold: - lyrics += ' font-weight:bold; ' + lyrics += 'font-weight:bold; ' return lyrics diff --git a/tests/functional/openlp_core_lib/test_htmlbuilder.py b/tests/functional/openlp_core_lib/test_htmlbuilder.py index 157bfca86..1cde4d8ba 100644 --- a/tests/functional/openlp_core_lib/test_htmlbuilder.py +++ b/tests/functional/openlp_core_lib/test_htmlbuilder.py @@ -9,6 +9,7 @@ from PyQt4 import QtCore 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 BUILD_HTML = """ @@ -180,8 +181,10 @@ LYRICS_CSS = """ } """ LYRICS_OUTLINE_CSS = ' -webkit-text-stroke: 0.125em #000000; -webkit-text-fill-color: #FFFFFF; ' -LYRICS_FORMAT_CSS = """ -""" +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 = """ left: 10px; bottom: 0px; @@ -279,11 +282,28 @@ class Htmbuilder(TestCase): # THEN: The css should be equal. assert LYRICS_OUTLINE_CSS == css, 'The outline css should be equal.' - def build_lyrics_format_css_tes(self): + def build_lyrics_format_css_test(self): """ Test the build_lyrics_format_css() function """ - pass + # GIVEN: Mocked arguments. + theme_data = MagicMock() + theme_data.display_horizontal_align = HorizontalType.Justify + theme_data.display_vertical_align = VerticalType.Bottom + theme_data.font_main_name = 'Arial' + theme_data.font_main_size = 40 + theme_data.font_main_color = '#FFFFFF' + theme_data.font_main_italics = True + theme_data.font_main_bold = True + theme_data.font_main_line_adjustment = 8 + width = 1580 + height = 810 + + # WHEN: Get the css. + 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.' def build_footer_css_test(self): """ From f1009fbc18487268575fdcab35c7cfd4545c5d20 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Wed, 11 Sep 2013 16:23:32 +0200 Subject: [PATCH 51/93] blank --- tests/functional/openlp_core_lib/test_htmlbuilder.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/functional/openlp_core_lib/test_htmlbuilder.py b/tests/functional/openlp_core_lib/test_htmlbuilder.py index 1cde4d8ba..3daf36a44 100644 --- a/tests/functional/openlp_core_lib/test_htmlbuilder.py +++ b/tests/functional/openlp_core_lib/test_htmlbuilder.py @@ -184,7 +184,6 @@ 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 = """ left: 10px; bottom: 0px; From ffe0ccb625f13ab974634b5ff61837a08af71641 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Wed, 11 Sep 2013 17:08:08 +0200 Subject: [PATCH 52/93] clean up --- tests/functional/openlp_core_lib/test_htmlbuilder.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/functional/openlp_core_lib/test_htmlbuilder.py b/tests/functional/openlp_core_lib/test_htmlbuilder.py index 3daf36a44..ec8e3a4a3 100644 --- a/tests/functional/openlp_core_lib/test_htmlbuilder.py +++ b/tests/functional/openlp_core_lib/test_htmlbuilder.py @@ -12,7 +12,7 @@ from openlp.core.lib.htmlbuilder import build_html, build_background_css, build_ from openlp.core.lib.theme import HorizontalType, VerticalType -BUILD_HTML = """ +HTML = """ @@ -225,7 +225,7 @@ class Htmbuilder(TestCase): html = build_html(item, screen, is_live, background, plugins=plugins) # THEN: The returned html should match. - assert html == BUILD_HTML + assert html == HTML def build_background_css_radial_test(self): """ From cb62bf593621fdc8d11360a6488f584b8fbc3131 Mon Sep 17 00:00:00 2001 From: "Jeffrey S. Smith" Date: Wed, 11 Sep 2013 10:09:36 -0500 Subject: [PATCH 53/93] Need a different approach for platform-specific tests --- tests/functional/openlp_core_utils/test_utils.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/functional/openlp_core_utils/test_utils.py b/tests/functional/openlp_core_utils/test_utils.py index 2aa1f9611..6eacb2e48 100644 --- a/tests/functional/openlp_core_utils/test_utils.py +++ b/tests/functional/openlp_core_utils/test_utils.py @@ -8,8 +8,6 @@ from mock import patch from openlp.core.utils import clean_filename, get_filesystem_encoding, _get_frozen_path, get_locale_key, \ get_natural_key, split_filename -import os - class TestUtils(TestCase): """ @@ -122,8 +120,6 @@ class TestUtils(TestCase): # THEN: We get a properly sorted list test_passes = sorted(unsorted_list, key=get_locale_key) == ['Aushang', '\u00C4u\u00DFerung', 'Auszug'] assert test_passes, 'Strings should be sorted properly' - if os.name != 'nt': - del get_locale_key_windows_test def get_locale_key_linux_test(self): From 5295697677137421b033d4b64e5808a65fc33e91 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Wed, 11 Sep 2013 20:00:30 +0100 Subject: [PATCH 54/93] fix error message --- openlp/core/lib/formattingtags.py | 5 ++++- openlp/core/ui/formattingtagcontroller.py | 3 +++ openlp/core/ui/formattingtagform.py | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/openlp/core/lib/formattingtags.py b/openlp/core/lib/formattingtags.py index bbc912058..f914677c6 100644 --- a/openlp/core/lib/formattingtags.py +++ b/openlp/core/lib/formattingtags.py @@ -50,7 +50,10 @@ class FormattingTags(object): @staticmethod def save_html_tags(new_tags): """ - Saves all formatting tags except protected ones. + Saves all formatting tags except protected ones + + `new_tags` + The tags to be saved.. """ # Formatting Tags were also known as display tags. Settings().setValue('formattingTags/html_tags', json.dumps(new_tags) if new_tags else '') diff --git a/openlp/core/ui/formattingtagcontroller.py b/openlp/core/ui/formattingtagcontroller.py index b0c10630a..46b463f98 100644 --- a/openlp/core/ui/formattingtagcontroller.py +++ b/openlp/core/ui/formattingtagcontroller.py @@ -107,6 +107,9 @@ class FormattingTagController(object): def _strip(self, tag): """ Remove tag wrappers for editing. + + `tag` + Tag to be stripped """ tag = tag.replace('{', '') tag = tag.replace('}', '') diff --git a/openlp/core/ui/formattingtagform.py b/openlp/core/ui/formattingtagform.py index 5a2409322..589e7cb6b 100644 --- a/openlp/core/ui/formattingtagform.py +++ b/openlp/core/ui/formattingtagform.py @@ -90,7 +90,7 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog, FormattingTagCont new_row = self.tag_table_widget.rowCount() self.tag_table_widget.insertRow(new_row) self.tag_table_widget.setItem(new_row, 0, - QtGui.QTableWidgetItem(translate('OpenLP.FormattingTagForm', 'New Tag'))) + QtGui.QTableWidgetItem(translate('OpenLP.FormattingTagForm', 'New Tag%s') % str(new_row))) self.tag_table_widget.setItem(new_row, 1, QtGui.QTableWidgetItem('n%s' % str(new_row))) self.tag_table_widget.setItem(new_row, 2, QtGui.QTableWidgetItem(translate('OpenLP.FormattingTagForm', ''))) From d0813eddacaf1030b8e5e6c83f77c436ce3dc676 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Wed, 11 Sep 2013 22:06:17 +0100 Subject: [PATCH 55/93] Start to remove CherryPy --- openlp/plugins/remotes/lib/__init__.py | 5 +- openlp/plugins/remotes/lib/httprouter.py | 526 +++++++++++++++++ openlp/plugins/remotes/lib/httpserver.py | 705 ++++------------------- openlp/plugins/remotes/remoteplugin.py | 10 +- 4 files changed, 631 insertions(+), 615 deletions(-) create mode 100644 openlp/plugins/remotes/lib/httprouter.py diff --git a/openlp/plugins/remotes/lib/__init__.py b/openlp/plugins/remotes/lib/__init__.py index 72a090647..873e651d8 100644 --- a/openlp/plugins/remotes/lib/__init__.py +++ b/openlp/plugins/remotes/lib/__init__.py @@ -28,6 +28,7 @@ ############################################################################### from .remotetab import RemoteTab -from .httpserver import HttpServer +from .httprouter import HttpRouter +from .httpserver import OpenLPServer -__all__ = ['RemoteTab', 'HttpServer'] +__all__ = ['RemoteTab', 'OpenLPServer', 'HttpRouter'] diff --git a/openlp/plugins/remotes/lib/httprouter.py b/openlp/plugins/remotes/lib/httprouter.py new file mode 100644 index 000000000..77927723c --- /dev/null +++ b/openlp/plugins/remotes/lib/httprouter.py @@ -0,0 +1,526 @@ +# -*- 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:`http` module contains the API web server. This is a lightweight web +server used by remotes to interact with OpenLP. It uses JSON to communicate with +the remotes. + +*Routes:* + +``/`` + Go to the web interface. + +``/stage`` + Show the stage view. + +``/files/{filename}`` + Serve a static file. + +``/stage/api/poll`` + Poll to see if there are any changes. Returns a JSON-encoded dict of + any changes that occurred:: + + {"results": {"type": "controller"}} + + Or, if there were no results, False:: + + {"results": False} + +``/api/display/{hide|show}`` + Blank or unblank the screen. + +``/api/alert`` + Sends an alert message to the alerts plugin. This method expects a + JSON-encoded dict like this:: + + {"request": {"text": ""}} + +``/api/controller/{live|preview}/{action}`` + Perform ``{action}`` on the live or preview controller. Valid actions + are: + + ``next`` + Load the next slide. + + ``previous`` + Load the previous slide. + + ``set`` + Set a specific slide. Requires an id return in a JSON-encoded dict like + this:: + + {"request": {"id": 1}} + + ``first`` + Load the first slide. + + ``last`` + Load the last slide. + + ``text`` + Fetches the text of the current song. The output is a JSON-encoded + dict which looks like this:: + + {"result": {"slides": ["...", "..."]}} + +``/api/service/{action}`` + Perform ``{action}`` on the service manager (e.g. go live). Data is + passed as a json-encoded ``data`` parameter. Valid actions are: + + ``next`` + Load the next item in the service. + + ``previous`` + Load the previews item in the service. + + ``set`` + Set a specific item in the service. Requires an id returned in a + JSON-encoded dict like this:: + + {"request": {"id": 1}} + + ``list`` + Request a list of items in the service. Returns a list of items in the + current service in a JSON-encoded dict like this:: + + {"results": {"items": [{...}, {...}]}} +""" + +import json +import logging +import os +import re +import urllib.request +import urllib.parse +import urllib.error +import urllib.parse + + +from mako.template import Template +from PyQt4 import QtCore + +from openlp.core.lib import Registry, Settings, PluginStatus, StringContent, image_to_byte +from openlp.core.utils import AppLocation, translate + +from hashlib import sha1 + +log = logging.getLogger(__name__) + + +class HttpRouter(object): + """ + This code is called by the HttpServer upon a request and it processes it based on the routing table. + """ + def initialise(self): + """ + Initialise the router + """ + self.routes = [ + ('^/$', self.serve_file), + ('^/(stage)$', self.serve_file), + ('^/(main)$', self.serve_file), + (r'^/files/(.*)$', self.serve_file), + (r'^/api/poll$', self.poll), + (r'^/stage/poll$', self.poll), + (r'^/main/poll$', self.main_poll), + (r'^/main/image$', self.main_image), + (r'^/api/controller/(live|preview)/(.*)$', self.controller), + (r'^/stage/controller/(live|preview)/(.*)$', self.controller), + (r'^/api/service/(.*)$', self.service), + (r'^/stage/service/(.*)$', self.service), + (r'^/api/display/(hide|show|blank|theme|desktop)$', self.display), + (r'^/api/alert$', self.alert), + (r'^/api/plugin/(search)$', self.plugin_info), + (r'^/api/(.*)/search$', self.search), + (r'^/api/(.*)/live$', self.go_live), + (r'^/api/(.*)/add$', self.add_to_service) + ] + self.translate() + self.html_dir = os.path.join(AppLocation.get_directory(AppLocation.PluginsDir), 'remotes', 'html') + + def process_http_request(self, url_path, *args): + """ + Common function to process HTTP requests + + ``url_path`` + The requested URL. + + ``*args`` + Any passed data. + """ + response = None + for route, func in self.routes: + match = re.match(route, url_path) + if match: + log.debug('Route "%s" matched "%s"', route, url_path) + args = [] + for param in match.groups(): + args.append(param) + response = func(*args) + break + if response: + return response + else: + log.debug('Path not found %s', url_path) + return self._http_not_found() + + def _get_service_items(self): + """ + Read the service item in use and return the data as a json object + """ + service_items = [] + if self.live_controller.service_item: + current_unique_identifier = self.live_controller.service_item.unique_identifier + else: + current_unique_identifier = None + for item in self.service_manager.service_items: + service_item = item['service_item'] + service_items.append({ + 'id': str(service_item.unique_identifier), + 'title': str(service_item.get_display_title()), + 'plugin': str(service_item.name), + 'notes': str(service_item.notes), + 'selected': (service_item.unique_identifier == current_unique_identifier) + }) + return service_items + + def translate(self): + """ + Translate various strings in the mobile app. + """ + self.template_vars = { + 'app_title': translate('RemotePlugin.Mobile', 'OpenLP 2.1 Remote'), + 'stage_title': translate('RemotePlugin.Mobile', 'OpenLP 2.1 Stage View'), + 'live_title': translate('RemotePlugin.Mobile', 'OpenLP 2.1 Live View'), + 'service_manager': translate('RemotePlugin.Mobile', 'Service Manager'), + 'slide_controller': translate('RemotePlugin.Mobile', 'Slide Controller'), + 'alerts': translate('RemotePlugin.Mobile', 'Alerts'), + 'search': translate('RemotePlugin.Mobile', 'Search'), + 'home': translate('RemotePlugin.Mobile', 'Home'), + 'refresh': translate('RemotePlugin.Mobile', 'Refresh'), + 'blank': translate('RemotePlugin.Mobile', 'Blank'), + 'theme': translate('RemotePlugin.Mobile', 'Theme'), + 'desktop': translate('RemotePlugin.Mobile', 'Desktop'), + 'show': translate('RemotePlugin.Mobile', 'Show'), + 'prev': translate('RemotePlugin.Mobile', 'Prev'), + 'next': translate('RemotePlugin.Mobile', 'Next'), + 'text': translate('RemotePlugin.Mobile', 'Text'), + 'show_alert': translate('RemotePlugin.Mobile', 'Show Alert'), + 'go_live': translate('RemotePlugin.Mobile', 'Go Live'), + 'add_to_service': translate('RemotePlugin.Mobile', 'Add to Service'), + 'add_and_go_to_service': translate('RemotePlugin.Mobile', 'Add & Go to Service'), + 'no_results': translate('RemotePlugin.Mobile', 'No Results'), + 'options': translate('RemotePlugin.Mobile', 'Options'), + 'service': translate('RemotePlugin.Mobile', 'Service'), + 'slides': translate('RemotePlugin.Mobile', 'Slides') + } + + def serve_file(self, file_name=None): + """ + Send a file to the socket. For now, just a subset of file types and must be top level inside the html folder. + If subfolders requested return 404, easier for security for the present. + + Ultimately for i18n, this could first look for xx/file.html before falling back to file.html. + where xx is the language, e.g. 'en' + """ + log.debug('serve file request %s' % file_name) + if not file_name: + file_name = 'index.html' + elif file_name == 'stage': + file_name = 'stage.html' + elif file_name == 'main': + file_name = 'main.html' + path = os.path.normpath(os.path.join(self.html_dir, file_name)) + if not path.startswith(self.html_dir): + return self._http_not_found() + ext = os.path.splitext(file_name)[1] + html = None + if ext == '.html': + mimetype = 'text/html' + variables = self.template_vars + html = Template(filename=path, input_encoding='utf-8', output_encoding='utf-8').render(**variables) + elif ext == '.css': + mimetype = 'text/css' + elif ext == '.js': + mimetype = 'application/x-javascript' + elif ext == '.jpg': + mimetype = 'image/jpeg' + elif ext == '.gif': + mimetype = 'image/gif' + elif ext == '.png': + mimetype = 'image/png' + else: + mimetype = 'text/plain' + file_handle = None + try: + if html: + content = html + else: + file_handle = open(path, 'rb') + log.debug('Opened %s' % path) + content = file_handle.read() + except IOError: + log.exception('Failed to open %s' % path) + return self._http_not_found() + finally: + if file_handle: + file_handle.close() + return content + + def poll(self): + """ + Poll OpenLP to determine the current slide number and item name. + """ + result = { + 'service': self.service_manager.service_id, + 'slide': self.live_controller.selected_row or 0, + 'item': self.live_controller.service_item.unique_identifier if self.live_controller.service_item else '', + 'twelve': Settings().value('remotes/twelve hour'), + 'blank': self.live_controller.blank_screen.isChecked(), + 'theme': self.live_controller.theme_screen.isChecked(), + 'display': self.live_controller.desktop_screen.isChecked() + } + return json.dumps({'results': result}).encode() + + def main_poll(self): + """ + Poll OpenLP to determine the current slide count. + """ + result = { + 'slide_count': self.live_controller.slide_count + } + return json.dumps({'results': result}).encode() + + def main_image(self): + """ + Return the latest display image as a byte stream. + """ + result = { + 'slide_image': 'data:image/png;base64,' + str(image_to_byte(self.live_controller.slide_image)) + } + return json.dumps({'results': result}).encode() + + def display(self, action): + """ + Hide or show the display screen. + This is a cross Thread call and UI is updated so Events need to be used. + + ``action`` + This is the action, either ``hide`` or ``show``. + """ + self.live_controller.emit(QtCore.SIGNAL('slidecontroller_toggle_display'), action) + return json.dumps({'results': {'success': True}}).encode() + + def alert(self): + """ + Send an alert. + """ + plugin = self.plugin_manager.get_plugin_by_name("alerts") + if plugin.status == PluginStatus.Active: + try: + text = json.loads(self.request_data)['request']['text'] + except KeyError as ValueError: + return self._http_bad_request() + text = urllib.parse.unquote(text) + self.alerts_manager.emit(QtCore.SIGNAL('alerts_text'), [text]) + success = True + else: + success = False + return json.dumps({'results': {'success': success}}).encode() + + def controller(self, display_type, action): + """ + Perform an action on the slide controller. + + ``display_type`` + This is the type of slide controller, either ``preview`` or ``live``. + + ``action`` + The action to perform. + """ + event = 'slidecontroller_%s_%s' % (display_type, action) + if action == 'text': + current_item = self.live_controller.service_item + data = [] + if current_item: + for index, frame in enumerate(current_item.get_frames()): + item = {} + if current_item.is_text(): + if frame['verseTag']: + item['tag'] = str(frame['verseTag']) + else: + item['tag'] = str(index + 1) + item['text'] = str(frame['text']) + item['html'] = str(frame['html']) + else: + item['tag'] = str(index + 1) + item['text'] = str(frame['title']) + item['html'] = str(frame['title']) + item['selected'] = (self.live_controller.selected_row == index) + data.append(item) + json_data = {'results': {'slides': data}} + if current_item: + json_data['results']['item'] = self.live_controller.service_item.unique_identifier + else: + if self.request_data: + try: + data = json.loads(self.request_data)['request']['id'] + except KeyError as ValueError: + return self._http_bad_request() + log.info(data) + # This slot expects an int within a list. + self.live_controller.emit(QtCore.SIGNAL(event), [data]) + else: + self.live_controller.emit(QtCore.SIGNAL(event)) + json_data = {'results': {'success': True}} + return json.dumps(json_data).encode() + + def service(self, action): + """ + Handles requests for service items in the service manager + + ``action`` + The action to perform. + """ + event = 'servicemanager_%s' % action + if action == 'list': + return json.dumps({'results': {'items': self._get_service_items()}}).encode() + event += '_item' + if self.request_data: + try: + data = json.loads(self.request_data)['request']['id'] + except KeyError: + return self._http_bad_request() + self.service_manager.emit(QtCore.SIGNAL(event), data) + else: + Registry().execute(event) + return json.dumps({'results': {'success': True}}).encode() + + def plugin_info(self, action): + """ + Return plugin related information, based on the action. + + ``action`` + The action to perform. If *search* return a list of plugin names + which support search. + """ + if action == 'search': + searches = [] + for plugin in self.plugin_manager.plugins: + if plugin.status == PluginStatus.Active and plugin.media_item and plugin.media_item.has_search: + searches.append([plugin.name, str(plugin.text_strings[StringContent.Name]['plural'])]) + return json.dumps({'results': {'items': searches}}).encode() + + def search(self, plugin_name): + """ + Return a list of items that match the search text. + + ``plugin`` + The plugin name to search in. + """ + try: + text = json.loads(self.request_data)['request']['text'] + except KeyError as ValueError: + return self._http_bad_request() + text = urllib.parse.unquote(text) + plugin = self.plugin_manager.get_plugin_by_name(plugin_name) + if plugin.status == PluginStatus.Active and plugin.media_item and plugin.media_item.has_search: + results = plugin.media_item.search(text, False) + else: + results = [] + return json.dumps({'results': {'items': results}}).encode() + + def go_live(self, plugin_name): + """ + Go live on an item of type ``plugin``. + """ + try: + id = json.loads(self.request_data)['request']['id'] + except KeyError as ValueError: + return self._http_bad_request() + plugin = self.plugin_manager.get_plugin_by_name(plugin_name) + if plugin.status == PluginStatus.Active and plugin.media_item: + plugin.media_item.emit(QtCore.SIGNAL('%s_go_live' % plugin_name), [id, True]) + return self._http_success() + + def add_to_service(self, plugin_name): + """ + Add item of type ``plugin_name`` to the end of the service. + """ + try: + id = json.loads(self.request_data)['request']['id'] + except KeyError as ValueError: + return self._http_bad_request() + plugin = self.plugin_manager.get_plugin_by_name(plugin_name) + if plugin.status == PluginStatus.Active and plugin.media_item: + item_id = plugin.media_item.create_item_from_id(id) + plugin.media_item.emit(QtCore.SIGNAL('%s_add_to_service' % plugin_name), [item_id, True]) + self._http_success() + + def _get_service_manager(self): + """ + Adds the service manager to the class dynamically + """ + if not hasattr(self, '_service_manager'): + self._service_manager = Registry().get('service_manager') + return self._service_manager + + service_manager = property(_get_service_manager) + + def _get_live_controller(self): + """ + Adds the live controller to the class dynamically + """ + if not hasattr(self, '_live_controller'): + self._live_controller = Registry().get('live_controller') + return self._live_controller + + live_controller = property(_get_live_controller) + + def _get_plugin_manager(self): + """ + Adds the plugin manager to the class dynamically + """ + if not hasattr(self, '_plugin_manager'): + self._plugin_manager = Registry().get('plugin_manager') + return self._plugin_manager + + plugin_manager = property(_get_plugin_manager) + + def _get_alerts_manager(self): + """ + Adds the alerts manager to the class dynamically + """ + if not hasattr(self, '_alerts_manager'): + self._alerts_manager = Registry().get('alerts_manager') + return self._alerts_manager + + alerts_manager = property(_get_alerts_manager) + diff --git a/openlp/plugins/remotes/lib/httpserver.py b/openlp/plugins/remotes/lib/httpserver.py index 89cb3a64b..cee58b9ab 100644 --- a/openlp/plugins/remotes/lib/httpserver.py +++ b/openlp/plugins/remotes/lib/httpserver.py @@ -31,101 +31,23 @@ The :mod:`http` module contains the API web server. This is a lightweight web server used by remotes to interact with OpenLP. It uses JSON to communicate with the remotes. - -*Routes:* - -``/`` - Go to the web interface. - -``/stage`` - Show the stage view. - -``/files/{filename}`` - Serve a static file. - -``/stage/api/poll`` - Poll to see if there are any changes. Returns a JSON-encoded dict of - any changes that occurred:: - - {"results": {"type": "controller"}} - - Or, if there were no results, False:: - - {"results": False} - -``/api/display/{hide|show}`` - Blank or unblank the screen. - -``/api/alert`` - Sends an alert message to the alerts plugin. This method expects a - JSON-encoded dict like this:: - - {"request": {"text": ""}} - -``/api/controller/{live|preview}/{action}`` - Perform ``{action}`` on the live or preview controller. Valid actions - are: - - ``next`` - Load the next slide. - - ``previous`` - Load the previous slide. - - ``set`` - Set a specific slide. Requires an id return in a JSON-encoded dict like - this:: - - {"request": {"id": 1}} - - ``first`` - Load the first slide. - - ``last`` - Load the last slide. - - ``text`` - Fetches the text of the current song. The output is a JSON-encoded - dict which looks like this:: - - {"result": {"slides": ["...", "..."]}} - -``/api/service/{action}`` - Perform ``{action}`` on the service manager (e.g. go live). Data is - passed as a json-encoded ``data`` parameter. Valid actions are: - - ``next`` - Load the next item in the service. - - ``previous`` - Load the previews item in the service. - - ``set`` - Set a specific item in the service. Requires an id returned in a - JSON-encoded dict like this:: - - {"request": {"id": 1}} - - ``list`` - Request a list of items in the service. Returns a list of items in the - current service in a JSON-encoded dict like this:: - - {"results": {"items": [{...}, {...}]}} """ -import json -import logging +import ssl +import socket import os -import re -import urllib.request, urllib.parse, urllib.error -import urllib.parse -import cherrypy +import logging +from urllib.parse import urlparse, parse_qs -from mako.template import Template from PyQt4 import QtCore -from openlp.core.lib import Registry, Settings, PluginStatus, StringContent, image_to_byte -from openlp.core.utils import AppLocation, translate +from openlp.core.lib import Settings +from openlp.core.utils import AppLocation + +from openlp.plugins.remotes.lib import HttpRouter + +from socketserver import BaseServer, ThreadingMixIn +from http.server import BaseHTTPRequestHandler, HTTPServer from hashlib import sha1 @@ -150,542 +72,111 @@ def fetch_password(username): return make_sha_hash(Settings().value('remotes/password')) -class HttpServer(object): +class CustomHandler(BaseHTTPRequestHandler, HttpRouter): """ - Ability to control OpenLP via a web browser. - This class controls the Cherrypy server and configuration. + Main class to present webpages and authentication. """ - _cp_config = { - 'tools.sessions.on': True, - 'tools.auth.on': True - } + def do_POST(self): + self.do_GET() + + def do_GET(self): + """ + Present frontpage with user authentication. + """ + if self.path == '/favicon.ico': + return + print(self.headers['content-type']) + if self.headers['content-type'] == 'application/json': + length = int(self.headers['content-length']) + postvars = parse_qs(self.rfile.read(length), keep_blank_values=1) + for var in postvars: + print(var.decode("utf-8")) + if not hasattr(self, 'auth'): + self.initialise() + function, args = self.process_http_request(self.path) + if not function: + self.do_http_error() + return + if function['secure']: + if self.headers['Authorization'] is None: + self.do_authorisation() + self.wfile.write(bytes('no auth header received', 'UTF-8')) + elif self.headers['Authorization'] == 'Basic %s' % self.auth: + self.do_http_success() + self.call_function(function, *args) + else: + self.do_authorisation() + self.wfile.write(bytes(self.headers['Authorization'], 'UTF-8')) + self.wfile.write(bytes(' not authenticated', 'UTF-8')) + else: + self.call_function(function, *args) + + +class ThreadingHTTPServer(ThreadingMixIn, HTTPServer): + pass + + +class HttpThread(QtCore.QThread): + """ + A special Qt thread class to allow the HTTP server to run at the same time as the UI. + """ + def __init__(self, server): + """ + Constructor for the thread class. + + ``server`` + The http server class. + """ + super(HttpThread, self).__init__(None) + self.http_server = server + + def run(self): + """ + Run the thread. + """ + self.http_server.start_server() + + +class OpenLPServer(): def __init__(self): """ Initialise the http server, and start the server. """ log.debug('Initialise httpserver') self.settings_section = 'remotes' - self.router = HttpRouter() + self.http_thread = HttpThread(self) + self.http_thread.start() def start_server(self): - """ - Start the http server based on configuration. - """ - log.debug('Start CherryPy server') - # Define to security levels and inject the router code - self.root = self.Public() - self.root.files = self.Files() - self.root.stage = self.Stage() - self.root.main = self.Main() - self.root.router = self.router - self.root.files.router = self.router - self.root.stage.router = self.router - self.root.main.router = self.router - cherrypy.tree.mount(self.root, '/', config=self.define_config()) - # Turn off the flood of access messages cause by poll - cherrypy.log.access_log.propagate = False - cherrypy.engine.start() - - def define_config(self): - """ - Define the configuration of the server. - """ + address = Settings().value(self.settings_section + '/ip address') if Settings().value(self.settings_section + '/https enabled'): port = Settings().value(self.settings_section + '/https port') - address = Settings().value(self.settings_section + '/ip address') - local_data = AppLocation.get_directory(AppLocation.DataDir) - cherrypy.config.update({'server.socket_host': str(address), - 'server.socket_port': port, - 'server.ssl_certificate': os.path.join(local_data, 'remotes', 'openlp.crt'), - 'server.ssl_private_key': os.path.join(local_data, 'remotes', 'openlp.key')}) + self.httpd = HTTPSServer((address, port), CustomHandler) + print('started ssl httpd...') else: port = Settings().value(self.settings_section + '/port') - address = Settings().value(self.settings_section + '/ip address') - cherrypy.config.update({'server.socket_host': str(address)}) - cherrypy.config.update({'server.socket_port': port}) - cherrypy.config.update({'environment': 'embedded'}) - cherrypy.config.update({'engine.autoreload_on': False}) - directory_config = {'/': {'tools.staticdir.on': True, - 'tools.staticdir.dir': self.router.html_dir, - 'tools.basic_auth.on': Settings().value('remotes/authentication enabled'), - 'tools.basic_auth.realm': 'OpenLP Remote Login', - 'tools.basic_auth.users': fetch_password, - 'tools.basic_auth.encrypt': make_sha_hash}, - '/files': {'tools.staticdir.on': True, - 'tools.staticdir.dir': self.router.html_dir, - 'tools.basic_auth.on': False}, - '/stage': {'tools.staticdir.on': True, - 'tools.staticdir.dir': self.router.html_dir, - 'tools.basic_auth.on': False}, - '/main': {'tools.staticdir.on': True, - 'tools.staticdir.dir': self.router.html_dir, - 'tools.basic_auth.on': False}} - return directory_config + self.httpd = ThreadingHTTPServer((address, port), CustomHandler) + print('started non ssl httpd...') + self.httpd.serve_forever() - class Public(object): - """ - Main access class with may have security enabled on it. - """ - @cherrypy.expose - def default(self, *args, **kwargs): - self.router.request_data = None - if isinstance(kwargs, dict): - self.router.request_data = kwargs.get('data', None) - url = urllib.parse.urlparse(cherrypy.url()) - return self.router.process_http_request(url.path, *args) - - class Files(object): - """ - Provides access to files and has no security available. These are read only accesses - """ - @cherrypy.expose - def default(self, *args, **kwargs): - url = urllib.parse.urlparse(cherrypy.url()) - return self.router.process_http_request(url.path, *args) - - class Stage(object): - """ - Stage view is read only so security is not relevant and would reduce it's usability - """ - @cherrypy.expose - def default(self, *args, **kwargs): - url = urllib.parse.urlparse(cherrypy.url()) - return self.router.process_http_request(url.path, *args) - - class Main(object): - """ - Main view is read only so security is not relevant and would reduce it's usability - """ - @cherrypy.expose - def default(self, *args, **kwargs): - url = urllib.parse.urlparse(cherrypy.url()) - return self.router.process_http_request(url.path, *args) - - def close(self): - """ - Close down the http server. - """ - log.debug('close http server') - cherrypy.engine.exit() + def stop_server(self): + self.httpd.socket.close() + self.httpd = None -class HttpRouter(object): - """ - This code is called by the HttpServer upon a request and it processes it based on the routing table. - """ - def __init__(self): - """ - Initialise the router - """ - self.routes = [ - ('^/$', self.serve_file), - ('^/(stage)$', self.serve_file), - ('^/(main)$', self.serve_file), - (r'^/files/(.*)$', self.serve_file), - (r'^/api/poll$', self.poll), - (r'^/stage/poll$', self.poll), - (r'^/main/poll$', self.main_poll), - (r'^/main/image$', self.main_image), - (r'^/api/controller/(live|preview)/(.*)$', self.controller), - (r'^/stage/controller/(live|preview)/(.*)$', self.controller), - (r'^/api/service/(.*)$', self.service), - (r'^/stage/service/(.*)$', self.service), - (r'^/api/display/(hide|show|blank|theme|desktop)$', self.display), - (r'^/api/alert$', self.alert), - (r'^/api/plugin/(search)$', self.plugin_info), - (r'^/api/(.*)/search$', self.search), - (r'^/api/(.*)/live$', self.go_live), - (r'^/api/(.*)/add$', self.add_to_service) - ] - self.translate() - self.html_dir = os.path.join(AppLocation.get_directory(AppLocation.PluginsDir), 'remotes', 'html') +class HTTPSServer(HTTPServer): + def __init__(self, address, handler): + BaseServer.__init__(self, address, handler) + local_data = AppLocation.get_directory(AppLocation.DataDir) + self.socket = ssl.SSLSocket( + sock=socket.socket(self.address_family, self.socket_type), + ssl_version=ssl.PROTOCOL_TLSv1, + certfile=os.path.join(local_data, 'remotes', 'openlp.crt'), + keyfile=os.path.join(local_data, 'remotes', 'openlp.key'), + server_side=True) + self.server_bind() + self.server_activate() - def process_http_request(self, url_path, *args): - """ - Common function to process HTTP requests - ``url_path`` - The requested URL. - ``*args`` - Any passed data. - """ - response = None - for route, func in self.routes: - match = re.match(route, url_path) - if match: - log.debug('Route "%s" matched "%s"', route, url_path) - args = [] - for param in match.groups(): - args.append(param) - response = func(*args) - break - if response: - return response - else: - log.debug('Path not found %s', url_path) - return self._http_not_found() - - def _get_service_items(self): - """ - Read the service item in use and return the data as a json object - """ - service_items = [] - if self.live_controller.service_item: - current_unique_identifier = self.live_controller.service_item.unique_identifier - else: - current_unique_identifier = None - for item in self.service_manager.service_items: - service_item = item['service_item'] - service_items.append({ - 'id': str(service_item.unique_identifier), - 'title': str(service_item.get_display_title()), - 'plugin': str(service_item.name), - 'notes': str(service_item.notes), - 'selected': (service_item.unique_identifier == current_unique_identifier) - }) - return service_items - - def translate(self): - """ - Translate various strings in the mobile app. - """ - self.template_vars = { - 'app_title': translate('RemotePlugin.Mobile', 'OpenLP 2.1 Remote'), - 'stage_title': translate('RemotePlugin.Mobile', 'OpenLP 2.1 Stage View'), - 'live_title': translate('RemotePlugin.Mobile', 'OpenLP 2.1 Live View'), - 'service_manager': translate('RemotePlugin.Mobile', 'Service Manager'), - 'slide_controller': translate('RemotePlugin.Mobile', 'Slide Controller'), - 'alerts': translate('RemotePlugin.Mobile', 'Alerts'), - 'search': translate('RemotePlugin.Mobile', 'Search'), - 'home': translate('RemotePlugin.Mobile', 'Home'), - 'refresh': translate('RemotePlugin.Mobile', 'Refresh'), - 'blank': translate('RemotePlugin.Mobile', 'Blank'), - 'theme': translate('RemotePlugin.Mobile', 'Theme'), - 'desktop': translate('RemotePlugin.Mobile', 'Desktop'), - 'show': translate('RemotePlugin.Mobile', 'Show'), - 'prev': translate('RemotePlugin.Mobile', 'Prev'), - 'next': translate('RemotePlugin.Mobile', 'Next'), - 'text': translate('RemotePlugin.Mobile', 'Text'), - 'show_alert': translate('RemotePlugin.Mobile', 'Show Alert'), - 'go_live': translate('RemotePlugin.Mobile', 'Go Live'), - 'add_to_service': translate('RemotePlugin.Mobile', 'Add to Service'), - 'add_and_go_to_service': translate('RemotePlugin.Mobile', 'Add & Go to Service'), - 'no_results': translate('RemotePlugin.Mobile', 'No Results'), - 'options': translate('RemotePlugin.Mobile', 'Options'), - 'service': translate('RemotePlugin.Mobile', 'Service'), - 'slides': translate('RemotePlugin.Mobile', 'Slides') - } - - def serve_file(self, file_name=None): - """ - Send a file to the socket. For now, just a subset of file types and must be top level inside the html folder. - If subfolders requested return 404, easier for security for the present. - - Ultimately for i18n, this could first look for xx/file.html before falling back to file.html. - where xx is the language, e.g. 'en' - """ - log.debug('serve file request %s' % file_name) - if not file_name: - file_name = 'index.html' - elif file_name == 'stage': - file_name = 'stage.html' - elif file_name == 'main': - file_name = 'main.html' - path = os.path.normpath(os.path.join(self.html_dir, file_name)) - if not path.startswith(self.html_dir): - return self._http_not_found() - ext = os.path.splitext(file_name)[1] - html = None - if ext == '.html': - mimetype = 'text/html' - variables = self.template_vars - html = Template(filename=path, input_encoding='utf-8', output_encoding='utf-8').render(**variables) - elif ext == '.css': - mimetype = 'text/css' - elif ext == '.js': - mimetype = 'application/x-javascript' - elif ext == '.jpg': - mimetype = 'image/jpeg' - elif ext == '.gif': - mimetype = 'image/gif' - elif ext == '.png': - mimetype = 'image/png' - else: - mimetype = 'text/plain' - file_handle = None - try: - if html: - content = html - else: - file_handle = open(path, 'rb') - log.debug('Opened %s' % path) - content = file_handle.read() - except IOError: - log.exception('Failed to open %s' % path) - return self._http_not_found() - finally: - if file_handle: - file_handle.close() - cherrypy.response.headers['Content-Type'] = mimetype - return content - - def poll(self): - """ - Poll OpenLP to determine the current slide number and item name. - """ - result = { - 'service': self.service_manager.service_id, - 'slide': self.live_controller.selected_row or 0, - 'item': self.live_controller.service_item.unique_identifier if self.live_controller.service_item else '', - 'twelve': Settings().value('remotes/twelve hour'), - 'blank': self.live_controller.blank_screen.isChecked(), - 'theme': self.live_controller.theme_screen.isChecked(), - 'display': self.live_controller.desktop_screen.isChecked() - } - cherrypy.response.headers['Content-Type'] = 'application/json' - return json.dumps({'results': result}).encode() - - def main_poll(self): - """ - Poll OpenLP to determine the current slide count. - """ - result = { - 'slide_count': self.live_controller.slide_count - } - cherrypy.response.headers['Content-Type'] = 'application/json' - return json.dumps({'results': result}).encode() - - def main_image(self): - """ - Return the latest display image as a byte stream. - """ - result = { - 'slide_image': 'data:image/png;base64,' + str(image_to_byte(self.live_controller.slide_image)) - } - cherrypy.response.headers['Content-Type'] = 'application/json' - return json.dumps({'results': result}).encode() - - def display(self, action): - """ - Hide or show the display screen. - This is a cross Thread call and UI is updated so Events need to be used. - - ``action`` - This is the action, either ``hide`` or ``show``. - """ - self.live_controller.emit(QtCore.SIGNAL('slidecontroller_toggle_display'), action) - cherrypy.response.headers['Content-Type'] = 'application/json' - return json.dumps({'results': {'success': True}}).encode() - - def alert(self): - """ - Send an alert. - """ - plugin = self.plugin_manager.get_plugin_by_name("alerts") - if plugin.status == PluginStatus.Active: - try: - text = json.loads(self.request_data)['request']['text'] - except KeyError as ValueError: - return self._http_bad_request() - text = urllib.parse.unquote(text) - self.alerts_manager.emit(QtCore.SIGNAL('alerts_text'), [text]) - success = True - else: - success = False - cherrypy.response.headers['Content-Type'] = 'application/json' - return json.dumps({'results': {'success': success}}).encode() - - def controller(self, display_type, action): - """ - Perform an action on the slide controller. - - ``display_type`` - This is the type of slide controller, either ``preview`` or ``live``. - - ``action`` - The action to perform. - """ - event = 'slidecontroller_%s_%s' % (display_type, action) - if action == 'text': - current_item = self.live_controller.service_item - data = [] - if current_item: - for index, frame in enumerate(current_item.get_frames()): - item = {} - if current_item.is_text(): - if frame['verseTag']: - item['tag'] = str(frame['verseTag']) - else: - item['tag'] = str(index + 1) - item['text'] = str(frame['text']) - item['html'] = str(frame['html']) - else: - item['tag'] = str(index + 1) - item['text'] = str(frame['title']) - item['html'] = str(frame['title']) - item['selected'] = (self.live_controller.selected_row == index) - data.append(item) - json_data = {'results': {'slides': data}} - if current_item: - json_data['results']['item'] = self.live_controller.service_item.unique_identifier - else: - if self.request_data: - try: - data = json.loads(self.request_data)['request']['id'] - except KeyError as ValueError: - return self._http_bad_request() - log.info(data) - # This slot expects an int within a list. - self.live_controller.emit(QtCore.SIGNAL(event), [data]) - else: - self.live_controller.emit(QtCore.SIGNAL(event)) - json_data = {'results': {'success': True}} - cherrypy.response.headers['Content-Type'] = 'application/json' - return json.dumps(json_data).encode() - - def service(self, action): - """ - Handles requests for service items in the service manager - - ``action`` - The action to perform. - """ - event = 'servicemanager_%s' % action - if action == 'list': - cherrypy.response.headers['Content-Type'] = 'application/json' - return json.dumps({'results': {'items': self._get_service_items()}}).encode() - event += '_item' - if self.request_data: - try: - data = json.loads(self.request_data)['request']['id'] - except KeyError: - return self._http_bad_request() - self.service_manager.emit(QtCore.SIGNAL(event), data) - else: - Registry().execute(event) - cherrypy.response.headers['Content-Type'] = 'application/json' - return json.dumps({'results': {'success': True}}).encode() - - def plugin_info(self, action): - """ - Return plugin related information, based on the action. - - ``action`` - The action to perform. If *search* return a list of plugin names - which support search. - """ - if action == 'search': - searches = [] - for plugin in self.plugin_manager.plugins: - if plugin.status == PluginStatus.Active and plugin.media_item and plugin.media_item.has_search: - searches.append([plugin.name, str(plugin.text_strings[StringContent.Name]['plural'])]) - cherrypy.response.headers['Content-Type'] = 'application/json' - return json.dumps({'results': {'items': searches}}).encode() - - def search(self, plugin_name): - """ - Return a list of items that match the search text. - - ``plugin`` - The plugin name to search in. - """ - try: - text = json.loads(self.request_data)['request']['text'] - except KeyError as ValueError: - return self._http_bad_request() - text = urllib.parse.unquote(text) - plugin = self.plugin_manager.get_plugin_by_name(plugin_name) - if plugin.status == PluginStatus.Active and plugin.media_item and plugin.media_item.has_search: - results = plugin.media_item.search(text, False) - else: - results = [] - cherrypy.response.headers['Content-Type'] = 'application/json' - return json.dumps({'results': {'items': results}}).encode() - - def go_live(self, plugin_name): - """ - Go live on an item of type ``plugin``. - """ - try: - id = json.loads(self.request_data)['request']['id'] - except KeyError as ValueError: - return self._http_bad_request() - plugin = self.plugin_manager.get_plugin_by_name(plugin_name) - if plugin.status == PluginStatus.Active and plugin.media_item: - plugin.media_item.emit(QtCore.SIGNAL('%s_go_live' % plugin_name), [id, True]) - return self._http_success() - - def add_to_service(self, plugin_name): - """ - Add item of type ``plugin_name`` to the end of the service. - """ - try: - id = json.loads(self.request_data)['request']['id'] - except KeyError as ValueError: - return self._http_bad_request() - plugin = self.plugin_manager.get_plugin_by_name(plugin_name) - if plugin.status == PluginStatus.Active and plugin.media_item: - item_id = plugin.media_item.create_item_from_id(id) - plugin.media_item.emit(QtCore.SIGNAL('%s_add_to_service' % plugin_name), [item_id, True]) - self._http_success() - - def _http_success(self): - """ - Set the HTTP success return code. - """ - cherrypy.response.status = 200 - - def _http_bad_request(self): - """ - Set the HTTP bad response return code. - """ - cherrypy.response.status = 400 - - def _http_not_found(self): - """ - Set the HTTP not found return code. - """ - cherrypy.response.status = 404 - cherrypy.response.body = [b'Sorry, an error occurred '] - - def _get_service_manager(self): - """ - Adds the service manager to the class dynamically - """ - if not hasattr(self, '_service_manager'): - self._service_manager = Registry().get('service_manager') - return self._service_manager - - service_manager = property(_get_service_manager) - - def _get_live_controller(self): - """ - Adds the live controller to the class dynamically - """ - if not hasattr(self, '_live_controller'): - self._live_controller = Registry().get('live_controller') - return self._live_controller - - live_controller = property(_get_live_controller) - - def _get_plugin_manager(self): - """ - Adds the plugin manager to the class dynamically - """ - if not hasattr(self, '_plugin_manager'): - self._plugin_manager = Registry().get('plugin_manager') - return self._plugin_manager - - plugin_manager = property(_get_plugin_manager) - - def _get_alerts_manager(self): - """ - Adds the alerts manager to the class dynamically - """ - if not hasattr(self, '_alerts_manager'): - self._alerts_manager = Registry().get('alerts_manager') - return self._alerts_manager - - alerts_manager = property(_get_alerts_manager) diff --git a/openlp/plugins/remotes/remoteplugin.py b/openlp/plugins/remotes/remoteplugin.py index 6d51ae28d..0c23978b6 100644 --- a/openlp/plugins/remotes/remoteplugin.py +++ b/openlp/plugins/remotes/remoteplugin.py @@ -29,10 +29,8 @@ import logging -from PyQt4 import QtGui - from openlp.core.lib import Plugin, StringContent, translate, build_icon -from openlp.plugins.remotes.lib import RemoteTab, HttpServer +from openlp.plugins.remotes.lib import RemoteTab, OpenLPServer log = logging.getLogger(__name__) @@ -67,8 +65,8 @@ class RemotesPlugin(Plugin): """ log.debug('initialise') super(RemotesPlugin, self).initialise() - self.server = HttpServer() - self.server.start_server() + self.server = OpenLPServer() + #self.server.start_server() def finalise(self): """ @@ -77,7 +75,7 @@ class RemotesPlugin(Plugin): log.debug('finalise') super(RemotesPlugin, self).finalise() if self.server: - self.server.close() + self.server.stop_server() self.server = None def about(self): From 448da916e9f820e8d9333579a68f3b9c09cb19d9 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Thu, 12 Sep 2013 17:46:57 +0100 Subject: [PATCH 56/93] Readonly works - next comes updates --- openlp/plugins/remotes/lib/httprouter.py | 120 ++++++++++++++++------- openlp/plugins/remotes/lib/httpserver.py | 46 ++++----- 2 files changed, 108 insertions(+), 58 deletions(-) diff --git a/openlp/plugins/remotes/lib/httprouter.py b/openlp/plugins/remotes/lib/httprouter.py index 77927723c..4d9133e65 100644 --- a/openlp/plugins/remotes/lib/httprouter.py +++ b/openlp/plugins/remotes/lib/httprouter.py @@ -112,15 +112,14 @@ the remotes. {"results": {"items": [{...}, {...}]}} """ - +import base64 import json import logging import os import re import urllib.request -import urllib.parse import urllib.error -import urllib.parse +from urllib.parse import urlparse, parse_qs from mako.template import Template @@ -129,46 +128,77 @@ from PyQt4 import QtCore from openlp.core.lib import Registry, Settings, PluginStatus, StringContent, image_to_byte from openlp.core.utils import AppLocation, translate -from hashlib import sha1 - log = logging.getLogger(__name__) class HttpRouter(object): """ This code is called by the HttpServer upon a request and it processes it based on the routing table. + This code is stateless so need """ def initialise(self): """ - Initialise the router + Initialise the router stack and any other varables. """ + authcode = "%s:%s" % (Settings().value('remotes/user id'), Settings().value('remotes/password')) + try: + self.auth = base64.b64encode(authcode) + except TypeError: + self.auth = base64.b64encode(authcode.encode()).decode() self.routes = [ - ('^/$', self.serve_file), - ('^/(stage)$', self.serve_file), - ('^/(main)$', self.serve_file), - (r'^/files/(.*)$', self.serve_file), - (r'^/api/poll$', self.poll), - (r'^/stage/poll$', self.poll), - (r'^/main/poll$', self.main_poll), - (r'^/main/image$', self.main_image), - (r'^/api/controller/(live|preview)/(.*)$', self.controller), - (r'^/stage/controller/(live|preview)/(.*)$', self.controller), - (r'^/api/service/(.*)$', self.service), - (r'^/stage/service/(.*)$', self.service), - (r'^/api/display/(hide|show|blank|theme|desktop)$', self.display), - (r'^/api/alert$', self.alert), - (r'^/api/plugin/(search)$', self.plugin_info), - (r'^/api/(.*)/search$', self.search), - (r'^/api/(.*)/live$', self.go_live), - (r'^/api/(.*)/add$', self.add_to_service) + ('^/$', {'function': self.serve_file, 'secure': False}), + ('^/(stage)$', {'function': self.serve_file, 'secure': False}), + ('^/(main)$', {'function': self.serve_file, 'secure': False}), + (r'^/files/(.*)$', {'function': self.serve_file, 'secure': False}), + (r'^/api/poll$', {'function': self.poll, 'secure': False}), + (r'^/stage/poll$', {'function': self.poll, 'secure': False}), + (r'^/main/poll$', {'function': self.poll, 'secure': False}), + (r'^/main/image$', {'function': self.main_poll, 'secure': False}), + (r'^/api/controller/(live|preview)/(.*)$', {'function': self.controller, 'secure': False}), + (r'^/stage/controller/(live|preview)/(.*)$', {'function': self.controller, 'secure': False}), + (r'^/api/service/(.*)$', {'function':self.service, 'secure': False}), + (r'^/stage/service/(.*)$', {'function': self.service, 'secure': False}), + (r'^/api/display/(hide|show|blank|theme|desktop)$', {'function': self.display, 'secure': True}), + (r'^/api/alert$', {'function': self.alert, 'secure': True}), + (r'^/api/plugin/(search)$', {'function': self.plugin_info, 'secure': False}), + (r'^/api/(.*)/search$', {'function': self.search, 'secure': False}), + (r'^/api/(.*)/live$', {'function': self.go_live, 'secure': True}), + (r'^/api/(.*)/add$', {'function': self.add_to_service, 'secure': True}) ] self.translate() self.html_dir = os.path.join(AppLocation.get_directory(AppLocation.PluginsDir), 'remotes', 'html') + def call_function(self, function, *args): + response = function['function'](*args) + if response: + self.wfile.write(response) + return + def process_http_request(self, url_path, *args): """ Common function to process HTTP requests + ``url_path`` + The requested URL. + + ``*args`` + Any passed data. + """ + url_path_split = urlparse(url_path) + for route, func in self.routes: + match = re.match(route, url_path_split.path) + if match: + print('Route "%s" matched "%s"', route, url_path) + args = [] + for param in match.groups(): + args.append(param) + return func, args + return None, None + + def _process_http_request(self, url_path, *args): + """ + Common function to process HTTP requests + ``url_path`` The requested URL. @@ -189,7 +219,29 @@ class HttpRouter(object): return response else: log.debug('Path not found %s', url_path) - return self._http_not_found() + return self.do_not_found() + + def do_http_success(self): + self.send_response(200) + self.send_header('Content-type', 'text/html') + self.end_headers() + + def do_http_error(self): + self.send_response(404) + self.send_header('Content-type', 'text/html') + self.end_headers() + + def do_authorisation(self): + self.send_response(401) + self.send_header('WWW-Authenticate', 'Basic realm=\"Test\"') + self.send_header('Content-type', 'text/html') + self.end_headers() + + def do_notfound(self): + self.send_response(404) + self.send_header('Content-type', 'text/html') + self.end_headers() + self.wfile.write(bytes('Sorry, an error occurred ', 'UTF-8')) def _get_service_items(self): """ @@ -259,25 +311,27 @@ class HttpRouter(object): file_name = 'main.html' path = os.path.normpath(os.path.join(self.html_dir, file_name)) if not path.startswith(self.html_dir): - return self._http_not_found() + return self.http_not_found() ext = os.path.splitext(file_name)[1] html = None if ext == '.html': - mimetype = 'text/html' + self.send_header('Content-type', 'text/html') variables = self.template_vars html = Template(filename=path, input_encoding='utf-8', output_encoding='utf-8').render(**variables) elif ext == '.css': - mimetype = 'text/css' + self.send_header('Content-type', 'text/css') elif ext == '.js': - mimetype = 'application/x-javascript' + self.send_header('Content-type', 'application/x-javascript') elif ext == '.jpg': - mimetype = 'image/jpeg' + self.send_header('Content-type', 'image/jpeg') elif ext == '.gif': - mimetype = 'image/gif' + self.send_header('Content-type', 'image/gif') + elif ext == '.ico': + self.send_header('Content-type', 'image/ico') elif ext == '.png': - mimetype = 'image/png' + self.send_header('Content-type', 'image/png') else: - mimetype = 'text/plain' + self.send_header('Content-type', 'text/plain') file_handle = None try: if html: diff --git a/openlp/plugins/remotes/lib/httpserver.py b/openlp/plugins/remotes/lib/httpserver.py index cee58b9ab..a819d8ac2 100644 --- a/openlp/plugins/remotes/lib/httpserver.py +++ b/openlp/plugins/remotes/lib/httpserver.py @@ -49,40 +49,26 @@ from openlp.plugins.remotes.lib import HttpRouter from socketserver import BaseServer, ThreadingMixIn from http.server import BaseHTTPRequestHandler, HTTPServer -from hashlib import sha1 - log = logging.getLogger(__name__) -def make_sha_hash(password): - """ - Create an encrypted password for the given password. - """ - log.debug("make_sha_hash") - return sha1(password.encode()).hexdigest() - - -def fetch_password(username): - """ - Fetch the password for a provided user. - """ - log.debug("Fetch Password") - if username != Settings().value('remotes/user id'): - return None - return make_sha_hash(Settings().value('remotes/password')) - - class CustomHandler(BaseHTTPRequestHandler, HttpRouter): """ - Main class to present webpages and authentication. + Stateless session handler to handle the HTTP request and process it. + This class handles just the overrides to the base methods and the logic to invoke the + methods within the HttpRouter class. + DO not try change the structure as this is as per the documentation. """ def do_POST(self): + """ + Present pages / data and invoke URL level user authentication. + """ self.do_GET() def do_GET(self): """ - Present frontpage with user authentication. + Present pages / data and invoke URL level user authentication. """ if self.path == '/favicon.ico': return @@ -141,7 +127,7 @@ class HttpThread(QtCore.QThread): class OpenLPServer(): def __init__(self): """ - Initialise the http server, and start the server. + Initialise the http server, and start the server of the correct type http / https """ log.debug('Initialise httpserver') self.settings_section = 'remotes' @@ -149,24 +135,34 @@ class OpenLPServer(): self.http_thread.start() def start_server(self): + """ + Start the correct server and save the handler + """ address = Settings().value(self.settings_section + '/ip address') if Settings().value(self.settings_section + '/https enabled'): port = Settings().value(self.settings_section + '/https port') self.httpd = HTTPSServer((address, port), CustomHandler) - print('started ssl httpd...') + log.debug('Started ssl httpd...') else: port = Settings().value(self.settings_section + '/port') self.httpd = ThreadingHTTPServer((address, port), CustomHandler) - print('started non ssl httpd...') + log.debug('Started non ssl httpd...') self.httpd.serve_forever() def stop_server(self): + """ + Stop the server + """ self.httpd.socket.close() self.httpd = None + log.debug('Stopped the server.') class HTTPSServer(HTTPServer): def __init__(self, address, handler): + """ + Initialise the secure handlers for the SSL server if required.s + """ BaseServer.__init__(self, address, handler) local_data = AppLocation.get_directory(AppLocation.DataDir) self.socket = ssl.SSLSocket( From 1ac75c85fdea60698c975b19ad5969a0be1e3a80 Mon Sep 17 00:00:00 2001 From: "Jeffrey S. Smith" Date: Fri, 13 Sep 2013 14:23:31 -0500 Subject: [PATCH 57/93] Handle tests with platform-dependent behavior carefully --- .../openlp_core_utils/test_utils.py | 23 +---------- .../songs/test_worshipcenterproimport.py | 38 ++++++++++--------- 2 files changed, 23 insertions(+), 38 deletions(-) diff --git a/tests/functional/openlp_core_utils/test_utils.py b/tests/functional/openlp_core_utils/test_utils.py index 6eacb2e48..d3f574a8a 100644 --- a/tests/functional/openlp_core_utils/test_utils.py +++ b/tests/functional/openlp_core_utils/test_utils.py @@ -105,33 +105,14 @@ class TestUtils(TestCase): # THEN: The file name should be cleaned. assert result == wanted_name, 'The file name should not contain any special characters.' - def get_locale_key_windows_test(self): + def get_locale_key_test(self): """ Test the get_locale_key(string) function """ - with patch('openlp.core.utils.languagemanager.LanguageManager.get_language') as mocked_get_language, \ - patch('openlp.core.utils.os') as mocked_os: + with patch('openlp.core.utils.languagemanager.LanguageManager.get_language') as mocked_get_language: # GIVEN: The language is German # 0x00C3 (A with diaresis) should be sorted as "A". 0x00DF (sharp s) should be sorted as "ss". mocked_get_language.return_value = 'de' - mocked_os.name = 'nt' - unsorted_list = ['Auszug', 'Aushang', '\u00C4u\u00DFerung'] - # WHEN: We sort the list and use get_locale_key() to generate the sorting keys - # THEN: We get a properly sorted list - test_passes = sorted(unsorted_list, key=get_locale_key) == ['Aushang', '\u00C4u\u00DFerung', 'Auszug'] - assert test_passes, 'Strings should be sorted properly' - - def get_locale_key_linux_test(self): - - """ - Test the get_locale_key(string) function - """ - with patch('openlp.core.utils.languagemanager.LanguageManager.get_language') as mocked_get_language, \ - patch('openlp.core.utils.os.name') as mocked_os: - # GIVEN: The language is German - # 0x00C3 (A with diaresis) should be sorted as "A". 0x00DF (sharp s) should be sorted as "ss". - mocked_get_language.return_value = 'de' - mocked_os.name = 'linux' unsorted_list = ['Auszug', 'Aushang', '\u00C4u\u00DFerung'] # WHEN: We sort the list and use get_locale_key() to generate the sorting keys # THEN: We get a properly sorted list diff --git a/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py b/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py index 7c9b80056..d5c107342 100644 --- a/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py +++ b/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py @@ -5,11 +5,13 @@ This module contains tests for the WorshipCenter Pro song importer. """ -from unittest import TestCase +from unittest import TestCase, skipIf from mock import patch, MagicMock -import pyodbc -from openlp.plugins.songs.lib.worshipcenterproimport import WorshipCenterProImport +import os +if os.name == 'nt': + import pyodbc + from openlp.plugins.songs.lib.worshipcenterproimport import WorshipCenterProImport class TestRecord(object): """ @@ -23,22 +25,23 @@ class TestRecord(object): self.Field = field self.Value = value -class WorshipCenterProImportLogger(WorshipCenterProImport): - """ - This class logs changes in the title instance variable - """ - _title_assignment_list = [] +if os.name == 'nt': + class WorshipCenterProImportLogger(WorshipCenterProImport): + """ + This class logs changes in the title instance variable + """ + _title_assignment_list = [] - def __init__(self, manager): - WorshipCenterProImport.__init__(self, manager) + def __init__(self, manager): + WorshipCenterProImport.__init__(self, manager) - @property - def title(self): - return self._title_assignment_list[-1] + @property + def title(self): + return self._title_assignment_list[-1] - @title.setter - def title(self, title): - self._title_assignment_list.append(title) + @title.setter + def title(self, title): + self._title_assignment_list.append(title) RECORDSET_TEST_DATA = [TestRecord(1, 'TITLE', 'Amazing Grace'), @@ -98,6 +101,7 @@ SONG_TEST_DATA = [{'title': 'Amazing Grace', ('There\'s a garden where\nJesus is waiting,\nAnd He bids you to come,\nmeet Him there;\n' 'Just to bow and\nreceive a new blessing\nIn the beautiful\ngarden of prayer.')]}] +@skipIf(os.name != 'nt', 'WorshipCenter Pro import only supported on Windows') class TestWorshipCenterProSongImport(TestCase): """ Test the functions in the :mod:`worshipcenterproimport` module. @@ -189,4 +193,4 @@ class TestWorshipCenterProSongImport(TestCase): for call in verse_calls: mocked_add_verse.assert_any_call(call) self.assertEqual(mocked_add_verse.call_count, add_verse_call_count, - 'Incorrect number of calls made to addVerse') \ No newline at end of file + 'Incorrect number of calls made to addVerse') From 9b82b65ccc3bc6ed48bd2ebf1520360c6cc76973 Mon Sep 17 00:00:00 2001 From: "Jeffrey S. Smith" Date: Fri, 13 Sep 2013 21:42:12 -0500 Subject: [PATCH 58/93] Ignore __pycache__ folders --- .bzrignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.bzrignore b/.bzrignore index 7084e1563..d87c55a61 100644 --- a/.bzrignore +++ b/.bzrignore @@ -27,3 +27,4 @@ openlp.pro tests.kdev4 *.nja *.orig +__pycache__ From cca76a90bd340b8812d425a886b8f371c15aed0f Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sat, 14 Sep 2013 08:22:01 +0100 Subject: [PATCH 59/93] pass data to functions --- openlp/plugins/remotes/lib/httprouter.py | 38 ++++++------------------ openlp/plugins/remotes/lib/httpserver.py | 7 +++-- 2 files changed, 13 insertions(+), 32 deletions(-) diff --git a/openlp/plugins/remotes/lib/httprouter.py b/openlp/plugins/remotes/lib/httprouter.py index 4d9133e65..799396454 100644 --- a/openlp/plugins/remotes/lib/httprouter.py +++ b/openlp/plugins/remotes/lib/httprouter.py @@ -154,7 +154,7 @@ class HttpRouter(object): (r'^/stage/poll$', {'function': self.poll, 'secure': False}), (r'^/main/poll$', {'function': self.poll, 'secure': False}), (r'^/main/image$', {'function': self.main_poll, 'secure': False}), - (r'^/api/controller/(live|preview)/(.*)$', {'function': self.controller, 'secure': False}), + (r'^/api/controller/(live|preview)/(.*)$', {'function': self.controller, 'secure': True}), (r'^/stage/controller/(live|preview)/(.*)$', {'function': self.controller, 'secure': False}), (r'^/api/service/(.*)$', {'function':self.service, 'secure': False}), (r'^/stage/service/(.*)$', {'function': self.service, 'secure': False}), @@ -184,42 +184,20 @@ class HttpRouter(object): ``*args`` Any passed data. """ + self.request_data = None url_path_split = urlparse(url_path) + url_query = parse_qs(url_path_split.query) + if 'data' in url_query.keys(): + self.request_data = url_query['data'][0] for route, func in self.routes: match = re.match(route, url_path_split.path) - if match: - print('Route "%s" matched "%s"', route, url_path) - args = [] - for param in match.groups(): - args.append(param) - return func, args - return None, None - - def _process_http_request(self, url_path, *args): - """ - Common function to process HTTP requests - - ``url_path`` - The requested URL. - - ``*args`` - Any passed data. - """ - response = None - for route, func in self.routes: - match = re.match(route, url_path) if match: log.debug('Route "%s" matched "%s"', route, url_path) args = [] for param in match.groups(): args.append(param) - response = func(*args) - break - if response: - return response - else: - log.debug('Path not found %s', url_path) - return self.do_not_found() + return func, args + return None, None def do_http_success(self): self.send_response(200) @@ -469,6 +447,8 @@ class HttpRouter(object): event += '_item' if self.request_data: try: +# print(json.loads(self.request_data['data'])) + print(json.loads(self.request_data)) data = json.loads(self.request_data)['request']['id'] except KeyError: return self._http_bad_request() diff --git a/openlp/plugins/remotes/lib/httpserver.py b/openlp/plugins/remotes/lib/httpserver.py index a819d8ac2..5535847a6 100644 --- a/openlp/plugins/remotes/lib/httpserver.py +++ b/openlp/plugins/remotes/lib/httpserver.py @@ -72,12 +72,13 @@ class CustomHandler(BaseHTTPRequestHandler, HttpRouter): """ if self.path == '/favicon.ico': return - print(self.headers['content-type']) - if self.headers['content-type'] == 'application/json': + #print(self.headers['content-type'],self.headers['content-length']) + if self.headers['content-type'] == 'application/text': length = int(self.headers['content-length']) postvars = parse_qs(self.rfile.read(length), keep_blank_values=1) for var in postvars: - print(var.decode("utf-8")) + print(var) + #{"request": {"id": 1}} if not hasattr(self, 'auth'): self.initialise() function, args = self.process_http_request(self.path) From 2531513ec58229e626a881a3affaef9b848d8c70 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sat, 14 Sep 2013 19:46:49 +0100 Subject: [PATCH 60/93] basic working model --- openlp/plugins/remotes/html/openlp.js | 6 +- openlp/plugins/remotes/html/stage.js | 6 +- openlp/plugins/remotes/lib/httprouter.py | 120 ++++++++++++----------- openlp/plugins/remotes/lib/httpserver.py | 10 +- 4 files changed, 75 insertions(+), 67 deletions(-) diff --git a/openlp/plugins/remotes/html/openlp.js b/openlp/plugins/remotes/html/openlp.js index 10bc9e328..dd9e7a98b 100644 --- a/openlp/plugins/remotes/html/openlp.js +++ b/openlp/plugins/remotes/html/openlp.js @@ -40,6 +40,8 @@ window.OpenLP = { // defeat Safari bug targ = targ.parentNode; } + var isSecure = false; + var isAuthorised = false; return $(targ); }, getSearchablePlugins: function () { @@ -147,11 +149,13 @@ window.OpenLP = { }, pollServer: function () { $.getJSON( - "/stage/poll", + "/api/poll", function (data, status) { var prevItem = OpenLP.currentItem; OpenLP.currentSlide = data.results.slide; OpenLP.currentItem = data.results.item; + OpenLP.isSecure = data.results.isSecure; + OpenLP.isAuthorised = data.results.isAuthorised; if ($("#service-manager").is(":visible")) { if (OpenLP.currentService != data.results.service) { OpenLP.currentService = data.results.service; diff --git a/openlp/plugins/remotes/html/stage.js b/openlp/plugins/remotes/html/stage.js index 42b7712f9..dcc2e4b70 100644 --- a/openlp/plugins/remotes/html/stage.js +++ b/openlp/plugins/remotes/html/stage.js @@ -26,7 +26,7 @@ window.OpenLP = { loadService: function (event) { $.getJSON( - "/stage/service/list", + "/api/service/list", function (data, status) { OpenLP.nextSong = ""; $("#notes").html(""); @@ -46,7 +46,7 @@ window.OpenLP = { }, loadSlides: function (event) { $.getJSON( - "/stage/controller/live/text", + "/api/controller/live/text", function (data, status) { OpenLP.currentSlides = data.results.slides; OpenLP.currentSlide = 0; @@ -137,7 +137,7 @@ window.OpenLP = { }, pollServer: function () { $.getJSON( - "/stage/poll", + "/api/poll", function (data, status) { OpenLP.updateClock(data); if (OpenLP.currentItem != data.results.item || diff --git a/openlp/plugins/remotes/lib/httprouter.py b/openlp/plugins/remotes/lib/httprouter.py index 799396454..11e1900ef 100644 --- a/openlp/plugins/remotes/lib/httprouter.py +++ b/openlp/plugins/remotes/lib/httprouter.py @@ -151,13 +151,12 @@ class HttpRouter(object): ('^/(main)$', {'function': self.serve_file, 'secure': False}), (r'^/files/(.*)$', {'function': self.serve_file, 'secure': False}), (r'^/api/poll$', {'function': self.poll, 'secure': False}), - (r'^/stage/poll$', {'function': self.poll, 'secure': False}), (r'^/main/poll$', {'function': self.poll, 'secure': False}), (r'^/main/image$', {'function': self.main_poll, 'secure': False}), + (r'^/api/controller/(live|preview)/text$', {'function': self.controller_text, 'secure': False}), (r'^/api/controller/(live|preview)/(.*)$', {'function': self.controller, 'secure': True}), - (r'^/stage/controller/(live|preview)/(.*)$', {'function': self.controller, 'secure': False}), - (r'^/api/service/(.*)$', {'function':self.service, 'secure': False}), - (r'^/stage/service/(.*)$', {'function': self.service, 'secure': False}), + (r'^/api/service/list$', {'function': self.service_list, 'secure': False}), + (r'^/api/service/(.*)$', {'function': self.service, 'secure': True}), (r'^/api/display/(hide|show|blank|theme|desktop)$', {'function': self.display, 'secure': True}), (r'^/api/alert$', {'function': self.alert, 'secure': True}), (r'^/api/plugin/(search)$', {'function': self.plugin_info, 'secure': False}), @@ -165,6 +164,7 @@ class HttpRouter(object): (r'^/api/(.*)/live$', {'function': self.go_live, 'secure': True}), (r'^/api/(.*)/add$', {'function': self.add_to_service, 'secure': True}) ] + self.settings_section = 'remotes' self.translate() self.html_dir = os.path.join(AppLocation.get_directory(AppLocation.PluginsDir), 'remotes', 'html') @@ -215,7 +215,7 @@ class HttpRouter(object): self.send_header('Content-type', 'text/html') self.end_headers() - def do_notfound(self): + def do_not_found(self): self.send_response(404) self.send_header('Content-type', 'text/html') self.end_headers() @@ -289,7 +289,7 @@ class HttpRouter(object): file_name = 'main.html' path = os.path.normpath(os.path.join(self.html_dir, file_name)) if not path.startswith(self.html_dir): - return self.http_not_found() + return self.do_not_found() ext = os.path.splitext(file_name)[1] html = None if ext == '.html': @@ -320,7 +320,7 @@ class HttpRouter(object): content = file_handle.read() except IOError: log.exception('Failed to open %s' % path) - return self._http_not_found() + return self.do_not_found() finally: if file_handle: file_handle.close() @@ -337,7 +337,10 @@ class HttpRouter(object): 'twelve': Settings().value('remotes/twelve hour'), 'blank': self.live_controller.blank_screen.isChecked(), 'theme': self.live_controller.theme_screen.isChecked(), - 'display': self.live_controller.desktop_screen.isChecked() + 'display': self.live_controller.desktop_screen.isChecked(), + 'version': 2, + 'isSecure': Settings().value(self.settings_section + '/authentication enabled'), + 'isAuthorised': self.authorised } return json.dumps({'results': result}).encode() @@ -379,7 +382,7 @@ class HttpRouter(object): try: text = json.loads(self.request_data)['request']['text'] except KeyError as ValueError: - return self._http_bad_request() + return self.do_http_error() text = urllib.parse.unquote(text) self.alerts_manager.emit(QtCore.SIGNAL('alerts_text'), [text]) success = True @@ -387,6 +390,33 @@ class HttpRouter(object): success = False return json.dumps({'results': {'success': success}}).encode() + def controller_text(self, var): + """ + Perform an action on the slide controller. + """ + current_item = self.live_controller.service_item + data = [] + if current_item: + for index, frame in enumerate(current_item.get_frames()): + item = {} + if current_item.is_text(): + if frame['verseTag']: + item['tag'] = str(frame['verseTag']) + else: + item['tag'] = str(index + 1) + item['text'] = str(frame['text']) + item['html'] = str(frame['html']) + else: + item['tag'] = str(index + 1) + item['text'] = str(frame['title']) + item['html'] = str(frame['title']) + item['selected'] = (self.live_controller.selected_row == index) + data.append(item) + json_data = {'results': {'slides': data}} + if current_item: + json_data['results']['item'] = self.live_controller.service_item.unique_identifier + return json.dumps(json_data).encode() + def controller(self, display_type, action): """ Perform an action on the slide controller. @@ -398,42 +428,28 @@ class HttpRouter(object): The action to perform. """ event = 'slidecontroller_%s_%s' % (display_type, action) - if action == 'text': - current_item = self.live_controller.service_item - data = [] - if current_item: - for index, frame in enumerate(current_item.get_frames()): - item = {} - if current_item.is_text(): - if frame['verseTag']: - item['tag'] = str(frame['verseTag']) - else: - item['tag'] = str(index + 1) - item['text'] = str(frame['text']) - item['html'] = str(frame['html']) - else: - item['tag'] = str(index + 1) - item['text'] = str(frame['title']) - item['html'] = str(frame['title']) - item['selected'] = (self.live_controller.selected_row == index) - data.append(item) - json_data = {'results': {'slides': data}} - if current_item: - json_data['results']['item'] = self.live_controller.service_item.unique_identifier + if self.request_data: + try: + data = json.loads(self.request_data)['request']['id'] + except KeyError as ValueError: + return self.do_http_error() + log.info(data) + # This slot expects an int within a list. + self.live_controller.emit(QtCore.SIGNAL(event), [data]) else: - if self.request_data: - try: - data = json.loads(self.request_data)['request']['id'] - except KeyError as ValueError: - return self._http_bad_request() - log.info(data) - # This slot expects an int within a list. - self.live_controller.emit(QtCore.SIGNAL(event), [data]) - else: - self.live_controller.emit(QtCore.SIGNAL(event)) - json_data = {'results': {'success': True}} + self.live_controller.emit(QtCore.SIGNAL(event)) + json_data = {'results': {'success': True}} return json.dumps(json_data).encode() + def service_list(self): + """ + Handles requests for service items in the service manager + + ``action`` + The action to perform. + """ + return json.dumps({'results': {'items': self._get_service_items()}}).encode() + def service(self, action): """ Handles requests for service items in the service manager @@ -441,17 +457,12 @@ class HttpRouter(object): ``action`` The action to perform. """ - event = 'servicemanager_%s' % action - if action == 'list': - return json.dumps({'results': {'items': self._get_service_items()}}).encode() - event += '_item' + event = 'servicemanager_%s_item' % action if self.request_data: try: -# print(json.loads(self.request_data['data'])) - print(json.loads(self.request_data)) data = json.loads(self.request_data)['request']['id'] except KeyError: - return self._http_bad_request() + return self.do_http_error() self.service_manager.emit(QtCore.SIGNAL(event), data) else: Registry().execute(event) @@ -482,7 +493,7 @@ class HttpRouter(object): try: text = json.loads(self.request_data)['request']['text'] except KeyError as ValueError: - return self._http_bad_request() + return self.do_http_error() text = urllib.parse.unquote(text) plugin = self.plugin_manager.get_plugin_by_name(plugin_name) if plugin.status == PluginStatus.Active and plugin.media_item and plugin.media_item.has_search: @@ -498,11 +509,11 @@ class HttpRouter(object): try: id = json.loads(self.request_data)['request']['id'] except KeyError as ValueError: - return self._http_bad_request() + return self.do_http_error() plugin = self.plugin_manager.get_plugin_by_name(plugin_name) if plugin.status == PluginStatus.Active and plugin.media_item: plugin.media_item.emit(QtCore.SIGNAL('%s_go_live' % plugin_name), [id, True]) - return self._http_success() + return self.do_http_success() def add_to_service(self, plugin_name): """ @@ -511,12 +522,12 @@ class HttpRouter(object): try: id = json.loads(self.request_data)['request']['id'] except KeyError as ValueError: - return self._http_bad_request() + return self.do_http_error() plugin = self.plugin_manager.get_plugin_by_name(plugin_name) if plugin.status == PluginStatus.Active and plugin.media_item: item_id = plugin.media_item.create_item_from_id(id) plugin.media_item.emit(QtCore.SIGNAL('%s_add_to_service' % plugin_name), [item_id, True]) - self._http_success() + self.do_http_success() def _get_service_manager(self): """ @@ -557,4 +568,3 @@ class HttpRouter(object): return self._alerts_manager alerts_manager = property(_get_alerts_manager) - diff --git a/openlp/plugins/remotes/lib/httpserver.py b/openlp/plugins/remotes/lib/httpserver.py index 5535847a6..932a4af69 100644 --- a/openlp/plugins/remotes/lib/httpserver.py +++ b/openlp/plugins/remotes/lib/httpserver.py @@ -72,20 +72,14 @@ class CustomHandler(BaseHTTPRequestHandler, HttpRouter): """ if self.path == '/favicon.ico': return - #print(self.headers['content-type'],self.headers['content-length']) - if self.headers['content-type'] == 'application/text': - length = int(self.headers['content-length']) - postvars = parse_qs(self.rfile.read(length), keep_blank_values=1) - for var in postvars: - print(var) - #{"request": {"id": 1}} if not hasattr(self, 'auth'): self.initialise() function, args = self.process_http_request(self.path) if not function: self.do_http_error() return - if function['secure']: + self.authorised = self.headers['Authorization'] is None + if function['secure'] and Settings().value(self.settings_section + '/authentication enabled'): if self.headers['Authorization'] is None: self.do_authorisation() self.wfile.write(bytes('no auth header received', 'UTF-8')) From b868ecc57dffab2835b5e2d0939289876d6a7a3f Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sat, 14 Sep 2013 20:16:14 +0100 Subject: [PATCH 61/93] remove reminances of cherrypy --- openlp/core/ui/exceptionform.py | 7 ------- openlp/plugins/remotes/remoteplugin.py | 7 ++++--- scripts/check_dependencies.py | 14 +++++++++----- .../openlp_plugins/remotes/test_server.py | 4 +--- 4 files changed, 14 insertions(+), 18 deletions(-) diff --git a/openlp/core/ui/exceptionform.py b/openlp/core/ui/exceptionform.py index 758fc6ecc..2dc034f71 100644 --- a/openlp/core/ui/exceptionform.py +++ b/openlp/core/ui/exceptionform.py @@ -75,12 +75,6 @@ try: ICU_VERSION = 'OK' except ImportError: ICU_VERSION = '-' -try: - import cherrypy - CHERRYPY_VERSION = cherrypy.__version__ -except ImportError: - CHERRYPY_VERSION = '-' - try: WEBKIT_VERSION = QtWebKit.qWebKitVersion() except AttributeError: @@ -140,7 +134,6 @@ class ExceptionForm(QtGui.QDialog, Ui_ExceptionDialog): 'Chardet: %s\n' % CHARDET_VERSION + \ 'PyEnchant: %s\n' % ENCHANT_VERSION + \ 'Mako: %s\n' % MAKO_VERSION + \ - 'CherryPy: %s\n' % CHERRYPY_VERSION + \ 'pyICU: %s\n' % ICU_VERSION + \ 'pyUNO bridge: %s\n' % self._pyuno_import() + \ 'VLC: %s\n' % VLC_VERSION diff --git a/openlp/plugins/remotes/remoteplugin.py b/openlp/plugins/remotes/remoteplugin.py index 0c23978b6..d5b5545ba 100644 --- a/openlp/plugins/remotes/remoteplugin.py +++ b/openlp/plugins/remotes/remoteplugin.py @@ -28,6 +28,7 @@ ############################################################################### import logging +import time from openlp.core.lib import Plugin, StringContent, translate, build_icon from openlp.plugins.remotes.lib import RemoteTab, OpenLPServer @@ -66,7 +67,6 @@ class RemotesPlugin(Plugin): log.debug('initialise') super(RemotesPlugin, self).initialise() self.server = OpenLPServer() - #self.server.start_server() def finalise(self): """ @@ -107,5 +107,6 @@ class RemotesPlugin(Plugin): Called when Config is changed to restart the server on new address or port """ log.debug('remote config changed') - self.main_window.information_message(translate('RemotePlugin', 'Configuration Change'), - translate('RemotePlugin', 'OpenLP will need to be restarted for the Remote changes to become active.')) + self.finalise() + time.sleep(0.5) + self.initialise() diff --git a/scripts/check_dependencies.py b/scripts/check_dependencies.py index 348a809eb..698a65a96 100755 --- a/scripts/check_dependencies.py +++ b/scripts/check_dependencies.py @@ -48,6 +48,7 @@ except ImportError: IS_WIN = sys.platform.startswith('win') + VERS = { 'Python': '2.6', 'PyQt4': '4.6', @@ -82,7 +83,6 @@ MODULES = [ 'enchant', 'bs4', 'mako', - 'cherrypy', 'uno', 'icu', 'bs4', @@ -98,6 +98,7 @@ OPTIONAL_MODULES = [ w = sys.stdout.write + def check_vers(version, required, text): if not isinstance(version, str): version = '.'.join(map(str, version)) @@ -111,13 +112,16 @@ def check_vers(version, required, text): w('FAIL' + os.linesep) return False + def print_vers_fail(required, text): print(' %s >= %s ... FAIL' % (text, required)) + def verify_python(): if not check_vers(list(sys.version_info), VERS['Python'], text='Python'): exit(1) + def verify_versions(): print('Verifying version of modules...') try: @@ -138,6 +142,7 @@ def verify_versions(): except ImportError: print_vers_fail(VERS['enchant'], 'enchant') + def check_module(mod, text='', indent=' '): space = (30 - len(mod) - len(text)) * ' ' w(indent + '%s%s... ' % (mod, text) + space) @@ -148,6 +153,7 @@ def check_module(mod, text='', indent=' '): w('FAIL') w(os.linesep) + def verify_pyenchant(): w('Enchant (spell checker)... ') try: @@ -160,6 +166,7 @@ def verify_pyenchant(): except ImportError: w('FAIL' + os.linesep) + def verify_pyqt(): w('Qt4 image formats... ') try: @@ -174,22 +181,19 @@ def verify_pyqt(): except ImportError: w('FAIL' + os.linesep) + def main(): verify_python() - print('Checking for modules...') for m in MODULES: check_module(m) - print('Checking for optional modules...') for m in OPTIONAL_MODULES: check_module(m[0], text=m[1]) - if IS_WIN: print('Checking for Windows specific modules...') for m in WIN32_MODULES: check_module(m) - verify_versions() verify_pyqt() verify_pyenchant() diff --git a/tests/interfaces/openlp_plugins/remotes/test_server.py b/tests/interfaces/openlp_plugins/remotes/test_server.py index 63975370f..101444829 100644 --- a/tests/interfaces/openlp_plugins/remotes/test_server.py +++ b/tests/interfaces/openlp_plugins/remotes/test_server.py @@ -7,9 +7,8 @@ from unittest import TestCase from tempfile import mkstemp from mock import MagicMock import urllib.request, urllib.error, urllib.parse -import cherrypy -from BeautifulSoup import BeautifulSoup +from bs4 import BeautifulSoup from openlp.core.lib import Settings from openlp.plugins.remotes.lib.httpserver import HttpServer @@ -133,6 +132,5 @@ def process_http_request(url_path, *args): ``*args`` Some args. """ - cherrypy.response.status = 200 return None From 539ed55777097b74a1b121683684c3b61e08131c Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sat, 14 Sep 2013 22:00:58 +0100 Subject: [PATCH 62/93] More cleanups --- openlp/plugins/remotes/lib/httprouter.py | 53 ++++++- openlp/plugins/remotes/lib/httpserver.py | 27 +--- .../openlp_plugins/remotes/test_router.py | 51 +++---- .../openlp_plugins/remotes/test_server.py | 136 ------------------ 4 files changed, 72 insertions(+), 195 deletions(-) delete mode 100644 tests/interfaces/openlp_plugins/remotes/test_server.py diff --git a/openlp/plugins/remotes/lib/httprouter.py b/openlp/plugins/remotes/lib/httprouter.py index 11e1900ef..6f1268433 100644 --- a/openlp/plugins/remotes/lib/httprouter.py +++ b/openlp/plugins/remotes/lib/httprouter.py @@ -134,11 +134,12 @@ log = logging.getLogger(__name__) class HttpRouter(object): """ This code is called by the HttpServer upon a request and it processes it based on the routing table. - This code is stateless so need + This code is stateless and is created on each request. + Some variables may look incorrect but this extends BaseHTTPRequestHandler. """ def initialise(self): """ - Initialise the router stack and any other varables. + Initialise the router stack and any other variables. """ authcode = "%s:%s" % (Settings().value('remotes/user id'), Settings().value('remotes/password')) try: @@ -168,7 +169,43 @@ class HttpRouter(object): self.translate() self.html_dir = os.path.join(AppLocation.get_directory(AppLocation.PluginsDir), 'remotes', 'html') + def do_post_processor(self): + """ + Handle the POST amd GET requests placed on the server. + """ + if self.path == '/favicon.ico': + return + if not hasattr(self, 'auth'): + self.initialise() + function, args = self.process_http_request(self.path) + if not function: + self.do_http_error() + return + self.authorised = self.headers['Authorization'] is None + if function['secure'] and Settings().value(self.settings_section + '/authentication enabled'): + if self.headers['Authorization'] is None: + self.do_authorisation() + self.wfile.write(bytes('no auth header received', 'UTF-8')) + elif self.headers['Authorization'] == 'Basic %s' % self.auth: + self.do_http_success() + self.call_function(function, *args) + else: + self.do_authorisation() + self.wfile.write(bytes(self.headers['Authorization'], 'UTF-8')) + self.wfile.write(bytes(' not authenticated', 'UTF-8')) + else: + self.call_function(function, *args) + def call_function(self, function, *args): + """ + Invoke the route function passing the relevant values + + ``function`` + The function to be calledL. + + ``*args`` + Any passed data. + """ response = function['function'](*args) if response: self.wfile.write(response) @@ -200,22 +237,34 @@ class HttpRouter(object): return None, None def do_http_success(self): + """ + Create a success http header. + """ self.send_response(200) self.send_header('Content-type', 'text/html') self.end_headers() def do_http_error(self): + """ + Create a error http header. + """ self.send_response(404) self.send_header('Content-type', 'text/html') self.end_headers() def do_authorisation(self): + """ + Create a needs authorisation http header. + """ self.send_response(401) self.send_header('WWW-Authenticate', 'Basic realm=\"Test\"') self.send_header('Content-type', 'text/html') self.end_headers() def do_not_found(self): + """ + Create a not found http header. + """ self.send_response(404) self.send_header('Content-type', 'text/html') self.end_headers() diff --git a/openlp/plugins/remotes/lib/httpserver.py b/openlp/plugins/remotes/lib/httpserver.py index 932a4af69..7776812fa 100644 --- a/openlp/plugins/remotes/lib/httpserver.py +++ b/openlp/plugins/remotes/lib/httpserver.py @@ -64,34 +64,13 @@ class CustomHandler(BaseHTTPRequestHandler, HttpRouter): """ Present pages / data and invoke URL level user authentication. """ - self.do_GET() + self.do_post_processor() def do_GET(self): """ Present pages / data and invoke URL level user authentication. """ - if self.path == '/favicon.ico': - return - if not hasattr(self, 'auth'): - self.initialise() - function, args = self.process_http_request(self.path) - if not function: - self.do_http_error() - return - self.authorised = self.headers['Authorization'] is None - if function['secure'] and Settings().value(self.settings_section + '/authentication enabled'): - if self.headers['Authorization'] is None: - self.do_authorisation() - self.wfile.write(bytes('no auth header received', 'UTF-8')) - elif self.headers['Authorization'] == 'Basic %s' % self.auth: - self.do_http_success() - self.call_function(function, *args) - else: - self.do_authorisation() - self.wfile.write(bytes(self.headers['Authorization'], 'UTF-8')) - self.wfile.write(bytes(' not authenticated', 'UTF-8')) - else: - self.call_function(function, *args) + self.do_post_processor() class ThreadingHTTPServer(ThreadingMixIn, HTTPServer): @@ -148,7 +127,7 @@ class OpenLPServer(): """ Stop the server """ - self.httpd.socket.close() + self.http_thread.exit(0) self.httpd = None log.debug('Stopped the server.') diff --git a/tests/functional/openlp_plugins/remotes/test_router.py b/tests/functional/openlp_plugins/remotes/test_router.py index af1849a65..0e154a1a1 100644 --- a/tests/functional/openlp_plugins/remotes/test_router.py +++ b/tests/functional/openlp_plugins/remotes/test_router.py @@ -8,7 +8,7 @@ from tempfile import mkstemp from mock import MagicMock from openlp.core.lib import Settings -from openlp.plugins.remotes.lib.httpserver import HttpRouter, fetch_password, make_sha_hash +from openlp.plugins.remotes.lib.httpserver import HttpRouter from PyQt4 import QtGui __default_settings__ = { @@ -44,40 +44,22 @@ class TestRouter(TestCase): del self.application os.unlink(self.ini_file) - def fetch_password_unknown_test(self): + def password_encrypter_test(self): """ - Test the fetch password code with an unknown userid + Test hash userid and password function """ # GIVEN: A default configuration - # WHEN: called with the defined userid - password = fetch_password('itwinkle') + Settings().setValue('remotes/user id', 'openlp') + Settings().setValue('remotes/password', 'password') - # THEN: the function should return None - self.assertEqual(password, None, 'The result for fetch_password should be None') - - def fetch_password_known_test(self): - """ - Test the fetch password code with the defined userid - """ - # GIVEN: A default configuration # WHEN: called with the defined userid - password = fetch_password('openlp') - required_password = make_sha_hash('password') + router = HttpRouter() + router.initialise() + test_value = 'b3BlbmxwOnBhc3N3b3Jk' + print(router.auth) # THEN: the function should return the correct password - self.assertEqual(password, required_password, 'The result for fetch_password should be the defined password') - - def sha_password_encrypter_test(self): - """ - Test hash password function - """ - # GIVEN: A default configuration - # WHEN: called with the defined userid - required_password = make_sha_hash('password') - test_value = '5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8' - - # THEN: the function should return the correct password - self.assertEqual(required_password, test_value, + self.assertEqual(router.auth, test_value, 'The result for make_sha_hash should return the correct encrypted password') def process_http_request_test(self): @@ -85,15 +67,18 @@ class TestRouter(TestCase): Test the router control functionality """ # GIVEN: A testing set of Routes + router = HttpRouter() mocked_function = MagicMock() test_route = [ - (r'^/stage/api/poll$', mocked_function), + (r'^/stage/api/poll$', {'function': mocked_function, 'secure': False}), ] - self.router.routes = test_route + router.routes = test_route # WHEN: called with a poll route - self.router.process_http_request('/stage/api/poll', None) + function, args = router.process_http_request('/stage/api/poll', None) # THEN: the function should have been called only once - assert mocked_function.call_count == 1, \ - 'The mocked function should have been matched and called once.' + assert function['function'] == mocked_function, \ + 'The mocked function should match defined value.' + assert function['secure'] == False, \ + 'The mocked function should not require any security.' \ No newline at end of file diff --git a/tests/interfaces/openlp_plugins/remotes/test_server.py b/tests/interfaces/openlp_plugins/remotes/test_server.py deleted file mode 100644 index 101444829..000000000 --- a/tests/interfaces/openlp_plugins/remotes/test_server.py +++ /dev/null @@ -1,136 +0,0 @@ -""" -This module contains tests for the lib submodule of the Remotes plugin. -""" -import os - -from unittest import TestCase -from tempfile import mkstemp -from mock import MagicMock -import urllib.request, urllib.error, urllib.parse - -from bs4 import BeautifulSoup - -from openlp.core.lib import Settings -from openlp.plugins.remotes.lib.httpserver import HttpServer -from PyQt4 import QtGui - -__default_settings__ = { - 'remotes/twelve hour': True, - 'remotes/port': 4316, - 'remotes/https port': 4317, - 'remotes/https enabled': False, - 'remotes/user id': 'openlp', - 'remotes/password': 'password', - 'remotes/authentication enabled': False, - 'remotes/ip address': '0.0.0.0' -} - - -class TestRouter(TestCase): - """ - Test the functions in the :mod:`lib` module. - """ - def setUp(self): - """ - Create the UI - """ - fd, self.ini_file = mkstemp('.ini') - Settings().set_filename(self.ini_file) - self.application = QtGui.QApplication.instance() - Settings().extend_default_settings(__default_settings__) - self.server = HttpServer() - - def tearDown(self): - """ - Delete all the C++ objects at the end so that we don't have a segfault - """ - del self.application - os.unlink(self.ini_file) - self.server.close() - - def start_server(self): - """ - Common function to start server then mock out the router. CherryPy crashes if you mock before you start - """ - self.server.start_server() - self.server.router = MagicMock() - self.server.router.process_http_request = process_http_request - - def start_default_server_test(self): - """ - Test the default server serves the correct initial page - """ - # GIVEN: A default configuration - Settings().setValue('remotes/authentication enabled', False) - self.start_server() - - # WHEN: called the route location - code, page = call_remote_server('http://localhost:4316') - - # THEN: default title will be returned - self.assertEqual(BeautifulSoup(page).title.text, 'OpenLP 2.1 Remote', - 'The default menu should be returned') - - def start_authenticating_server_test(self): - """ - Test the default server serves the correctly with authentication - """ - # GIVEN: A default authorised configuration - Settings().setValue('remotes/authentication enabled', True) - self.start_server() - - # WHEN: called the route location with no user details - code, page = call_remote_server('http://localhost:4316') - - # THEN: then server will ask for details - self.assertEqual(code, 401, 'The basic authorisation request should be returned') - - # WHEN: called the route location with user details - code, page = call_remote_server('http://localhost:4316', 'openlp', 'password') - - # THEN: default title will be returned - self.assertEqual(BeautifulSoup(page).title.text, 'OpenLP 2.1 Remote', - 'The default menu should be returned') - - # WHEN: called the route location with incorrect user details - code, page = call_remote_server('http://localhost:4316', 'itwinkle', 'password') - - # THEN: then server will ask for details - self.assertEqual(code, 401, 'The basic authorisation request should be returned') - - -def call_remote_server(url, username=None, password=None): - """ - Helper function - - ``username`` - The username. - - ``password`` - The password. - """ - if username: - passman = urllib.request.HTTPPasswordMgrWithDefaultRealm() - passman.add_password(None, url, username, password) - authhandler = urllib.request.HTTPBasicAuthHandler(passman) - opener = urllib.request.build_opener(authhandler) - urllib.request.install_opener(opener) - try: - page = urllib.request.urlopen(url) - return 0, page.read() - except urllib.error.HTTPError as e: - return e.code, '' - - -def process_http_request(url_path, *args): - """ - Override function to make the Mock work but does nothing. - - ``Url_path`` - The url_path. - - ``*args`` - Some args. - """ - return None - From f725f8a92b667df609b5c8d741c0cca7134c6f1b Mon Sep 17 00:00:00 2001 From: Philip Ridout Date: Sat, 14 Sep 2013 23:22:05 +0100 Subject: [PATCH 63/93] filter returns an itterator in Py3 (as apposed to a list in Py2) --- openlp/plugins/songs/lib/__init__.py | 2 +- openlp/plugins/songs/lib/songshowplusimport.py | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/openlp/plugins/songs/lib/__init__.py b/openlp/plugins/songs/lib/__init__.py index 271a94710..e98e54427 100644 --- a/openlp/plugins/songs/lib/__init__.py +++ b/openlp/plugins/songs/lib/__init__.py @@ -365,7 +365,7 @@ def retrieve_windows_encoding(recommendation=None): [pair[1] for pair in encodings], 0, False) if not choice[1]: return None - return filter(lambda item: item[1] == choice[0], encodings)[0][0] + return next(filter(lambda item: item[1] == choice[0], encodings))[0] def clean_string(string): diff --git a/openlp/plugins/songs/lib/songshowplusimport.py b/openlp/plugins/songs/lib/songshowplusimport.py index d1fa9dec3..35cd44b8a 100644 --- a/openlp/plugins/songs/lib/songshowplusimport.py +++ b/openlp/plugins/songs/lib/songshowplusimport.py @@ -37,7 +37,7 @@ import re import struct from openlp.core.ui.wizard import WizardStrings -from openlp.plugins.songs.lib import VerseType +from openlp.plugins.songs.lib import VerseType, retrieve_windows_encoding from openlp.plugins.songs.lib.songimport import SongImport TITLE = 1 @@ -133,16 +133,16 @@ class SongShowPlusImport(SongImport): else: length_descriptor, = struct.unpack("B", song_data.read(1)) log.debug(length_descriptor_size) - data = song_data.read(length_descriptor).decode() + data = song_data.read(length_descriptor) if block_key == TITLE: self.title = self.decode(data) elif block_key == AUTHOR: - authors = data.split(" / ") + authors = self.decode(data).split(" / ") for author in authors: if author.find(",") !=-1: authorParts = author.split(", ") author = authorParts[1] + " " + authorParts[0] - self.parse_author(self.decode(author)) + self.parse_author(author) elif block_key == COPYRIGHT: self.addCopyright(self.decode(data)) elif block_key == CCLI_NO: @@ -158,7 +158,7 @@ class SongShowPlusImport(SongImport): elif block_key == COMMENTS: self.comments = self.decode(data) elif block_key == VERSE_ORDER: - verse_tag = self.to_openlp_verse_tag(data, True) + verse_tag = self.to_openlp_verse_tag(self.decode(data), True) if verse_tag: if not isinstance(verse_tag, str): verse_tag = self.decode(verse_tag) @@ -212,4 +212,4 @@ class SongShowPlusImport(SongImport): try: return str(data, chardet.detect(data)['encoding']) except: - return str(data, 'cp1252') \ No newline at end of file + return str(data, retrieve_windows_encoding()) \ No newline at end of file From 4d1ba13128ad80ff74e51d075a318828ae258895 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sun, 15 Sep 2013 07:37:43 +0100 Subject: [PATCH 64/93] minor fixes --- openlp/core/ui/formattingtagdialog.py | 2 +- openlp/core/ui/formattingtagform.py | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/openlp/core/ui/formattingtagdialog.py b/openlp/core/ui/formattingtagdialog.py index d0f2ac9c0..6d7dd2453 100644 --- a/openlp/core/ui/formattingtagdialog.py +++ b/openlp/core/ui/formattingtagdialog.py @@ -118,7 +118,7 @@ class Ui_FormattingTagDialog(object): formatting_tag_dialog.setWindowTitle(translate('OpenLP.FormattingTagDialog', 'Configure Formatting Tags')) self.delete_button.setText(UiStrings().Delete) self.new_button.setText(UiStrings().New) - self.tag_table_widget_read_label.setText(translate('OpenLP.FormattingTagDialog', 'Static Formatting')) + self.tag_table_widget_read_label.setText(translate('OpenLP.FormattingTagDialog', 'Default Formatting')) self.tag_table_widget_read.horizontalHeaderItem(0).\ setText(translate('OpenLP.FormattingTagDialog', 'Description')) self.tag_table_widget_read.horizontalHeaderItem(1).setText(translate('OpenLP.FormattingTagDialog', 'Tag')) diff --git a/openlp/core/ui/formattingtagform.py b/openlp/core/ui/formattingtagform.py index 589e7cb6b..a7c793c6f 100644 --- a/openlp/core/ui/formattingtagform.py +++ b/openlp/core/ui/formattingtagform.py @@ -68,6 +68,7 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog, FormattingTagCont self.button_box.rejected.connect(self.close) # Forces reloading of tags from openlp configuration. FormattingTags.load_tags() + self.is_deleting = False def exec_(self): """ @@ -105,6 +106,7 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog, FormattingTagCont """ selected = self.tag_table_widget.currentRow() if selected != -1: + self.is_deleting = True self.tag_table_widget.removeRow(selected) def accept(self): @@ -160,6 +162,9 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog, FormattingTagCont """ This function processes all user edits in the table. It is called on each cell change. """ + if self.is_deleting: + self.is_deleting = False + return # only process for editable rows if self.tag_table_widget.item(pre_row, 0): item = self.tag_table_widget.item(pre_row, pre_col) From dc03e288f8cadaeb0ab33fb69b116122db05fd68 Mon Sep 17 00:00:00 2001 From: Oliver Wieland Date: Mon, 16 Sep 2013 18:30:59 +0200 Subject: [PATCH 65/93] minor changes in event handler --- openlp/plugins/bibles/lib/biblestab.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/openlp/plugins/bibles/lib/biblestab.py b/openlp/plugins/bibles/lib/biblestab.py index 7450d59c8..dd95d9b33 100644 --- a/openlp/plugins/bibles/lib/biblestab.py +++ b/openlp/plugins/bibles/lib/biblestab.py @@ -217,10 +217,7 @@ class BiblesTab(SettingsTab): """ Event handler for the 'verse number visible' check box """ - self.is_verse_number_visible = False - # We have a set value convert to True/False. - if check_state == QtCore.Qt.Checked: - self.is_verse_number_visible = True + self.is_verse_number_visible = (check_state == QtCore.Qt.Checked) self.check_is_verse_number_visible() def on_new_chapters_check_box_changed(self, check_state): From 870a232e4be1cdf8a38a82f2739dd21439e46b7e Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Thu, 19 Sep 2013 18:05:07 +0100 Subject: [PATCH 66/93] Fix mime types add json debugging --- openlp/plugins/remotes/lib/httprouter.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/openlp/plugins/remotes/lib/httprouter.py b/openlp/plugins/remotes/lib/httprouter.py index 6f1268433..1b29bc416 100644 --- a/openlp/plugins/remotes/lib/httprouter.py +++ b/openlp/plugins/remotes/lib/httprouter.py @@ -175,6 +175,14 @@ class HttpRouter(object): """ if self.path == '/favicon.ico': return + ########### + print(self.headers['content-type']) + if self.headers['content-type'] == 'application/json': + length = int(self.headers['content-length']) + postvars = parse_qs(self.rfile.read(length), keep_blank_values=1) + for var in postvars: + print(var.decode("utf-8")) + ############## if not hasattr(self, 'auth'): self.initialise() function, args = self.process_http_request(self.path) @@ -348,13 +356,13 @@ class HttpRouter(object): elif ext == '.css': self.send_header('Content-type', 'text/css') elif ext == '.js': - self.send_header('Content-type', 'application/x-javascript') + self.send_header('Content-type', 'application/javascript') elif ext == '.jpg': self.send_header('Content-type', 'image/jpeg') elif ext == '.gif': self.send_header('Content-type', 'image/gif') elif ext == '.ico': - self.send_header('Content-type', 'image/ico') + self.send_header('Content-type', 'image/x-icon') elif ext == '.png': self.send_header('Content-type', 'image/png') else: From 148da9724570dfa57d0d60e7179d605940a425e1 Mon Sep 17 00:00:00 2001 From: Raoul Snyman Date: Thu, 19 Sep 2013 23:02:28 +0200 Subject: [PATCH 67/93] Update the tests to use a conditionally imported mock and cleaned things up a little. --- tests/functional/__init__.py | 6 ++ tests/functional/openlp_core_lib/test_db.py | 30 +++++- .../openlp_core_lib/test_formattingtags.py | 31 +++++- .../openlp_core_lib/test_image_manager.py | 36 ++++++- tests/functional/openlp_core_lib/test_lib.py | 32 +++++- .../openlp_core_lib/test_pluginmanager.py | 31 +++++- .../openlp_core_lib/test_registry.py | 31 +++++- .../functional/openlp_core_lib/test_screen.py | 32 +++++- .../openlp_core_lib/test_serviceitem.py | 33 ++++++- .../openlp_core_lib/test_settings.py | 34 ++++++- .../openlp_core_lib/test_uistrings.py | 29 +++++- .../openlp_core_utils/test_actions.py | 31 ++++++ .../openlp_core_utils/test_applocation.py | 53 +++++++--- .../openlp_core_utils/test_utils.py | 99 ++++++++++++++----- .../openlp_plugins/bibles/test_lib.py | 28 ++++++ .../bibles/test_versereferencelist.py | 88 +++++++++++------ .../openlp_plugins/images/test_lib.py | 90 ++++++++++------- .../presentations/test_mediaitem.py | 35 +++++-- .../openlp_plugins/remotes/test_remotetab.py | 54 +++++++--- .../openlp_plugins/remotes/test_router.py | 34 ++++++- .../openlp_plugins/songs/test_ewimport.py | 30 +++++- .../songs/test_foilpresenterimport.py | 5 +- .../openlp_plugins/songs/test_lib.py | 48 ++++++--- .../openlp_plugins/songs/test_mediaitem.py | 4 +- .../songs/test_songshowplusimport.py | 30 +++++- .../songs/test_worshipcenterproimport.py | 37 ++++++- 26 files changed, 819 insertions(+), 172 deletions(-) diff --git a/tests/functional/__init__.py b/tests/functional/__init__.py index 24d8d5b28..f311052f9 100644 --- a/tests/functional/__init__.py +++ b/tests/functional/__init__.py @@ -7,7 +7,13 @@ sip.setapi('QTime', 2) sip.setapi('QUrl', 2) sip.setapi('QVariant', 2) +import sys from PyQt4 import QtGui +if sys.version_info[1] >= 3: + from unittest.mock import patch, MagicMock +else: + from mock import patch, MagicMockv + # Only one QApplication can be created. Use QtGui.QApplication.instance() when you need to "create" a QApplication. application = QtGui.QApplication([]) diff --git a/tests/functional/openlp_core_lib/test_db.py b/tests/functional/openlp_core_lib/test_db.py index b485f61e2..cdb046642 100644 --- a/tests/functional/openlp_core_lib/test_db.py +++ b/tests/functional/openlp_core_lib/test_db.py @@ -1,14 +1,42 @@ +# -*- 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 # +############################################################################### """ Package to test the openlp.core.lib package. """ from unittest import TestCase -from mock import MagicMock, patch from sqlalchemy.pool import NullPool from sqlalchemy.orm.scoping import ScopedSession from sqlalchemy import MetaData from openlp.core.lib.db import init_db, get_upgrade_op +from tests.functional import patch, MagicMock class TestDB(TestCase): diff --git a/tests/functional/openlp_core_lib/test_formattingtags.py b/tests/functional/openlp_core_lib/test_formattingtags.py index 5d04544fa..a200318ff 100644 --- a/tests/functional/openlp_core_lib/test_formattingtags.py +++ b/tests/functional/openlp_core_lib/test_formattingtags.py @@ -1,12 +1,39 @@ +# -*- 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 # +############################################################################### """ Package to test the openlp.core.lib.formattingtags package. """ import copy from unittest import TestCase -from mock import patch - from openlp.core.lib import FormattingTags +from tests.functional import patch TAG = { diff --git a/tests/functional/openlp_core_lib/test_image_manager.py b/tests/functional/openlp_core_lib/test_image_manager.py index a1bc7624a..74f699e8e 100644 --- a/tests/functional/openlp_core_lib/test_image_manager.py +++ b/tests/functional/openlp_core_lib/test_image_manager.py @@ -1,15 +1,41 @@ -""" - Package to test the openlp.core.ui package. -""" +# -*- 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 # +############################################################################### +""" +Package to test the openlp.core.ui package. +""" import os from unittest import TestCase -from PyQt4 import QtCore, QtGui +from PyQt4 import QtGui from openlp.core.lib import Registry, ImageManager, ScreenList - TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'resources')) diff --git a/tests/functional/openlp_core_lib/test_lib.py b/tests/functional/openlp_core_lib/test_lib.py index fa7fe92e9..4b67cee2b 100644 --- a/tests/functional/openlp_core_lib/test_lib.py +++ b/tests/functional/openlp_core_lib/test_lib.py @@ -1,3 +1,31 @@ +# -*- 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 # +############################################################################### """ Package to test the openlp.core.lib package. """ @@ -6,17 +34,15 @@ import os from unittest import TestCase from datetime import datetime, timedelta -from mock import MagicMock, patch from PyQt4 import QtCore, QtGui from openlp.core.lib import str_to_bool, create_thumb, translate, check_directory_exists, get_text_file_string, \ build_icon, image_to_byte, check_item_selected, validate_thumb, create_separated_list, clean_tags, expand_tags - +from tests.functional import MagicMock, patch TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'resources')) - class TestLib(TestCase): def str_to_bool_with_bool_test(self): diff --git a/tests/functional/openlp_core_lib/test_pluginmanager.py b/tests/functional/openlp_core_lib/test_pluginmanager.py index 17e3c3e67..a3114c9e5 100644 --- a/tests/functional/openlp_core_lib/test_pluginmanager.py +++ b/tests/functional/openlp_core_lib/test_pluginmanager.py @@ -1,12 +1,39 @@ +# -*- 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 # +############################################################################### """ Package to test the openlp.core.lib.pluginmanager package. """ from unittest import TestCase -from mock import MagicMock - from openlp.core.lib.pluginmanager import PluginManager from openlp.core.lib import Settings, Registry, PluginStatus +from tests.functional import MagicMock class TestPluginManager(TestCase): diff --git a/tests/functional/openlp_core_lib/test_registry.py b/tests/functional/openlp_core_lib/test_registry.py index c8003559f..06307630b 100644 --- a/tests/functional/openlp_core_lib/test_registry.py +++ b/tests/functional/openlp_core_lib/test_registry.py @@ -1,12 +1,39 @@ +# -*- 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 # +############################################################################### """ Package to test the openlp.core.lib package. """ import os from unittest import TestCase -from mock import MagicMock - from openlp.core.lib import Registry +from tests.functional import MagicMock TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'resources')) diff --git a/tests/functional/openlp_core_lib/test_screen.py b/tests/functional/openlp_core_lib/test_screen.py index a73883ab9..b8b617d3c 100644 --- a/tests/functional/openlp_core_lib/test_screen.py +++ b/tests/functional/openlp_core_lib/test_screen.py @@ -1,14 +1,40 @@ +# -*- 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 # +############################################################################### """ Package to test the openlp.core.lib.screenlist package. """ - from unittest import TestCase -from mock import MagicMock from PyQt4 import QtGui, QtCore from openlp.core.lib import Registry, ScreenList - +from tests.functional import MagicMock SCREEN = { 'primary': False, diff --git a/tests/functional/openlp_core_lib/test_serviceitem.py b/tests/functional/openlp_core_lib/test_serviceitem.py index c387d83ad..ce176c82c 100644 --- a/tests/functional/openlp_core_lib/test_serviceitem.py +++ b/tests/functional/openlp_core_lib/test_serviceitem.py @@ -1,16 +1,40 @@ # -*- 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 # +############################################################################### """ - Package to test the openlp.core.lib package. +Package to test the openlp.core.lib package. """ import os import json -import tempfile from unittest import TestCase -from mock import MagicMock, patch from openlp.core.lib import ItemCapabilities, ServiceItem, Registry -from lxml import objectify, etree +from tests.functional import MagicMock, patch VERSE = 'The Lord said to {r}Noah{/r}: \n'\ 'There\'s gonna be a {su}floody{/su}, {sb}floody{/sb}\n'\ @@ -20,7 +44,6 @@ VERSE = 'The Lord said to {r}Noah{/r}: \n'\ '{r}C{/r}{b}h{/b}{bl}i{/bl}{y}l{/y}{g}d{/g}{pk}'\ 'r{/pk}{o}e{/o}{pp}n{/pp} of the Lord\n' FOOTER = ['Arky Arky (Unknown)', 'Public Domain', 'CCLI 123456'] - TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'resources')) diff --git a/tests/functional/openlp_core_lib/test_settings.py b/tests/functional/openlp_core_lib/test_settings.py index 444b206d6..e9ee02443 100644 --- a/tests/functional/openlp_core_lib/test_settings.py +++ b/tests/functional/openlp_core_lib/test_settings.py @@ -1,14 +1,42 @@ +# -*- 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 # +############################################################################### """ - Package to test the openlp.core.lib.settings package. +Package to test the openlp.core.lib.settings package. """ import os from unittest import TestCase from tempfile import mkstemp -from openlp.core.lib import Settings - from PyQt4 import QtGui +from openlp.core.lib import Settings + class TestSettings(TestCase): """ diff --git a/tests/functional/openlp_core_lib/test_uistrings.py b/tests/functional/openlp_core_lib/test_uistrings.py index 0070533db..a69905680 100644 --- a/tests/functional/openlp_core_lib/test_uistrings.py +++ b/tests/functional/openlp_core_lib/test_uistrings.py @@ -1,7 +1,34 @@ +# -*- 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 # +############################################################################### """ Package to test the openlp.core.lib.uistrings package. """ - from unittest import TestCase from openlp.core.lib import UiStrings diff --git a/tests/functional/openlp_core_utils/test_actions.py b/tests/functional/openlp_core_utils/test_actions.py index 0fa096b07..42a7c7079 100644 --- a/tests/functional/openlp_core_utils/test_actions.py +++ b/tests/functional/openlp_core_utils/test_actions.py @@ -1,3 +1,31 @@ +# -*- 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 # +############################################################################### """ Package to test the openlp.core.utils.actions package. """ @@ -12,6 +40,9 @@ from openlp.core.utils import ActionList class TestActionList(TestCase): + """ + Test the ActionList class + """ def setUp(self): """ diff --git a/tests/functional/openlp_core_utils/test_applocation.py b/tests/functional/openlp_core_utils/test_applocation.py index 997ff7417..d34c0cfa5 100644 --- a/tests/functional/openlp_core_utils/test_applocation.py +++ b/tests/functional/openlp_core_utils/test_applocation.py @@ -1,13 +1,39 @@ +# -*- 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 # +############################################################################### """ Functional tests to test the AppLocation class and related methods. """ import copy from unittest import TestCase -from mock import patch - from openlp.core.utils import AppLocation - +from tests.functional import patch FILE_LIST = ['file1', 'file2', 'file3.txt', 'file4.txt', 'file5.mp3', 'file6.mp3'] @@ -38,7 +64,7 @@ class TestAppLocation(TestCase): mocked_settings.contains.assert_called_with('advanced/data path') mocked_get_directory.assert_called_with(AppLocation.DataDir) mocked_check_directory_exists.assert_called_with('test/dir') - assert data_path == 'test/dir', 'Result should be "test/dir"' + self.assertEqual('test/dir', data_path, 'Result should be "test/dir"') def get_data_path_with_custom_location_test(self): """ @@ -58,7 +84,7 @@ class TestAppLocation(TestCase): # THEN: the mocked Settings methods were called and the value returned was our set up value mocked_settings.contains.assert_called_with('advanced/data path') mocked_settings.value.assert_called_with('advanced/data path') - assert data_path == 'custom/dir', 'Result should be "custom/dir"' + self.assertEqual('custom/dir', data_path, 'Result should be "custom/dir"') def get_files_no_section_no_extension_test(self): """ @@ -74,7 +100,7 @@ class TestAppLocation(TestCase): result = AppLocation.get_files() # Then: check if the file lists are identical. - assert result == FILE_LIST, 'The file lists should be identical.' + self.assertListEqual(FILE_LIST, result, 'The file lists should be identical.') def get_files_test(self): """ @@ -93,7 +119,7 @@ class TestAppLocation(TestCase): mocked_listdir.assert_called_with('test/dir/section') # Then: check if the file lists are identical. - assert result == ['file5.mp3', 'file6.mp3'], 'The file lists should be identical.' + self.assertListEqual(['file5.mp3', 'file6.mp3'], result, 'The file lists should be identical.') def get_section_data_path_test(self): """ @@ -110,25 +136,27 @@ class TestAppLocation(TestCase): # THEN: check that all the correct methods were called, and the result is correct mocked_check_directory_exists.assert_called_with('test/dir/section') - assert data_path == 'test/dir/section', 'Result should be "test/dir/section"' + self.assertEqual('test/dir/section', data_path, 'Result should be "test/dir/section"') def get_directory_for_app_dir_test(self): """ Test the AppLocation.get_directory() method for AppLocation.AppDir """ + # GIVEN: A mocked out _get_frozen_path function with patch('openlp.core.utils.applocation._get_frozen_path') as mocked_get_frozen_path: mocked_get_frozen_path.return_value = 'app/dir' # WHEN: We call AppLocation.get_directory directory = AppLocation.get_directory(AppLocation.AppDir) - # THEN: - assert directory == 'app/dir', 'Directory should be "app/dir"' + # THEN: check that the correct directory is returned + self.assertEqual('app/dir', directory, 'Directory should be "app/dir"') def get_directory_for_plugins_dir_test(self): """ Test the AppLocation.get_directory() method for AppLocation.PluginsDir """ + # GIVEN: _get_frozen_path, abspath, split and sys are mocked out with patch('openlp.core.utils.applocation._get_frozen_path') as mocked_get_frozen_path, \ patch('openlp.core.utils.applocation.os.path.abspath') as mocked_abspath, \ patch('openlp.core.utils.applocation.os.path.split') as mocked_split, \ @@ -142,6 +170,5 @@ class TestAppLocation(TestCase): # WHEN: We call AppLocation.get_directory directory = AppLocation.get_directory(AppLocation.PluginsDir) - # THEN: - assert directory == 'plugins/dir', 'Directory should be "plugins/dir"' - + # THEN: The correct directory should be returned + self.assertEqual('plugins/dir', directory, 'Directory should be "plugins/dir"') diff --git a/tests/functional/openlp_core_utils/test_utils.py b/tests/functional/openlp_core_utils/test_utils.py index 6eacb2e48..03f0f3654 100644 --- a/tests/functional/openlp_core_utils/test_utils.py +++ b/tests/functional/openlp_core_utils/test_utils.py @@ -1,25 +1,52 @@ +# -*- 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 # +############################################################################### """ Functional tests to test the AppLocation class and related methods. """ from unittest import TestCase -from mock import patch - from openlp.core.utils import clean_filename, get_filesystem_encoding, _get_frozen_path, get_locale_key, \ get_natural_key, split_filename +from tests.functional import patch class TestUtils(TestCase): """ A test suite to test out various methods around the AppLocation class. """ - def get_filesystem_encoding_test(self): + def get_filesystem_encoding_sys_function_not_called_test(self): """ - Test the get_filesystem_encoding() function + Test the get_filesystem_encoding() function does not call the sys.getdefaultencoding() function """ + # GIVEN: sys.getfilesystemencoding returns "cp1252" with patch('openlp.core.utils.sys.getfilesystemencoding') as mocked_getfilesystemencoding, \ patch('openlp.core.utils.sys.getdefaultencoding') as mocked_getdefaultencoding: - # GIVEN: sys.getfilesystemencoding returns "cp1252" mocked_getfilesystemencoding.return_value = 'cp1252' # WHEN: get_filesystem_encoding() is called @@ -27,10 +54,16 @@ class TestUtils(TestCase): # THEN: getdefaultencoding should have been called mocked_getfilesystemencoding.assert_called_with() - assert not mocked_getdefaultencoding.called - assert result == 'cp1252', 'The result should be "cp1252"' + self.assertEqual(0, mocked_getdefaultencoding.called, 'getdefaultencoding should not have been called') + self.assertEqual('cp1252', result, 'The result should be "cp1252"') - # GIVEN: sys.getfilesystemencoding returns None and sys.getdefaultencoding returns "utf-8" + def get_filesystem_encoding_sys_function_is_called_test(self): + """ + Test the get_filesystem_encoding() function calls the sys.getdefaultencoding() function + """ + # GIVEN: sys.getfilesystemencoding returns None and sys.getdefaultencoding returns "utf-8" + with patch('openlp.core.utils.sys.getfilesystemencoding') as mocked_getfilesystemencoding, \ + patch('openlp.core.utils.sys.getdefaultencoding') as mocked_getdefaultencoding: mocked_getfilesystemencoding.return_value = None mocked_getdefaultencoding.return_value = 'utf-8' @@ -40,23 +73,35 @@ class TestUtils(TestCase): # THEN: getdefaultencoding should have been called mocked_getfilesystemencoding.assert_called_with() mocked_getdefaultencoding.assert_called_with() - assert result == 'utf-8', 'The result should be "utf-8"' + self.assertEqual('utf-8', result, 'The result should be "utf-8"') - def get_frozen_path_test(self): + def get_frozen_path_in_unfrozen_app_test(self): """ - Test the _get_frozen_path() function + Test the _get_frozen_path() function when the application is not frozen (compiled by PyInstaller) """ with patch('openlp.core.utils.sys') as mocked_sys: # GIVEN: The sys module "without" a "frozen" attribute mocked_sys.frozen = None + # WHEN: We call _get_frozen_path() with two parameters + frozen_path = _get_frozen_path('frozen', 'not frozen') + # THEN: The non-frozen parameter is returned - assert _get_frozen_path('frozen', 'not frozen') == 'not frozen', 'Should return "not frozen"' + self.assertEqual('not frozen', frozen_path, '_get_frozen_path should return "not frozen"') + + def get_frozen_path_in_frozen_app_test(self): + """ + Test the _get_frozen_path() function when the application is frozen (compiled by PyInstaller) + """ + with patch('openlp.core.utils.sys') as mocked_sys: # GIVEN: The sys module *with* a "frozen" attribute mocked_sys.frozen = 1 + # WHEN: We call _get_frozen_path() with two parameters + frozen_path = _get_frozen_path('frozen', 'not frozen') + # THEN: The frozen parameter is returned - assert _get_frozen_path('frozen', 'not frozen') == 'frozen', 'Should return "frozen"' + self.assertEqual('frozen', frozen_path, 'Should return "frozen"') def split_filename_with_file_path_test(self): """ @@ -72,7 +117,7 @@ class TestUtils(TestCase): result = split_filename(file_path) # THEN: A tuple should be returned. - assert result == wanted_result, 'A tuple with the directory and file name should have been returned.' + self.assertEqual(wanted_result, result, 'A tuple with the dir and file name should have been returned') def split_filename_with_dir_path_test(self): """ @@ -88,8 +133,8 @@ class TestUtils(TestCase): result = split_filename(file_path) # THEN: A tuple should be returned. - assert result == wanted_result, \ - 'A two-entry tuple with the directory and file name (empty) should have been returned.' + self.assertEqual(wanted_result, result, + 'A two-entry tuple with the directory and file name (empty) should have been returned.') def clean_filename_test(self): """ @@ -103,7 +148,7 @@ class TestUtils(TestCase): result = clean_filename(invalid_name) # THEN: The file name should be cleaned. - assert result == wanted_name, 'The file name should not contain any special characters.' + self.assertEqual(wanted_name, result, 'The file name should not contain any special characters.') def get_locale_key_windows_test(self): """ @@ -116,13 +161,15 @@ class TestUtils(TestCase): mocked_get_language.return_value = 'de' mocked_os.name = 'nt' unsorted_list = ['Auszug', 'Aushang', '\u00C4u\u00DFerung'] + # WHEN: We sort the list and use get_locale_key() to generate the sorting keys + sorted_list = sorted(unsorted_list, key=get_locale_key) + # THEN: We get a properly sorted list - test_passes = sorted(unsorted_list, key=get_locale_key) == ['Aushang', '\u00C4u\u00DFerung', 'Auszug'] - assert test_passes, 'Strings should be sorted properly' + self.assertEqual(['Aushang', '\u00C4u\u00DFerung', 'Auszug'], sorted_list, + 'Strings should be sorted properly') def get_locale_key_linux_test(self): - """ Test the get_locale_key(string) function """ @@ -134,9 +181,11 @@ class TestUtils(TestCase): mocked_os.name = 'linux' unsorted_list = ['Auszug', 'Aushang', '\u00C4u\u00DFerung'] # WHEN: We sort the list and use get_locale_key() to generate the sorting keys + sorted_list = sorted(unsorted_list, key=get_locale_key) + # THEN: We get a properly sorted list - test_passes = sorted(unsorted_list, key=get_locale_key) == ['Aushang', '\u00C4u\u00DFerung', 'Auszug'] - assert test_passes, 'Strings should be sorted properly' + self.assertEqual(['Aushang', '\u00C4u\u00DFerung', 'Auszug'], sorted_list, + 'Strings should be sorted properly') def get_natural_key_test(self): """ @@ -146,7 +195,9 @@ class TestUtils(TestCase): # GIVEN: The language is English (a language, which sorts digits before letters) mocked_get_language.return_value = 'en' unsorted_list = ['item 10a', 'item 3b', '1st item'] + # WHEN: We sort the list and use get_natural_key() to generate the sorting keys + sorted_list = sorted(unsorted_list, key=get_natural_key) + # THEN: We get a properly sorted list - test_passes = sorted(unsorted_list, key=get_natural_key) == ['1st item', 'item 3b', 'item 10a'] - assert test_passes, 'Numbers should be sorted naturally' + self.assertEqual(['1st item', 'item 3b', 'item 10a'], sorted_list, 'Numbers should be sorted naturally') diff --git a/tests/functional/openlp_plugins/bibles/test_lib.py b/tests/functional/openlp_plugins/bibles/test_lib.py index 942b4deee..11044475b 100644 --- a/tests/functional/openlp_plugins/bibles/test_lib.py +++ b/tests/functional/openlp_plugins/bibles/test_lib.py @@ -1,3 +1,31 @@ +# -*- 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 # +############################################################################### """ This module contains tests for the lib submodule of the Bibles plugin. """ diff --git a/tests/functional/openlp_plugins/bibles/test_versereferencelist.py b/tests/functional/openlp_plugins/bibles/test_versereferencelist.py index 372264a76..0c7ccd01e 100644 --- a/tests/functional/openlp_plugins/bibles/test_versereferencelist.py +++ b/tests/functional/openlp_plugins/bibles/test_versereferencelist.py @@ -1,15 +1,43 @@ +# -*- 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 # +############################################################################### """ This module contains tests for the versereferencelist submodule of the Bibles plugin. """ from unittest import TestCase + from openlp.plugins.bibles.lib.versereferencelist import VerseReferenceList + class TestVerseReferenceList(TestCase): - def setUp(self): - """ - Initializes all we need - """ - + """ + Test the VerseReferenceList class + """ def add_first_verse_test(self): """ Test the addition of a verse to the empty list @@ -20,12 +48,12 @@ class TestVerseReferenceList(TestCase): chapter = 1 verse = 1 version = 'testVersion' - copyright = 'testCopyright' + copyright_ = 'testCopyright' permission = 'testPermision' - + # WHEN: We add it to the verse list - reference_list.add(book, chapter, verse, version, copyright, permission) - + reference_list.add(book, chapter, verse, version, copyright_, permission) + # THEN: The entries should be in the first entry of the list self.assertEqual(reference_list.current_index, 0, 'The current index should be 0') self.assertEqual(reference_list.verse_list[0]['book'], book, 'The book in first entry should be %s' % book) @@ -33,7 +61,7 @@ class TestVerseReferenceList(TestCase): self.assertEqual(reference_list.verse_list[0]['start'], verse, 'The start in first entry should be %u' % verse) self.assertEqual(reference_list.verse_list[0]['version'], version, 'The version in first entry should be %s' % version) self.assertEqual(reference_list.verse_list[0]['end'], verse, 'The end in first entry should be %u' % verse) - + def add_next_verse_test(self): """ Test the addition of the following verse @@ -44,14 +72,14 @@ class TestVerseReferenceList(TestCase): verse = 1 next_verse = 2 version = 'testVersion' - copyright = 'testCopyright' + copyright_ = 'testCopyright' permission = 'testPermision' reference_list = VerseReferenceList() - reference_list.add(book, chapter, verse, version, copyright, permission) + reference_list.add(book, chapter, verse, version, copyright_, permission) # WHEN: We add the following verse to the verse list - reference_list.add(book, chapter, next_verse, version, copyright, permission) - + reference_list.add(book, chapter, next_verse, version, copyright_, permission) + # THEN: The current index should be 0 and the end pointer of the entry should be '2' self.assertEqual(reference_list.current_index, 0, 'The current index should be 0') self.assertEqual(reference_list.verse_list[0]['end'], next_verse, 'The end in first entry should be %u' % next_verse) @@ -64,19 +92,18 @@ class TestVerseReferenceList(TestCase): book = 'testBook' chapter = 1 verse = 1 - next_verse = 2 another_book = 'testBook2' another_chapter = 2 another_verse = 5 version = 'testVersion' - copyright = 'testCopyright' + copyright_ = 'testCopyright' permission = 'testPermision' reference_list = VerseReferenceList() - reference_list.add(book, chapter, verse, version, copyright, permission) + reference_list.add(book, chapter, verse, version, copyright_, permission) # WHEN: We add a verse of another book to the verse list - reference_list.add(another_book, another_chapter, another_verse, version, copyright, permission) - + reference_list.add(another_book, another_chapter, another_verse, version, copyright_, permission) + # THEN: the current index should be 1 self.assertEqual(reference_list.current_index, 1, 'The current index should be 1') @@ -87,17 +114,18 @@ class TestVerseReferenceList(TestCase): # GIVEN: version, copyright and permission reference_list = VerseReferenceList() version = 'testVersion' - copyright = 'testCopyright' + copyright_ = 'testCopyright' permission = 'testPermision' # WHEN: a not existing version will be added - reference_list.add_version(version, copyright, permission) - + reference_list.add_version(version, copyright_, permission) + # THEN: the data will be appended to the list self.assertEqual(len(reference_list.version_list), 1, 'The version data should be appended') - self.assertEqual(reference_list.version_list[0], {'version': version, 'copyright': copyright, 'permission': permission}, - 'The version data should be appended') - + self.assertEqual(reference_list.version_list[0], + {'version': version, 'copyright': copyright_, 'permission': permission}, + 'The version data should be appended') + def add_existing_version_test(self): """ Test the addition of an existing version to the list @@ -105,12 +133,12 @@ class TestVerseReferenceList(TestCase): # GIVEN: version, copyright and permission, added to the version list reference_list = VerseReferenceList() version = 'testVersion' - copyright = 'testCopyright' + copyright_ = 'testCopyright' permission = 'testPermision' - reference_list.add_version(version, copyright, permission) - + reference_list.add_version(version, copyright_, permission) + # WHEN: an existing version will be added - reference_list.add_version(version, copyright, permission) - + reference_list.add_version(version, copyright_, permission) + # THEN: the data will not be appended to the list self.assertEqual(len(reference_list.version_list), 1, 'The version data should not be appended') diff --git a/tests/functional/openlp_plugins/images/test_lib.py b/tests/functional/openlp_plugins/images/test_lib.py index 4090d4e9e..50fd258d2 100644 --- a/tests/functional/openlp_plugins/images/test_lib.py +++ b/tests/functional/openlp_plugins/images/test_lib.py @@ -1,16 +1,40 @@ # -*- 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 # +############################################################################### """ This module contains tests for the lib submodule of the Images plugin. """ - from unittest import TestCase -from mock import MagicMock, patch - from openlp.core.lib import Registry from openlp.plugins.images.lib.db import ImageFilenames, ImageGroups from openlp.plugins.images.lib.mediaitem import ImageMediaItem +from tests.functional import MagicMock, patch class TestImageMediaItem(TestCase): @@ -36,7 +60,7 @@ class TestImageMediaItem(TestCase): """ # GIVEN: An empty image_list image_list = [] - with patch('openlp.plugins.images.lib.mediaitem.ImageMediaItem.load_full_list') as mocked_load_full_list: + with patch('openlp.plugins.images.lib.mediaitem.ImageMediaItem.load_full_list'): self.media_item.manager = MagicMock() # WHEN: We run save_new_images_list with the empty list @@ -50,8 +74,8 @@ class TestImageMediaItem(TestCase): """ Test that the save_new_images_list() calls load_full_list() when reload_list is set to True """ - # GIVEN: A list with 1 image - image_list = [ 'test_image.jpg' ] + # GIVEN: A list with 1 image and a mocked out manager + image_list = ['test_image.jpg'] with patch('openlp.plugins.images.lib.mediaitem.ImageMediaItem.load_full_list') as mocked_load_full_list: ImageFilenames.filename = '' self.media_item.manager = MagicMock() @@ -69,8 +93,8 @@ class TestImageMediaItem(TestCase): """ Test that the save_new_images_list() doesn't call load_full_list() when reload_list is set to False """ - # GIVEN: A list with 1 image - image_list = [ 'test_image.jpg' ] + # GIVEN: A list with 1 image and a mocked out manager + image_list = ['test_image.jpg'] with patch('openlp.plugins.images.lib.mediaitem.ImageMediaItem.load_full_list') as mocked_load_full_list: self.media_item.manager = MagicMock() @@ -126,31 +150,6 @@ class TestImageMediaItem(TestCase): self.media_item.reset_action.setVisible.assert_called_with(False) self.media_item.live_controller.display.reset_image.assert_called_with() - def _recursively_delete_group_side_effect(*args, **kwargs): - """ - Side effect method that creates custom retun values for the recursively_delete_group method - """ - if args[1] == ImageFilenames and args[2]: - # Create some fake objects that should be removed - returned_object1 = ImageFilenames() - returned_object1.id = 1 - returned_object1.filename = '/tmp/test_file_1.jpg' - returned_object2 = ImageFilenames() - returned_object2.id = 2 - returned_object2.filename = '/tmp/test_file_2.jpg' - returned_object3 = ImageFilenames() - returned_object3.id = 3 - returned_object3.filename = '/tmp/test_file_3.jpg' - return [returned_object1, returned_object2, returned_object3] - if args[1] == ImageGroups and args[2]: - # Change the parent_id that is matched so we don't get into an endless loop - ImageGroups.parent_id = 0 - # Create a fake group that will be used in the next run - returned_object1 = ImageGroups() - returned_object1.id = 1 - return [returned_object1] - return [] - def recursively_delete_group_test(self): """ Test that recursively_delete_group() works @@ -176,3 +175,28 @@ class TestImageMediaItem(TestCase): # CLEANUP: Remove added attribute from ImageFilenames and ImageGroups delattr(ImageFilenames, 'group_id') delattr(ImageGroups, 'parent_id') + + def _recursively_delete_group_side_effect(*args, **kwargs): + """ + Side effect method that creates custom return values for the recursively_delete_group method + """ + if args[1] == ImageFilenames and args[2]: + # Create some fake objects that should be removed + returned_object1 = ImageFilenames() + returned_object1.id = 1 + returned_object1.filename = '/tmp/test_file_1.jpg' + returned_object2 = ImageFilenames() + returned_object2.id = 2 + returned_object2.filename = '/tmp/test_file_2.jpg' + returned_object3 = ImageFilenames() + returned_object3.id = 3 + returned_object3.filename = '/tmp/test_file_3.jpg' + return [returned_object1, returned_object2, returned_object3] + if args[1] == ImageGroups and args[2]: + # Change the parent_id that is matched so we don't get into an endless loop + ImageGroups.parent_id = 0 + # Create a fake group that will be used in the next run + returned_object1 = ImageGroups() + returned_object1.id = 1 + return [returned_object1] + return [] diff --git a/tests/functional/openlp_plugins/presentations/test_mediaitem.py b/tests/functional/openlp_plugins/presentations/test_mediaitem.py index 95fea2bdb..49b47252c 100644 --- a/tests/functional/openlp_plugins/presentations/test_mediaitem.py +++ b/tests/functional/openlp_plugins/presentations/test_mediaitem.py @@ -1,17 +1,41 @@ +# -*- 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 # +############################################################################### """ This module contains tests for the lib submodule of the Presentations plugin. """ -import os -from tempfile import mkstemp from unittest import TestCase -from mock import patch, MagicMock - from PyQt4 import QtGui from openlp.core.lib import Registry - from openlp.plugins.presentations.lib.mediaitem import PresentationMediaItem +from tests.functional import patch, MagicMock class TestMediaItem(TestCase): @@ -68,4 +92,3 @@ class TestMediaItem(TestCase): # THEN: The file mask should be generated. assert self.media_item.on_new_file_masks == 'Presentations (*.odp *.ppt )', \ 'The file mask should contain the odp and ppt extensions' - diff --git a/tests/functional/openlp_plugins/remotes/test_remotetab.py b/tests/functional/openlp_plugins/remotes/test_remotetab.py index e683699cd..86652ffa4 100644 --- a/tests/functional/openlp_plugins/remotes/test_remotetab.py +++ b/tests/functional/openlp_plugins/remotes/test_remotetab.py @@ -1,17 +1,44 @@ +# -*- 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 # +############################################################################### """ This module contains tests for the lib submodule of the Remotes plugin. """ import os import re - from unittest import TestCase from tempfile import mkstemp -from mock import patch + +from PyQt4 import QtGui from openlp.core.lib import Settings from openlp.plugins.remotes.lib.remotetab import RemoteTab - -from PyQt4 import QtGui +from tests.functional import patch __default_settings__ = { 'remotes/twelve hour': True, @@ -23,9 +50,7 @@ __default_settings__ = { 'remotes/authentication enabled': False, 'remotes/ip address': '0.0.0.0' } - ZERO_URL = '0.0.0.0' - TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..', 'resources')) @@ -60,7 +85,8 @@ class TestRemoteTab(TestCase): # WHEN: the default ip address is given ip_address = self.form.get_ip_address(ZERO_URL) # THEN: the default ip address will be returned - self.assertTrue(re.match('\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}', ip_address), 'The return value should be a valid ip address') + self.assertTrue(re.match('\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}', ip_address), + 'The return value should be a valid ip address') def get_ip_address_with_ip_test(self): """ @@ -80,9 +106,9 @@ class TestRemoteTab(TestCase): """ # GIVEN: A mocked location with patch('openlp.core.utils.applocation.Settings') as mocked_class, \ - patch('openlp.core.utils.AppLocation.get_directory') as mocked_get_directory, \ - patch('openlp.core.utils.applocation.check_directory_exists') as mocked_check_directory_exists, \ - patch('openlp.core.utils.applocation.os') as mocked_os: + patch('openlp.core.utils.AppLocation.get_directory') as mocked_get_directory, \ + patch('openlp.core.utils.applocation.check_directory_exists') as mocked_check_directory_exists, \ + patch('openlp.core.utils.applocation.os') as mocked_os: # GIVEN: A mocked out Settings class and a mocked out AppLocation.get_directory() mocked_settings = mocked_class.return_value mocked_settings.contains.return_value = False @@ -96,7 +122,7 @@ class TestRemoteTab(TestCase): # THEN: the following screen values should be set self.assertEqual(self.form.address_edit.text(), ZERO_URL, 'The default URL should be set on the screen') self.assertEqual(self.form.https_settings_group_box.isEnabled(), False, - 'The Https box should not be enabled') + 'The Https box should not be enabled') self.assertEqual(self.form.https_settings_group_box.isChecked(), False, 'The Https checked box should note be Checked') self.assertEqual(self.form.user_login_group_box.isChecked(), False, @@ -108,9 +134,9 @@ class TestRemoteTab(TestCase): """ # GIVEN: A mocked location with patch('openlp.core.utils.applocation.Settings') as mocked_class, \ - patch('openlp.core.utils.AppLocation.get_directory') as mocked_get_directory, \ - patch('openlp.core.utils.applocation.check_directory_exists') as mocked_check_directory_exists, \ - patch('openlp.core.utils.applocation.os') as mocked_os: + patch('openlp.core.utils.AppLocation.get_directory') as mocked_get_directory, \ + patch('openlp.core.utils.applocation.check_directory_exists') as mocked_check_directory_exists, \ + patch('openlp.core.utils.applocation.os') as mocked_os: # GIVEN: A mocked out Settings class and a mocked out AppLocation.get_directory() mocked_settings = mocked_class.return_value mocked_settings.contains.return_value = False diff --git a/tests/functional/openlp_plugins/remotes/test_router.py b/tests/functional/openlp_plugins/remotes/test_router.py index af1849a65..8fed7d286 100644 --- a/tests/functional/openlp_plugins/remotes/test_router.py +++ b/tests/functional/openlp_plugins/remotes/test_router.py @@ -1,15 +1,43 @@ +# -*- 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 # +############################################################################### """ This module contains tests for the lib submodule of the Remotes plugin. """ import os - from unittest import TestCase from tempfile import mkstemp -from mock import MagicMock + +from PyQt4 import QtGui from openlp.core.lib import Settings from openlp.plugins.remotes.lib.httpserver import HttpRouter, fetch_password, make_sha_hash -from PyQt4 import QtGui +from tests.functional import MagicMock __default_settings__ = { 'remotes/twelve hour': True, diff --git a/tests/functional/openlp_plugins/songs/test_ewimport.py b/tests/functional/openlp_plugins/songs/test_ewimport.py index 3f1735a3c..b0ab77850 100644 --- a/tests/functional/openlp_plugins/songs/test_ewimport.py +++ b/tests/functional/openlp_plugins/songs/test_ewimport.py @@ -1,13 +1,39 @@ # -*- 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 # +############################################################################### """ This module contains tests for the EasyWorship song importer. """ import os from unittest import TestCase -from mock import patch, MagicMock + +from tests.functional import MagicMock, patch from openlp.plugins.songs.lib.ewimport import EasyWorshipSongImport, FieldDescEntry, FieldType @@ -43,6 +69,7 @@ SONG_TEST_DATA = [ 'Just to bow and receive a new blessing,\nIn the beautiful garden of prayer.', 'v3')], 'verse_order_list': []}] + class EasyWorshipSongImportLogger(EasyWorshipSongImport): """ This class logs changes in the title instance variable @@ -60,6 +87,7 @@ class EasyWorshipSongImportLogger(EasyWorshipSongImport): def title(self, title): self._title_assignment_list.append(title) + class TestFieldDesc: def __init__(self, name, field_type, size): self.name = name diff --git a/tests/functional/openlp_plugins/songs/test_foilpresenterimport.py b/tests/functional/openlp_plugins/songs/test_foilpresenterimport.py index 200f20aa6..05e07c4cf 100644 --- a/tests/functional/openlp_plugins/songs/test_foilpresenterimport.py +++ b/tests/functional/openlp_plugins/songs/test_foilpresenterimport.py @@ -32,9 +32,8 @@ This module contains tests for the SongShow Plus song importer. import os from unittest import TestCase -from mock import patch, MagicMock +from tests.functional import patch, MagicMock -from openlp.plugins.songs.lib import VerseType from openlp.plugins.songs.lib.foilpresenterimport import FoilPresenter TEST_PATH = os.path.abspath( @@ -192,4 +191,4 @@ class TestFoilPresenter(TestCase): # THEN: _process_lyrics should return None and the song_import logError method should have been called once self.assertIsNone(result) self.mocked_song_import.logError.assert_called_once_with('Element Text', 'Translated String') - self.process_lyrics_patcher.start() \ No newline at end of file + self.process_lyrics_patcher.start() diff --git a/tests/functional/openlp_plugins/songs/test_lib.py b/tests/functional/openlp_plugins/songs/test_lib.py index a9e64b5c9..327bf68e8 100644 --- a/tests/functional/openlp_plugins/songs/test_lib.py +++ b/tests/functional/openlp_plugins/songs/test_lib.py @@ -1,13 +1,39 @@ +# -*- 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 # +############################################################################### """ This module contains tests for the lib submodule of the Songs plugin. """ - from unittest import TestCase -from mock import patch, MagicMock - from openlp.plugins.songs.lib import VerseType, clean_string, clean_title, strip_rtf from openlp.plugins.songs.lib.songcompare import songs_probably_equal, _remove_typos, _op_length +from tests.functional import patch, MagicMock class TestLib(TestCase): @@ -68,10 +94,10 @@ class TestLib(TestCase): # GIVEN: Two equal songs. self.song1.search_lyrics = self.full_lyrics self.song2.search_lyrics = self.full_lyrics - + # WHEN: We compare those songs for equality. result = songs_probably_equal(self.song1, self.song2) - + # THEN: The result should be True. assert result == True, 'The result should be True' @@ -82,10 +108,10 @@ class TestLib(TestCase): # GIVEN: A song and a short version of the same song. self.song1.search_lyrics = self.full_lyrics self.song2.search_lyrics = self.short_lyrics - + # WHEN: We compare those songs for equality. result = songs_probably_equal(self.song1, self.song2) - + # THEN: The result should be True. assert result == True, 'The result should be True' @@ -96,10 +122,10 @@ class TestLib(TestCase): # GIVEN: A song and the same song with lots of errors. self.song1.search_lyrics = self.full_lyrics self.song2.search_lyrics = self.error_lyrics - + # WHEN: We compare those songs for equality. result = songs_probably_equal(self.song1, self.song2) - + # THEN: The result should be True. assert result == True, 'The result should be True' @@ -110,10 +136,10 @@ class TestLib(TestCase): # GIVEN: Two different songs. self.song1.search_lyrics = self.full_lyrics self.song2.search_lyrics = self.different_lyrics - + # WHEN: We compare those songs for equality. result = songs_probably_equal(self.song1, self.song2) - + # THEN: The result should be False. assert result == False, 'The result should be False' diff --git a/tests/functional/openlp_plugins/songs/test_mediaitem.py b/tests/functional/openlp_plugins/songs/test_mediaitem.py index 47940911d..a519f7021 100644 --- a/tests/functional/openlp_plugins/songs/test_mediaitem.py +++ b/tests/functional/openlp_plugins/songs/test_mediaitem.py @@ -5,13 +5,11 @@ import os from tempfile import mkstemp from unittest import TestCase -from mock import patch, MagicMock - from PyQt4 import QtCore, QtGui from openlp.core.lib import Registry, ServiceItem, Settings - from openlp.plugins.songs.lib.mediaitem import SongMediaItem +from tests.functional import patch, MagicMock class TestMediaItem(TestCase): diff --git a/tests/functional/openlp_plugins/songs/test_songshowplusimport.py b/tests/functional/openlp_plugins/songs/test_songshowplusimport.py index 2276784e0..7fbb76917 100644 --- a/tests/functional/openlp_plugins/songs/test_songshowplusimport.py +++ b/tests/functional/openlp_plugins/songs/test_songshowplusimport.py @@ -1,13 +1,41 @@ +# -*- 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 # +############################################################################### """ This module contains tests for the SongShow Plus song importer. """ import os from unittest import TestCase -from mock import patch, MagicMock from openlp.plugins.songs.lib import VerseType from openlp.plugins.songs.lib.songshowplusimport import SongShowPlusImport +from tests.functional import patch, MagicMock TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../../resources/songshowplussongs')) SONG_TEST_DATA = {'Amazing Grace.sbsong': diff --git a/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py b/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py index 7c9b80056..836c5340b 100644 --- a/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py +++ b/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py @@ -1,15 +1,45 @@ # -*- 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 # +############################################################################### """ This module contains tests for the WorshipCenter Pro song importer. """ +import os +from unittest import TestCase, SkipTest + +if os.name != 'nt': + raise SkipTest('Not Windows, skipping test') -from unittest import TestCase -from mock import patch, MagicMock import pyodbc from openlp.plugins.songs.lib.worshipcenterproimport import WorshipCenterProImport +from tests.functional import patch, MagicMock + class TestRecord(object): """ @@ -23,6 +53,7 @@ class TestRecord(object): self.Field = field self.Value = value + class WorshipCenterProImportLogger(WorshipCenterProImport): """ This class logs changes in the title instance variable @@ -189,4 +220,4 @@ class TestWorshipCenterProSongImport(TestCase): for call in verse_calls: mocked_add_verse.assert_any_call(call) self.assertEqual(mocked_add_verse.call_count, add_verse_call_count, - 'Incorrect number of calls made to addVerse') \ No newline at end of file + 'Incorrect number of calls made to addVerse') From 188f3126693bd54a22c480557b18f66019cda584 Mon Sep 17 00:00:00 2001 From: Raoul Snyman Date: Fri, 20 Sep 2013 11:35:50 +0200 Subject: [PATCH 68/93] Ooops. --- tests/functional/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/__init__.py b/tests/functional/__init__.py index f311052f9..3f471cf61 100644 --- a/tests/functional/__init__.py +++ b/tests/functional/__init__.py @@ -13,7 +13,7 @@ from PyQt4 import QtGui if sys.version_info[1] >= 3: from unittest.mock import patch, MagicMock else: - from mock import patch, MagicMockv + from mock import patch, MagicMock # Only one QApplication can be created. Use QtGui.QApplication.instance() when you need to "create" a QApplication. application = QtGui.QApplication([]) From 1215494303c49b0cd760ca304412f80b8eaa23ed Mon Sep 17 00:00:00 2001 From: Raoul Snyman Date: Sun, 22 Sep 2013 23:11:03 +0200 Subject: [PATCH 69/93] Updated more tests --- .../openlp_core_lib/test_pluginmanager.py | 34 ++-- .../openlp_core_lib/test_registry.py | 6 +- .../functional/openlp_core_lib/test_screen.py | 5 +- .../openlp_core_lib/test_serviceitem.py | 148 +++++++++--------- .../openlp_core_lib/test_settings.py | 12 +- .../openlp_core_lib/test_uistrings.py | 2 +- tests/utils/__init__.py | 51 ++++++ 7 files changed, 156 insertions(+), 102 deletions(-) diff --git a/tests/functional/openlp_core_lib/test_pluginmanager.py b/tests/functional/openlp_core_lib/test_pluginmanager.py index a3114c9e5..afd06fc47 100644 --- a/tests/functional/openlp_core_lib/test_pluginmanager.py +++ b/tests/functional/openlp_core_lib/test_pluginmanager.py @@ -69,8 +69,8 @@ class TestPluginManager(TestCase): plugin_manager.hook_media_manager() # THEN: The create_media_manager_item() method should have been called - assert mocked_plugin.create_media_manager_item.call_count == 0, \ - 'The create_media_manager_item() method should not have been called.' + self.assertEqual(0, mocked_plugin.create_media_manager_item.call_count, + 'The create_media_manager_item() method should not have been called.') def hook_media_manager_with_active_plugin_test(self): """ @@ -102,8 +102,8 @@ class TestPluginManager(TestCase): plugin_manager.hook_settings_tabs() # THEN: The hook_settings_tabs() method should have been called - assert mocked_plugin.create_media_manager_item.call_count == 0, \ - 'The create_media_manager_item() method should not have been called.' + self.assertEqual(0, mocked_plugin.create_media_manager_item.call_count, + 'The create_media_manager_item() method should not have been called.') def hook_settings_tabs_with_disabled_plugin_and_mocked_form_test(self): """ @@ -122,10 +122,10 @@ class TestPluginManager(TestCase): plugin_manager.hook_settings_tabs() # THEN: The create_settings_tab() method should not have been called, but the plugins lists should be the same - assert mocked_plugin.create_settings_tab.call_count == 0, \ - 'The create_media_manager_item() method should not have been called.' + self.assertEqual(0, mocked_plugin.create_settings_tab.call_count, + 'The create_media_manager_item() method should not have been called.') self.assertEqual(mocked_settings_form.plugin_manager.plugins, plugin_manager.plugins, - 'The plugins on the settings form should be the same as the plugins in the plugin manager') + 'The plugins on the settings form should be the same as the plugins in the plugin manager') def hook_settings_tabs_with_active_plugin_and_mocked_form_test(self): """ @@ -144,10 +144,10 @@ class TestPluginManager(TestCase): plugin_manager.hook_settings_tabs() # THEN: The create_media_manager_item() method should have been called with the mocked settings form - assert mocked_plugin.create_settings_tab.call_count == 1, \ - 'The create_media_manager_item() method should have been called once.' - self.assertEqual(mocked_settings_form.plugin_manager.plugins, plugin_manager.plugins, - 'The plugins on the settings form should be the same as the plugins in the plugin manager') + self.assertEqual(1, mocked_plugin.create_settings_tab.call_count, + 'The create_media_manager_item() method should have been called once.') + self.assertEqual(plugin_manager.plugins, mocked_settings_form.plugin_manager.plugins, + 'The plugins on the settings form should be the same as the plugins in the plugin manager') def hook_settings_tabs_with_active_plugin_and_no_form_test(self): """ @@ -179,8 +179,8 @@ class TestPluginManager(TestCase): plugin_manager.hook_import_menu() # THEN: The create_media_manager_item() method should have been called - assert mocked_plugin.add_import_menu_item.call_count == 0, \ - 'The add_import_menu_item() method should not have been called.' + self.assertEqual(0, mocked_plugin.add_import_menu_item.call_count, + 'The add_import_menu_item() method should not have been called.') def hook_import_menu_with_active_plugin_test(self): """ @@ -212,8 +212,8 @@ class TestPluginManager(TestCase): plugin_manager.hook_export_menu() # THEN: The add_export_menu_Item() method should not have been called - assert mocked_plugin.add_export_menu_Item.call_count == 0, \ - 'The add_export_menu_Item() method should not have been called.' + self.assertEqual(0, mocked_plugin.add_export_menu_Item.call_count, + 'The add_export_menu_Item() method should not have been called.') def hook_export_menu_with_active_plugin_test(self): """ @@ -246,8 +246,8 @@ class TestPluginManager(TestCase): plugin_manager.hook_upgrade_plugin_settings(settings) # THEN: The upgrade_settings() method should not have been called - assert mocked_plugin.upgrade_settings.call_count == 0, \ - 'The upgrade_settings() method should not have been called.' + self.assertEqual(0, mocked_plugin.upgrade_settings.call_count, + 'The upgrade_settings() method should not have been called.') def hook_upgrade_plugin_settings_with_active_plugin_test(self): """ diff --git a/tests/functional/openlp_core_lib/test_registry.py b/tests/functional/openlp_core_lib/test_registry.py index 06307630b..e1e5e268b 100644 --- a/tests/functional/openlp_core_lib/test_registry.py +++ b/tests/functional/openlp_core_lib/test_registry.py @@ -59,14 +59,14 @@ class TestRegistry(TestCase): with self.assertRaises(KeyError) as context: Registry().register('test1', mock_1) self.assertEqual(context.exception.args[0], 'Duplicate service exception test1', - 'KeyError exception should have been thrown for duplicate service') + 'KeyError exception should have been thrown for duplicate service') # WHEN I try to get back a non existent component # THEN I will get an exception with self.assertRaises(KeyError) as context: temp = Registry().get('test2') self.assertEqual(context.exception.args[0], 'Service test2 not found in list', - 'KeyError exception should have been thrown for missing service') + 'KeyError exception should have been thrown for missing service') # WHEN I try to replace a component I should be allowed (testing only) Registry().remove('test1') @@ -74,7 +74,7 @@ class TestRegistry(TestCase): with self.assertRaises(KeyError) as context: temp = Registry().get('test1') self.assertEqual(context.exception.args[0], 'Service test1 not found in list', - 'KeyError exception should have been thrown for deleted service') + 'KeyError exception should have been thrown for deleted service') def registry_function_test(self): """ diff --git a/tests/functional/openlp_core_lib/test_screen.py b/tests/functional/openlp_core_lib/test_screen.py index b8b617d3c..c541fbb12 100644 --- a/tests/functional/openlp_core_lib/test_screen.py +++ b/tests/functional/openlp_core_lib/test_screen.py @@ -81,5 +81,6 @@ class TestScreenList(TestCase): # THEN: The screen should have been added and the screens should be identical new_screen_count = len(self.screens.screen_list) - assert old_screen_count + 1 == new_screen_count, 'The new_screens list should be bigger' - assert SCREEN == self.screens.screen_list.pop(), 'The 2nd screen should be identical to the first screen' + self.assertEqual(old_screen_count + 1, new_screen_count, 'The new_screens list should be bigger') + self.assertEqual(SCREEN, self.screens.screen_list.pop(), + 'The 2nd screen should be identical to the first screen') diff --git a/tests/functional/openlp_core_lib/test_serviceitem.py b/tests/functional/openlp_core_lib/test_serviceitem.py index ce176c82c..0e1b150ae 100644 --- a/tests/functional/openlp_core_lib/test_serviceitem.py +++ b/tests/functional/openlp_core_lib/test_serviceitem.py @@ -30,11 +30,11 @@ Package to test the openlp.core.lib package. """ import os -import json from unittest import TestCase from openlp.core.lib import ItemCapabilities, ServiceItem, Registry from tests.functional import MagicMock, patch +from tests.utils import assert_length, convert_file_service_item VERSE = 'The Lord said to {r}Noah{/r}: \n'\ 'There\'s gonna be a {su}floody{/su}, {sb}floody{/sb}\n'\ @@ -59,7 +59,7 @@ class TestServiceItem(TestCase): Registry().register('renderer', mocked_renderer) Registry().register('image_manager', MagicMock()) - def serviceitem_basic_test(self): + def service_item_basic_test(self): """ Test the Service Item - basic test """ @@ -69,10 +69,10 @@ class TestServiceItem(TestCase): service_item = ServiceItem(None) # THEN: We should get back a valid service item - assert service_item.is_valid is True, 'The new service item should be valid' - assert service_item.missing_frames() is True, 'There should not be any frames in the service item' + self.assertTrue(service_item.is_valid, 'The new service item should be valid') + self.assertTrue(service_item.missing_frames(), 'There should not be any frames in the service item') - def serviceitem_load_custom_from_service_test(self): + def service_item_load_custom_from_service_test(self): """ Test the Service Item - adding a custom slide from a saved service """ @@ -80,24 +80,29 @@ class TestServiceItem(TestCase): service_item = ServiceItem(None) service_item.add_icon = MagicMock() - # WHEN: adding a custom from a saved Service - line = self.convert_file_service_item('serviceitem_custom_1.osj') + # WHEN: We add a custom from a saved service + line = convert_file_service_item('serviceitem_custom_1.osj') service_item.set_from_service(line) # THEN: We should get back a valid service item - assert service_item.is_valid is True, 'The new service item should be valid' - assert len(service_item._display_frames) == 0, 'The service item should have no display frames' - assert len(service_item.capabilities) == 5, 'There should be 5 default custom item capabilities' - service_item.render(True) - assert service_item.get_display_title() == 'Test Custom', 'The title should be "Test Custom"' - assert service_item.get_frames()[0]['text'] == VERSE[:-1], \ - 'The returned text matches the input, except the last line feed' - assert service_item.get_rendered_frame(1) == VERSE.split('\n', 1)[0], 'The first line has been returned' - assert service_item.get_frame_title(0) == 'Slide 1', '"Slide 1" has been returned as the title' - assert service_item.get_frame_title(1) == 'Slide 2', '"Slide 2" has been returned as the title' - assert service_item.get_frame_title(2) == '', 'Blank has been returned as the title of slide 3' + self.assertTrue(service_item.is_valid, 'The new service item should be valid') + assert_length(0, service_item._display_frames, 'The service item should have no display frames') + assert_length(5, service_item.capabilities, 'There should be 5 default custom item capabilities') - def serviceitem_load_image_from_service_test(self): + # WHEN: We render the frames of the service item + service_item.render(True) + + # THEN: The frames should also be valid + self.assertEqual('Test Custom', service_item.get_display_title(), 'The title should be "Test Custom"') + self.assertEqual(VERSE[:-1], service_item.get_frames()[0]['text'], + 'The returned text matches the input, except the last line feed') + self.assertEqual(VERSE.split('\n', 1)[0], service_item.get_rendered_frame(1), + 'The first line has been returned') + self.assertEqual('Slide 1', service_item.get_frame_title(0), '"Slide 1" has been returned as the title') + self.assertEqual('Slide 2', service_item.get_frame_title(1), '"Slide 2" has been returned as the title') + self.assertEqual('', service_item.get_frame_title(2), 'Blank has been returned as the title of slide 3') + + def service_item_load_image_from_service_test(self): """ Test the Service Item - adding an image from a saved service """ @@ -110,29 +115,34 @@ class TestServiceItem(TestCase): service_item.add_icon = MagicMock() # WHEN: adding an image from a saved Service and mocked exists - line = self.convert_file_service_item('serviceitem_image_1.osj') + line = convert_file_service_item(TEST_PATH, 'serviceitem_image_1.osj') with patch('openlp.core.ui.servicemanager.os.path.exists') as mocked_exists: mocked_exists.return_value = True service_item.set_from_service(line, TEST_PATH) # THEN: We should get back a valid service item - assert service_item.is_valid is True, 'The new service item should be valid' - assert service_item.get_rendered_frame(0) == test_file, 'The first frame should match the path to the image' - assert service_item.get_frames()[0] == frame_array, 'The return should match frame array1' - assert service_item.get_frame_path(0) == test_file, 'The frame path should match the full path to the image' - assert service_item.get_frame_title(0) == image_name, 'The frame title should match the image name' - assert service_item.get_display_title() == image_name, 'The display title should match the first image name' - assert service_item.is_image() is True, 'This service item should be of an "image" type' - assert service_item.is_capable(ItemCapabilities.CanMaintain) is True, \ - 'This service item should be able to be Maintained' - assert service_item.is_capable(ItemCapabilities.CanPreview) is True, \ - 'This service item should be able to be be Previewed' - assert service_item.is_capable(ItemCapabilities.CanLoop) is True, \ - 'This service item should be able to be run in a can be made to Loop' - assert service_item.is_capable(ItemCapabilities.CanAppend) is True, \ - 'This service item should be able to have new items added to it' + self.assertTrue(service_item.is_valid, 'The new service item should be valid') + self.assertEqual(test_file, service_item.get_rendered_frame(0), + 'The first frame should match the path to the image') + self.assertEqual(frame_array, service_item.get_frames()[0], + 'The return should match frame array1') + self.assertEqual(test_file, service_item.get_frame_path(0), + 'The frame path should match the full path to the image') + self.assertEqual(image_name, service_item.get_frame_title(0), + 'The frame title should match the image name') + self.assertEqual(image_name, service_item.get_display_title(), + 'The display title should match the first image name') + self.assertTrue(service_item.is_image(), 'This service item should be of an "image" type') + self.assertTrue(service_item.is_capable(ItemCapabilities.CanMaintain), + 'This service item should be able to be Maintained') + self.assertTrue(service_item.is_capable(ItemCapabilities.CanPreview), + 'This service item should be able to be be Previewed') + self.assertTrue(service_item.is_capable(ItemCapabilities.CanLoop), + 'This service item should be able to be run in a can be made to Loop') + self.assertTrue(service_item.is_capable(ItemCapabilities.CanAppend), + 'This service item should be able to have new items added to it') - def serviceitem_load_image_from_local_service_test(self): + def service_item_load_image_from_local_service_test(self): """ Test the Service Item - adding an image from a saved local service """ @@ -151,50 +161,42 @@ class TestServiceItem(TestCase): service_item2.add_icon = MagicMock() # WHEN: adding an image from a saved Service and mocked exists - line = self.convert_file_service_item('serviceitem_image_2.osj') - line2 = self.convert_file_service_item('serviceitem_image_2.osj', 1) + line = convert_file_service_item(TEST_PATH, 'serviceitem_image_2.osj') + line2 = convert_file_service_item(TEST_PATH, 'serviceitem_image_2.osj', 1) with patch('openlp.core.ui.servicemanager.os.path.exists') as mocked_exists: mocked_exists.return_value = True service_item2.set_from_service(line2) service_item.set_from_service(line) - # THEN: We should get back a valid service item # This test is copied from service_item.py, but is changed since to conform to # new layout of service item. The layout use in serviceitem_image_2.osd is actually invalid now. - assert service_item.is_valid is True, 'The first service item should be valid' - assert service_item2.is_valid is True, 'The second service item should be valid' - assert service_item.get_rendered_frame(0) == test_file1, 'The first frame should match the path to the image' - assert service_item2.get_rendered_frame(0) == test_file2, 'The Second frame should match the path to the image' - assert service_item.get_frames()[0] == frame_array1, 'The return should match the frame array1' - assert service_item2.get_frames()[0] == frame_array2, 'The return should match the frame array2' - assert service_item.get_frame_path(0) == test_file1, 'The frame path should match the full path to the image' - assert service_item2.get_frame_path(0) == test_file2, 'The frame path should match the full path to the image' - assert service_item.get_frame_title(0) == image_name1, 'The 1st frame title should match the image name' - assert service_item2.get_frame_title(0) == image_name2, 'The 2nd frame title should match the image name' - assert service_item.title.lower() == service_item.name, \ - 'The plugin name should match the display title, as there are > 1 Images' - assert service_item.is_image() is True, 'This service item should be of an "image" type' - assert service_item.is_capable(ItemCapabilities.CanMaintain) is True, \ - 'This service item should be able to be Maintained' - assert service_item.is_capable(ItemCapabilities.CanPreview) is True, \ - 'This service item should be able to be be Previewed' - assert service_item.is_capable(ItemCapabilities.CanLoop) is True, \ - 'This service item should be able to be run in a can be made to Loop' - assert service_item.is_capable(ItemCapabilities.CanAppend) is True, \ - 'This service item should be able to have new items added to it' - - def convert_file_service_item(self, name, row=0): - service_file = os.path.join(TEST_PATH, name) - try: - open_file = open(service_file, 'r') - items = json.load(open_file) - first_line = items[row] - except IOError: - first_line = '' - finally: - open_file.close() - return first_line - + self.assertTrue(service_item.is_valid, 'The first service item should be valid') + self.assertTrue(service_item2.is_valid, 'The second service item should be valid') + self.assertEqual(test_file1, service_item.get_rendered_frame(0), + 'The first frame should match the path to the image') + self.assertEqual(test_file2, service_item2.get_rendered_frame(0), + 'The Second frame should match the path to the image') + self.assertEqual(frame_array1, service_item.get_frames()[0], 'The return should match the frame array1') + self.assertEqual(frame_array2, service_item2.get_frames()[0], 'The return should match the frame array2') + self.assertEqual(test_file1, service_item.get_frame_path(0), + 'The frame path should match the full path to the image') + self.assertEqual(test_file2, service_item2.get_frame_path(0), + 'The frame path should match the full path to the image') + self.assertEqual(image_name1, service_item.get_frame_title(0), + 'The 1st frame title should match the image name') + self.assertEqual(image_name2, service_item2.get_frame_title(0), + 'The 2nd frame title should match the image name') + self.assertEqual(service_item.name, service_item.title.lower(), + 'The plugin name should match the display title, as there are > 1 Images') + self.assertTrue(service_item.is_image(), 'This service item should be of an "image" type') + self.assertTrue(service_item.is_capable(ItemCapabilities.CanMaintain), + 'This service item should be able to be Maintained') + self.assertTrue(service_item.is_capable(ItemCapabilities.CanPreview), + 'This service item should be able to be be Previewed') + self.assertTrue(service_item.is_capable(ItemCapabilities.CanLoop), + 'This service item should be able to be run in a can be made to Loop') + self.assertTrue(service_item.is_capable(ItemCapabilities.CanAppend), + 'This service item should be able to have new items added to it') diff --git a/tests/functional/openlp_core_lib/test_settings.py b/tests/functional/openlp_core_lib/test_settings.py index e9ee02443..25647a6e1 100644 --- a/tests/functional/openlp_core_lib/test_settings.py +++ b/tests/functional/openlp_core_lib/test_settings.py @@ -68,13 +68,13 @@ class TestSettings(TestCase): default_value = Settings().value('core/has run wizard') # THEN the default value is returned - assert default_value is False, 'The default value should be False' + self.assertFalse(default_value, 'The default value should be False') # WHEN a new value is saved into config Settings().setValue('core/has run wizard', True) # THEN the new value is returned when re-read - assert Settings().value('core/has run wizard') is True, 'The saved value should have been returned' + self.assertTrue(Settings().value('core/has run wizard'), 'The saved value should have been returned') def settings_override_test(self): """ @@ -90,13 +90,13 @@ class TestSettings(TestCase): extend = Settings().value('test/extend') # THEN the default value is returned - assert extend == 'very wide', 'The default value of "very wide" should be returned' + self.assertEqual('very wide', extend, 'The default value of "very wide" should be returned') # WHEN a new value is saved into config Settings().setValue('test/extend', 'very short') # THEN the new value is returned when re-read - assert Settings().value('test/extend') == 'very short', 'The saved value should be returned' + self.assertEqual('very short', Settings().value('test/extend'), 'The saved value should be returned') def settings_override_with_group_test(self): """ @@ -114,10 +114,10 @@ class TestSettings(TestCase): extend = settings.value('extend') # THEN the default value is returned - assert extend == 'very wide', 'The default value defined should be returned' + self.assertEqual('very wide', extend, 'The default value defined should be returned') # WHEN a new value is saved into config Settings().setValue('test/extend', 'very short') # THEN the new value is returned when re-read - assert Settings().value('test/extend') == 'very short', 'The saved value should be returned' + self.assertEqual('very short', Settings().value('test/extend'), 'The saved value should be returned') diff --git a/tests/functional/openlp_core_lib/test_uistrings.py b/tests/functional/openlp_core_lib/test_uistrings.py index a69905680..fbfe07c78 100644 --- a/tests/functional/openlp_core_lib/test_uistrings.py +++ b/tests/functional/openlp_core_lib/test_uistrings.py @@ -45,6 +45,6 @@ class TestUiStrings(TestCase): second_instance = UiStrings() # THEN: Check if the instances are the same. - assert first_instance is second_instance, "They should be the same instance!" + self.assertIs(first_instance, second_instance, 'Two UiStrings objects should be the same instance') diff --git a/tests/utils/__init__.py b/tests/utils/__init__.py index e69de29bb..d983f1b6e 100644 --- a/tests/utils/__init__.py +++ b/tests/utils/__init__.py @@ -0,0 +1,51 @@ +# -*- 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 # +############################################################################### +import os +import json + + +def assert_length(expected, iterable, msg=None): + if len(iterable) != expected: + if not msg: + msg = 'Expected length %s, got %s' % (expected, len(iterable)) + raise AssertionError(msg) + + +def convert_file_service_item(test_path, name, row=0): + service_file = os.path.join(test_path, name) + open_file = open(service_file, 'r') + try: + items = json.load(open_file) + first_line = items[row] + except IOError: + first_line = '' + finally: + open_file.close() + return first_line + From c05928c3d308e9829f7e063dddae7b21cf7c8669 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Fri, 27 Sep 2013 21:37:42 +0100 Subject: [PATCH 70/93] Fix main ui routes --- openlp/plugins/remotes/lib/httprouter.py | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/openlp/plugins/remotes/lib/httprouter.py b/openlp/plugins/remotes/lib/httprouter.py index 1b29bc416..6a2a71ab4 100644 --- a/openlp/plugins/remotes/lib/httprouter.py +++ b/openlp/plugins/remotes/lib/httprouter.py @@ -152,8 +152,8 @@ class HttpRouter(object): ('^/(main)$', {'function': self.serve_file, 'secure': False}), (r'^/files/(.*)$', {'function': self.serve_file, 'secure': False}), (r'^/api/poll$', {'function': self.poll, 'secure': False}), - (r'^/main/poll$', {'function': self.poll, 'secure': False}), - (r'^/main/image$', {'function': self.main_poll, 'secure': False}), + (r'^/main/poll$', {'function': self.main_poll, 'secure': False}), + (r'^/main/image$', {'function': self.main_image, 'secure': False}), (r'^/api/controller/(live|preview)/text$', {'function': self.controller_text, 'secure': False}), (r'^/api/controller/(live|preview)/(.*)$', {'function': self.controller, 'secure': True}), (r'^/api/service/list$', {'function': self.service_list, 'secure': False}), @@ -175,14 +175,6 @@ class HttpRouter(object): """ if self.path == '/favicon.ico': return - ########### - print(self.headers['content-type']) - if self.headers['content-type'] == 'application/json': - length = int(self.headers['content-length']) - postvars = parse_qs(self.rfile.read(length), keep_blank_values=1) - for var in postvars: - print(var.decode("utf-8")) - ############## if not hasattr(self, 'auth'): self.initialise() function, args = self.process_http_request(self.path) @@ -399,6 +391,7 @@ class HttpRouter(object): 'isSecure': Settings().value(self.settings_section + '/authentication enabled'), 'isAuthorised': self.authorised } + self.send_header('Content-type', 'application/json') return json.dumps({'results': result}).encode() def main_poll(self): @@ -408,6 +401,7 @@ class HttpRouter(object): result = { 'slide_count': self.live_controller.slide_count } + self.send_header('Content-type', 'application/json') return json.dumps({'results': result}).encode() def main_image(self): @@ -417,6 +411,7 @@ class HttpRouter(object): result = { 'slide_image': 'data:image/png;base64,' + str(image_to_byte(self.live_controller.slide_image)) } + self.send_header('Content-type', 'application/json') return json.dumps({'results': result}).encode() def display(self, action): @@ -428,6 +423,7 @@ class HttpRouter(object): This is the action, either ``hide`` or ``show``. """ self.live_controller.emit(QtCore.SIGNAL('slidecontroller_toggle_display'), action) + self.send_header('Content-type', 'application/json') return json.dumps({'results': {'success': True}}).encode() def alert(self): @@ -445,6 +441,7 @@ class HttpRouter(object): success = True else: success = False + self.send_header('Content-type', 'application/json') return json.dumps({'results': {'success': success}}).encode() def controller_text(self, var): @@ -472,6 +469,7 @@ class HttpRouter(object): json_data = {'results': {'slides': data}} if current_item: json_data['results']['item'] = self.live_controller.service_item.unique_identifier + self.send_header('Content-type', 'application/json') return json.dumps(json_data).encode() def controller(self, display_type, action): @@ -496,6 +494,7 @@ class HttpRouter(object): else: self.live_controller.emit(QtCore.SIGNAL(event)) json_data = {'results': {'success': True}} + self.send_header('Content-type', 'application/json') return json.dumps(json_data).encode() def service_list(self): @@ -505,6 +504,7 @@ class HttpRouter(object): ``action`` The action to perform. """ + self.send_header('Content-type', 'application/json') return json.dumps({'results': {'items': self._get_service_items()}}).encode() def service(self, action): @@ -523,6 +523,7 @@ class HttpRouter(object): self.service_manager.emit(QtCore.SIGNAL(event), data) else: Registry().execute(event) + self.send_header('Content-type', 'application/json') return json.dumps({'results': {'success': True}}).encode() def plugin_info(self, action): @@ -538,6 +539,7 @@ class HttpRouter(object): for plugin in self.plugin_manager.plugins: if plugin.status == PluginStatus.Active and plugin.media_item and plugin.media_item.has_search: searches.append([plugin.name, str(plugin.text_strings[StringContent.Name]['plural'])]) + self.send_header('Content-type', 'application/json') return json.dumps({'results': {'items': searches}}).encode() def search(self, plugin_name): @@ -557,6 +559,7 @@ class HttpRouter(object): results = plugin.media_item.search(text, False) else: results = [] + self.send_header('Content-type', 'application/json') return json.dumps({'results': {'items': results}}).encode() def go_live(self, plugin_name): From 6533fb5bb000ce5276d983bc477b99f886707849 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sat, 28 Sep 2013 06:10:44 +0100 Subject: [PATCH 71/93] Fix mime types for JSON --- openlp/plugins/remotes/lib/httprouter.py | 30 +++++++++++++++--------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/openlp/plugins/remotes/lib/httprouter.py b/openlp/plugins/remotes/lib/httprouter.py index 6a2a71ab4..f12fbb290 100644 --- a/openlp/plugins/remotes/lib/httprouter.py +++ b/openlp/plugins/remotes/lib/httprouter.py @@ -244,6 +244,14 @@ class HttpRouter(object): self.send_header('Content-type', 'text/html') self.end_headers() + def do_json_header(self): + """ + Create a header for JSON messages + """ + self.send_response(200) + self.send_header('Content-type', 'application/json') + self.end_headers() + def do_http_error(self): """ Create a error http header. @@ -391,7 +399,7 @@ class HttpRouter(object): 'isSecure': Settings().value(self.settings_section + '/authentication enabled'), 'isAuthorised': self.authorised } - self.send_header('Content-type', 'application/json') + self.do_json_header() return json.dumps({'results': result}).encode() def main_poll(self): @@ -401,7 +409,7 @@ class HttpRouter(object): result = { 'slide_count': self.live_controller.slide_count } - self.send_header('Content-type', 'application/json') + self.do_json_header() return json.dumps({'results': result}).encode() def main_image(self): @@ -411,7 +419,7 @@ class HttpRouter(object): result = { 'slide_image': 'data:image/png;base64,' + str(image_to_byte(self.live_controller.slide_image)) } - self.send_header('Content-type', 'application/json') + self.do_json_header() return json.dumps({'results': result}).encode() def display(self, action): @@ -423,7 +431,7 @@ class HttpRouter(object): This is the action, either ``hide`` or ``show``. """ self.live_controller.emit(QtCore.SIGNAL('slidecontroller_toggle_display'), action) - self.send_header('Content-type', 'application/json') + self.do_json_header() return json.dumps({'results': {'success': True}}).encode() def alert(self): @@ -441,7 +449,7 @@ class HttpRouter(object): success = True else: success = False - self.send_header('Content-type', 'application/json') + self.do_json_header() return json.dumps({'results': {'success': success}}).encode() def controller_text(self, var): @@ -469,7 +477,7 @@ class HttpRouter(object): json_data = {'results': {'slides': data}} if current_item: json_data['results']['item'] = self.live_controller.service_item.unique_identifier - self.send_header('Content-type', 'application/json') + self.do_json_header() return json.dumps(json_data).encode() def controller(self, display_type, action): @@ -494,7 +502,7 @@ class HttpRouter(object): else: self.live_controller.emit(QtCore.SIGNAL(event)) json_data = {'results': {'success': True}} - self.send_header('Content-type', 'application/json') + self.do_json_header() return json.dumps(json_data).encode() def service_list(self): @@ -504,7 +512,7 @@ class HttpRouter(object): ``action`` The action to perform. """ - self.send_header('Content-type', 'application/json') + self.do_json_header() return json.dumps({'results': {'items': self._get_service_items()}}).encode() def service(self, action): @@ -523,7 +531,7 @@ class HttpRouter(object): self.service_manager.emit(QtCore.SIGNAL(event), data) else: Registry().execute(event) - self.send_header('Content-type', 'application/json') + self.do_json_header() return json.dumps({'results': {'success': True}}).encode() def plugin_info(self, action): @@ -539,7 +547,7 @@ class HttpRouter(object): for plugin in self.plugin_manager.plugins: if plugin.status == PluginStatus.Active and plugin.media_item and plugin.media_item.has_search: searches.append([plugin.name, str(plugin.text_strings[StringContent.Name]['plural'])]) - self.send_header('Content-type', 'application/json') + self.do_json_header() return json.dumps({'results': {'items': searches}}).encode() def search(self, plugin_name): @@ -559,7 +567,7 @@ class HttpRouter(object): results = plugin.media_item.search(text, False) else: results = [] - self.send_header('Content-type', 'application/json') + self.do_json_header() return json.dumps({'results': {'items': results}}).encode() def go_live(self, plugin_name): From a1b47f08adc5606f9718f03f08417308479ad73c Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sat, 28 Sep 2013 21:43:00 +0100 Subject: [PATCH 72/93] fix ref --- openlp/plugins/remotes/lib/remotetab.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openlp/plugins/remotes/lib/remotetab.py b/openlp/plugins/remotes/lib/remotetab.py index c06d71ee9..17d368bd2 100644 --- a/openlp/plugins/remotes/lib/remotetab.py +++ b/openlp/plugins/remotes/lib/remotetab.py @@ -207,8 +207,8 @@ class RemoteTab(SettingsTab): https_url_temp = https_url + 'stage' self.stage_url.setText('%s' % (http_url_temp, http_url_temp)) self.stage_https_url.setText('%s' % (https_url_temp, https_url_temp)) - http_url_temp = http_url + 'live' - https_url_temp = https_url + 'live' + http_url_temp = http_url + 'main' + https_url_temp = https_url + 'main' self.live_url.setText('%s' % (http_url_temp, http_url_temp)) self.live_https_url.setText('%s' % (https_url_temp, https_url_temp)) From d630b1525c62e90bee2570f94afbccd213fec0b5 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sat, 28 Sep 2013 21:55:25 +0100 Subject: [PATCH 73/93] fix reload --- openlp/core/ui/formattingtagform.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/openlp/core/ui/formattingtagform.py b/openlp/core/ui/formattingtagform.py index a7c793c6f..da1fef175 100644 --- a/openlp/core/ui/formattingtagform.py +++ b/openlp/core/ui/formattingtagform.py @@ -69,6 +69,7 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog, FormattingTagCont # Forces reloading of tags from openlp configuration. FormattingTags.load_tags() self.is_deleting = False + self.reloading = False def exec_(self): """ @@ -132,6 +133,7 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog, FormattingTagCont """ Reset List for loading. """ + self.reloading = True self.tag_table_widget_read.clearContents() self.tag_table_widget_read.setRowCount(0) self.tag_table_widget.clearContents() @@ -157,6 +159,7 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog, FormattingTagCont self.tag_table_widget.resizeRowsToContents() # Permanent (persistent) tags do not have this key html[u'temporary'] = False + self.reloading = False def on_current_cell_changed(self, cur_row, cur_col, pre_row, pre_col): """ @@ -165,6 +168,8 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog, FormattingTagCont if self.is_deleting: self.is_deleting = False return + if self.reloading: + return # only process for editable rows if self.tag_table_widget.item(pre_row, 0): item = self.tag_table_widget.item(pre_row, pre_col) From c30eedda089f022175522f084d6f7b36fd7771d5 Mon Sep 17 00:00:00 2001 From: Raoul Snyman Date: Sun, 29 Sep 2013 23:21:47 +0200 Subject: [PATCH 74/93] Some more updates to some of the tests. --- tests/functional/openlp_core_lib/test_lib.py | 92 +++++++++++-------- .../openlp_core_lib/test_pluginmanager.py | 12 +-- 2 files changed, 61 insertions(+), 43 deletions(-) diff --git a/tests/functional/openlp_core_lib/test_lib.py b/tests/functional/openlp_core_lib/test_lib.py index 4b67cee2b..5e2bd187b 100644 --- a/tests/functional/openlp_core_lib/test_lib.py +++ b/tests/functional/openlp_core_lib/test_lib.py @@ -45,9 +45,9 @@ TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', class TestLib(TestCase): - def str_to_bool_with_bool_test(self): + def str_to_bool_with_bool_true_test(self): """ - Test the str_to_bool function with boolean input + Test the str_to_bool function with boolean input of True """ # GIVEN: A boolean value set to true true_boolean = True @@ -56,9 +56,13 @@ class TestLib(TestCase): true_result = str_to_bool(true_boolean) # THEN: We should get back a True bool - assert isinstance(true_result, bool), 'The result should be a boolean' - assert true_result is True, 'The result should be True' + self.assertIsInstance(true_result, bool, 'The result should be a boolean') + self.assertTrue(true_result, 'The result should be True') + def str_to_bool_with_bool_false_test(self): + """ + Test the str_to_bool function with boolean input of False + """ # GIVEN: A boolean value set to false false_boolean = False @@ -66,12 +70,12 @@ class TestLib(TestCase): false_result = str_to_bool(false_boolean) # THEN: We should get back a True bool - assert isinstance(false_result, bool), 'The result should be a boolean' - assert false_result is False, 'The result should be True' + self.assertIsinstance(false_result, bool, 'The result should be a boolean') + self.assertFalse(false_result, 'The result should be True') - def str_to_bool_with_invalid_test(self): + def str_to_bool_with_integer_test(self): """ - Test the str_to_bool function with a set of invalid inputs + Test the str_to_bool function with an integer input """ # GIVEN: An integer value int_string = 1 @@ -80,8 +84,12 @@ class TestLib(TestCase): int_result = str_to_bool(int_string) # THEN: we should get back a false - assert int_result is False, 'The result should be False' + self.assertFalse(int_result, 'The result should be False') + def str_to_bool_with_invalid_string_test(self): + """ + Test the str_to_bool function with an invalid string + """ # GIVEN: An string value with completely invalid input invalid_string = 'my feet are wet' @@ -89,11 +97,11 @@ class TestLib(TestCase): str_result = str_to_bool(invalid_string) # THEN: we should get back a false - assert str_result is False, 'The result should be False' + self.assertFalse(str_result, 'The result should be False') - def str_to_bool_with_false_values_test(self): + def str_to_bool_with_string_false_test(self): """ - Test the str_to_bool function with a set of false inputs + Test the str_to_bool function with a string saying "false" """ # GIVEN: A string set to "false" false_string = 'false' @@ -102,8 +110,12 @@ class TestLib(TestCase): false_result = str_to_bool(false_string) # THEN: we should get back a false - assert false_result is False, 'The result should be False' + self.assertFalse(false_result, 'The result should be False') + def str_to_bool_with_string_no_test(self): + """ + Test the str_to_bool function with a string saying "NO" + """ # GIVEN: An string set to "NO" no_string = 'NO' @@ -111,11 +123,11 @@ class TestLib(TestCase): str_result = str_to_bool(no_string) # THEN: we should get back a false - assert str_result is False, 'The result should be False' + self.assertFalse(str_result, 'The result should be False') - def str_to_bool_with_true_values_test(self): + def str_to_bool_with_true_string_value_test(self): """ - Test the str_to_bool function with a set of true inputs + Test the str_to_bool function with a string set to "True" """ # GIVEN: A string set to "True" true_string = 'True' @@ -124,8 +136,12 @@ class TestLib(TestCase): true_result = str_to_bool(true_string) # THEN: we should get back a true - assert true_result is True, 'The result should be True' + self.assertTrue(true_result, 'The result should be True') + def str_to_bool_with_yes_string_value_test(self): + """ + Test the str_to_bool function with a string set to "yes" + """ # GIVEN: An string set to "yes" yes_string = 'yes' @@ -133,7 +149,7 @@ class TestLib(TestCase): str_result = str_to_bool(yes_string) # THEN: we should get back a true - assert str_result is True, 'The result should be True' + self.assertTrue(str_result, 'The result should be True') def translate_test(self): """ @@ -152,7 +168,7 @@ class TestLib(TestCase): # THEN: the translated string should be returned, and the mocked function should have been called mocked_translate.assert_called_with(context, text, comment, encoding, n) - assert result == 'Translated string', 'The translated string should have been returned' + self.assertEqual('Translated string', result, 'The translated string should have been returned') def check_directory_exists_test(self): """ @@ -169,7 +185,7 @@ class TestLib(TestCase): # THEN: Only os.path.exists should have been called mocked_exists.assert_called_with(directory_to_check) - assert not mocked_makedirs.called, 'os.makedirs should not have been called' + self.assertIsNot(mocked_makedirs.called, 'os.makedirs should not have been called') # WHEN: os.path.exists returns False and we check the directory exists mocked_exists.return_value = False @@ -207,13 +223,14 @@ class TestLib(TestCase): # THEN: The result should be False mocked_isfile.assert_called_with(filename) - assert result is False, 'False should be returned if no file exists' + self.assertFalse(result, 'False should be returned if no file exists') def get_text_file_string_read_error_test(self): """ Test the get_text_file_string() method when a read error happens """ - with patch('openlp.core.lib.os.path.isfile') as mocked_isfile, patch('openlp.core.lib.open', create=True) as mocked_open: + with patch('openlp.core.lib.os.path.isfile') as mocked_isfile, \ + patch('openlp.core.lib.open', create=True) as mocked_open: # GIVEN: A mocked-out open() which raises an exception and isfile returns True filename = 'testfile.txt' mocked_isfile.return_value = True @@ -225,13 +242,13 @@ class TestLib(TestCase): # THEN: None should be returned mocked_isfile.assert_called_with(filename) mocked_open.assert_called_with(filename, 'r') - assert result is None, 'None should be returned if the file cannot be opened' + self.assertIsNone(result, 'None should be returned if the file cannot be opened') def get_text_file_string_decode_error_test(self): """ Test the get_text_file_string() method when the contents cannot be decoded """ - assert True, 'Impossible to test due to conflicts when mocking out the "open" function' + self.skipTest('Impossible to test due to conflicts when mocking out the "open" function') def build_icon_with_qicon_test(self): """ @@ -246,7 +263,7 @@ class TestLib(TestCase): result = build_icon(mocked_icon) # THEN: The result should be our mocked QIcon - assert result is mocked_icon, 'The result should be the mocked QIcon' + self.assertIs(mocked_icon, result, 'The result should be the mocked QIcon') def build_icon_with_resource_test(self): """ @@ -268,7 +285,7 @@ class TestLib(TestCase): MockedQPixmap.assert_called_with(resource_uri) # There really should be more assert statements here but due to type checking and things they all break. The # best we can do is to assert that we get back a MagicMock object. - assert isinstance(result, MagicMock), 'The result should be a MagicMock, because we mocked it out' + self.assertIsInstance(result, MagicMock, 'The result should be a MagicMock, because we mocked it out') def image_to_byte_test(self): """ @@ -293,7 +310,8 @@ class TestLib(TestCase): mocked_buffer.open.assert_called_with('writeonly') mocked_image.save.assert_called_with(mocked_buffer, "PNG") mocked_byte_array.toBase64.assert_called_with() - assert result == 'base64mock', 'The result should be the return value of the mocked out base64 method' + self.assertEqual('base64mock', result, + 'The result should be the return value of the mocked out base64 method') def create_thumb_with_size_test(self): """ @@ -312,16 +330,16 @@ class TestLib(TestCase): pass # Only continue when the thumb does not exist. - assert not os.path.exists(thumb_path), 'Test was not ran, because the thumb already exists.' + self.assertFalse(os.path.exists(thumb_path), 'Test was not run, because the thumb already exists.') # WHEN: Create the thumb. icon = create_thumb(image_path, thumb_path, size=thumb_size) # THEN: Check if the thumb was created. - assert os.path.exists(thumb_path), 'Test was not ran, because the thumb already exists.' - assert isinstance(icon, QtGui.QIcon), 'The icon should be a QIcon.' - assert not icon.isNull(), 'The icon should not be null.' - assert QtGui.QImageReader(thumb_path).size() == thumb_size, 'The thumb should have the given size.' + self.assertTrue(os.path.exists(thumb_path), 'Test was not ran, because the thumb already exists') + self.assertIsInstance(icon, QtGui.QIcon, 'The icon should be a QIcon') + self.assertFalse(icon.isNull(), 'The icon should not be null') + self.assertEqual(thumb_size, QtGui.QImageReader(thumb_path).size(), 'The thumb should have the given size') # Remove the thumb so that the test actually tests if the thumb will be created. try: @@ -344,7 +362,7 @@ class TestLib(TestCase): # THEN: The selectedIndexes function should have been called and the result should be true mocked_list_widget.selectedIndexes.assert_called_with() - assert result, 'The result should be True' + self.assertTrue(result, 'The result should be True') def check_item_selected_false_test(self): """ @@ -352,7 +370,7 @@ class TestLib(TestCase): """ # GIVEN: A mocked out QtGui module and a list widget with selected indexes with patch('openlp.core.lib.QtGui') as MockedQtGui, \ - patch('openlp.core.lib.translate') as mocked_translate: + patch('openlp.core.lib.translate') as mocked_translate: mocked_translate.return_value = 'mocked translate' mocked_list_widget = MagicMock() mocked_list_widget.selectedIndexes.return_value = False @@ -365,7 +383,7 @@ class TestLib(TestCase): # THEN: The selectedIndexes function should have been called and the result should be true mocked_list_widget.selectedIndexes.assert_called_with() MockedQtGui.QMessageBox.information.assert_called_with('parent', 'mocked translate', 'message') - assert not result, 'The result should be False' + self.assertFalse(result, 'The result should be False') def clean_tags_test(self): """ @@ -387,7 +405,7 @@ class TestLib(TestCase): result_string = clean_tags(string_to_pass) # THEN: The strings should be identical. - assert result_string == wanted_string, 'The strings should be identical.' + self.assertEqual(wanted_string, result_string, 'The strings should be identical') def expand_tags_test(self): """ @@ -426,7 +444,7 @@ class TestLib(TestCase): result_string = expand_tags(string_to_pass) # THEN: The strings should be identical. - assert result_string == wanted_string, 'The strings should be identical.' + self.assertEqual(wanted_string, result_string, 'The strings should be identical.') def validate_thumb_file_does_not_exist_test(self): """ diff --git a/tests/functional/openlp_core_lib/test_pluginmanager.py b/tests/functional/openlp_core_lib/test_pluginmanager.py index afd06fc47..485288344 100644 --- a/tests/functional/openlp_core_lib/test_pluginmanager.py +++ b/tests/functional/openlp_core_lib/test_pluginmanager.py @@ -280,8 +280,8 @@ class TestPluginManager(TestCase): plugin_manager.hook_tools_menu() # THEN: The add_tools_menu_item() method should have been called - assert mocked_plugin.add_tools_menu_item.call_count == 0, \ - 'The add_tools_menu_item() method should not have been called.' + self.assertEqual(0, mocked_plugin.add_tools_menu_item.call_count, + 'The add_tools_menu_item() method should not have been called.') def hook_tools_menu_with_active_plugin_test(self): """ @@ -315,7 +315,7 @@ class TestPluginManager(TestCase): # THEN: The is_active() method should have been called, and initialise() method should NOT have been called mocked_plugin.is_active.assert_called_with() - assert mocked_plugin.initialise.call_count == 0, 'The initialise() method should not have been called.' + self.assertEqual(0, mocked_plugin.initialise.call_count, 'The initialise() method should not have been called.') def initialise_plugins_with_active_plugin_test(self): """ @@ -351,7 +351,7 @@ class TestPluginManager(TestCase): # THEN: The is_active() method should have been called, and initialise() method should NOT have been called mocked_plugin.is_active.assert_called_with() - assert mocked_plugin.finalise.call_count == 0, 'The finalise() method should not have been called.' + self.assertEqual(0, mocked_plugin.finalise.call_count, 'The finalise() method should not have been called.') def finalise_plugins_with_active_plugin_test(self): """ @@ -419,8 +419,8 @@ class TestPluginManager(TestCase): # THEN: The isActive() method should have been called, and initialise() method should NOT have been called mocked_plugin.is_active.assert_called_with() - assert mocked_plugin.new_service_created.call_count == 0,\ - 'The new_service_created() method should not have been called.' + self.assertEqual(0, mocked_plugin.new_service_created.call_count, + 'The new_service_created() method should not have been called.') def new_service_created_with_active_plugin_test(self): """ From 3503c54ce44c33192fa375321c7b18d9e9a100d1 Mon Sep 17 00:00:00 2001 From: Raoul Snyman Date: Sun, 29 Sep 2013 23:28:53 +0200 Subject: [PATCH 75/93] Some bugs and minor tweaks. --- tests/functional/openlp_core_lib/test_lib.py | 2 +- tests/functional/openlp_core_lib/test_serviceitem.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/functional/openlp_core_lib/test_lib.py b/tests/functional/openlp_core_lib/test_lib.py index 5e2bd187b..80e0e35f3 100644 --- a/tests/functional/openlp_core_lib/test_lib.py +++ b/tests/functional/openlp_core_lib/test_lib.py @@ -70,7 +70,7 @@ class TestLib(TestCase): false_result = str_to_bool(false_boolean) # THEN: We should get back a True bool - self.assertIsinstance(false_result, bool, 'The result should be a boolean') + self.assertIsInstance(false_result, bool, 'The result should be a boolean') self.assertFalse(false_result, 'The result should be True') def str_to_bool_with_integer_test(self): diff --git a/tests/functional/openlp_core_lib/test_serviceitem.py b/tests/functional/openlp_core_lib/test_serviceitem.py index 0e1b150ae..fda471428 100644 --- a/tests/functional/openlp_core_lib/test_serviceitem.py +++ b/tests/functional/openlp_core_lib/test_serviceitem.py @@ -81,7 +81,7 @@ class TestServiceItem(TestCase): service_item.add_icon = MagicMock() # WHEN: We add a custom from a saved service - line = convert_file_service_item('serviceitem_custom_1.osj') + line = convert_file_service_item(TEST_PATH, 'serviceitem_custom_1.osj') service_item.set_from_service(line) # THEN: We should get back a valid service item From 1347ad61d1059d5e79ccc53afb91be47314ae2d1 Mon Sep 17 00:00:00 2001 From: Raoul Snyman Date: Wed, 2 Oct 2013 23:07:20 +0200 Subject: [PATCH 76/93] Changed the way we create and initialise MediaManagerItems so that we can test things better. --- openlp/core/lib/mediamanageritem.py | 17 ++++++++-- openlp/plugins/bibles/bibleplugin.py | 14 ++++++--- openlp/plugins/bibles/lib/mediaitem.py | 5 +++ openlp/plugins/custom/lib/mediaitem.py | 7 ++++- openlp/plugins/images/lib/mediaitem.py | 31 ++++++++++++++----- openlp/plugins/media/lib/mediaitem.py | 7 ++++- openlp/plugins/presentations/lib/mediaitem.py | 30 ++++++++++-------- .../presentations/presentationplugin.py | 3 +- openlp/plugins/songs/lib/mediaitem.py | 5 +++ .../openlp_core_lib/test_serviceitem.py | 4 +-- .../openlp_plugins/images/test_lib.py | 9 +++--- .../presentations/test_mediaitem.py | 16 +++++----- .../openlp_plugins/songs/test_mediaitem.py | 4 +-- 13 files changed, 104 insertions(+), 48 deletions(-) diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py index 9e7eb8d73..3fddc18f2 100644 --- a/openlp/core/lib/mediamanageritem.py +++ b/openlp/core/lib/mediamanageritem.py @@ -82,10 +82,17 @@ class MediaManagerItem(QtGui.QWidget): """ Constructor to create the media manager item. """ - super(MediaManagerItem, self).__init__() + super(MediaManagerItem, self).__init__(parent) + self.plugin = plugin + self._setup() + self.setup_item() + + def _setup(self): + """ + Run some initial setup. This method is separate from __init__ in order to mock it out in tests. + """ self.hide() self.whitespace = re.compile(r'[\W_]+', re.UNICODE) - self.plugin = plugin visible_title = self.plugin.get_string(StringContent.VisibleName) self.title = str(visible_title['title']) Registry().register(self.plugin.name, self) @@ -106,6 +113,12 @@ class MediaManagerItem(QtGui.QWidget): QtCore.QObject.connect(self, QtCore.SIGNAL('%s_go_live' % self.plugin.name), self.go_live_remote) QtCore.QObject.connect(self, QtCore.SIGNAL('%s_add_to_service' % self.plugin.name), self.add_to_service_remote) + def setup_item(self): + """ + Override this for additional Plugin setup + """ + pass + def required_icons(self): """ This method is called to define the icons for the plugin. It provides a default set and the plugin is able to diff --git a/openlp/plugins/bibles/bibleplugin.py b/openlp/plugins/bibles/bibleplugin.py index 18aab2a0d..4121b474c 100644 --- a/openlp/plugins/bibles/bibleplugin.py +++ b/openlp/plugins/bibles/bibleplugin.py @@ -67,6 +67,9 @@ __default_settings__ = { class BiblePlugin(Plugin): + """ + The Bible plugin provides a plugin for managing and displaying Bibles. + """ log.info('Bible Plugin loaded') def __init__(self): @@ -74,13 +77,14 @@ class BiblePlugin(Plugin): self.weight = -9 self.icon_path = ':/plugins/plugin_bibles.png' self.icon = build_icon(self.icon_path) - self.manager = None + self.manager = BibleManager(self) def initialise(self): + """ + Initialise the Bible plugin. + """ log.info('bibles Initialising') - if self.manager is None: - self.manager = BibleManager(self) - Plugin.initialise(self) + super(BiblePlugin, self).initialise() self.import_bible_item.setVisible(True) action_list = ActionList.get_instance() action_list.add_action(self.import_bible_item, UiStrings().Import) @@ -107,7 +111,7 @@ class BiblePlugin(Plugin): """ Perform tasks on application startup """ - Plugin.app_startup(self) + super(BiblePlugin, self).app_startup() if self.manager.old_bible_databases: if QtGui.QMessageBox.information(self.main_window, translate('OpenLP', 'Information'), diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index 94f015fdb..4ccd37df1 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -64,6 +64,11 @@ class BibleMediaItem(MediaManagerItem): self.lock_icon = build_icon(':/bibles/bibles_search_lock.png') self.unlock_icon = build_icon(':/bibles/bibles_search_unlock.png') MediaManagerItem.__init__(self, parent, plugin) + + def setup_item(self): + """ + Do some additional setup. + """ # Place to store the search results for both bibles. self.settings = self.plugin.settings_tab self.quick_preview_allowed = True diff --git a/openlp/plugins/custom/lib/mediaitem.py b/openlp/plugins/custom/lib/mediaitem.py index a01ba427f..f5b518ce8 100644 --- a/openlp/plugins/custom/lib/mediaitem.py +++ b/openlp/plugins/custom/lib/mediaitem.py @@ -58,6 +58,11 @@ class CustomMediaItem(MediaManagerItem): def __init__(self, parent, plugin): self.icon_path = 'custom/custom' super(CustomMediaItem, self).__init__(parent, plugin) + + def setup_item(self): + """ + Do some additional setup. + """ self.edit_custom_form = EditCustomForm(self, self.main_window, self.plugin.manager) self.single_service_item = False self.quick_preview_allowed = True @@ -65,7 +70,7 @@ class CustomMediaItem(MediaManagerItem): # Holds information about whether the edit is remotely triggered and # which Custom is required. self.remote_custom = -1 - self.manager = plugin.manager + self.manager = self.plugin.manager def add_end_header_bar(self): self.toolbar.addSeparator() diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index 9e396c3cf..70d4630a0 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -52,10 +52,18 @@ class ImageMediaItem(MediaManagerItem): def __init__(self, parent, plugin): self.icon_path = 'images/image' + self.manager = None + self.choose_group_form = None + self.add_group_form = None super(ImageMediaItem, self).__init__(parent, plugin) + + def setup_item(self): + """ + Do some additional setup. + """ self.quick_preview_allowed = True self.has_search = True - self.manager = plugin.manager + self.manager = self.plugin.manager self.choose_group_form = ChooseGroupForm(self) self.add_group_form = AddGroupForm(self) self.fill_groups_combobox(self.choose_group_form.group_combobox) @@ -91,8 +99,8 @@ class ImageMediaItem(MediaManagerItem): self.list_view.setIconSize(QtCore.QSize(88, 50)) self.list_view.setIndentation(self.list_view.default_indentation) self.list_view.allow_internal_dnd = True - self.servicePath = os.path.join(AppLocation.get_section_data_path(self.settings_section), 'thumbnails') - check_directory_exists(self.servicePath) + self.service_path = os.path.join(AppLocation.get_section_data_path(self.settings_section), 'thumbnails') + check_directory_exists(self.service_path) # Load images from the database self.load_full_list( self.manager.get_all_objects(ImageFilenames, order_by_ref=ImageFilenames.filename), initial_load=True) @@ -193,7 +201,7 @@ class ImageMediaItem(MediaManagerItem): """ images = self.manager.get_all_objects(ImageFilenames, ImageFilenames.group_id == image_group.id) for image in images: - delete_file(os.path.join(self.servicePath, os.path.split(image.filename)[1])) + delete_file(os.path.join(self.service_path, os.path.split(image.filename)[1])) self.manager.delete_object(ImageFilenames, image.id) image_groups = self.manager.get_all_objects(ImageGroups, ImageGroups.parent_id == image_group.id) for group in image_groups: @@ -215,7 +223,7 @@ class ImageMediaItem(MediaManagerItem): if row_item: item_data = row_item.data(0, QtCore.Qt.UserRole) if isinstance(item_data, ImageFilenames): - delete_file(os.path.join(self.servicePath, row_item.text(0))) + delete_file(os.path.join(self.service_path, row_item.text(0))) if item_data.group_id == 0: self.list_view.takeTopLevelItem(self.list_view.indexOfTopLevelItem(row_item)) else: @@ -339,7 +347,7 @@ class ImageMediaItem(MediaManagerItem): for imageFile in images: log.debug('Loading image: %s', imageFile.filename) filename = os.path.split(imageFile.filename)[1] - thumb = os.path.join(self.servicePath, filename) + thumb = os.path.join(self.service_path, filename) if not os.path.exists(imageFile.filename): icon = build_icon(':/general/general_delete.png') else: @@ -672,7 +680,16 @@ class ImageMediaItem(MediaManagerItem): translate('ImagePlugin.MediaItem', 'There was a problem replacing your background, ' 'the image file "%s" no longer exists.') % filename) - def search(self, string, showError): + def search(self, string, show_error=True): + """ + Perform a search on the image file names. + + ``string`` + The glob to search for + + ``show_error`` + Unused. + """ files = self.manager.get_all_objects(ImageFilenames, filter_clause=ImageFilenames.filename.contains(string), order_by_ref=ImageFilenames.filename) results = [] diff --git a/openlp/plugins/media/lib/mediaitem.py b/openlp/plugins/media/lib/mediaitem.py index a5b0397c5..4f173db84 100644 --- a/openlp/plugins/media/lib/mediaitem.py +++ b/openlp/plugins/media/lib/mediaitem.py @@ -61,10 +61,15 @@ class MediaMediaItem(MediaManagerItem): self.background = False self.automatic = '' super(MediaMediaItem, self).__init__(parent, plugin) + + def setup_item(self): + """ + Do some additional setup. + """ self.single_service_item = False self.has_search = True self.media_object = None - self.display_controller = DisplayController(parent) + self.display_controller = DisplayController(self.parent()) self.display_controller.controller_layout = QtGui.QVBoxLayout() self.media_controller.register_controller(self.display_controller) self.media_controller.set_controls_visible(self.display_controller, False) diff --git a/openlp/plugins/presentations/lib/mediaitem.py b/openlp/plugins/presentations/lib/mediaitem.py index f1d0f1110..695baddc5 100644 --- a/openlp/plugins/presentations/lib/mediaitem.py +++ b/openlp/plugins/presentations/lib/mediaitem.py @@ -52,14 +52,26 @@ class PresentationMediaItem(MediaManagerItem): """ log.info('Presentations Media Item loaded') - def __init__(self, parent, plugin, icon, controllers): + def __init__(self, parent, plugin, controllers): """ Constructor. Setup defaults """ - self.controllers = controllers self.icon_path = 'presentations/presentation' - self.Automatic = '' + self.controllers = controllers super(PresentationMediaItem, self).__init__(parent, plugin) + + def retranslateUi(self): + """ + The name of the plugin media displayed in UI + """ + self.on_new_prompt = translate('PresentationPlugin.MediaItem', 'Select Presentation(s)') + self.automatic = translate('PresentationPlugin.MediaItem', 'Automatic') + self.display_type_label.setText(translate('PresentationPlugin.MediaItem', 'Present using:')) + + def setup_item(self): + """ + Do some additional setup. + """ self.message_listener = MessageListener(self) self.has_search = True self.single_service_item = False @@ -68,14 +80,6 @@ class PresentationMediaItem(MediaManagerItem): # Allow DnD from the desktop self.list_view.activateDnD() - def retranslateUi(self): - """ - The name of the plugin media displayed in UI - """ - self.on_new_prompt = translate('PresentationPlugin.MediaItem', 'Select Presentation(s)') - self.Automatic = translate('PresentationPlugin.MediaItem', 'Automatic') - self.display_type_label.setText(translate('PresentationPlugin.MediaItem', 'Present using:')) - def build_file_mask_string(self): """ Build the list of file extensions to be used in the Open file dialog. @@ -137,7 +141,7 @@ class PresentationMediaItem(MediaManagerItem): if self.controllers[item].enabled(): self.display_type_combo_box.addItem(item) if self.display_type_combo_box.count() > 1: - self.display_type_combo_box.insertItem(0, self.Automatic) + self.display_type_combo_box.insertItem(0, self.automatic) self.display_type_combo_box.setCurrentIndex(0) if Settings().value(self.settings_section + '/override app') == QtCore.Qt.Checked: self.presentation_widget.show() @@ -253,7 +257,7 @@ class PresentationMediaItem(MediaManagerItem): (path, name) = os.path.split(filename) service_item.title = name if os.path.exists(filename): - if service_item.processor == self.Automatic: + if service_item.processor == self.automatic: service_item.processor = self.findControllerByType(filename) if not service_item.processor: return False diff --git a/openlp/plugins/presentations/presentationplugin.py b/openlp/plugins/presentations/presentationplugin.py index 1efd5ac0a..be5d3f52d 100644 --- a/openlp/plugins/presentations/presentationplugin.py +++ b/openlp/plugins/presentations/presentationplugin.py @@ -109,8 +109,7 @@ class PresentationPlugin(Plugin): """ Create the Media Manager List. """ - self.media_item = PresentationMediaItem( - self.main_window.media_dock_manager.media_dock, self, self.icon, self.controllers) + self.media_item = PresentationMediaItem(self.main_window.media_dock_manager.media_dock, self, self.controllers) def register_controllers(self, controller): """ diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index 68cd706a3..957edbca0 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -72,6 +72,11 @@ class SongMediaItem(MediaManagerItem): def __init__(self, parent, plugin): self.icon_path = 'songs/song' super(SongMediaItem, self).__init__(parent, plugin) + + def setup_item(self): + """ + Do some additional setup. + """ self.single_service_item = False # Holds information about whether the edit is remotely triggered and which Song is required. self.remote_song = -1 diff --git a/tests/functional/openlp_core_lib/test_serviceitem.py b/tests/functional/openlp_core_lib/test_serviceitem.py index fda471428..f23c92b16 100644 --- a/tests/functional/openlp_core_lib/test_serviceitem.py +++ b/tests/functional/openlp_core_lib/test_serviceitem.py @@ -95,9 +95,9 @@ class TestServiceItem(TestCase): # THEN: The frames should also be valid self.assertEqual('Test Custom', service_item.get_display_title(), 'The title should be "Test Custom"') self.assertEqual(VERSE[:-1], service_item.get_frames()[0]['text'], - 'The returned text matches the input, except the last line feed') + 'The returned text matches the input, except the last line feed') self.assertEqual(VERSE.split('\n', 1)[0], service_item.get_rendered_frame(1), - 'The first line has been returned') + 'The first line has been returned') self.assertEqual('Slide 1', service_item.get_frame_title(0), '"Slide 1" has been returned as the title') self.assertEqual('Slide 2', service_item.get_frame_title(1), '"Slide 2" has been returned as the title') self.assertEqual('', service_item.get_frame_title(2), 'Blank has been returned as the title of slide 3') diff --git a/tests/functional/openlp_plugins/images/test_lib.py b/tests/functional/openlp_plugins/images/test_lib.py index 50fd258d2..f4d0dc30d 100644 --- a/tests/functional/openlp_plugins/images/test_lib.py +++ b/tests/functional/openlp_plugins/images/test_lib.py @@ -48,11 +48,10 @@ class TestImageMediaItem(TestCase): Registry().register('service_list', MagicMock()) Registry().register('main_window', self.mocked_main_window) Registry().register('live_controller', MagicMock()) - mocked_parent = MagicMock() mocked_plugin = MagicMock() - with patch('openlp.plugins.images.lib.mediaitem.ImageMediaItem.__init__') as mocked_init: - mocked_init.return_value = None - self.media_item = ImageMediaItem(mocked_parent, mocked_plugin) + with patch('openlp.plugins.images.lib.mediaitem.MediaManagerItem._setup'), \ + patch('openlp.plugins.images.lib.mediaitem.ImageMediaItem.setup_item'): + self.media_item = ImageMediaItem(None, mocked_plugin) def save_new_images_list_empty_list_test(self): """ @@ -160,7 +159,7 @@ class TestImageMediaItem(TestCase): ImageGroups.parent_id = 1 self.media_item.manager = MagicMock() self.media_item.manager.get_all_objects.side_effect = self._recursively_delete_group_side_effect - self.media_item.servicePath = "" + self.media_item.service_path = "" test_group = ImageGroups() test_group.id = 1 diff --git a/tests/functional/openlp_plugins/presentations/test_mediaitem.py b/tests/functional/openlp_plugins/presentations/test_mediaitem.py index 49b47252c..5b5c99e78 100644 --- a/tests/functional/openlp_plugins/presentations/test_mediaitem.py +++ b/tests/functional/openlp_plugins/presentations/test_mediaitem.py @@ -49,11 +49,9 @@ class TestMediaItem(TestCase): Registry.create() Registry().register('service_manager', MagicMock()) Registry().register('main_window', MagicMock()) - - with patch('openlp.plugins.presentations.lib.mediaitem.PresentationMediaItem.__init__') as mocked_init: - mocked_init.return_value = None - self.media_item = PresentationMediaItem(MagicMock(), MagicMock, MagicMock(), MagicMock()) - + with patch('openlp.plugins.presentations.lib.mediaitem.MediaManagerItem._setup'), \ + patch('openlp.plugins.presentations.lib.mediaitem.PresentationMediaItem.setup_item'): + self.media_item = PresentationMediaItem(None, MagicMock, MagicMock()) self.application = QtGui.QApplication.instance() def tearDown(self): @@ -89,6 +87,8 @@ class TestMediaItem(TestCase): mocked_translate.side_effect = lambda module, string_to_translate: string_to_translate self.media_item.build_file_mask_string() - # THEN: The file mask should be generated. - assert self.media_item.on_new_file_masks == 'Presentations (*.odp *.ppt )', \ - 'The file mask should contain the odp and ppt extensions' + # THEN: The file mask should be generated correctly + self.assertIn('*.odp', self.media_item.on_new_file_masks, + 'The file mask should contain the odp extension') + self.assertIn('*.ppt', self.media_item.on_new_file_masks, + 'The file mask should contain the ppt extension') diff --git a/tests/functional/openlp_plugins/songs/test_mediaitem.py b/tests/functional/openlp_plugins/songs/test_mediaitem.py index a519f7021..39f3146de 100644 --- a/tests/functional/openlp_plugins/songs/test_mediaitem.py +++ b/tests/functional/openlp_plugins/songs/test_mediaitem.py @@ -23,9 +23,9 @@ class TestMediaItem(TestCase): Registry.create() Registry().register('service_list', MagicMock()) Registry().register('main_window', MagicMock()) - with patch('openlp.core.lib.mediamanageritem.MediaManagerItem.__init__'), \ + with patch('openlp.core.lib.mediamanageritem.MediaManagerItem._setup'), \ patch('openlp.plugins.songs.forms.editsongform.EditSongForm.__init__'): - self.media_item = SongMediaItem(MagicMock(), MagicMock()) + self.media_item = SongMediaItem(None, MagicMock()) fd, self.ini_file = mkstemp('.ini') Settings().set_filename(self.ini_file) From 316f33c0588a97f371c4e949d3b4ef054a86823d Mon Sep 17 00:00:00 2001 From: Raoul Snyman Date: Wed, 2 Oct 2013 23:37:00 +0200 Subject: [PATCH 77/93] Change the indentation back to 4 --- tests/functional/openlp_core_lib/test_lib.py | 2 +- .../openlp_core_lib/test_pluginmanager.py | 22 +++++----- .../openlp_core_lib/test_registry.py | 6 +-- .../functional/openlp_core_lib/test_screen.py | 2 +- .../openlp_core_lib/test_serviceitem.py | 40 +++++++++---------- .../openlp_core_utils/test_utils.py | 7 ++-- .../bibles/test_versereferencelist.py | 7 ++-- 7 files changed, 44 insertions(+), 42 deletions(-) diff --git a/tests/functional/openlp_core_lib/test_lib.py b/tests/functional/openlp_core_lib/test_lib.py index 80e0e35f3..9bb74e0d3 100644 --- a/tests/functional/openlp_core_lib/test_lib.py +++ b/tests/functional/openlp_core_lib/test_lib.py @@ -311,7 +311,7 @@ class TestLib(TestCase): mocked_image.save.assert_called_with(mocked_buffer, "PNG") mocked_byte_array.toBase64.assert_called_with() self.assertEqual('base64mock', result, - 'The result should be the return value of the mocked out base64 method') + 'The result should be the return value of the mocked out base64 method') def create_thumb_with_size_test(self): """ diff --git a/tests/functional/openlp_core_lib/test_pluginmanager.py b/tests/functional/openlp_core_lib/test_pluginmanager.py index 485288344..2ab3b2dd7 100644 --- a/tests/functional/openlp_core_lib/test_pluginmanager.py +++ b/tests/functional/openlp_core_lib/test_pluginmanager.py @@ -70,7 +70,7 @@ class TestPluginManager(TestCase): # THEN: The create_media_manager_item() method should have been called self.assertEqual(0, mocked_plugin.create_media_manager_item.call_count, - 'The create_media_manager_item() method should not have been called.') + 'The create_media_manager_item() method should not have been called.') def hook_media_manager_with_active_plugin_test(self): """ @@ -103,7 +103,7 @@ class TestPluginManager(TestCase): # THEN: The hook_settings_tabs() method should have been called self.assertEqual(0, mocked_plugin.create_media_manager_item.call_count, - 'The create_media_manager_item() method should not have been called.') + 'The create_media_manager_item() method should not have been called.') def hook_settings_tabs_with_disabled_plugin_and_mocked_form_test(self): """ @@ -123,9 +123,9 @@ class TestPluginManager(TestCase): # THEN: The create_settings_tab() method should not have been called, but the plugins lists should be the same self.assertEqual(0, mocked_plugin.create_settings_tab.call_count, - 'The create_media_manager_item() method should not have been called.') + 'The create_media_manager_item() method should not have been called.') self.assertEqual(mocked_settings_form.plugin_manager.plugins, plugin_manager.plugins, - 'The plugins on the settings form should be the same as the plugins in the plugin manager') + 'The plugins on the settings form should be the same as the plugins in the plugin manager') def hook_settings_tabs_with_active_plugin_and_mocked_form_test(self): """ @@ -145,9 +145,9 @@ class TestPluginManager(TestCase): # THEN: The create_media_manager_item() method should have been called with the mocked settings form self.assertEqual(1, mocked_plugin.create_settings_tab.call_count, - 'The create_media_manager_item() method should have been called once.') + 'The create_media_manager_item() method should have been called once.') self.assertEqual(plugin_manager.plugins, mocked_settings_form.plugin_manager.plugins, - 'The plugins on the settings form should be the same as the plugins in the plugin manager') + 'The plugins on the settings form should be the same as the plugins in the plugin manager') def hook_settings_tabs_with_active_plugin_and_no_form_test(self): """ @@ -180,7 +180,7 @@ class TestPluginManager(TestCase): # THEN: The create_media_manager_item() method should have been called self.assertEqual(0, mocked_plugin.add_import_menu_item.call_count, - 'The add_import_menu_item() method should not have been called.') + 'The add_import_menu_item() method should not have been called.') def hook_import_menu_with_active_plugin_test(self): """ @@ -213,7 +213,7 @@ class TestPluginManager(TestCase): # THEN: The add_export_menu_Item() method should not have been called self.assertEqual(0, mocked_plugin.add_export_menu_Item.call_count, - 'The add_export_menu_Item() method should not have been called.') + 'The add_export_menu_Item() method should not have been called.') def hook_export_menu_with_active_plugin_test(self): """ @@ -247,7 +247,7 @@ class TestPluginManager(TestCase): # THEN: The upgrade_settings() method should not have been called self.assertEqual(0, mocked_plugin.upgrade_settings.call_count, - 'The upgrade_settings() method should not have been called.') + 'The upgrade_settings() method should not have been called.') def hook_upgrade_plugin_settings_with_active_plugin_test(self): """ @@ -281,7 +281,7 @@ class TestPluginManager(TestCase): # THEN: The add_tools_menu_item() method should have been called self.assertEqual(0, mocked_plugin.add_tools_menu_item.call_count, - 'The add_tools_menu_item() method should not have been called.') + 'The add_tools_menu_item() method should not have been called.') def hook_tools_menu_with_active_plugin_test(self): """ @@ -420,7 +420,7 @@ class TestPluginManager(TestCase): # THEN: The isActive() method should have been called, and initialise() method should NOT have been called mocked_plugin.is_active.assert_called_with() self.assertEqual(0, mocked_plugin.new_service_created.call_count, - 'The new_service_created() method should not have been called.') + 'The new_service_created() method should not have been called.') def new_service_created_with_active_plugin_test(self): """ diff --git a/tests/functional/openlp_core_lib/test_registry.py b/tests/functional/openlp_core_lib/test_registry.py index e1e5e268b..06307630b 100644 --- a/tests/functional/openlp_core_lib/test_registry.py +++ b/tests/functional/openlp_core_lib/test_registry.py @@ -59,14 +59,14 @@ class TestRegistry(TestCase): with self.assertRaises(KeyError) as context: Registry().register('test1', mock_1) self.assertEqual(context.exception.args[0], 'Duplicate service exception test1', - 'KeyError exception should have been thrown for duplicate service') + 'KeyError exception should have been thrown for duplicate service') # WHEN I try to get back a non existent component # THEN I will get an exception with self.assertRaises(KeyError) as context: temp = Registry().get('test2') self.assertEqual(context.exception.args[0], 'Service test2 not found in list', - 'KeyError exception should have been thrown for missing service') + 'KeyError exception should have been thrown for missing service') # WHEN I try to replace a component I should be allowed (testing only) Registry().remove('test1') @@ -74,7 +74,7 @@ class TestRegistry(TestCase): with self.assertRaises(KeyError) as context: temp = Registry().get('test1') self.assertEqual(context.exception.args[0], 'Service test1 not found in list', - 'KeyError exception should have been thrown for deleted service') + 'KeyError exception should have been thrown for deleted service') def registry_function_test(self): """ diff --git a/tests/functional/openlp_core_lib/test_screen.py b/tests/functional/openlp_core_lib/test_screen.py index c541fbb12..ba635ce83 100644 --- a/tests/functional/openlp_core_lib/test_screen.py +++ b/tests/functional/openlp_core_lib/test_screen.py @@ -83,4 +83,4 @@ class TestScreenList(TestCase): new_screen_count = len(self.screens.screen_list) self.assertEqual(old_screen_count + 1, new_screen_count, 'The new_screens list should be bigger') self.assertEqual(SCREEN, self.screens.screen_list.pop(), - 'The 2nd screen should be identical to the first screen') + 'The 2nd screen should be identical to the first screen') diff --git a/tests/functional/openlp_core_lib/test_serviceitem.py b/tests/functional/openlp_core_lib/test_serviceitem.py index f23c92b16..bcf9410ca 100644 --- a/tests/functional/openlp_core_lib/test_serviceitem.py +++ b/tests/functional/openlp_core_lib/test_serviceitem.py @@ -123,24 +123,24 @@ class TestServiceItem(TestCase): # THEN: We should get back a valid service item self.assertTrue(service_item.is_valid, 'The new service item should be valid') self.assertEqual(test_file, service_item.get_rendered_frame(0), - 'The first frame should match the path to the image') + 'The first frame should match the path to the image') self.assertEqual(frame_array, service_item.get_frames()[0], - 'The return should match frame array1') + 'The return should match frame array1') self.assertEqual(test_file, service_item.get_frame_path(0), - 'The frame path should match the full path to the image') + 'The frame path should match the full path to the image') self.assertEqual(image_name, service_item.get_frame_title(0), - 'The frame title should match the image name') + 'The frame title should match the image name') self.assertEqual(image_name, service_item.get_display_title(), - 'The display title should match the first image name') + 'The display title should match the first image name') self.assertTrue(service_item.is_image(), 'This service item should be of an "image" type') self.assertTrue(service_item.is_capable(ItemCapabilities.CanMaintain), - 'This service item should be able to be Maintained') + 'This service item should be able to be Maintained') self.assertTrue(service_item.is_capable(ItemCapabilities.CanPreview), - 'This service item should be able to be be Previewed') + 'This service item should be able to be be Previewed') self.assertTrue(service_item.is_capable(ItemCapabilities.CanLoop), - 'This service item should be able to be run in a can be made to Loop') + 'This service item should be able to be run in a can be made to Loop') self.assertTrue(service_item.is_capable(ItemCapabilities.CanAppend), - 'This service item should be able to have new items added to it') + 'This service item should be able to have new items added to it') def service_item_load_image_from_local_service_test(self): """ @@ -176,27 +176,27 @@ class TestServiceItem(TestCase): self.assertTrue(service_item.is_valid, 'The first service item should be valid') self.assertTrue(service_item2.is_valid, 'The second service item should be valid') self.assertEqual(test_file1, service_item.get_rendered_frame(0), - 'The first frame should match the path to the image') + 'The first frame should match the path to the image') self.assertEqual(test_file2, service_item2.get_rendered_frame(0), - 'The Second frame should match the path to the image') + 'The Second frame should match the path to the image') self.assertEqual(frame_array1, service_item.get_frames()[0], 'The return should match the frame array1') self.assertEqual(frame_array2, service_item2.get_frames()[0], 'The return should match the frame array2') self.assertEqual(test_file1, service_item.get_frame_path(0), - 'The frame path should match the full path to the image') + 'The frame path should match the full path to the image') self.assertEqual(test_file2, service_item2.get_frame_path(0), - 'The frame path should match the full path to the image') + 'The frame path should match the full path to the image') self.assertEqual(image_name1, service_item.get_frame_title(0), - 'The 1st frame title should match the image name') + 'The 1st frame title should match the image name') self.assertEqual(image_name2, service_item2.get_frame_title(0), - 'The 2nd frame title should match the image name') + 'The 2nd frame title should match the image name') self.assertEqual(service_item.name, service_item.title.lower(), - 'The plugin name should match the display title, as there are > 1 Images') + 'The plugin name should match the display title, as there are > 1 Images') self.assertTrue(service_item.is_image(), 'This service item should be of an "image" type') self.assertTrue(service_item.is_capable(ItemCapabilities.CanMaintain), - 'This service item should be able to be Maintained') + 'This service item should be able to be Maintained') self.assertTrue(service_item.is_capable(ItemCapabilities.CanPreview), - 'This service item should be able to be be Previewed') + 'This service item should be able to be be Previewed') self.assertTrue(service_item.is_capable(ItemCapabilities.CanLoop), - 'This service item should be able to be run in a can be made to Loop') + 'This service item should be able to be run in a can be made to Loop') self.assertTrue(service_item.is_capable(ItemCapabilities.CanAppend), - 'This service item should be able to have new items added to it') + 'This service item should be able to have new items added to it') diff --git a/tests/functional/openlp_core_utils/test_utils.py b/tests/functional/openlp_core_utils/test_utils.py index 03f0f3654..8ecc15018 100644 --- a/tests/functional/openlp_core_utils/test_utils.py +++ b/tests/functional/openlp_core_utils/test_utils.py @@ -134,7 +134,7 @@ class TestUtils(TestCase): # THEN: A tuple should be returned. self.assertEqual(wanted_result, result, - 'A two-entry tuple with the directory and file name (empty) should have been returned.') + 'A two-entry tuple with the directory and file name (empty) should have been returned.') def clean_filename_test(self): """ @@ -167,7 +167,7 @@ class TestUtils(TestCase): # THEN: We get a properly sorted list self.assertEqual(['Aushang', '\u00C4u\u00DFerung', 'Auszug'], sorted_list, - 'Strings should be sorted properly') + 'Strings should be sorted properly') def get_locale_key_linux_test(self): """ @@ -180,12 +180,13 @@ class TestUtils(TestCase): mocked_get_language.return_value = 'de' mocked_os.name = 'linux' unsorted_list = ['Auszug', 'Aushang', '\u00C4u\u00DFerung'] + # WHEN: We sort the list and use get_locale_key() to generate the sorting keys sorted_list = sorted(unsorted_list, key=get_locale_key) # THEN: We get a properly sorted list self.assertEqual(['Aushang', '\u00C4u\u00DFerung', 'Auszug'], sorted_list, - 'Strings should be sorted properly') + 'Strings should be sorted properly') def get_natural_key_test(self): """ diff --git a/tests/functional/openlp_plugins/bibles/test_versereferencelist.py b/tests/functional/openlp_plugins/bibles/test_versereferencelist.py index 0c7ccd01e..bb3179dda 100644 --- a/tests/functional/openlp_plugins/bibles/test_versereferencelist.py +++ b/tests/functional/openlp_plugins/bibles/test_versereferencelist.py @@ -82,7 +82,8 @@ class TestVerseReferenceList(TestCase): # THEN: The current index should be 0 and the end pointer of the entry should be '2' self.assertEqual(reference_list.current_index, 0, 'The current index should be 0') - self.assertEqual(reference_list.verse_list[0]['end'], next_verse, 'The end in first entry should be %u' % next_verse) + self.assertEqual(reference_list.verse_list[0]['end'], next_verse, + 'The end in first entry should be %u' % next_verse) def add_another_verse_test(self): """ @@ -123,8 +124,8 @@ class TestVerseReferenceList(TestCase): # THEN: the data will be appended to the list self.assertEqual(len(reference_list.version_list), 1, 'The version data should be appended') self.assertEqual(reference_list.version_list[0], - {'version': version, 'copyright': copyright_, 'permission': permission}, - 'The version data should be appended') + {'version': version, 'copyright': copyright_, 'permission': permission}, + 'The version data should be appended') def add_existing_version_test(self): """ From 3994669c1b44d76292cf4dc950382f2428959353 Mon Sep 17 00:00:00 2001 From: Raoul Snyman Date: Thu, 3 Oct 2013 21:56:12 +0200 Subject: [PATCH 78/93] Modified the check_dependencies.py script to only require mock if we're using Python 3.2 and earlier. --- scripts/check_dependencies.py | 86 +++++++++++++------ .../openlp_core_lib/test_pluginmanager.py | 2 +- 2 files changed, 61 insertions(+), 27 deletions(-) diff --git a/scripts/check_dependencies.py b/scripts/check_dependencies.py index 607525bf2..0758b30e3 100755 --- a/scripts/check_dependencies.py +++ b/scripts/check_dependencies.py @@ -44,7 +44,7 @@ from distutils.version import LooseVersion try: import nose except ImportError: - pass + nose = None IS_WIN = sys.platform.startswith('win') @@ -90,21 +90,34 @@ MODULES = [ OPTIONAL_MODULES = [ - ('MySQLdb', ' (MySQL support)'), - ('psycopg2', ' (PostgreSQL support)'), - ('nose', ' (testing framework)'), - ('mock', ' (testing module)'), + ('MySQLdb', '(MySQL support)', True), + ('psycopg2', '(PostgreSQL support)', True), + ('nose', '(testing framework)', True), + ('mock', '(testing module)', sys.version_info[1] < 3), ] w = sys.stdout.write def check_vers(version, required, text): + """ + Check the version of a dependency. Returns ``True`` if the version is greater than or equal, or False if less than. + + ``version`` + The actual version of the dependency + + ``required`` + The required version of the dependency + + ``text`` + The dependency's name + """ + space = (27 - len(required) - len(text)) * ' ' if not isinstance(version, str): version = '.'.join(map(str, version)) if not isinstance(required, str): required = '.'.join(map(str, required)) - w(' %s >= %s ... ' % (text, required)) + w(' %s >= %s ... ' % (text, required) + space) if LooseVersion(version) >= LooseVersion(required): w(version + os.linesep) return True @@ -113,6 +126,29 @@ def check_vers(version, required, text): return False +def check_module(mod, text='', indent=' '): + """ + Check that a module is installed. + + ``mod`` + The module to check for. + + ``text`` + The text to display. + + ``indent`` + How much to indent the text by. + """ + space = (31 - len(mod) - len(text)) * ' ' + w(indent + '%s %s... ' % (mod, text) + space) + try: + __import__(mod) + w('OK') + except ImportError: + w('FAIL') + w(os.linesep) + + def print_vers_fail(required, text): print(' %s >= %s ... FAIL' % (text, required)) @@ -143,18 +179,10 @@ def verify_versions(): print_vers_fail(VERS['enchant'], 'enchant') -def check_module(mod, text='', indent=' '): - space = (30 - len(mod) - len(text)) * ' ' - w(indent + '%s%s... ' % (mod, text) + space) - try: - __import__(mod) - w('OK') - except ImportError: - w('FAIL') - w(os.linesep) - - -def verify_pyenchant(): +def print_enchant_backends_and_languages(): + """ + Check if PyEnchant is installed. + """ w('Enchant (spell checker)... ') try: import enchant @@ -167,14 +195,15 @@ def verify_pyenchant(): w('FAIL' + os.linesep) -def verify_pyqt(): +def print_qt_image_formats(): + """ + Print out the image formats that Qt4 supports. + """ w('Qt4 image formats... ') try: from PyQt4 import QtGui - read_f = ', '.join([str(format).lower() - for format in QtGui.QImageReader.supportedImageFormats()]) - write_f = ', '.join([str(format).lower() - for format in QtGui.QImageWriter.supportedImageFormats()]) + read_f = ', '.join([bytes(fmt).decode().lower() for fmt in QtGui.QImageReader.supportedImageFormats()]) + write_f = ', '.join([bytes(fmt).decode().lower() for fmt in QtGui.QImageWriter.supportedImageFormats()]) w(os.linesep) print(' read: %s' % read_f) print(' write: %s' % write_f) @@ -183,20 +212,25 @@ def verify_pyqt(): def main(): + """ + Run the dependency checker. + """ + print('Checking Python version...') verify_python() print('Checking for modules...') for m in MODULES: check_module(m) print('Checking for optional modules...') for m in OPTIONAL_MODULES: - check_module(m[0], text=m[1]) + if m[2]: + check_module(m[0], text=m[1]) if IS_WIN: print('Checking for Windows specific modules...') for m in WIN32_MODULES: check_module(m) verify_versions() - verify_pyqt() - verify_pyenchant() + print_qt_image_formats() + print_enchant_backends_and_languages() if __name__ == '__main__': main() diff --git a/tests/functional/openlp_core_lib/test_pluginmanager.py b/tests/functional/openlp_core_lib/test_pluginmanager.py index 2ab3b2dd7..eb6d80f8c 100644 --- a/tests/functional/openlp_core_lib/test_pluginmanager.py +++ b/tests/functional/openlp_core_lib/test_pluginmanager.py @@ -420,7 +420,7 @@ class TestPluginManager(TestCase): # THEN: The isActive() method should have been called, and initialise() method should NOT have been called mocked_plugin.is_active.assert_called_with() self.assertEqual(0, mocked_plugin.new_service_created.call_count, - 'The new_service_created() method should not have been called.') + 'The new_service_created() method should not have been called.') def new_service_created_with_active_plugin_test(self): """ From 2fe31991e25193c81e4dd94e8d4648c6b2c34cd1 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Fri, 4 Oct 2013 20:27:53 +0100 Subject: [PATCH 79/93] Fixes --- openlp/core/ui/formattingtagcontroller.py | 10 +++++----- openlp/core/ui/formattingtagform.py | 20 +++++++++---------- .../tests_formattingtagscontroller.py | 4 ++-- .../tests_formattingtagsform.py | 6 +++--- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/openlp/core/ui/formattingtagcontroller.py b/openlp/core/ui/formattingtagcontroller.py index 46b463f98..9b891849b 100644 --- a/openlp/core/ui/formattingtagcontroller.py +++ b/openlp/core/ui/formattingtagcontroller.py @@ -77,14 +77,14 @@ class FormattingTagController(object): """ for linenumber, html1 in enumerate(self.protected_tags): - if self._strip(html1[u'start tag']) == tag: + if self._strip(html1['start tag']) == tag: return translate('OpenLP.FormattingTagForm', 'Tag %s already defined.') % tag - if self._strip(html1[u'desc']) == desc: + if self._strip(html1['desc']) == desc: return translate('OpenLP.FormattingTagForm', 'Description %s already defined.') % tag for linenumber, html1 in enumerate(self.custom_tags): - if self._strip(html1[u'start tag']) == tag: + if self._strip(html1['start tag']) == tag: return translate('OpenLP.FormattingTagForm', 'Tag %s already defined.') % tag - if self._strip(html1[u'desc']) == desc: + if self._strip(html1['desc']) == desc: return translate('OpenLP.FormattingTagForm', 'Description %s already defined.') % tag tag = { 'desc': desc, @@ -136,7 +136,7 @@ class FormattingTagController(object): elif not match.group('empty'): end_tags.append(tag) match = self.html_tag_regex.search(start_html, match.end()) - return u''.join(map(lambda tag: '' % tag, reversed(end_tags))) + return ''.join(map(lambda tag: '' % tag, reversed(end_tags))) def start_tag_changed(self, start_html, end_html): """ diff --git a/openlp/core/ui/formattingtagform.py b/openlp/core/ui/formattingtagform.py index da1fef175..c6906fc95 100644 --- a/openlp/core/ui/formattingtagform.py +++ b/openlp/core/ui/formattingtagform.py @@ -141,24 +141,24 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog, FormattingTagCont self.new_button.setEnabled(True) self.delete_button.setEnabled(False) for linenumber, html in enumerate(FormattingTags.get_html_tags()): - if html[u'protected']: + if html['protected']: line = self.tag_table_widget_read.rowCount() self.tag_table_widget_read.setRowCount(line + 1) - self.tag_table_widget_read.setItem(line, 0, QtGui.QTableWidgetItem(html[u'desc'])) - self.tag_table_widget_read.setItem(line, 1, QtGui.QTableWidgetItem(self._strip(html[u'start tag']))) - self.tag_table_widget_read.setItem(line, 2, QtGui.QTableWidgetItem(html[u'start html'])) - self.tag_table_widget_read.setItem(line, 3, QtGui.QTableWidgetItem(html[u'end html'])) + self.tag_table_widget_read.setItem(line, 0, QtGui.QTableWidgetItem(html['desc'])) + self.tag_table_widget_read.setItem(line, 1, QtGui.QTableWidgetItem(self._strip(html['start tag']))) + self.tag_table_widget_read.setItem(line, 2, QtGui.QTableWidgetItem(html['start html'])) + self.tag_table_widget_read.setItem(line, 3, QtGui.QTableWidgetItem(html['end html'])) self.tag_table_widget_read.resizeRowsToContents() else: line = self.tag_table_widget.rowCount() self.tag_table_widget.setRowCount(line + 1) - self.tag_table_widget.setItem(line, 0, QtGui.QTableWidgetItem(html[u'desc'])) - self.tag_table_widget.setItem(line, 1, QtGui.QTableWidgetItem(self._strip(html[u'start tag']))) - self.tag_table_widget.setItem(line, 2, QtGui.QTableWidgetItem(html[u'start html'])) - self.tag_table_widget.setItem(line, 3, QtGui.QTableWidgetItem(html[u'end html'])) + self.tag_table_widget.setItem(line, 0, QtGui.QTableWidgetItem(html['desc'])) + self.tag_table_widget.setItem(line, 1, QtGui.QTableWidgetItem(self._strip(html['start tag']))) + self.tag_table_widget.setItem(line, 2, QtGui.QTableWidgetItem(html['start html'])) + self.tag_table_widget.setItem(line, 3, QtGui.QTableWidgetItem(html['end html'])) self.tag_table_widget.resizeRowsToContents() # Permanent (persistent) tags do not have this key - html[u'temporary'] = False + html['temporary'] = False self.reloading = False def on_current_cell_changed(self, cur_row, cur_col, pre_row, pre_col): diff --git a/tests/functional/openlp_core_ui/tests_formattingtagscontroller.py b/tests/functional/openlp_core_ui/tests_formattingtagscontroller.py index 279dd43f1..3a5fab289 100644 --- a/tests/functional/openlp_core_ui/tests_formattingtagscontroller.py +++ b/tests/functional/openlp_core_ui/tests_formattingtagscontroller.py @@ -16,13 +16,13 @@ class TestFormattingTagController(TestCase): Test that the _strip strips the correct chars """ # GIVEN: An instance of the Formatting Tag Form and a string containing a tag - tag = u'{tag}' + tag = '{tag}' # WHEN: Calling _strip result = self.services._strip(tag) # THEN: The tag should be returned with the wrappers removed. - self.assertEqual(result, u'tag', u'FormattingTagForm._strip should return u\'tag\' when called with u\'{tag}\'') + self.assertEqual(result, 'tag', 'FormattingTagForm._strip should return u\'tag\' when called with u\'{tag}\'') def test_end_tag_changed_processes_correctly(self): """ diff --git a/tests/functional/openlp_core_ui/tests_formattingtagsform.py b/tests/functional/openlp_core_ui/tests_formattingtagsform.py index 7edd861d9..ee863439c 100644 --- a/tests/functional/openlp_core_ui/tests_formattingtagsform.py +++ b/tests/functional/openlp_core_ui/tests_formattingtagsform.py @@ -19,9 +19,9 @@ from openlp.core.ui.formattingtagform import FormattingTagForm class TestFormattingTagForm(TestCase): def setUp(self): - self.init_patcher = patch(u'openlp.core.ui.formattingtagform.FormattingTagForm.__init__') - self.qdialog_patcher = patch(u'openlp.core.ui.formattingtagform.QtGui.QDialog') - self.ui_formatting_tag_dialog_patcher = patch(u'openlp.core.ui.formattingtagform.Ui_FormattingTagDialog') + self.init_patcher = patch('openlp.core.ui.formattingtagform.FormattingTagForm.__init__') + self.qdialog_patcher = patch('openlp.core.ui.formattingtagform.QtGui.QDialog') + self.ui_formatting_tag_dialog_patcher = patch('openlp.core.ui.formattingtagform.Ui_FormattingTagDialog') self.mocked_init = self.init_patcher.start() self.mocked_qdialog = self.qdialog_patcher.start() self.mocked_ui_formatting_tag_dialog = self.ui_formatting_tag_dialog_patcher.start() From 40350aec0f9f4461a9fc89e8c5dd57ef16c7484e Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Fri, 4 Oct 2013 21:36:02 +0100 Subject: [PATCH 80/93] tests updates --- .../tests_formattingtagscontroller.py | 28 +++++++++++++++++ .../tests_formattingtagsform.py | 30 ++++++++++++++++++- 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/tests/functional/openlp_core_ui/tests_formattingtagscontroller.py b/tests/functional/openlp_core_ui/tests_formattingtagscontroller.py index 3a5fab289..dc9495775 100644 --- a/tests/functional/openlp_core_ui/tests_formattingtagscontroller.py +++ b/tests/functional/openlp_core_ui/tests_formattingtagscontroller.py @@ -1,3 +1,31 @@ +# -*- 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 # +############################################################################### """ Package to test the openlp.core.ui.formattingtagscontroller package. """ diff --git a/tests/functional/openlp_core_ui/tests_formattingtagsform.py b/tests/functional/openlp_core_ui/tests_formattingtagsform.py index ee863439c..7dc3d4b6f 100644 --- a/tests/functional/openlp_core_ui/tests_formattingtagsform.py +++ b/tests/functional/openlp_core_ui/tests_formattingtagsform.py @@ -1,9 +1,37 @@ +# -*- 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 # +############################################################################### """ Package to test the openlp.core.ui.formattingtagsform package. """ from unittest import TestCase -from mock import MagicMock, patch +from tests.functional import MagicMock, patch from openlp.core.ui.formattingtagform import FormattingTagForm From cf9ddadf35b024942a94a026c9995b1d2ebe8852 Mon Sep 17 00:00:00 2001 From: "Jeffrey S. Smith" Date: Fri, 4 Oct 2013 16:44:58 -0500 Subject: [PATCH 81/93] Missed removing one line when merging --- .../openlp_plugins/songs/test_worshipcenterproimport.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py b/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py index 102caf98e..836c5340b 100644 --- a/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py +++ b/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py @@ -129,7 +129,6 @@ SONG_TEST_DATA = [{'title': 'Amazing Grace', ('There\'s a garden where\nJesus is waiting,\nAnd He bids you to come,\nmeet Him there;\n' 'Just to bow and\nreceive a new blessing\nIn the beautiful\ngarden of prayer.')]}] -@skipIf(os.name != 'nt', 'WorshipCenter Pro import only supported on Windows') class TestWorshipCenterProSongImport(TestCase): """ Test the functions in the :mod:`worshipcenterproimport` module. From 2b3825eaf0a597fc473a333f31937bb93f93c972 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Fri, 11 Oct 2013 12:13:04 +0200 Subject: [PATCH 82/93] missing imports --- tests/functional/openlp_core_lib/test_serviceitem.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/functional/openlp_core_lib/test_serviceitem.py b/tests/functional/openlp_core_lib/test_serviceitem.py index 60698ed88..6893b8ca3 100644 --- a/tests/functional/openlp_core_lib/test_serviceitem.py +++ b/tests/functional/openlp_core_lib/test_serviceitem.py @@ -36,6 +36,8 @@ from unittest import TestCase from tests.functional import MagicMock, patch from tests.utils import assert_length, convert_file_service_item +from openlp.core.lib import ItemCapabilities, ServiceItem, Registry + VERSE = 'The Lord said to {r}Noah{/r}: \n'\ 'There\'s gonna be a {su}floody{/su}, {sb}floody{/sb}\n'\ From 29bdf095d3972e7acd588c21e210375b17c4bc92 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Fri, 11 Oct 2013 12:15:09 +0200 Subject: [PATCH 83/93] removed import --- tests/functional/openlp_core_lib/test_settings.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/functional/openlp_core_lib/test_settings.py b/tests/functional/openlp_core_lib/test_settings.py index f9c3dd87c..6836ce0e8 100644 --- a/tests/functional/openlp_core_lib/test_settings.py +++ b/tests/functional/openlp_core_lib/test_settings.py @@ -35,8 +35,6 @@ from tempfile import mkstemp from unittest import TestCase from PyQt4 import QtGui -from PyQt4 import QtGui - from openlp.core.lib import Settings From 257fba8548a75e34a8ce2766fcfedf893516c583 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sun, 13 Oct 2013 14:51:13 +0100 Subject: [PATCH 84/93] Start theme clean up --- openlp/core/__init__.py | 3 +- openlp/core/{theme => common}/__init__.py | 29 +- openlp/core/{utils => common}/applocation.py | 26 +- openlp/core/lib/db.py | 3 +- openlp/core/lib/json/theme.json | 49 ++++ openlp/core/lib/pluginmanager.py | 2 +- openlp/core/lib/theme.py | 15 +- openlp/core/theme/theme.py | 252 ------------------ openlp/core/ui/__init__.py | 3 +- openlp/core/ui/advancedtab.py | 3 +- openlp/core/ui/firsttimeform.py | 3 +- openlp/core/ui/mainwindow.py | 4 +- openlp/core/ui/media/mediacontroller.py | 2 +- openlp/core/ui/printserviceform.py | 2 +- openlp/core/ui/servicemanager.py | 3 +- openlp/core/ui/thememanager.py | 150 +++-------- openlp/core/ui/thememanagerhelper.py | 38 +++ openlp/core/utils/__init__.py | 19 +- openlp/core/utils/languagemanager.py | 2 +- .../plugins/bibles/forms/bibleimportform.py | 3 +- .../plugins/bibles/forms/bibleupgradeform.py | 3 +- openlp/plugins/bibles/lib/db.py | 3 +- openlp/plugins/bibles/lib/manager.py | 3 +- openlp/plugins/bibles/lib/osis.py | 2 +- openlp/plugins/images/lib/mediaitem.py | 3 +- openlp/plugins/media/lib/mediaitem.py | 3 +- .../lib/presentationcontroller.py | 2 +- .../presentations/presentationplugin.py | 2 +- openlp/plugins/remotes/lib/httprouter.py | 4 +- openlp/plugins/remotes/lib/httpserver.py | 3 +- openlp/plugins/remotes/lib/remotetab.py | 3 +- .../songs/forms/duplicatesongremovalform.py | 2 +- openlp/plugins/songs/forms/editsongform.py | 2 +- openlp/plugins/songs/lib/__init__.py | 3 +- openlp/plugins/songs/lib/mediaitem.py | 2 +- openlp/plugins/songs/lib/songimport.py | 2 +- .../openlp_core_utils/test_applocation.py | 2 +- .../openlp_plugins/remotes/test_remotetab.py | 12 +- 38 files changed, 229 insertions(+), 438 deletions(-) rename openlp/core/{theme => common}/__init__.py (75%) rename openlp/core/{utils => common}/applocation.py (88%) create mode 100644 openlp/core/lib/json/theme.json delete mode 100644 openlp/core/theme/theme.py create mode 100644 openlp/core/ui/thememanagerhelper.py diff --git a/openlp/core/__init__.py b/openlp/core/__init__.py index f86f9036f..a814ae04c 100644 --- a/openlp/core/__init__.py +++ b/openlp/core/__init__.py @@ -43,6 +43,7 @@ from traceback import format_exception from PyQt4 import QtCore, QtGui +from openlp.core.common import AppLocation from openlp.core.lib import Settings, ScreenList, UiStrings, Registry, check_directory_exists from openlp.core.resources import qInitResources from openlp.core.ui.mainwindow import MainWindow @@ -50,7 +51,7 @@ from openlp.core.ui.firsttimelanguageform import FirstTimeLanguageForm from openlp.core.ui.firsttimeform import FirstTimeForm from openlp.core.ui.exceptionform import ExceptionForm from openlp.core.ui import SplashScreen -from openlp.core.utils import AppLocation, LanguageManager, VersionThread, get_application_version +from openlp.core.utils import LanguageManager, VersionThread, get_application_version __all__ = ['OpenLP', 'main'] diff --git a/openlp/core/theme/__init__.py b/openlp/core/common/__init__.py similarity index 75% rename from openlp/core/theme/__init__.py rename to openlp/core/common/__init__.py index 4db399fa7..e2c95b6cc 100644 --- a/openlp/core/theme/__init__.py +++ b/openlp/core/common/__init__.py @@ -27,10 +27,31 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### """ -The :mod:`~openlp.core.theme` module contains all the themeing functions used by -OpenLP when displaying a song or a scripture. +The :mod:`common` module contains most of the components and libraries that make +OpenLP work. """ +import os +import logging -from openlp.core.theme.theme import Theme +log = logging.getLogger(__name__) -__all__ = ['Theme'] + +def check_directory_exists(directory, do_not_log=False): + """ + Check a theme directory exists and if not create it + + ``directory`` + The directory to make sure exists + + ``do_not_log`` + To not log anything. This is need for the start up, when the log isn't ready. + """ + if not do_not_log: + log.debug('check_directory_exists %s' % directory) + try: + if not os.path.exists(directory): + os.makedirs(directory) + except IOError: + pass + +from .applocation import AppLocation \ No newline at end of file diff --git a/openlp/core/utils/applocation.py b/openlp/core/common/applocation.py similarity index 88% rename from openlp/core/utils/applocation.py rename to openlp/core/common/applocation.py index 726cbbeda..7d0010316 100644 --- a/openlp/core/utils/applocation.py +++ b/openlp/core/common/applocation.py @@ -27,16 +27,12 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### """ -The :mod:`openlp.core.utils.applocation` module provides an utility for OpenLP receiving the data path etc. +The :mod:`openlp.core.common.applocation` module provides an utility for OpenLP receiving the data path etc. """ import logging import os import sys -from openlp.core.lib import Settings -from openlp.core.utils import _get_frozen_path - - if sys.platform != 'win32' and sys.platform != 'darwin': try: from xdg import BaseDirectory @@ -45,7 +41,7 @@ if sys.platform != 'win32' and sys.platform != 'darwin': XDG_BASE_AVAILABLE = False import openlp -from openlp.core.lib import check_directory_exists +from openlp.core.common import check_directory_exists log = logging.getLogger(__name__) @@ -74,15 +70,15 @@ class AppLocation(object): The directory type you want, for instance the data directory. Default *AppLocation.AppDir* """ if dir_type == AppLocation.AppDir: - return _get_frozen_path(os.path.abspath(os.path.split(sys.argv[0])[0]), os.path.split(openlp.__file__)[0]) + return get_frozen_path(os.path.abspath(os.path.split(sys.argv[0])[0]), os.path.split(openlp.__file__)[0]) elif dir_type == AppLocation.PluginsDir: app_path = os.path.abspath(os.path.split(sys.argv[0])[0]) - return _get_frozen_path(os.path.join(app_path, 'plugins'), + return get_frozen_path(os.path.join(app_path, 'plugins'), os.path.join(os.path.split(openlp.__file__)[0], 'plugins')) elif dir_type == AppLocation.VersionDir: - return _get_frozen_path(os.path.abspath(os.path.split(sys.argv[0])[0]), os.path.split(openlp.__file__)[0]) + return get_frozen_path(os.path.abspath(os.path.split(sys.argv[0])[0]), os.path.split(openlp.__file__)[0]) elif dir_type == AppLocation.LanguageDir: - app_path = _get_frozen_path(os.path.abspath(os.path.split(sys.argv[0])[0]), _get_os_dir_path(dir_type)) + app_path = get_frozen_path(os.path.abspath(os.path.split(sys.argv[0])[0]), _get_os_dir_path(dir_type)) return os.path.join(app_path, 'i18n') elif dir_type == AppLocation.DataDir and AppLocation.BaseDir: return os.path.join(AppLocation.BaseDir, 'data') @@ -95,6 +91,7 @@ class AppLocation(object): Return the path OpenLP stores all its data under. """ # Check if we have a different data location. + from openlp.core.lib import Settings if Settings().contains('advanced/data path'): path = Settings().value('advanced/data path') else: @@ -139,6 +136,15 @@ class AppLocation(object): return path +def get_frozen_path(frozen_option, non_frozen_option): + """ + Return a path based on the system status. + """ + if hasattr(sys, 'frozen') and sys.frozen == 1: + return frozen_option + return non_frozen_option + + def _get_os_dir_path(dir_type): """ Return a path based on which OS and environment we are running in. diff --git a/openlp/core/lib/db.py b/openlp/core/lib/db.py index 19cde98eb..0fff6e871 100644 --- a/openlp/core/lib/db.py +++ b/openlp/core/lib/db.py @@ -41,9 +41,10 @@ from sqlalchemy.pool import NullPool from alembic.migration import MigrationContext from alembic.operations import Operations +from openlp.core.common import AppLocation from openlp.core.lib import translate, Settings from openlp.core.lib.ui import critical_error_message_box -from openlp.core.utils import AppLocation, delete_file +from openlp.core.utils import delete_file log = logging.getLogger(__name__) diff --git a/openlp/core/lib/json/theme.json b/openlp/core/lib/json/theme.json new file mode 100644 index 000000000..1050458d8 --- /dev/null +++ b/openlp/core/lib/json/theme.json @@ -0,0 +1,49 @@ +{ + "background_border_color": "#000000", + "background_color": "#000000", + "background_direction": "vertical", + "background_end_color": "#000000", + "background_filename": "", + "background_start_color": "#000000", + "background_type": "solid", + "display_horizontal_align": 0, + "display_slide_transition": false, + "display_vertical_align": 0, + "font_footer_bold": false, + "font_footer_color": "#FFFFFF", + "font_footer_height": 78, + "font_footer_italics": false, + "font_footer_line_adjustment": 0, + "font_footer_location": "", + "font_footer_name": "Arial", + "font_footer_outline": false, + "font_footer_outline_color": "#000000", + "font_footer_outline_size": 2, + "font_footer_override": false, + "font_footer_shadow": true, + "font_footer_shadow_color": "#000000", + "font_footer_shadow_size": 5, + "font_footer_size": 12, + "font_footer_width": 1004, + "font_footer_x": 10, + "font_footer_y": 690, + "font_main_bold": false, + "font_main_color": "#FFFFFF", + "font_main_height": 690, + "font_main_italics": false, + "font_main_line_adjustment": 0, + "font_main_location": "", + "font_main_name": "Arial", + "font_main_outline": false, + "font_main_outline_color": "#000000", + "font_main_outline_size": 2, + "font_main_override": false, + "font_main_shadow": true, + "font_main_shadow_color": "#000000", + "font_main_shadow_size": 5, + "font_main_size": 40, + "font_main_width": 1004, + "font_main_x": 10, + "font_main_y": 10, + "theme_name": "" +} \ No newline at end of file diff --git a/openlp/core/lib/pluginmanager.py b/openlp/core/lib/pluginmanager.py index 7b385d140..af7126535 100644 --- a/openlp/core/lib/pluginmanager.py +++ b/openlp/core/lib/pluginmanager.py @@ -35,7 +35,7 @@ import logging import imp from openlp.core.lib import Plugin, PluginStatus, Registry -from openlp.core.utils import AppLocation +from openlp.core.common import AppLocation log = logging.getLogger(__name__) diff --git a/openlp/core/lib/theme.py b/openlp/core/lib/theme.py index 28c0bcc91..d6e67a745 100644 --- a/openlp/core/lib/theme.py +++ b/openlp/core/lib/theme.py @@ -32,11 +32,13 @@ Provide the theme XML and handling functions for OpenLP v2 themes. import os import re import logging +import json from xml.dom.minidom import Document from lxml import etree, objectify +from openlp.core.common import AppLocation -from openlp.core.lib import str_to_bool, ScreenList +from openlp.core.lib import str_to_bool, ScreenList, get_text_file_string log = logging.getLogger(__name__) @@ -202,6 +204,8 @@ class VerticalType(object): BOOLEAN_LIST = ['bold', 'italics', 'override', 'outline', 'shadow', 'slide_transition'] +BOOLEAN_LIST2 = ['True', 'False'] + INTEGER_LIST = ['size', 'line_adjustment', 'x', 'height', 'y', 'width', 'shadow_size', 'outline_size', 'horizontal_align', 'vertical_align', 'wrap_style'] @@ -218,8 +222,12 @@ class ThemeXML(object): Initialise the theme object. """ # Create the minidom document - self.theme_xml = Document() - self.parse_xml(BLANK_THEME_XML) + json_dir = os.path.join(AppLocation.get_directory(AppLocation.AppDir), 'core', 'lib', 'json') + json_file = os.path.join(json_dir, 'theme.json') + jsn = get_text_file_string(json_file) + jsn = json.loads(jsn) + for key, value in jsn.items(): + setattr(self, key, value) def extend_image_filename(self, path): """ @@ -559,6 +567,7 @@ class ThemeXML(object): """ Create the attributes with the correct data types and name format """ + #print(master, element, value) reject, master, element, value = self._translate_tags(master, element, value) if reject: return diff --git a/openlp/core/theme/theme.py b/openlp/core/theme/theme.py deleted file mode 100644 index 7bb581512..000000000 --- a/openlp/core/theme/theme.py +++ /dev/null @@ -1,252 +0,0 @@ -# -*- 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 # -############################################################################### -""" -OpenLP version 1 theme handling - -Provides reference data, a default v1 XML theme and class wrapper for -processing version 1 themes in OpenLP version 2. -""" - -from xml.etree.ElementTree import ElementTree, XML -from PyQt4 import QtGui - -DELPHI_COLORS = { - 'clAqua': 0x00FFFF, - 'clBlack': 0x000000, - 'clBlue': 0x0000FF, - 'clFuchsia': 0xFF00FF, - 'clGray': 0x808080, - 'clGreen': 0x008000, - 'clLime': 0x00FF00, - 'clMaroon': 0x800000, - 'clNavy': 0x000080, - 'clOlive': 0x808000, - 'clPurple': 0x800080, - 'clRed': 0xFF0000, - 'clSilver': 0xC0C0C0, - 'clTeal': 0x008080, - 'clWhite': 0xFFFFFF, - 'clYellow': 0xFFFF00 -} - -BLANK_STYLE_XML = \ -''' - - BlankStyle - 1 - 0 - $000000 - - - Arial - clWhite - 30 - pixels - 0 - 0 - 0 - 0 - 0 - -''' - - -class Theme(object): - """ - Provide a class wrapper storing data from an XML theme - - ``name`` - Theme name - - ``BackgroundMode`` - The behaviour of the background. Valid modes are: - - * ``0`` - Transparent - * ``1`` - Opaque - - ``BackgroundType`` - The content of the background. Valid types are: - - * ``0`` - solid color - * ``1`` - gradient color - * ``2`` - image - - ``BackgroundParameter1`` - Extra information about the background. The contents of this attribute - depend on the BackgroundType: - - * ``image`` - image filename - * ``gradient`` - start color - * ``solid`` - color - - ``BackgroundParameter2`` - Extra information about the background. The contents of this attribute - depend on the BackgroundType: - - * ``image`` - border color - * ``gradient`` - end color - * ``solid`` - N/A - - ``BackgroundParameter3`` - Extra information about the background. The contents of this attribute - depend on the BackgroundType: - - * ``image`` - N/A - * ``gradient`` - The direction of the gradient. Valid entries are: - - * ``0`` - vertical - * ``1`` - horizontal - - * ``solid`` - N/A - - ``FontName`` - Name of the font to use for the main font. - - ``FontColor`` - The color for the main font - - ``FontProportion`` - The size of the main font - - ``FontUnits`` - The units for FontProportion, either or - - ``Shadow`` - The shadow type to apply to the main font. - - * ``0`` - no shadow - * non-zero - use shadow - - ``ShadowColor`` - Color for the shadow - - ``Outline`` - The outline to apply to the main font - - * ``0`` - no outline - * non-zero - use outline - - ``OutlineColor`` - Color for the outline (or None if Outline is 0) - - ``HorizontalAlign`` - The horizontal alignment to apply to text. Valid alignments are: - - * ``0`` - left align - * ``1`` - right align - * ``2`` - centre align - - ``VerticalAlign`` - The vertical alignment to apply to the text. Valid alignments are: - - * ``0`` - top align - * ``1`` - bottom align - * ``2`` - centre align - - ``WrapStyle`` - The wrap style to apply to the text. Valid styles are: - - * ``0`` - normal - * ``1`` - lyrics - """ - - def __init__(self, xml): - """ - Initialise a theme with data from xml - - ``xml`` - The data to initialise the theme with - """ - # init to defaults - self._set_from_xml(BLANK_STYLE_XML) - self._set_from_xml(xml) - - def _get_as_string(self): - """ - Return single line string representation of a theme - """ - theme_strings = [] - keys = dir(self) - keys.sort() - for key in keys: - if key[0:1] != '_': - theme_strings.append('_%s_' % (getattr(self, key))) - return ''.join(theme_strings) - - def _set_from_xml(self, xml): - """ - Set theme class attributes with data from XML - - ``xml`` - The data to apply to the theme - """ - root = ElementTree(element=XML(xml.encode('ascii', 'xmlcharrefreplace'))) - xml_iter = root.getiterator() - for element in xml_iter: - delphi_color_change = False - if element.tag != 'Theme': - element_text = element.text - val = 0 - if element_text is None: - val = element_text - # strings need special handling to sort the colours out - if isinstance(element_text, str): - if element_text[0] == '$': - # might be a hex number - try: - val = int(element_text[1:], 16) - except ValueError: - # nope - pass - elif element_text in DELPHI_COLORS: - val = DELPHI_COLORS[element_text] - delphi_color_change = True - else: - try: - val = int(element_text) - except ValueError: - val = element_text - if (element.tag.find('Color') > 0 or (element.tag.find('BackgroundParameter') == 0 and - isinstance(val, int))): - # convert to a wx.Colour - if not delphi_color_change: - val = QtGui.QColor(val & 0xFF, (val >> 8) & 0xFF, (val >> 16) & 0xFF) - else: - val = QtGui.QColor((val >> 16) & 0xFF, (val >> 8) & 0xFF, val & 0xFF) - setattr(self, element.tag, val) - - def __str__(self): - """ - Provide Python string representation for the class (multiline output) - """ - theme_strings = [] - for key in dir(self): - if key[0:1] != '_': - theme_strings.append('%30s : %s' % (key, getattr(self, key))) - return '\n'.join(theme_strings) diff --git a/openlp/core/ui/__init__.py b/openlp/core/ui/__init__.py index 410a8fc16..691736c8f 100644 --- a/openlp/core/ui/__init__.py +++ b/openlp/core/ui/__init__.py @@ -99,10 +99,11 @@ from .formattingtagcontroller import FormattingTagController from .shortcutlistform import ShortcutListForm from .mediadockmanager import MediaDockManager from .servicemanager import ServiceManager +from .thememanagerhelper import ThemeManagerHelper from .thememanager import ThemeManager __all__ = ['SplashScreen', 'AboutForm', 'SettingsForm', 'MainDisplay', 'SlideController', 'ServiceManager', 'ThemeManager', 'MediaDockManager', 'ServiceItemEditForm', 'FirstTimeForm', 'FirstTimeLanguageForm', 'ThemeForm', 'ThemeLayoutForm', 'FileRenameForm', 'StartTimeForm', 'MainDisplay', 'Display', 'ServiceNoteForm', 'SlideController', 'DisplayController', 'GeneralTab', 'ThemesTab', 'AdvancedTab', 'PluginForm', - 'FormattingTagForm', 'ShortcutListForm', 'FormattingTagController'] + 'FormattingTagForm', 'ShortcutListForm', 'FormattingTagController', 'ThemeManagerHelper'] diff --git a/openlp/core/ui/advancedtab.py b/openlp/core/ui/advancedtab.py index 92f565a71..c61432c57 100644 --- a/openlp/core/ui/advancedtab.py +++ b/openlp/core/ui/advancedtab.py @@ -36,8 +36,9 @@ import sys from PyQt4 import QtCore, QtGui +from openlp.core.common.applocation import AppLocation from openlp.core.lib import SettingsTab, Settings, UiStrings, translate, build_icon -from openlp.core.utils import AppLocation, format_time, get_images_filter +from openlp.core.utils import format_time, get_images_filter from openlp.core.lib import SlideLimits log = logging.getLogger(__name__) diff --git a/openlp/core/ui/firsttimeform.py b/openlp/core/ui/firsttimeform.py index 509316658..182894037 100644 --- a/openlp/core/ui/firsttimeform.py +++ b/openlp/core/ui/firsttimeform.py @@ -41,8 +41,9 @@ from configparser import SafeConfigParser from PyQt4 import QtCore, QtGui +from openlp.core.common.applocation import AppLocation from openlp.core.lib import PluginStatus, Settings, Registry, build_icon, check_directory_exists, translate -from openlp.core.utils import AppLocation, get_web_page +from openlp.core.utils import get_web_page from .firsttimewizard import Ui_FirstTimeWizard, FirstTimePage log = logging.getLogger(__name__) diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index edd898be1..37d215c85 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -46,8 +46,10 @@ from openlp.core.lib import Renderer, OpenLPDockWidget, PluginManager, ImageMana from openlp.core.lib.ui import UiStrings, create_action from openlp.core.ui import AboutForm, SettingsForm, ServiceManager, ThemeManager, SlideController, PluginForm, \ MediaDockManager, ShortcutListForm, FormattingTagForm + +from openlp.core.common.applocation import AppLocation from openlp.core.ui.media import MediaController -from openlp.core.utils import AppLocation, LanguageManager, add_actions, get_application_version +from openlp.core.utils import LanguageManager, add_actions, get_application_version from openlp.core.utils.actions import ActionList, CategoryOrder from openlp.core.ui.firsttimeform import FirstTimeForm diff --git a/openlp/core/ui/media/mediacontroller.py b/openlp/core/ui/media/mediacontroller.py index b1fa43ea9..83b9630fc 100644 --- a/openlp/core/ui/media/mediacontroller.py +++ b/openlp/core/ui/media/mediacontroller.py @@ -39,7 +39,7 @@ from openlp.core.lib import OpenLPToolbar, Settings, Registry, UiStrings, transl from openlp.core.lib.ui import critical_error_message_box from openlp.core.ui.media import MediaState, MediaInfo, MediaType, get_media_players, set_media_players from openlp.core.ui.media.mediaplayer import MediaPlayer -from openlp.core.utils import AppLocation +from openlp.core.common import AppLocation from openlp.core.ui import DisplayControllerType log = logging.getLogger(__name__) diff --git a/openlp/core/ui/printserviceform.py b/openlp/core/ui/printserviceform.py index 660b708d8..153b6bed9 100644 --- a/openlp/core/ui/printserviceform.py +++ b/openlp/core/ui/printserviceform.py @@ -38,7 +38,7 @@ from lxml import html from openlp.core.lib import Settings, UiStrings, Registry, translate, get_text_file_string from openlp.core.ui.printservicedialog import Ui_PrintServiceDialog, ZoomSize -from openlp.core.utils import AppLocation +from openlp.core.common import AppLocation DEFAULT_CSS = """/* Edit this file to customize the service order print. Note, that not all CSS diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index c00f4159b..7610b8dcb 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -42,13 +42,14 @@ log = logging.getLogger(__name__) from PyQt4 import QtCore, QtGui +from openlp.core.common.applocation import AppLocation from openlp.core.lib import OpenLPToolbar, ServiceItem, ItemCapabilities, Settings, PluginStatus, Registry, \ UiStrings, build_icon, translate, str_to_bool, check_directory_exists from openlp.core.lib.theme import ThemeLevel from openlp.core.lib.ui import critical_error_message_box, create_widget_action, find_and_set_in_combo_box from openlp.core.ui import ServiceNoteForm, ServiceItemEditForm, StartTimeForm from openlp.core.ui.printserviceform import PrintServiceForm -from openlp.core.utils import AppLocation, delete_file, split_filename, format_time +from openlp.core.utils import delete_file, split_filename, format_time from openlp.core.utils.actions import ActionList, CategoryOrder diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py index cbc6701df..2b2f6f855 100644 --- a/openlp/core/ui/thememanager.py +++ b/openlp/core/ui/thememanager.py @@ -38,18 +38,18 @@ import re from xml.etree.ElementTree import ElementTree, XML from PyQt4 import QtCore, QtGui +from openlp.core.common.applocation import AppLocation from openlp.core.lib import ImageSource, OpenLPToolbar, Registry, Settings, UiStrings, get_text_file_string, \ build_icon, translate, check_item_selected, check_directory_exists, create_thumb, validate_thumb -from openlp.core.lib.theme import ThemeXML, BackgroundType, VerticalType, BackgroundGradientType +from openlp.core.lib.theme import ThemeXML, BackgroundType from openlp.core.lib.ui import critical_error_message_box, create_widget_action -from openlp.core.theme import Theme -from openlp.core.ui import FileRenameForm, ThemeForm -from openlp.core.utils import AppLocation, delete_file, get_locale_key, get_filesystem_encoding +from openlp.core.ui import FileRenameForm, ThemeForm, ThemeManagerHelper +from openlp.core.utils import delete_file, get_locale_key, get_filesystem_encoding log = logging.getLogger(__name__) -class ThemeManager(QtGui.QWidget): +class ThemeManager(QtGui.QWidget, ThemeManagerHelper): """ Manages the orders of Theme. """ @@ -328,8 +328,8 @@ class ThemeManager(QtGui.QWidget): try: encoding = get_filesystem_encoding() shutil.rmtree(os.path.join(self.path, theme).encode(encoding)) - except OSError as xxx_todo_changeme1: - shutil.Error = xxx_todo_changeme1 + except OSError as os_error: + shutil.Error = os_error log.exception('Error deleting theme %s', theme) def on_export_theme(self): @@ -469,7 +469,7 @@ class ThemeManager(QtGui.QWidget): log.debug('No theme data - using default theme') return ThemeXML() else: - return self._create_theme_fom_Xml(xml, self.path) + return self._create_theme_from_Xml(xml, self.path) def over_write_message_box(self, theme_name): """ @@ -501,35 +501,30 @@ class ThemeManager(QtGui.QWidget): log.exception('Theme contains "%s" XML files' % len(xml_file)) raise Exception('validation') xml_tree = ElementTree(element=XML(theme_zip.read(xml_file[0]))).getroot() - v1_background = xml_tree.find('BackgroundType') - if v1_background is not None: - theme_name, file_xml, out_file, abort_import = \ - self.unzip_version_122(directory, theme_zip, xml_file[0], xml_tree, v1_background, out_file) + theme_name = xml_tree.find('name').text.strip() + theme_folder = os.path.join(directory, theme_name) + theme_exists = os.path.exists(theme_folder) + if theme_exists and not self.over_write_message_box(theme_name): + abort_import = True + return else: - theme_name = xml_tree.find('name').text.strip() - theme_folder = os.path.join(directory, theme_name) - theme_exists = os.path.exists(theme_folder) - if theme_exists and not self.over_write_message_box(theme_name): - abort_import = True - return + abort_import = False + for name in theme_zip.namelist(): + name = name.replace('/', os.path.sep) + split_name = name.split(os.path.sep) + if split_name[-1] == '' or len(split_name) == 1: + # is directory or preview file + continue + full_name = os.path.join(directory, name) + check_directory_exists(os.path.dirname(full_name)) + if os.path.splitext(name)[1].lower() == '.xml': + file_xml = str(theme_zip.read(name), 'utf-8') + out_file = open(full_name, 'w') + out_file.write(file_xml) else: - abort_import = False - for name in theme_zip.namelist(): - name = name.replace('/', os.path.sep) - split_name = name.split(os.path.sep) - if split_name[-1] == '' or len(split_name) == 1: - # is directory or preview file - continue - full_name = os.path.join(directory, name) - check_directory_exists(os.path.dirname(full_name)) - if os.path.splitext(name)[1].lower() == '.xml': - file_xml = str(theme_zip.read(name), 'utf-8') - out_file = open(full_name, 'w') - out_file.write(file_xml) - else: - out_file = open(full_name, 'wb') - out_file.write(theme_zip.read(name)) - out_file.close() + out_file = open(full_name, 'wb') + out_file.write(theme_zip.read(name)) + out_file.close() except (IOError, zipfile.BadZipfile): log.exception('Importing theme from zip failed %s' % file_name) raise Exception('validation') @@ -548,7 +543,7 @@ class ThemeManager(QtGui.QWidget): if not abort_import: # As all files are closed, we can create the Theme. if file_xml: - theme = self._create_theme_fom_Xml(file_xml, self.path) + theme = self._create_theme_from_Xml(file_xml, self.path) self.generate_and_save_image(directory, theme_name, theme) # Only show the error message, when IOError was not raised (in # this case the error message has already been shown). @@ -558,38 +553,6 @@ class ThemeManager(QtGui.QWidget): translate('OpenLP.ThemeManager', 'File is not a valid theme.')) log.exception('Theme file does not contain XML data %s' % file_name) - def unzip_version_122(self, dir_name, zip_file, xml_file, xml_tree, background, out_file): - """ - Unzip openlp.org 1.2x theme file and upgrade the theme xml. When calling - this method, please keep in mind, that some parameters are redundant. - """ - theme_name = xml_tree.find('Name').text.strip() - theme_name = self.bad_v1_name_chars.sub('', theme_name) - theme_folder = os.path.join(dir_name, theme_name) - theme_exists = os.path.exists(theme_folder) - if theme_exists and not self.over_write_message_box(theme_name): - return '', '', '', True - themedir = os.path.join(dir_name, theme_name) - check_directory_exists(themedir) - file_xml = str(zip_file.read(xml_file), 'utf-8') - file_xml = self._migrate_version_122(file_xml) - out_file = open(os.path.join(themedir, theme_name + '.xml'), 'w') - out_file.write(file_xml.encode('utf-8')) - out_file.close() - if background.text.strip() == '2': - image_name = xml_tree.find('BackgroundParameter1').text.strip() - # image file has same extension and is in subfolder - image_file = [name for name in zip_file.namelist() if os.path.splitext(name)[1].lower() - == os.path.splitext(image_name)[1].lower() and name.find(r'/')] - if len(image_file) >= 1: - out_file = open(os.path.join(themedir, image_name), 'wb') - out_file.write(zip_file.read(image_file[0])) - out_file.close() - else: - log.exception('Theme file does not contain image file "%s"' % image_name.decode('utf-8', 'replace')) - raise Exception('validation') - return theme_name, file_xml, out_file, False - def check_if_theme_exists(self, theme_name): """ Check if theme already exists and displays error message @@ -697,7 +660,7 @@ class ThemeManager(QtGui.QWidget): image = os.path.join(self.path, theme + '.png') return image - def _create_theme_fom_Xml(self, theme_xml, path): + def _create_theme_from_Xml(self, theme_xml, path): """ Return a theme object using information parsed from XML @@ -741,55 +704,6 @@ class ThemeManager(QtGui.QWidget): return True return False - def _migrate_version_122(self, xml_data): - """ - Convert the xml data from version 1 format to the current format. - - New fields are loaded with defaults to provide a complete, working - theme containing all compatible customisations from the old theme. - - ``xml_data`` - Version 1 theme to convert - """ - theme = Theme(xml_data) - new_theme = ThemeXML() - new_theme.theme_name = self.bad_v1_name_chars.sub('', theme.Name) - if theme.BackgroundType == BackgroundType.Solid: - new_theme.background_type = BackgroundType.to_string(BackgroundType.Solid) - new_theme.background_color = str(theme.BackgroundParameter1.name()) - elif theme.BackgroundType == BackgroundType.Horizontal: - new_theme.background_type = BackgroundType.to_string(BackgroundType.Gradient) - new_theme.background_direction = BackgroundGradientType.to_string(BackgroundGradientType.Horizontal) - if theme.BackgroundParameter3.name() == 1: - new_theme.background_direction = BackgroundGradientType.to_string(BackgroundGradientType.Horizontal) - new_theme.background_start_color = str(theme.BackgroundParameter1.name()) - new_theme.background_end_color = str(theme.BackgroundParameter2.name()) - elif theme.BackgroundType == BackgroundType.Image: - new_theme.background_type = BackgroundType.to_string(BackgroundType.Image) - new_theme.background_filename = str(theme.BackgroundParameter1) - elif theme.BackgroundType == BackgroundType.Transparent: - new_theme.background_type = BackgroundType.to_string(BackgroundType.Transparent) - new_theme.font_main_name = theme.FontName - new_theme.font_main_color = str(theme.FontColor.name()) - new_theme.font_main_size = theme.FontProportion * 3 - new_theme.font_footer_name = theme.FontName - new_theme.font_footer_color = str(theme.FontColor.name()) - new_theme.font_main_shadow = False - if theme.Shadow == 1: - new_theme.font_main_shadow = True - new_theme.font_main_shadow_color = str(theme.ShadowColor.name()) - if theme.Outline == 1: - new_theme.font_main_outline = True - new_theme.font_main_outline_color = str(theme.OutlineColor.name()) - vAlignCorrection = VerticalType.Top - if theme.VerticalAlign == 2: - vAlignCorrection = VerticalType.Middle - elif theme.VerticalAlign == 1: - vAlignCorrection = VerticalType.Bottom - new_theme.display_horizontal_align = theme.HorizontalAlign - new_theme.display_vertical_align = vAlignCorrection - return new_theme.extract_xml() - def _get_renderer(self): """ Adds the Renderer to the class dynamically diff --git a/openlp/core/ui/thememanagerhelper.py b/openlp/core/ui/thememanagerhelper.py new file mode 100644 index 000000000..8fa02bde5 --- /dev/null +++ b/openlp/core/ui/thememanagerhelper.py @@ -0,0 +1,38 @@ +# -*- 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 Theme Controller helps manages adding, deleteing and modifying of themes. +""" + + +class ThemeManagerHelper(object): + """ + Manages the non ui theme functions. + """ + pass \ No newline at end of file diff --git a/openlp/core/utils/__init__.py b/openlp/core/utils/__init__.py index d2e664e75..d07153a2a 100644 --- a/openlp/core/utils/__init__.py +++ b/openlp/core/utils/__init__.py @@ -37,10 +37,13 @@ import os import re from subprocess import Popen, PIPE import sys -import urllib.request, urllib.error, urllib.parse +import urllib.request +import urllib.error +import urllib.parse from PyQt4 import QtGui, QtCore +from openlp.core.common import AppLocation from openlp.core.lib import Registry, Settings @@ -81,15 +84,6 @@ class VersionThread(QtCore.QThread): Registry().execute('openlp_version_check', '%s' % version) -def _get_frozen_path(frozen_option, non_frozen_option): - """ - Return a path based on the system status. - """ - if hasattr(sys, 'frozen') and sys.frozen == 1: - return frozen_option - return non_frozen_option - - def get_application_version(): """ Returns the application version of the running instance of OpenLP:: @@ -418,18 +412,17 @@ def get_natural_key(string): """ key = DIGITS_OR_NONDIGITS.findall(string) key = [int(part) if part.isdigit() else get_locale_key(part) for part in key] - # Python 3 does not support comparision of different types anymore. So make sure, that we do not compare str + # Python 3 does not support comparison of different types anymore. So make sure, that we do not compare str # and int. if string[0].isdigit(): return [b''] + key return key -from .applocation import AppLocation from .languagemanager import LanguageManager from .actions import ActionList -__all__ = ['AppLocation', 'ActionList', 'LanguageManager', 'get_application_version', 'check_latest_version', +__all__ = ['ActionList', 'LanguageManager', 'get_application_version', 'check_latest_version', 'add_actions', 'get_filesystem_encoding', 'get_web_page', 'get_uno_command', 'get_uno_instance', 'delete_file', 'clean_filename', 'format_time', 'get_locale_key', 'get_natural_key'] diff --git a/openlp/core/utils/languagemanager.py b/openlp/core/utils/languagemanager.py index ed58cc037..d6b3cd668 100644 --- a/openlp/core/utils/languagemanager.py +++ b/openlp/core/utils/languagemanager.py @@ -35,7 +35,7 @@ import sys from PyQt4 import QtCore, QtGui -from openlp.core.utils import AppLocation +from openlp.core.common import AppLocation from openlp.core.lib import Settings, translate log = logging.getLogger(__name__) diff --git a/openlp/plugins/bibles/forms/bibleimportform.py b/openlp/plugins/bibles/forms/bibleimportform.py index 459fcbe8b..a4c275005 100644 --- a/openlp/plugins/bibles/forms/bibleimportform.py +++ b/openlp/plugins/bibles/forms/bibleimportform.py @@ -34,11 +34,12 @@ import os from PyQt4 import QtCore, QtGui +from openlp.core.common.applocation import AppLocation from openlp.core.lib import Settings, UiStrings, translate from openlp.core.lib.db import delete_database from openlp.core.lib.ui import critical_error_message_box from openlp.core.ui.wizard import OpenLPWizard, WizardStrings -from openlp.core.utils import AppLocation, get_locale_key +from openlp.core.utils import get_locale_key from openlp.plugins.bibles.lib.manager import BibleFormat from openlp.plugins.bibles.lib.db import BiblesResourcesDB, clean_filename diff --git a/openlp/plugins/bibles/forms/bibleupgradeform.py b/openlp/plugins/bibles/forms/bibleupgradeform.py index 299387e67..fc3508d8e 100644 --- a/openlp/plugins/bibles/forms/bibleupgradeform.py +++ b/openlp/plugins/bibles/forms/bibleupgradeform.py @@ -36,10 +36,11 @@ from tempfile import gettempdir from PyQt4 import QtCore, QtGui +from openlp.core.common.applocation import AppLocation from openlp.core.lib import Registry, Settings, UiStrings, translate, check_directory_exists from openlp.core.lib.ui import critical_error_message_box from openlp.core.ui.wizard import OpenLPWizard, WizardStrings -from openlp.core.utils import AppLocation, delete_file +from openlp.core.utils import delete_file from openlp.plugins.bibles.lib.db import BibleDB, BibleMeta, OldBibleDB, BiblesResourcesDB from openlp.plugins.bibles.lib.http import BSExtract, BGExtract, CWExtract diff --git a/openlp/plugins/bibles/lib/db.py b/openlp/plugins/bibles/lib/db.py index 8eaabd5ed..858bd6909 100644 --- a/openlp/plugins/bibles/lib/db.py +++ b/openlp/plugins/bibles/lib/db.py @@ -38,10 +38,11 @@ from sqlalchemy import Column, ForeignKey, Table, or_, types, func from sqlalchemy.orm import class_mapper, mapper, relation from sqlalchemy.orm.exc import UnmappedClassError +from openlp.core.common.applocation import AppLocation from openlp.core.lib import Registry, translate from openlp.core.lib.db import BaseModel, init_db, Manager from openlp.core.lib.ui import critical_error_message_box -from openlp.core.utils import AppLocation, clean_filename +from openlp.core.utils import clean_filename from . import upgrade log = logging.getLogger(__name__) diff --git a/openlp/plugins/bibles/lib/manager.py b/openlp/plugins/bibles/lib/manager.py index 3ee4862ec..f648bfaa1 100644 --- a/openlp/plugins/bibles/lib/manager.py +++ b/openlp/plugins/bibles/lib/manager.py @@ -30,8 +30,9 @@ import logging import os +from openlp.core.common.applocation import AppLocation from openlp.core.lib import Registry, Settings, translate -from openlp.core.utils import AppLocation, delete_file +from openlp.core.utils import delete_file from openlp.plugins.bibles.lib import parse_reference, get_reference_separator, LanguageSelection from openlp.plugins.bibles.lib.db import BibleDB, BibleMeta from .csvbible import CSVBible diff --git a/openlp/plugins/bibles/lib/osis.py b/openlp/plugins/bibles/lib/osis.py index 6184bff05..c2ce24da6 100644 --- a/openlp/plugins/bibles/lib/osis.py +++ b/openlp/plugins/bibles/lib/osis.py @@ -33,8 +33,8 @@ import chardet import codecs import re +from openlp.core.common import AppLocation from openlp.core.lib import translate -from openlp.core.utils import AppLocation from openlp.plugins.bibles.lib.db import BibleDB, BiblesResourcesDB log = logging.getLogger(__name__) diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index 70d4630a0..721018900 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -32,11 +32,12 @@ import os from PyQt4 import QtCore, QtGui +from openlp.core.common.applocation import AppLocation from openlp.core.lib import ItemCapabilities, MediaManagerItem, Registry, ServiceItemContext, Settings, \ StringContent, TreeWidgetWithDnD, UiStrings, build_icon, check_directory_exists, check_item_selected, \ create_thumb, translate, validate_thumb from openlp.core.lib.ui import create_widget_action, critical_error_message_box -from openlp.core.utils import AppLocation, delete_file, get_locale_key, get_images_filter +from openlp.core.utils import delete_file, get_locale_key, get_images_filter from openlp.plugins.images.forms import AddGroupForm, ChooseGroupForm from openlp.plugins.images.lib.db import ImageFilenames, ImageGroups diff --git a/openlp/plugins/media/lib/mediaitem.py b/openlp/plugins/media/lib/mediaitem.py index 4f173db84..929e1a32c 100644 --- a/openlp/plugins/media/lib/mediaitem.py +++ b/openlp/plugins/media/lib/mediaitem.py @@ -32,12 +32,13 @@ import os from PyQt4 import QtCore, QtGui +from openlp.core.common.applocation import AppLocation from openlp.core.lib import ItemCapabilities, MediaManagerItem,MediaType, Registry, ServiceItem, ServiceItemContext, \ Settings, UiStrings, build_icon, check_item_selected, check_directory_exists, translate from openlp.core.lib.ui import critical_error_message_box, create_horizontal_adjusting_combo_box from openlp.core.ui import DisplayController, Display, DisplayControllerType from openlp.core.ui.media import get_media_players, set_media_players -from openlp.core.utils import AppLocation, get_locale_key +from openlp.core.utils import get_locale_key log = logging.getLogger(__name__) diff --git a/openlp/plugins/presentations/lib/presentationcontroller.py b/openlp/plugins/presentations/lib/presentationcontroller.py index 0a70b174a..d5ac16314 100644 --- a/openlp/plugins/presentations/lib/presentationcontroller.py +++ b/openlp/plugins/presentations/lib/presentationcontroller.py @@ -33,8 +33,8 @@ import shutil from PyQt4 import QtCore +from openlp.core.common.applocation import AppLocation from openlp.core.lib import Registry, Settings, check_directory_exists, create_thumb, validate_thumb -from openlp.core.utils import AppLocation log = logging.getLogger(__name__) diff --git a/openlp/plugins/presentations/presentationplugin.py b/openlp/plugins/presentations/presentationplugin.py index be5d3f52d..20d07f0d7 100644 --- a/openlp/plugins/presentations/presentationplugin.py +++ b/openlp/plugins/presentations/presentationplugin.py @@ -35,8 +35,8 @@ import logging from PyQt4 import QtCore +from openlp.core.common.applocation import AppLocation from openlp.core.lib import Plugin, StringContent, build_icon, translate -from openlp.core.utils import AppLocation from openlp.plugins.presentations.lib import PresentationController, PresentationMediaItem, PresentationTab diff --git a/openlp/plugins/remotes/lib/httprouter.py b/openlp/plugins/remotes/lib/httprouter.py index f12fbb290..e6667c119 100644 --- a/openlp/plugins/remotes/lib/httprouter.py +++ b/openlp/plugins/remotes/lib/httprouter.py @@ -121,12 +121,12 @@ import urllib.request import urllib.error from urllib.parse import urlparse, parse_qs - from mako.template import Template from PyQt4 import QtCore +from openlp.core.common.applocation import AppLocation from openlp.core.lib import Registry, Settings, PluginStatus, StringContent, image_to_byte -from openlp.core.utils import AppLocation, translate +from openlp.core.utils import translate log = logging.getLogger(__name__) diff --git a/openlp/plugins/remotes/lib/httpserver.py b/openlp/plugins/remotes/lib/httpserver.py index 7776812fa..f2c641bb0 100644 --- a/openlp/plugins/remotes/lib/httpserver.py +++ b/openlp/plugins/remotes/lib/httpserver.py @@ -37,12 +37,11 @@ import ssl import socket import os import logging -from urllib.parse import urlparse, parse_qs from PyQt4 import QtCore +from openlp.core.common.applocation import AppLocation from openlp.core.lib import Settings -from openlp.core.utils import AppLocation from openlp.plugins.remotes.lib import HttpRouter diff --git a/openlp/plugins/remotes/lib/remotetab.py b/openlp/plugins/remotes/lib/remotetab.py index 17d368bd2..837d63053 100644 --- a/openlp/plugins/remotes/lib/remotetab.py +++ b/openlp/plugins/remotes/lib/remotetab.py @@ -31,9 +31,8 @@ import os.path from PyQt4 import QtCore, QtGui, QtNetwork +from openlp.core.common.applocation import AppLocation from openlp.core.lib import Settings, SettingsTab, translate -from openlp.core.utils import AppLocation - ZERO_URL = '0.0.0.0' diff --git a/openlp/plugins/songs/forms/duplicatesongremovalform.py b/openlp/plugins/songs/forms/duplicatesongremovalform.py index e058089aa..190bbea79 100644 --- a/openlp/plugins/songs/forms/duplicatesongremovalform.py +++ b/openlp/plugins/songs/forms/duplicatesongremovalform.py @@ -35,9 +35,9 @@ import os from PyQt4 import QtCore, QtGui +from openlp.core.common.applocation import AppLocation from openlp.core.lib import Registry, translate from openlp.core.ui.wizard import OpenLPWizard, WizardStrings -from openlp.core.utils import AppLocation from openlp.plugins.songs.lib import delete_song from openlp.plugins.songs.lib.db import Song, MediaFile from openlp.plugins.songs.forms.songreviewwidget import SongReviewWidget diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index 66f71545f..cf0ab94cf 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -38,10 +38,10 @@ import shutil from PyQt4 import QtCore, QtGui +from openlp.core.common.applocation import AppLocation from openlp.core.lib import Registry, PluginStatus, MediaType, UiStrings, translate, create_separated_list, \ check_directory_exists from openlp.core.lib.ui import set_case_insensitive_completer, critical_error_message_box, find_and_set_in_combo_box -from openlp.core.utils import AppLocation from openlp.plugins.songs.lib import VerseType, clean_song from openlp.plugins.songs.lib.db import Book, Song, Author, Topic, MediaFile from openlp.plugins.songs.lib.ui import SongStrings diff --git a/openlp/plugins/songs/lib/__init__.py b/openlp/plugins/songs/lib/__init__.py index 12874aa89..1ae645e09 100644 --- a/openlp/plugins/songs/lib/__init__.py +++ b/openlp/plugins/songs/lib/__init__.py @@ -36,8 +36,9 @@ import re from PyQt4 import QtGui +from openlp.core.common.applocation import AppLocation from openlp.core.lib import translate -from openlp.core.utils import AppLocation, CONTROL_CHARS +from openlp.core.utils import CONTROL_CHARS from openlp.plugins.songs.lib.db import MediaFile, Song from .db import Author from .ui import SongStrings diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index 957edbca0..469cb1258 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -35,10 +35,10 @@ import shutil from PyQt4 import QtCore, QtGui from sqlalchemy.sql import or_ +from openlp.core.common.applocation import AppLocation from openlp.core.lib import Registry, MediaManagerItem, ItemCapabilities, PluginStatus, ServiceItemContext, Settings, \ UiStrings, translate, check_item_selected, create_separated_list, check_directory_exists from openlp.core.lib.ui import create_widget_action -from openlp.core.utils import AppLocation from openlp.plugins.songs.forms.editsongform import EditSongForm from openlp.plugins.songs.forms.songmaintenanceform import SongMaintenanceForm from openlp.plugins.songs.forms.songimportform import SongImportForm diff --git a/openlp/plugins/songs/lib/songimport.py b/openlp/plugins/songs/lib/songimport.py index 2381959ad..04b8a2baa 100644 --- a/openlp/plugins/songs/lib/songimport.py +++ b/openlp/plugins/songs/lib/songimport.py @@ -34,9 +34,9 @@ import os from PyQt4 import QtCore +from openlp.core.common.applocation import AppLocation from openlp.core.lib import Registry, translate, check_directory_exists from openlp.core.ui.wizard import WizardStrings -from openlp.core.utils import AppLocation 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 diff --git a/tests/functional/openlp_core_utils/test_applocation.py b/tests/functional/openlp_core_utils/test_applocation.py index d34c0cfa5..4e2caae47 100644 --- a/tests/functional/openlp_core_utils/test_applocation.py +++ b/tests/functional/openlp_core_utils/test_applocation.py @@ -32,7 +32,7 @@ Functional tests to test the AppLocation class and related methods. import copy from unittest import TestCase -from openlp.core.utils import AppLocation +from openlp.core.common.applocation import AppLocation from tests.functional import patch FILE_LIST = ['file1', 'file2', 'file3.txt', 'file4.txt', 'file5.mp3', 'file6.mp3'] diff --git a/tests/functional/openlp_plugins/remotes/test_remotetab.py b/tests/functional/openlp_plugins/remotes/test_remotetab.py index 86652ffa4..51198bdc2 100644 --- a/tests/functional/openlp_plugins/remotes/test_remotetab.py +++ b/tests/functional/openlp_plugins/remotes/test_remotetab.py @@ -105,10 +105,10 @@ class TestRemoteTab(TestCase): Test the set_urls function with standard defaults """ # GIVEN: A mocked location - with patch('openlp.core.utils.applocation.Settings') as mocked_class, \ + with patch('openlp.core.common.applocation.Settings') as mocked_class, \ patch('openlp.core.utils.AppLocation.get_directory') as mocked_get_directory, \ - patch('openlp.core.utils.applocation.check_directory_exists') as mocked_check_directory_exists, \ - patch('openlp.core.utils.applocation.os') as mocked_os: + patch('openlp.core.common.applocation.check_directory_exists') as mocked_check_directory_exists, \ + patch('openlp.core.common.applocation.os') as mocked_os: # GIVEN: A mocked out Settings class and a mocked out AppLocation.get_directory() mocked_settings = mocked_class.return_value mocked_settings.contains.return_value = False @@ -133,10 +133,10 @@ class TestRemoteTab(TestCase): Test the set_urls function with certificate available """ # GIVEN: A mocked location - with patch('openlp.core.utils.applocation.Settings') as mocked_class, \ + with patch('openlp.core.common.applocation.Settings') as mocked_class, \ patch('openlp.core.utils.AppLocation.get_directory') as mocked_get_directory, \ - patch('openlp.core.utils.applocation.check_directory_exists') as mocked_check_directory_exists, \ - patch('openlp.core.utils.applocation.os') as mocked_os: + patch('openlp.core.common.applocation.check_directory_exists') as mocked_check_directory_exists, \ + patch('openlp.core.common.applocation.os') as mocked_os: # GIVEN: A mocked out Settings class and a mocked out AppLocation.get_directory() mocked_settings = mocked_class.return_value mocked_settings.contains.return_value = False From b860abb23bd48e2c4e798860703edad0662be8a1 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sun, 13 Oct 2013 16:52:04 +0100 Subject: [PATCH 85/93] fix fields --- openlp/core/common/__init__.py | 2 +- openlp/core/lib/json/theme.json | 2 +- openlp/core/lib/theme.py | 50 +------------------ openlp/core/ui/advancedtab.py | 2 +- openlp/core/ui/firsttimeform.py | 2 +- openlp/core/ui/mainwindow.py | 2 +- openlp/core/ui/servicemanager.py | 2 +- openlp/core/ui/thememanager.py | 2 +- .../plugins/bibles/forms/bibleimportform.py | 4 +- .../plugins/bibles/forms/bibleupgradeform.py | 2 +- openlp/plugins/bibles/lib/db.py | 2 +- openlp/plugins/bibles/lib/manager.py | 2 +- openlp/plugins/images/lib/mediaitem.py | 2 +- openlp/plugins/media/lib/mediaitem.py | 2 +- .../lib/presentationcontroller.py | 2 +- .../presentations/presentationplugin.py | 2 +- openlp/plugins/remotes/lib/httprouter.py | 2 +- openlp/plugins/remotes/lib/httpserver.py | 2 +- openlp/plugins/remotes/lib/remotetab.py | 2 +- .../songs/forms/duplicatesongremovalform.py | 2 +- openlp/plugins/songs/forms/editsongform.py | 2 +- openlp/plugins/songs/lib/__init__.py | 2 +- openlp/plugins/songs/lib/mediaitem.py | 2 +- openlp/plugins/songs/lib/songimport.py | 2 +- .../openlp_core_utils/test_applocation.py | 2 +- 25 files changed, 26 insertions(+), 74 deletions(-) diff --git a/openlp/core/common/__init__.py b/openlp/core/common/__init__.py index e2c95b6cc..1f205c958 100644 --- a/openlp/core/common/__init__.py +++ b/openlp/core/common/__init__.py @@ -54,4 +54,4 @@ def check_directory_exists(directory, do_not_log=False): except IOError: pass -from .applocation import AppLocation \ No newline at end of file +from .applocation import AppLocation diff --git a/openlp/core/lib/json/theme.json b/openlp/core/lib/json/theme.json index 1050458d8..9991f2b53 100644 --- a/openlp/core/lib/json/theme.json +++ b/openlp/core/lib/json/theme.json @@ -46,4 +46,4 @@ "font_main_x": 10, "font_main_y": 10, "theme_name": "" -} \ No newline at end of file +} diff --git a/openlp/core/lib/theme.py b/openlp/core/lib/theme.py index d6e67a745..c10a59560 100644 --- a/openlp/core/lib/theme.py +++ b/openlp/core/lib/theme.py @@ -42,52 +42,6 @@ from openlp.core.lib import str_to_bool, ScreenList, get_text_file_string log = logging.getLogger(__name__) -BLANK_THEME_XML = \ -''' - - - - - #000000 - - - #000000 - #000000 - vertical - - - #000000 - - - Arial - #FFFFFF - 40 - False - False - 0 - True - False - - - - Arial - #FFFFFF - 12 - False - False - 0 - True - False - - - - 0 - 0 - False - - -''' - class ThemeLevel(object): """ @@ -204,8 +158,6 @@ class VerticalType(object): BOOLEAN_LIST = ['bold', 'italics', 'override', 'outline', 'shadow', 'slide_transition'] -BOOLEAN_LIST2 = ['True', 'False'] - INTEGER_LIST = ['size', 'line_adjustment', 'x', 'height', 'y', 'width', 'shadow_size', 'outline_size', 'horizontal_align', 'vertical_align', 'wrap_style'] @@ -221,7 +173,7 @@ class ThemeXML(object): """ Initialise the theme object. """ - # Create the minidom document + # basic theme object with defaults json_dir = os.path.join(AppLocation.get_directory(AppLocation.AppDir), 'core', 'lib', 'json') json_file = os.path.join(json_dir, 'theme.json') jsn = get_text_file_string(json_file) diff --git a/openlp/core/ui/advancedtab.py b/openlp/core/ui/advancedtab.py index c61432c57..fbd44a9f8 100644 --- a/openlp/core/ui/advancedtab.py +++ b/openlp/core/ui/advancedtab.py @@ -36,7 +36,7 @@ import sys from PyQt4 import QtCore, QtGui -from openlp.core.common.applocation import AppLocation +from openlp.core.common import AppLocation from openlp.core.lib import SettingsTab, Settings, UiStrings, translate, build_icon from openlp.core.utils import format_time, get_images_filter from openlp.core.lib import SlideLimits diff --git a/openlp/core/ui/firsttimeform.py b/openlp/core/ui/firsttimeform.py index 182894037..062af6447 100644 --- a/openlp/core/ui/firsttimeform.py +++ b/openlp/core/ui/firsttimeform.py @@ -41,7 +41,7 @@ from configparser import SafeConfigParser from PyQt4 import QtCore, QtGui -from openlp.core.common.applocation import AppLocation +from openlp.core.common import AppLocation from openlp.core.lib import PluginStatus, Settings, Registry, build_icon, check_directory_exists, translate from openlp.core.utils import get_web_page from .firsttimewizard import Ui_FirstTimeWizard, FirstTimePage diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index 37d215c85..7cb4de6cf 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -47,7 +47,7 @@ from openlp.core.lib.ui import UiStrings, create_action from openlp.core.ui import AboutForm, SettingsForm, ServiceManager, ThemeManager, SlideController, PluginForm, \ MediaDockManager, ShortcutListForm, FormattingTagForm -from openlp.core.common.applocation import AppLocation +from openlp.core.common import AppLocation from openlp.core.ui.media import MediaController from openlp.core.utils import LanguageManager, add_actions, get_application_version from openlp.core.utils.actions import ActionList, CategoryOrder diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 7610b8dcb..94f6b512d 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -42,7 +42,7 @@ log = logging.getLogger(__name__) from PyQt4 import QtCore, QtGui -from openlp.core.common.applocation import AppLocation +from openlp.core.common import AppLocation from openlp.core.lib import OpenLPToolbar, ServiceItem, ItemCapabilities, Settings, PluginStatus, Registry, \ UiStrings, build_icon, translate, str_to_bool, check_directory_exists from openlp.core.lib.theme import ThemeLevel diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py index 2b2f6f855..3b07d0ed9 100644 --- a/openlp/core/ui/thememanager.py +++ b/openlp/core/ui/thememanager.py @@ -38,7 +38,7 @@ import re from xml.etree.ElementTree import ElementTree, XML from PyQt4 import QtCore, QtGui -from openlp.core.common.applocation import AppLocation +from openlp.core.common import AppLocation from openlp.core.lib import ImageSource, OpenLPToolbar, Registry, Settings, UiStrings, get_text_file_string, \ build_icon, translate, check_item_selected, check_directory_exists, create_thumb, validate_thumb from openlp.core.lib.theme import ThemeXML, BackgroundType diff --git a/openlp/plugins/bibles/forms/bibleimportform.py b/openlp/plugins/bibles/forms/bibleimportform.py index a4c275005..93bb5d565 100644 --- a/openlp/plugins/bibles/forms/bibleimportform.py +++ b/openlp/plugins/bibles/forms/bibleimportform.py @@ -32,9 +32,9 @@ The bible import functions for OpenLP import logging import os -from PyQt4 import QtCore, QtGui +from PyQt4 import QtGui -from openlp.core.common.applocation import AppLocation +from openlp.core.common import AppLocation from openlp.core.lib import Settings, UiStrings, translate from openlp.core.lib.db import delete_database from openlp.core.lib.ui import critical_error_message_box diff --git a/openlp/plugins/bibles/forms/bibleupgradeform.py b/openlp/plugins/bibles/forms/bibleupgradeform.py index fc3508d8e..2962eee9d 100644 --- a/openlp/plugins/bibles/forms/bibleupgradeform.py +++ b/openlp/plugins/bibles/forms/bibleupgradeform.py @@ -36,7 +36,7 @@ from tempfile import gettempdir from PyQt4 import QtCore, QtGui -from openlp.core.common.applocation import AppLocation +from openlp.core.common import AppLocation from openlp.core.lib import Registry, Settings, UiStrings, translate, check_directory_exists from openlp.core.lib.ui import critical_error_message_box from openlp.core.ui.wizard import OpenLPWizard, WizardStrings diff --git a/openlp/plugins/bibles/lib/db.py b/openlp/plugins/bibles/lib/db.py index 858bd6909..89ff8a61a 100644 --- a/openlp/plugins/bibles/lib/db.py +++ b/openlp/plugins/bibles/lib/db.py @@ -38,7 +38,7 @@ from sqlalchemy import Column, ForeignKey, Table, or_, types, func from sqlalchemy.orm import class_mapper, mapper, relation from sqlalchemy.orm.exc import UnmappedClassError -from openlp.core.common.applocation import AppLocation +from openlp.core.common import AppLocation from openlp.core.lib import Registry, translate from openlp.core.lib.db import BaseModel, init_db, Manager from openlp.core.lib.ui import critical_error_message_box diff --git a/openlp/plugins/bibles/lib/manager.py b/openlp/plugins/bibles/lib/manager.py index f648bfaa1..a05d0c301 100644 --- a/openlp/plugins/bibles/lib/manager.py +++ b/openlp/plugins/bibles/lib/manager.py @@ -30,7 +30,7 @@ import logging import os -from openlp.core.common.applocation import AppLocation +from openlp.core.common import AppLocation from openlp.core.lib import Registry, Settings, translate from openlp.core.utils import delete_file from openlp.plugins.bibles.lib import parse_reference, get_reference_separator, LanguageSelection diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index 721018900..9aeeeaee6 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -32,7 +32,7 @@ import os from PyQt4 import QtCore, QtGui -from openlp.core.common.applocation import AppLocation +from openlp.core.common import AppLocation from openlp.core.lib import ItemCapabilities, MediaManagerItem, Registry, ServiceItemContext, Settings, \ StringContent, TreeWidgetWithDnD, UiStrings, build_icon, check_directory_exists, check_item_selected, \ create_thumb, translate, validate_thumb diff --git a/openlp/plugins/media/lib/mediaitem.py b/openlp/plugins/media/lib/mediaitem.py index 929e1a32c..7464bd178 100644 --- a/openlp/plugins/media/lib/mediaitem.py +++ b/openlp/plugins/media/lib/mediaitem.py @@ -32,7 +32,7 @@ import os from PyQt4 import QtCore, QtGui -from openlp.core.common.applocation import AppLocation +from openlp.core.common import AppLocation from openlp.core.lib import ItemCapabilities, MediaManagerItem,MediaType, Registry, ServiceItem, ServiceItemContext, \ Settings, UiStrings, build_icon, check_item_selected, check_directory_exists, translate from openlp.core.lib.ui import critical_error_message_box, create_horizontal_adjusting_combo_box diff --git a/openlp/plugins/presentations/lib/presentationcontroller.py b/openlp/plugins/presentations/lib/presentationcontroller.py index d5ac16314..b850820c5 100644 --- a/openlp/plugins/presentations/lib/presentationcontroller.py +++ b/openlp/plugins/presentations/lib/presentationcontroller.py @@ -33,7 +33,7 @@ import shutil from PyQt4 import QtCore -from openlp.core.common.applocation import AppLocation +from openlp.core.common import AppLocation from openlp.core.lib import Registry, Settings, check_directory_exists, create_thumb, validate_thumb log = logging.getLogger(__name__) diff --git a/openlp/plugins/presentations/presentationplugin.py b/openlp/plugins/presentations/presentationplugin.py index 20d07f0d7..e37312095 100644 --- a/openlp/plugins/presentations/presentationplugin.py +++ b/openlp/plugins/presentations/presentationplugin.py @@ -35,7 +35,7 @@ import logging from PyQt4 import QtCore -from openlp.core.common.applocation import AppLocation +from openlp.core.common import AppLocation from openlp.core.lib import Plugin, StringContent, build_icon, translate from openlp.plugins.presentations.lib import PresentationController, PresentationMediaItem, PresentationTab diff --git a/openlp/plugins/remotes/lib/httprouter.py b/openlp/plugins/remotes/lib/httprouter.py index e6667c119..42b45abc6 100644 --- a/openlp/plugins/remotes/lib/httprouter.py +++ b/openlp/plugins/remotes/lib/httprouter.py @@ -124,7 +124,7 @@ from urllib.parse import urlparse, parse_qs from mako.template import Template from PyQt4 import QtCore -from openlp.core.common.applocation import AppLocation +from openlp.core.common import AppLocation from openlp.core.lib import Registry, Settings, PluginStatus, StringContent, image_to_byte from openlp.core.utils import translate diff --git a/openlp/plugins/remotes/lib/httpserver.py b/openlp/plugins/remotes/lib/httpserver.py index f2c641bb0..a8453fb2b 100644 --- a/openlp/plugins/remotes/lib/httpserver.py +++ b/openlp/plugins/remotes/lib/httpserver.py @@ -40,7 +40,7 @@ import logging from PyQt4 import QtCore -from openlp.core.common.applocation import AppLocation +from openlp.core.common import AppLocation from openlp.core.lib import Settings from openlp.plugins.remotes.lib import HttpRouter diff --git a/openlp/plugins/remotes/lib/remotetab.py b/openlp/plugins/remotes/lib/remotetab.py index 837d63053..d7ae97342 100644 --- a/openlp/plugins/remotes/lib/remotetab.py +++ b/openlp/plugins/remotes/lib/remotetab.py @@ -31,7 +31,7 @@ import os.path from PyQt4 import QtCore, QtGui, QtNetwork -from openlp.core.common.applocation import AppLocation +from openlp.core.common import AppLocation from openlp.core.lib import Settings, SettingsTab, translate ZERO_URL = '0.0.0.0' diff --git a/openlp/plugins/songs/forms/duplicatesongremovalform.py b/openlp/plugins/songs/forms/duplicatesongremovalform.py index 190bbea79..f4121c85a 100644 --- a/openlp/plugins/songs/forms/duplicatesongremovalform.py +++ b/openlp/plugins/songs/forms/duplicatesongremovalform.py @@ -35,7 +35,6 @@ import os from PyQt4 import QtCore, QtGui -from openlp.core.common.applocation import AppLocation from openlp.core.lib import Registry, translate from openlp.core.ui.wizard import OpenLPWizard, WizardStrings from openlp.plugins.songs.lib import delete_song @@ -45,6 +44,7 @@ from openlp.plugins.songs.lib.songcompare import songs_probably_equal log = logging.getLogger(__name__) + class DuplicateSongRemovalForm(OpenLPWizard): """ This is the Duplicate Song Removal Wizard. It provides functionality to diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index cf0ab94cf..cd44baf4f 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -38,7 +38,7 @@ import shutil from PyQt4 import QtCore, QtGui -from openlp.core.common.applocation import AppLocation +from openlp.core.common import AppLocation from openlp.core.lib import Registry, PluginStatus, MediaType, UiStrings, translate, create_separated_list, \ check_directory_exists from openlp.core.lib.ui import set_case_insensitive_completer, critical_error_message_box, find_and_set_in_combo_box diff --git a/openlp/plugins/songs/lib/__init__.py b/openlp/plugins/songs/lib/__init__.py index 1ae645e09..d2c25bd15 100644 --- a/openlp/plugins/songs/lib/__init__.py +++ b/openlp/plugins/songs/lib/__init__.py @@ -36,7 +36,7 @@ import re from PyQt4 import QtGui -from openlp.core.common.applocation import AppLocation +from openlp.core.common import AppLocation from openlp.core.lib import translate from openlp.core.utils import CONTROL_CHARS from openlp.plugins.songs.lib.db import MediaFile, Song diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index 469cb1258..b376c541b 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -35,7 +35,7 @@ import shutil from PyQt4 import QtCore, QtGui from sqlalchemy.sql import or_ -from openlp.core.common.applocation import AppLocation +from openlp.core.common import AppLocation from openlp.core.lib import Registry, MediaManagerItem, ItemCapabilities, PluginStatus, ServiceItemContext, Settings, \ UiStrings, translate, check_item_selected, create_separated_list, check_directory_exists from openlp.core.lib.ui import create_widget_action diff --git a/openlp/plugins/songs/lib/songimport.py b/openlp/plugins/songs/lib/songimport.py index 04b8a2baa..74e2a0272 100644 --- a/openlp/plugins/songs/lib/songimport.py +++ b/openlp/plugins/songs/lib/songimport.py @@ -34,7 +34,7 @@ import os from PyQt4 import QtCore -from openlp.core.common.applocation import AppLocation +from openlp.core.common import AppLocation from openlp.core.lib import Registry, translate, check_directory_exists from openlp.core.ui.wizard import WizardStrings from openlp.plugins.songs.lib import clean_song, VerseType diff --git a/tests/functional/openlp_core_utils/test_applocation.py b/tests/functional/openlp_core_utils/test_applocation.py index 4e2caae47..a79d5cefc 100644 --- a/tests/functional/openlp_core_utils/test_applocation.py +++ b/tests/functional/openlp_core_utils/test_applocation.py @@ -32,7 +32,7 @@ Functional tests to test the AppLocation class and related methods. import copy from unittest import TestCase -from openlp.core.common.applocation import AppLocation +from openlp.core.common import AppLocation from tests.functional import patch FILE_LIST = ['file1', 'file2', 'file3.txt', 'file4.txt', 'file5.mp3', 'file6.mp3'] From a013d17bdd5fe576e99ab02d55dde992f07ca27e Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sun, 13 Oct 2013 18:02:12 +0100 Subject: [PATCH 86/93] fix tests and add basic test --- openlp/core/common/__init__.py | 10 +++ openlp/core/common/applocation.py | 12 +--- .../functional/openlp_core_common/__init__.py | 1 + .../test_applocation.py | 64 +++++++++++++----- .../functional/openlp_core_lib/test_theme.py | 65 +++++++++++++++++++ .../openlp_core_utils/test_utils.py | 30 +-------- .../openlp_plugins/remotes/test_remotetab.py | 4 +- 7 files changed, 126 insertions(+), 60 deletions(-) create mode 100644 tests/functional/openlp_core_common/__init__.py rename tests/functional/{openlp_core_utils => openlp_core_common}/test_applocation.py (74%) create mode 100644 tests/functional/openlp_core_lib/test_theme.py diff --git a/openlp/core/common/__init__.py b/openlp/core/common/__init__.py index 1f205c958..02fab3512 100644 --- a/openlp/core/common/__init__.py +++ b/openlp/core/common/__init__.py @@ -32,6 +32,7 @@ OpenLP work. """ import os import logging +import sys log = logging.getLogger(__name__) @@ -54,4 +55,13 @@ def check_directory_exists(directory, do_not_log=False): except IOError: pass + +def get_frozen_path(frozen_option, non_frozen_option): + """ + Return a path based on the system status. + """ + if hasattr(sys, 'frozen') and sys.frozen == 1: + return frozen_option + return non_frozen_option + from .applocation import AppLocation diff --git a/openlp/core/common/applocation.py b/openlp/core/common/applocation.py index 7d0010316..8925b4c59 100644 --- a/openlp/core/common/applocation.py +++ b/openlp/core/common/applocation.py @@ -41,7 +41,7 @@ if sys.platform != 'win32' and sys.platform != 'darwin': XDG_BASE_AVAILABLE = False import openlp -from openlp.core.common import check_directory_exists +from openlp.core.common import check_directory_exists, get_frozen_path log = logging.getLogger(__name__) @@ -136,15 +136,6 @@ class AppLocation(object): return path -def get_frozen_path(frozen_option, non_frozen_option): - """ - Return a path based on the system status. - """ - if hasattr(sys, 'frozen') and sys.frozen == 1: - return frozen_option - return non_frozen_option - - def _get_os_dir_path(dir_type): """ Return a path based on which OS and environment we are running in. @@ -177,4 +168,3 @@ def _get_os_dir_path(dir_type): if dir_type == AppLocation.DataDir: return os.path.join(str(os.getenv('HOME')), '.openlp', 'data') return os.path.join(str(os.getenv('HOME')), '.openlp') - diff --git a/tests/functional/openlp_core_common/__init__.py b/tests/functional/openlp_core_common/__init__.py new file mode 100644 index 000000000..f87606f07 --- /dev/null +++ b/tests/functional/openlp_core_common/__init__.py @@ -0,0 +1 @@ +__author__ = 'tim' diff --git a/tests/functional/openlp_core_utils/test_applocation.py b/tests/functional/openlp_core_common/test_applocation.py similarity index 74% rename from tests/functional/openlp_core_utils/test_applocation.py rename to tests/functional/openlp_core_common/test_applocation.py index a79d5cefc..7ce891bd9 100644 --- a/tests/functional/openlp_core_utils/test_applocation.py +++ b/tests/functional/openlp_core_common/test_applocation.py @@ -32,7 +32,7 @@ Functional tests to test the AppLocation class and related methods. import copy from unittest import TestCase -from openlp.core.common import AppLocation +from openlp.core.common import AppLocation, get_frozen_path from tests.functional import patch FILE_LIST = ['file1', 'file2', 'file3.txt', 'file4.txt', 'file5.mp3', 'file6.mp3'] @@ -46,10 +46,10 @@ class TestAppLocation(TestCase): """ Test the AppLocation.get_data_path() method """ - with patch('openlp.core.utils.applocation.Settings') as mocked_class, \ - patch('openlp.core.utils.AppLocation.get_directory') as mocked_get_directory, \ - patch('openlp.core.utils.applocation.check_directory_exists') as mocked_check_directory_exists, \ - patch('openlp.core.utils.applocation.os') as mocked_os: + with patch('openlp.core.lib.Settings') as mocked_class, \ + patch('openlp.core.common.AppLocation.get_directory') as mocked_get_directory, \ + patch('openlp.core.common.applocation.check_directory_exists') as mocked_check_directory_exists, \ + patch('openlp.core.common.applocation.os') as mocked_os: # GIVEN: A mocked out Settings class and a mocked out AppLocation.get_directory() mocked_settings = mocked_class.return_value mocked_settings.contains.return_value = False @@ -70,8 +70,8 @@ class TestAppLocation(TestCase): """ Test the AppLocation.get_data_path() method when a custom location is set in the settings """ - with patch('openlp.core.utils.applocation.Settings') as mocked_class,\ - patch('openlp.core.utils.applocation.os') as mocked_os: + with patch('openlp.core.lib.Settings') as mocked_class,\ + patch('openlp.core.common.applocation.os') as mocked_os: # GIVEN: A mocked out Settings class which returns a custom data location mocked_settings = mocked_class.return_value mocked_settings.contains.return_value = True @@ -90,8 +90,8 @@ class TestAppLocation(TestCase): """ Test the AppLocation.get_files() method with no parameters passed. """ - with patch('openlp.core.utils.AppLocation.get_data_path') as mocked_get_data_path, \ - patch('openlp.core.utils.applocation.os.listdir') as mocked_listdir: + with patch('openlp.core.common.AppLocation.get_data_path') as mocked_get_data_path, \ + patch('openlp.core.common.applocation.os.listdir') as mocked_listdir: # GIVEN: Our mocked modules/methods. mocked_get_data_path.return_value = 'test/dir' mocked_listdir.return_value = copy.deepcopy(FILE_LIST) @@ -106,8 +106,8 @@ class TestAppLocation(TestCase): """ Test the AppLocation.get_files() method with all parameters passed. """ - with patch('openlp.core.utils.AppLocation.get_data_path') as mocked_get_data_path, \ - patch('openlp.core.utils.applocation.os.listdir') as mocked_listdir: + with patch('openlp.core.common.AppLocation.get_data_path') as mocked_get_data_path, \ + patch('openlp.core.common.applocation.os.listdir') as mocked_listdir: # GIVEN: Our mocked modules/methods. mocked_get_data_path.return_value = 'test/dir' mocked_listdir.return_value = copy.deepcopy(FILE_LIST) @@ -125,8 +125,8 @@ class TestAppLocation(TestCase): """ Test the AppLocation.get_section_data_path() method """ - with patch('openlp.core.utils.AppLocation.get_data_path') as mocked_get_data_path, \ - patch('openlp.core.utils.applocation.check_directory_exists') as mocked_check_directory_exists: + with patch('openlp.core.common.AppLocation.get_data_path') as mocked_get_data_path, \ + patch('openlp.core.common.applocation.check_directory_exists') as mocked_check_directory_exists: # GIVEN: A mocked out AppLocation.get_data_path() mocked_get_data_path.return_value = 'test/dir' mocked_check_directory_exists.return_value = True @@ -143,7 +143,7 @@ class TestAppLocation(TestCase): Test the AppLocation.get_directory() method for AppLocation.AppDir """ # GIVEN: A mocked out _get_frozen_path function - with patch('openlp.core.utils.applocation._get_frozen_path') as mocked_get_frozen_path: + with patch('openlp.core.common.applocation.get_frozen_path') as mocked_get_frozen_path: mocked_get_frozen_path.return_value = 'app/dir' # WHEN: We call AppLocation.get_directory @@ -157,10 +157,10 @@ class TestAppLocation(TestCase): Test the AppLocation.get_directory() method for AppLocation.PluginsDir """ # GIVEN: _get_frozen_path, abspath, split and sys are mocked out - with patch('openlp.core.utils.applocation._get_frozen_path') as mocked_get_frozen_path, \ - patch('openlp.core.utils.applocation.os.path.abspath') as mocked_abspath, \ - patch('openlp.core.utils.applocation.os.path.split') as mocked_split, \ - patch('openlp.core.utils.applocation.sys') as mocked_sys: + with patch('openlp.core.common.applocation.get_frozen_path') as mocked_get_frozen_path, \ + patch('openlp.core.common.applocation.os.path.abspath') as mocked_abspath, \ + patch('openlp.core.common.applocation.os.path.split') as mocked_split, \ + patch('openlp.core.common.applocation.sys') as mocked_sys: mocked_abspath.return_value = 'plugins/dir' mocked_split.return_value = ['openlp'] mocked_get_frozen_path.return_value = 'plugins/dir' @@ -172,3 +172,31 @@ class TestAppLocation(TestCase): # THEN: The correct directory should be returned self.assertEqual('plugins/dir', directory, 'Directory should be "plugins/dir"') + + def get_frozen_path_in_unfrozen_app_test(self): + """ + Test the _get_frozen_path() function when the application is not frozen (compiled by PyInstaller) + """ + with patch('openlp.core.utils.sys') as mocked_sys: + # GIVEN: The sys module "without" a "frozen" attribute + mocked_sys.frozen = None + + # WHEN: We call _get_frozen_path() with two parameters + frozen_path = get_frozen_path('frozen', 'not frozen') + + # THEN: The non-frozen parameter is returned + self.assertEqual('not frozen', frozen_path, '_get_frozen_path should return "not frozen"') + + def get_frozen_path_in_frozen_app_test(self): + """ + Test the get_frozen_path() function when the application is frozen (compiled by PyInstaller) + """ + with patch('openlp.core.common.sys') as mocked_sys: + # GIVEN: The sys module *with* a "frozen" attribute + mocked_sys.frozen = 1 + + # WHEN: We call _get_frozen_path() with two parameters + frozen_path = get_frozen_path('frozen', 'not frozen') + + # THEN: The frozen parameter is returned + self.assertEqual('frozen', frozen_path, 'Should return "frozen"') \ No newline at end of file diff --git a/tests/functional/openlp_core_lib/test_theme.py b/tests/functional/openlp_core_lib/test_theme.py new file mode 100644 index 000000000..e533b1b8d --- /dev/null +++ b/tests/functional/openlp_core_lib/test_theme.py @@ -0,0 +1,65 @@ +# -*- 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 # +############################################################################### +""" +Package to test the openlp.core.lib.theme package. +""" +from tests.functional import MagicMock, patch +from unittest import TestCase + +from openlp.core.lib.theme import ThemeXML + + +class TestTheme(TestCase): + """ + Test the functions in the Theme module + """ + def setUp(self): + """ + Create the UI + """ + pass + + def tearDown(self): + """ + Delete all the C++ objects at the end so that we don't have a segfault + """ + pass + + def test_new_theme(self): + """ + Test the theme creation - basic test + """ + # GIVEN: A new theme + + # WHEN: A theme is created + default_theme = ThemeXML() + + # THEN: We should get some default behaviours + self.assertTrue(default_theme.background_border_color == '#000000', 'The theme should have a black border') + self.assertTrue(default_theme.background_type == 'solid', 'There theme should have a solid backgrounds') \ No newline at end of file diff --git a/tests/functional/openlp_core_utils/test_utils.py b/tests/functional/openlp_core_utils/test_utils.py index 83cc24011..dfc31b245 100644 --- a/tests/functional/openlp_core_utils/test_utils.py +++ b/tests/functional/openlp_core_utils/test_utils.py @@ -31,7 +31,7 @@ Functional tests to test the AppLocation class and related methods. """ from unittest import TestCase -from openlp.core.utils import clean_filename, get_filesystem_encoding, _get_frozen_path, get_locale_key, \ +from openlp.core.utils import clean_filename, get_filesystem_encoding, get_locale_key, \ get_natural_key, split_filename from tests.functional import patch @@ -75,34 +75,6 @@ class TestUtils(TestCase): mocked_getdefaultencoding.assert_called_with() self.assertEqual('utf-8', result, 'The result should be "utf-8"') - def get_frozen_path_in_unfrozen_app_test(self): - """ - Test the _get_frozen_path() function when the application is not frozen (compiled by PyInstaller) - """ - with patch('openlp.core.utils.sys') as mocked_sys: - # GIVEN: The sys module "without" a "frozen" attribute - mocked_sys.frozen = None - - # WHEN: We call _get_frozen_path() with two parameters - frozen_path = _get_frozen_path('frozen', 'not frozen') - - # THEN: The non-frozen parameter is returned - self.assertEqual('not frozen', frozen_path, '_get_frozen_path should return "not frozen"') - - def get_frozen_path_in_frozen_app_test(self): - """ - Test the _get_frozen_path() function when the application is frozen (compiled by PyInstaller) - """ - with patch('openlp.core.utils.sys') as mocked_sys: - # GIVEN: The sys module *with* a "frozen" attribute - mocked_sys.frozen = 1 - - # WHEN: We call _get_frozen_path() with two parameters - frozen_path = _get_frozen_path('frozen', 'not frozen') - - # THEN: The frozen parameter is returned - self.assertEqual('frozen', frozen_path, 'Should return "frozen"') - def split_filename_with_file_path_test(self): """ Test the split_filename() function with a path to a file diff --git a/tests/functional/openlp_plugins/remotes/test_remotetab.py b/tests/functional/openlp_plugins/remotes/test_remotetab.py index 51198bdc2..d176a001d 100644 --- a/tests/functional/openlp_plugins/remotes/test_remotetab.py +++ b/tests/functional/openlp_plugins/remotes/test_remotetab.py @@ -105,7 +105,7 @@ class TestRemoteTab(TestCase): Test the set_urls function with standard defaults """ # GIVEN: A mocked location - with patch('openlp.core.common.applocation.Settings') as mocked_class, \ + with patch('openlp.core.lib.Settings') as mocked_class, \ patch('openlp.core.utils.AppLocation.get_directory') as mocked_get_directory, \ patch('openlp.core.common.applocation.check_directory_exists') as mocked_check_directory_exists, \ patch('openlp.core.common.applocation.os') as mocked_os: @@ -133,7 +133,7 @@ class TestRemoteTab(TestCase): Test the set_urls function with certificate available """ # GIVEN: A mocked location - with patch('openlp.core.common.applocation.Settings') as mocked_class, \ + with patch('openlp.core.lib.Settings') as mocked_class, \ patch('openlp.core.utils.AppLocation.get_directory') as mocked_get_directory, \ patch('openlp.core.common.applocation.check_directory_exists') as mocked_check_directory_exists, \ patch('openlp.core.common.applocation.os') as mocked_os: From a82c64237c3f26957bc46a11ecf94b52afa2535c Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sun, 13 Oct 2013 18:23:52 +0100 Subject: [PATCH 87/93] Fix move of check_directory_exists --- openlp/core/__init__.py | 4 +- openlp/core/lib/__init__.py | 44 +++++-------------- openlp/core/ui/firsttimeform.py | 4 +- openlp/core/ui/mainwindow.py | 4 +- openlp/core/ui/servicemanager.py | 4 +- openlp/core/ui/thememanager.py | 4 +- .../plugins/bibles/forms/bibleupgradeform.py | 4 +- openlp/plugins/images/lib/mediaitem.py | 6 +-- openlp/plugins/media/lib/mediaitem.py | 4 +- .../lib/presentationcontroller.py | 4 +- openlp/plugins/songs/forms/editsongform.py | 5 +-- openlp/plugins/songs/lib/mediaitem.py | 4 +- openlp/plugins/songs/lib/openlyricsexport.py | 3 +- openlp/plugins/songs/lib/songimport.py | 4 +- .../songusage/forms/songusagedetailform.py | 3 +- tests/functional/openlp_core_lib/test_lib.py | 3 +- .../openlp_plugins/remotes/test_remotetab.py | 4 +- 17 files changed, 45 insertions(+), 63 deletions(-) diff --git a/openlp/core/__init__.py b/openlp/core/__init__.py index a814ae04c..2ea41a3ec 100644 --- a/openlp/core/__init__.py +++ b/openlp/core/__init__.py @@ -43,8 +43,8 @@ from traceback import format_exception from PyQt4 import QtCore, QtGui -from openlp.core.common import AppLocation -from openlp.core.lib import Settings, ScreenList, UiStrings, Registry, check_directory_exists +from openlp.core.common import AppLocation, check_directory_exists +from openlp.core.lib import Settings, ScreenList, UiStrings, Registry from openlp.core.resources import qInitResources from openlp.core.ui.mainwindow import MainWindow from openlp.core.ui.firsttimelanguageform import FirstTimeLanguageForm diff --git a/openlp/core/lib/__init__.py b/openlp/core/lib/__init__.py index bc427830e..40a9c2367 100644 --- a/openlp/core/lib/__init__.py +++ b/openlp/core/lib/__init__.py @@ -326,52 +326,32 @@ def expand_tags(text): text = text.replace(tag['end tag'], tag['end html']) return text - -def check_directory_exists(directory, do_not_log=False): - """ - Check a theme directory exists and if not create it - - ``directory`` - The directory to make sure exists - - ``do_not_log`` - To not log anything. This is need for the start up, when the log isn't ready. - """ - if not do_not_log: - log.debug('check_directory_exists %s' % directory) - try: - if not os.path.exists(directory): - os.makedirs(directory) - except IOError: - pass - - -def create_separated_list(stringlist): +def create_separated_list(string_list): """ Returns a string that represents a join of a list of strings with a localized separator. This function corresponds to QLocale::createSeparatedList which was introduced in Qt 4.8 and implements the algorithm from http://www.unicode.org/reports/tr35/#ListPatterns - ``stringlist`` + ``string_list`` List of unicode strings """ if LooseVersion(Qt.PYQT_VERSION_STR) >= LooseVersion('4.9') and \ LooseVersion(Qt.qVersion()) >= LooseVersion('4.8'): - return QtCore.QLocale().createSeparatedList(stringlist) - if not stringlist: + return QtCore.QLocale().createSeparatedList(string_list) + if not string_list: return '' - elif len(stringlist) == 1: - return stringlist[0] - elif len(stringlist) == 2: + elif len(string_list) == 1: + return string_list[0] + elif len(string_list) == 2: return translate('OpenLP.core.lib', '%s and %s', - 'Locale list separator: 2 items') % (stringlist[0], stringlist[1]) + 'Locale list separator: 2 items') % (string_list[0], string_list[1]) else: merged = translate('OpenLP.core.lib', '%s, and %s', - 'Locale list separator: end') % (stringlist[-2], stringlist[-1]) - for index in reversed(list(range(1, len(stringlist) - 2))): + 'Locale list separator: end') % (string_list[-2], string_list[-1]) + for index in reversed(list(range(1, len(string_list) - 2))): merged = translate('OpenLP.core.lib', '%s, %s', - 'Locale list separator: middle') % (stringlist[index], merged) - return translate('OpenLP.core.lib', '%s, %s', 'Locale list separator: start') % (stringlist[0], merged) + 'Locale list separator: middle') % (string_list[index], merged) + return translate('OpenLP.core.lib', '%s, %s', 'Locale list separator: start') % (string_list[0], merged) from .registry import Registry diff --git a/openlp/core/ui/firsttimeform.py b/openlp/core/ui/firsttimeform.py index 062af6447..cbb5434f9 100644 --- a/openlp/core/ui/firsttimeform.py +++ b/openlp/core/ui/firsttimeform.py @@ -41,8 +41,8 @@ from configparser import SafeConfigParser from PyQt4 import QtCore, QtGui -from openlp.core.common import AppLocation -from openlp.core.lib import PluginStatus, Settings, Registry, build_icon, check_directory_exists, translate +from openlp.core.common import AppLocation, check_directory_exists +from openlp.core.lib import PluginStatus, Settings, Registry, build_icon, translate from openlp.core.utils import get_web_page from .firsttimewizard import Ui_FirstTimeWizard, FirstTimePage diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index 7cb4de6cf..4110c5e04 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -42,12 +42,12 @@ from datetime import datetime from PyQt4 import QtCore, QtGui from openlp.core.lib import Renderer, OpenLPDockWidget, PluginManager, ImageManager, PluginStatus, Registry, \ - Settings, ScreenList, build_icon, check_directory_exists, translate + Settings, ScreenList, build_icon, translate from openlp.core.lib.ui import UiStrings, create_action from openlp.core.ui import AboutForm, SettingsForm, ServiceManager, ThemeManager, SlideController, PluginForm, \ MediaDockManager, ShortcutListForm, FormattingTagForm -from openlp.core.common import AppLocation +from openlp.core.common import AppLocation, check_directory_exists from openlp.core.ui.media import MediaController from openlp.core.utils import LanguageManager, add_actions, get_application_version from openlp.core.utils.actions import ActionList, CategoryOrder diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 94f6b512d..ee4fa850f 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -42,9 +42,9 @@ log = logging.getLogger(__name__) from PyQt4 import QtCore, QtGui -from openlp.core.common import AppLocation +from openlp.core.common import AppLocation, check_directory_exists from openlp.core.lib import OpenLPToolbar, ServiceItem, ItemCapabilities, Settings, PluginStatus, Registry, \ - UiStrings, build_icon, translate, str_to_bool, check_directory_exists + UiStrings, build_icon, translate from openlp.core.lib.theme import ThemeLevel from openlp.core.lib.ui import critical_error_message_box, create_widget_action, find_and_set_in_combo_box from openlp.core.ui import ServiceNoteForm, ServiceItemEditForm, StartTimeForm diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py index 3b07d0ed9..8dd0727ea 100644 --- a/openlp/core/ui/thememanager.py +++ b/openlp/core/ui/thememanager.py @@ -38,9 +38,9 @@ import re from xml.etree.ElementTree import ElementTree, XML from PyQt4 import QtCore, QtGui -from openlp.core.common import AppLocation +from openlp.core.common import AppLocation, check_directory_exists from openlp.core.lib import ImageSource, OpenLPToolbar, Registry, Settings, UiStrings, get_text_file_string, \ - build_icon, translate, check_item_selected, check_directory_exists, create_thumb, validate_thumb + build_icon, translate, check_item_selected, create_thumb, validate_thumb from openlp.core.lib.theme import ThemeXML, BackgroundType from openlp.core.lib.ui import critical_error_message_box, create_widget_action from openlp.core.ui import FileRenameForm, ThemeForm, ThemeManagerHelper diff --git a/openlp/plugins/bibles/forms/bibleupgradeform.py b/openlp/plugins/bibles/forms/bibleupgradeform.py index 2962eee9d..ca74d6666 100644 --- a/openlp/plugins/bibles/forms/bibleupgradeform.py +++ b/openlp/plugins/bibles/forms/bibleupgradeform.py @@ -36,8 +36,8 @@ from tempfile import gettempdir from PyQt4 import QtCore, QtGui -from openlp.core.common import AppLocation -from openlp.core.lib import Registry, Settings, UiStrings, translate, check_directory_exists +from openlp.core.common import AppLocation, check_directory_exists +from openlp.core.lib import Registry, Settings, UiStrings, translate from openlp.core.lib.ui import critical_error_message_box from openlp.core.ui.wizard import OpenLPWizard, WizardStrings from openlp.core.utils import delete_file diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index 9aeeeaee6..ce7d55e17 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -32,10 +32,10 @@ import os from PyQt4 import QtCore, QtGui -from openlp.core.common import AppLocation +from openlp.core.common import AppLocation, check_directory_exists from openlp.core.lib import ItemCapabilities, MediaManagerItem, Registry, ServiceItemContext, Settings, \ - StringContent, TreeWidgetWithDnD, UiStrings, build_icon, check_directory_exists, check_item_selected, \ - create_thumb, translate, validate_thumb + StringContent, TreeWidgetWithDnD, UiStrings, build_icon, check_item_selected, create_thumb, translate, \ + validate_thumb from openlp.core.lib.ui import create_widget_action, critical_error_message_box from openlp.core.utils import delete_file, get_locale_key, get_images_filter from openlp.plugins.images.forms import AddGroupForm, ChooseGroupForm diff --git a/openlp/plugins/media/lib/mediaitem.py b/openlp/plugins/media/lib/mediaitem.py index 7464bd178..dc6b1456f 100644 --- a/openlp/plugins/media/lib/mediaitem.py +++ b/openlp/plugins/media/lib/mediaitem.py @@ -32,9 +32,9 @@ import os from PyQt4 import QtCore, QtGui -from openlp.core.common import AppLocation +from openlp.core.common import AppLocation, check_directory_exists from openlp.core.lib import ItemCapabilities, MediaManagerItem,MediaType, Registry, ServiceItem, ServiceItemContext, \ - Settings, UiStrings, build_icon, check_item_selected, check_directory_exists, translate + Settings, UiStrings, build_icon, check_item_selected, translate from openlp.core.lib.ui import critical_error_message_box, create_horizontal_adjusting_combo_box from openlp.core.ui import DisplayController, Display, DisplayControllerType from openlp.core.ui.media import get_media_players, set_media_players diff --git a/openlp/plugins/presentations/lib/presentationcontroller.py b/openlp/plugins/presentations/lib/presentationcontroller.py index b850820c5..08584038e 100644 --- a/openlp/plugins/presentations/lib/presentationcontroller.py +++ b/openlp/plugins/presentations/lib/presentationcontroller.py @@ -33,8 +33,8 @@ import shutil from PyQt4 import QtCore -from openlp.core.common import AppLocation -from openlp.core.lib import Registry, Settings, check_directory_exists, create_thumb, validate_thumb +from openlp.core.common import AppLocation, check_directory_exists +from openlp.core.lib import Registry, Settings, create_thumb, validate_thumb log = logging.getLogger(__name__) diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index cd44baf4f..83b4c8d04 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -38,9 +38,8 @@ import shutil from PyQt4 import QtCore, QtGui -from openlp.core.common import AppLocation -from openlp.core.lib import Registry, PluginStatus, MediaType, UiStrings, translate, create_separated_list, \ - check_directory_exists +from openlp.core.common import AppLocation, check_directory_exists +from openlp.core.lib import Registry, PluginStatus, MediaType, UiStrings, translate, create_separated_list from openlp.core.lib.ui import set_case_insensitive_completer, critical_error_message_box, find_and_set_in_combo_box from openlp.plugins.songs.lib import VerseType, clean_song from openlp.plugins.songs.lib.db import Book, Song, Author, Topic, MediaFile diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index b376c541b..a708948ae 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -35,9 +35,9 @@ import shutil from PyQt4 import QtCore, QtGui from sqlalchemy.sql import or_ -from openlp.core.common import AppLocation +from openlp.core.common import AppLocation, check_directory_exists from openlp.core.lib import Registry, MediaManagerItem, ItemCapabilities, PluginStatus, ServiceItemContext, Settings, \ - UiStrings, translate, check_item_selected, create_separated_list, check_directory_exists + UiStrings, translate, check_item_selected, create_separated_list from openlp.core.lib.ui import create_widget_action from openlp.plugins.songs.forms.editsongform import EditSongForm from openlp.plugins.songs.forms.songmaintenanceform import SongMaintenanceForm diff --git a/openlp/plugins/songs/lib/openlyricsexport.py b/openlp/plugins/songs/lib/openlyricsexport.py index a1d17d595..ef62a64f0 100644 --- a/openlp/plugins/songs/lib/openlyricsexport.py +++ b/openlp/plugins/songs/lib/openlyricsexport.py @@ -35,7 +35,8 @@ import os from lxml import etree -from openlp.core.lib import Registry, check_directory_exists, translate +from openlp.core.common import check_directory_exists +from openlp.core.lib import Registry, translate from openlp.core.utils import clean_filename from openlp.plugins.songs.lib.xml import OpenLyrics diff --git a/openlp/plugins/songs/lib/songimport.py b/openlp/plugins/songs/lib/songimport.py index 74e2a0272..378f18272 100644 --- a/openlp/plugins/songs/lib/songimport.py +++ b/openlp/plugins/songs/lib/songimport.py @@ -34,8 +34,8 @@ import os from PyQt4 import QtCore -from openlp.core.common import AppLocation -from openlp.core.lib import Registry, translate, check_directory_exists +from openlp.core.common import AppLocation, check_directory_exists +from openlp.core.lib import Registry, translate 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 diff --git a/openlp/plugins/songusage/forms/songusagedetailform.py b/openlp/plugins/songusage/forms/songusagedetailform.py index 725668bd3..e5925da78 100644 --- a/openlp/plugins/songusage/forms/songusagedetailform.py +++ b/openlp/plugins/songusage/forms/songusagedetailform.py @@ -33,7 +33,8 @@ import os from PyQt4 import QtGui from sqlalchemy.sql import and_ -from openlp.core.lib import Registry, Settings, translate, check_directory_exists +from openlp.core.common import check_directory_exists +from openlp.core.lib import Registry, Settings, translate from openlp.plugins.songusage.lib.db import SongUsageItem from .songusagedetaildialog import Ui_SongUsageDetailDialog diff --git a/tests/functional/openlp_core_lib/test_lib.py b/tests/functional/openlp_core_lib/test_lib.py index 9bb74e0d3..6ba7eddbb 100644 --- a/tests/functional/openlp_core_lib/test_lib.py +++ b/tests/functional/openlp_core_lib/test_lib.py @@ -36,7 +36,8 @@ from datetime import datetime, timedelta from PyQt4 import QtCore, QtGui -from openlp.core.lib import str_to_bool, create_thumb, translate, check_directory_exists, get_text_file_string, \ +from openlp.core.common import check_directory_exists +from openlp.core.lib import str_to_bool, create_thumb, translate, get_text_file_string, \ build_icon, image_to_byte, check_item_selected, validate_thumb, create_separated_list, clean_tags, expand_tags from tests.functional import MagicMock, patch diff --git a/tests/functional/openlp_plugins/remotes/test_remotetab.py b/tests/functional/openlp_plugins/remotes/test_remotetab.py index d176a001d..cd32479d4 100644 --- a/tests/functional/openlp_plugins/remotes/test_remotetab.py +++ b/tests/functional/openlp_plugins/remotes/test_remotetab.py @@ -107,7 +107,7 @@ class TestRemoteTab(TestCase): # GIVEN: A mocked location with patch('openlp.core.lib.Settings') as mocked_class, \ patch('openlp.core.utils.AppLocation.get_directory') as mocked_get_directory, \ - patch('openlp.core.common.applocation.check_directory_exists') as mocked_check_directory_exists, \ + patch('openlp.core.common.check_directory_exists') as mocked_check_directory_exists, \ patch('openlp.core.common.applocation.os') as mocked_os: # GIVEN: A mocked out Settings class and a mocked out AppLocation.get_directory() mocked_settings = mocked_class.return_value @@ -135,7 +135,7 @@ class TestRemoteTab(TestCase): # GIVEN: A mocked location with patch('openlp.core.lib.Settings') as mocked_class, \ patch('openlp.core.utils.AppLocation.get_directory') as mocked_get_directory, \ - patch('openlp.core.common.applocation.check_directory_exists') as mocked_check_directory_exists, \ + patch('openlp.core.common.check_directory_exists') as mocked_check_directory_exists, \ patch('openlp.core.common.applocation.os') as mocked_os: # GIVEN: A mocked out Settings class and a mocked out AppLocation.get_directory() mocked_settings = mocked_class.return_value From 63685cb29e621528f7eb0a00bcea729fe8ca83cc Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sun, 13 Oct 2013 21:36:42 +0100 Subject: [PATCH 88/93] Move Settings and translate --- openlp/core/__init__.py | 4 +- openlp/core/common/__init__.py | 42 ++ openlp/core/common/applocation.py | 4 +- openlp/core/{lib => common}/uistrings.py | 2 +- openlp/core/lib/__init__.py | 33 +- openlp/core/lib/db.py | 4 +- openlp/core/lib/formattingtags.py | 3 +- openlp/core/lib/mediamanageritem.py | 3 +- openlp/core/lib/plugin.py | 3 +- openlp/core/lib/renderer.py | 5 +- openlp/core/lib/screen.py | 4 +- openlp/core/lib/serviceitem.py | 3 +- openlp/core/lib/settings.py | 499 ------------------ openlp/core/lib/theme.py | 9 - openlp/core/lib/ui.py | 3 +- openlp/core/ui/aboutdialog.py | 3 +- openlp/core/ui/advancedtab.py | 5 +- openlp/core/ui/exceptionform.py | 2 +- openlp/core/ui/firsttimeform.py | 4 +- openlp/core/ui/formattingtagdialog.py | 3 +- openlp/core/ui/generaltab.py | 3 +- openlp/core/ui/maindisplay.py | 3 +- openlp/core/ui/mainwindow.py | 4 +- openlp/core/ui/media/__init__.py | 2 +- openlp/core/ui/media/mediacontroller.py | 3 +- openlp/core/ui/media/phononplayer.py | 3 +- openlp/core/ui/media/playertab.py | 3 +- openlp/core/ui/media/vlcplayer.py | 3 +- openlp/core/ui/media/webkitplayer.py | 3 +- openlp/core/ui/plugindialog.py | 2 +- openlp/core/ui/printservicedialog.py | 3 +- openlp/core/ui/printserviceform.py | 3 +- openlp/core/ui/servicemanager.py | 8 +- openlp/core/ui/shortcutlistform.py | 4 +- openlp/core/ui/slidecontroller.py | 5 +- openlp/core/ui/starttimedialog.py | 2 +- openlp/core/ui/starttimeform.py | 3 +- openlp/core/ui/themeform.py | 3 +- openlp/core/ui/thememanager.py | 6 +- openlp/core/ui/themestab.py | 4 +- openlp/core/ui/themewizard.py | 3 +- openlp/core/ui/wizard.py | 5 +- openlp/core/utils/__init__.py | 4 +- openlp/core/utils/actions.py | 2 +- openlp/core/utils/languagemanager.py | 4 +- openlp/plugins/alerts/alertsplugin.py | 3 +- openlp/plugins/alerts/lib/alertstab.py | 3 +- .../plugins/bibles/forms/bibleimportform.py | 3 +- .../plugins/bibles/forms/bibleupgradeform.py | 4 +- openlp/plugins/bibles/forms/booknamedialog.py | 2 +- openlp/plugins/bibles/forms/booknameform.py | 2 +- .../plugins/bibles/forms/editbibledialog.py | 3 +- openlp/plugins/bibles/forms/editbibleform.py | 3 +- openlp/plugins/bibles/forms/languagedialog.py | 2 +- openlp/plugins/bibles/forms/languageform.py | 2 +- openlp/plugins/bibles/lib/__init__.py | 3 +- openlp/plugins/bibles/lib/biblestab.py | 3 +- openlp/plugins/bibles/lib/manager.py | 4 +- openlp/plugins/bibles/lib/mediaitem.py | 5 +- .../plugins/custom/forms/editcustomdialog.py | 3 +- .../custom/forms/editcustomslidedialog.py | 3 +- .../custom/forms/editcustomslideform.py | 2 +- openlp/plugins/custom/lib/customtab.py | 3 +- openlp/plugins/custom/lib/mediaitem.py | 5 +- openlp/plugins/images/imageplugin.py | 5 +- openlp/plugins/images/lib/imagetab.py | 3 +- openlp/plugins/images/lib/mediaitem.py | 6 +- openlp/plugins/media/lib/mediaitem.py | 4 +- openlp/plugins/media/lib/mediatab.py | 3 +- openlp/plugins/media/mediaplugin.py | 2 +- openlp/plugins/presentations/lib/mediaitem.py | 5 +- .../lib/presentationcontroller.py | 4 +- .../presentations/lib/presentationtab.py | 3 +- openlp/plugins/remotes/lib/httprouter.py | 5 +- openlp/plugins/remotes/lib/httpserver.py | 3 +- openlp/plugins/remotes/lib/remotetab.py | 4 +- openlp/plugins/songs/forms/editsongdialog.py | 3 +- openlp/plugins/songs/forms/editsongform.py | 4 +- openlp/plugins/songs/forms/songexportform.py | 3 +- openlp/plugins/songs/forms/songimportform.py | 4 +- .../songs/forms/songmaintenancedialog.py | 3 +- .../songs/forms/songmaintenanceform.py | 3 +- openlp/plugins/songs/lib/importer.py | 2 +- openlp/plugins/songs/lib/mediaitem.py | 6 +- openlp/plugins/songs/lib/olpimport.py | 2 +- openlp/plugins/songs/lib/oooimport.py | 1 + openlp/plugins/songs/lib/openlyricsexport.py | 4 +- openlp/plugins/songs/lib/opensongimport.py | 3 +- openlp/plugins/songs/lib/powersongimport.py | 2 +- openlp/plugins/songs/lib/songimport.py | 4 +- openlp/plugins/songs/lib/songproimport.py | 1 + .../plugins/songs/lib/songshowplusimport.py | 5 +- openlp/plugins/songs/lib/songstab.py | 3 +- openlp/plugins/songs/lib/ui.py | 1 + .../songs/lib/worshipcenterproimport.py | 2 +- openlp/plugins/songs/lib/wowimport.py | 2 +- openlp/plugins/songs/lib/xml.py | 3 +- openlp/plugins/songs/lib/zionworximport.py | 2 +- openlp/plugins/songs/songsplugin.py | 3 +- .../songusage/forms/songusagedeletedialog.py | 2 +- .../songusage/forms/songusagedeleteform.py | 5 +- .../songusage/forms/songusagedetaildialog.py | 3 +- .../songusage/forms/songusagedetailform.py | 4 +- openlp/plugins/songusage/songusageplugin.py | 3 +- .../openlp_core_common/test_applocation.py | 5 +- .../test_settings.py | 2 +- .../test_uistrings.py | 2 +- .../openlp_core_lib/test_formattingtags.py | 4 +- tests/functional/openlp_core_lib/test_lib.py | 4 +- .../openlp_core_lib/test_pluginmanager.py | 3 +- .../openlp_core_utils/test_actions.py | 2 +- .../openlp_plugins/remotes/test_remotetab.py | 6 +- .../openlp_plugins/remotes/test_router.py | 2 +- .../openlp_plugins/songs/test_mediaitem.py | 3 +- .../openlp_core_lib/test_pluginmanager.py | 3 +- 115 files changed, 257 insertions(+), 693 deletions(-) rename openlp/core/{lib => common}/uistrings.py (99%) delete mode 100644 openlp/core/lib/settings.py rename tests/functional/{openlp_core_lib => openlp_core_common}/test_settings.py (99%) rename tests/functional/{openlp_core_lib => openlp_core_common}/test_uistrings.py (98%) diff --git a/openlp/core/__init__.py b/openlp/core/__init__.py index 2ea41a3ec..7d198db5e 100644 --- a/openlp/core/__init__.py +++ b/openlp/core/__init__.py @@ -43,8 +43,8 @@ from traceback import format_exception from PyQt4 import QtCore, QtGui -from openlp.core.common import AppLocation, check_directory_exists -from openlp.core.lib import Settings, ScreenList, UiStrings, Registry +from openlp.core.common import AppLocation, Settings, UiStrings, check_directory_exists +from openlp.core.lib import ScreenList, Registry from openlp.core.resources import qInitResources from openlp.core.ui.mainwindow import MainWindow from openlp.core.ui.firsttimelanguageform import FirstTimeLanguageForm diff --git a/openlp/core/common/__init__.py b/openlp/core/common/__init__.py index 02fab3512..42fde7065 100644 --- a/openlp/core/common/__init__.py +++ b/openlp/core/common/__init__.py @@ -34,6 +34,8 @@ import os import logging import sys +from PyQt4 import QtCore + log = logging.getLogger(__name__) @@ -64,4 +66,44 @@ def get_frozen_path(frozen_option, non_frozen_option): return frozen_option return non_frozen_option + +class ThemeLevel(object): + """ + Provides an enumeration for the level a theme applies to + """ + Global = 1 + Service = 2 + Song = 3 + + +def translate(context, text, comment=None, encoding=QtCore.QCoreApplication.CodecForTr, n=-1, + qt_translate=QtCore.QCoreApplication.translate): + """ + A special shortcut method to wrap around the Qt4 translation functions. This abstracts the translation procedure so + that we can change it if at a later date if necessary, without having to redo the whole of OpenLP. + + ``context`` + The translation context, used to give each string a context or a namespace. + + ``text`` + The text to put into the translation tables for translation. + + ``comment`` + An identifying string for when the same text is used in different roles within the same context. + """ + return qt_translate(context, text, comment, encoding, n) + + +class SlideLimits(object): + """ + Provides an enumeration for behaviour of OpenLP at the end limits of each service item when pressing the up/down + arrow keys + """ + End = 1 + Wrap = 2 + Next = 3 + +from .uistrings import UiStrings +from .settings import Settings from .applocation import AppLocation + diff --git a/openlp/core/common/applocation.py b/openlp/core/common/applocation.py index 8925b4c59..41b47ecbe 100644 --- a/openlp/core/common/applocation.py +++ b/openlp/core/common/applocation.py @@ -33,6 +33,9 @@ import logging import os import sys +from openlp.core.common import Settings + + if sys.platform != 'win32' and sys.platform != 'darwin': try: from xdg import BaseDirectory @@ -91,7 +94,6 @@ class AppLocation(object): Return the path OpenLP stores all its data under. """ # Check if we have a different data location. - from openlp.core.lib import Settings if Settings().contains('advanced/data path'): path = Settings().value('advanced/data path') else: diff --git a/openlp/core/lib/uistrings.py b/openlp/core/common/uistrings.py similarity index 99% rename from openlp/core/lib/uistrings.py rename to openlp/core/common/uistrings.py index 6d4b4d250..d7a4c1c42 100644 --- a/openlp/core/lib/uistrings.py +++ b/openlp/core/common/uistrings.py @@ -31,7 +31,7 @@ The :mod:`uistrings` module provides standard strings for OpenLP. """ import logging -from openlp.core.lib import translate +from openlp.core.common import translate log = logging.getLogger(__name__) diff --git a/openlp/core/lib/__init__.py b/openlp/core/lib/__init__.py index 40a9c2367..67ac409df 100644 --- a/openlp/core/lib/__init__.py +++ b/openlp/core/lib/__init__.py @@ -37,6 +37,8 @@ import os from PyQt4 import QtCore, QtGui, Qt +from openlp.core.common import translate + log = logging.getLogger(__name__) @@ -72,16 +74,6 @@ class MediaType(object): Video = 2 -class SlideLimits(object): - """ - Provides an enumeration for behaviour of OpenLP at the end limits of each service item when pressing the up/down - arrow keys - """ - End = 1 - Wrap = 2 - Next = 3 - - class ServiceItemAction(object): """ Provides an enumeration for the required action moving between service items by left/right arrow keys @@ -91,24 +83,6 @@ class ServiceItemAction(object): Next = 3 -def translate(context, text, comment=None, encoding=QtCore.QCoreApplication.CodecForTr, n=-1, - qt_translate=QtCore.QCoreApplication.translate): - """ - A special shortcut method to wrap around the Qt4 translation functions. This abstracts the translation procedure so - that we can change it if at a later date if necessary, without having to redo the whole of OpenLP. - - ``context`` - The translation context, used to give each string a context or a namespace. - - ``text`` - The text to put into the translation tables for translation. - - ``comment`` - An identifying string for when the same text is used in different roles within the same context. - """ - return qt_translate(context, text, comment, encoding, n) - - def get_text_file_string(text_file): """ Open a file and return its content as unicode string. If the supplied file name is not a file then the function @@ -326,6 +300,7 @@ def expand_tags(text): text = text.replace(tag['end tag'], tag['end html']) return text + def create_separated_list(string_list): """ Returns a string that represents a join of a list of strings with a localized separator. This function corresponds @@ -355,9 +330,7 @@ def create_separated_list(string_list): from .registry import Registry -from .uistrings import UiStrings from .screen import ScreenList -from .settings import Settings from .listwidgetwithdnd import ListWidgetWithDnD from .treewidgetwithdnd import TreeWidgetWithDnD from .formattingtags import FormattingTags diff --git a/openlp/core/lib/db.py b/openlp/core/lib/db.py index 0fff6e871..6bbca9b5c 100644 --- a/openlp/core/lib/db.py +++ b/openlp/core/lib/db.py @@ -41,8 +41,8 @@ from sqlalchemy.pool import NullPool from alembic.migration import MigrationContext from alembic.operations import Operations -from openlp.core.common import AppLocation -from openlp.core.lib import translate, Settings +from openlp.core.common import AppLocation, Settings +from openlp.core.lib import translate from openlp.core.lib.ui import critical_error_message_box from openlp.core.utils import delete_file diff --git a/openlp/core/lib/formattingtags.py b/openlp/core/lib/formattingtags.py index f914677c6..58e3226d1 100644 --- a/openlp/core/lib/formattingtags.py +++ b/openlp/core/lib/formattingtags.py @@ -31,7 +31,8 @@ Provide HTML Tag management and Formatting Tag access class """ import json -from openlp.core.lib import Settings, translate +from openlp.core.common import Settings +from openlp.core.lib import translate class FormattingTags(object): diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py index 3fddc18f2..01e16eef3 100644 --- a/openlp/core/lib/mediamanageritem.py +++ b/openlp/core/lib/mediamanageritem.py @@ -35,8 +35,9 @@ import re from PyQt4 import QtCore, QtGui +from openlp.core.common import Settings, UiStrings, translate from openlp.core.lib import OpenLPToolbar, ServiceItem, StringContent, ListWidgetWithDnD, \ - ServiceItemContext, Settings, Registry, UiStrings, translate + ServiceItemContext, Registry from openlp.core.lib.searchedit import SearchEdit from openlp.core.lib.ui import create_widget_action, critical_error_message_box diff --git a/openlp/core/lib/plugin.py b/openlp/core/lib/plugin.py index fc9830398..2e6f42c6b 100644 --- a/openlp/core/lib/plugin.py +++ b/openlp/core/lib/plugin.py @@ -34,7 +34,8 @@ import os from PyQt4 import QtCore -from openlp.core.lib import Settings, Registry, UiStrings +from openlp.core.common import Settings, UiStrings +from openlp.core.lib import Registry from openlp.core.utils import get_application_version log = logging.getLogger(__name__) diff --git a/openlp/core/lib/renderer.py b/openlp/core/lib/renderer.py index 4b2aacda6..b743f3962 100644 --- a/openlp/core/lib/renderer.py +++ b/openlp/core/lib/renderer.py @@ -31,9 +31,10 @@ import logging from PyQt4 import QtGui, QtCore, QtWebKit -from openlp.core.lib import Settings, FormattingTags, ImageSource, ItemCapabilities, Registry, ScreenList, \ +from openlp.core.common import Settings +from openlp.core.lib import FormattingTags, ImageSource, ItemCapabilities, Registry, ScreenList, \ ServiceItem, expand_tags, build_lyrics_format_css, build_lyrics_outline_css -from openlp.core.lib.theme import ThemeLevel +from openlp.core.common import ThemeLevel from openlp.core.ui import MainDisplay log = logging.getLogger(__name__) diff --git a/openlp/core/lib/screen.py b/openlp/core/lib/screen.py index d1cca99cd..ddae9fba1 100644 --- a/openlp/core/lib/screen.py +++ b/openlp/core/lib/screen.py @@ -36,7 +36,8 @@ import copy from PyQt4 import QtCore -from openlp.core.lib import Registry, translate +from openlp.core.common import Settings, translate +from openlp.core.lib import Registry log = logging.getLogger(__name__) @@ -244,7 +245,6 @@ class ScreenList(object): """ Loads the screen size and the monitor number from the settings. """ - from openlp.core.lib import Settings # Add the screen settings to the settings dict. This has to be done here due to cyclic dependency. # Do not do this anywhere else. screen_settings = { diff --git a/openlp/core/lib/serviceitem.py b/openlp/core/lib/serviceitem.py index cbb3c8458..c1438840b 100644 --- a/openlp/core/lib/serviceitem.py +++ b/openlp/core/lib/serviceitem.py @@ -39,7 +39,8 @@ import uuid from PyQt4 import QtGui -from openlp.core.lib import ImageSource, Settings, Registry, build_icon, clean_tags, expand_tags, translate +from openlp.core.common import Settings +from openlp.core.lib import ImageSource, Registry, build_icon, clean_tags, expand_tags, translate log = logging.getLogger(__name__) diff --git a/openlp/core/lib/settings.py b/openlp/core/lib/settings.py deleted file mode 100644 index a124132ab..000000000 --- a/openlp/core/lib/settings.py +++ /dev/null @@ -1,499 +0,0 @@ -# -*- 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 # -############################################################################### -""" -This class contains the core default settings. -""" -import datetime -import logging -import os -import sys - -from PyQt4 import QtCore, QtGui - -from openlp.core.lib import SlideLimits, UiStrings -from openlp.core.lib.theme import ThemeLevel - - -log = logging.getLogger(__name__) - - -# Fix for bug #1014422. -X11_BYPASS_DEFAULT = True -if sys.platform.startswith('linux'): - # Default to False on Gnome. - X11_BYPASS_DEFAULT = bool(not os.environ.get('GNOME_DESKTOP_SESSION_ID')) - # Default to False on Xfce. - if os.environ.get('DESKTOP_SESSION') == 'xfce': - X11_BYPASS_DEFAULT = False - - -class Settings(QtCore.QSettings): - """ - Class to wrap QSettings. - - * Exposes all the methods of QSettings. - * Adds functionality for OpenLP Portable. If the ``defaultFormat`` is set to - ``IniFormat``, and the path to the Ini file is set using ``set_filename``, - then the Settings constructor (without any arguments) will create a Settings - object for accessing settings stored in that Ini file. - - ``__default_settings__`` - This dict contains all core settings with their default values. - - ``__obsolete_settings__`` - Each entry is structured in the following way:: - - (u'general/enable slide loop', u'advanced/slide limits', - [(SlideLimits.Wrap, True), (SlideLimits.End, False)]) - - The first entry is the *old key*; it will be removed. - - The second entry is the *new key*; we will add it to the config. If this is just an empty string, we just remove - the old key. - - The last entry is a list containing two-pair tuples. If the list is empty, no conversion is made. Otherwise each - pair describes how to convert the old setting's value:: - - (SlideLimits.Wrap, True) - - This means, that if the value of ``general/enable slide loop`` is equal (``==``) ``True`` then we set - ``advanced/slide limits`` to ``SlideLimits.Wrap``. **NOTE**, this means that the rules have to cover all cases! - So, if the type of the old value is bool, then there must be two rules. - """ - __default_settings__ = { - 'advanced/add page break': False, - 'advanced/alternate rows': not sys.platform.startswith('win'), - 'advanced/current media plugin': -1, - 'advanced/data path': '', - 'advanced/default color': '#ffffff', - 'advanced/default image': ':/graphics/openlp-splash-screen.png', - # 7 stands for now, 0 to 6 is Monday to Sunday. - 'advanced/default service day': 7, - 'advanced/default service enabled': True, - 'advanced/default service hour': 11, - 'advanced/default service minute': 0, - 'advanced/default service name': UiStrings().DefaultServiceName, - 'advanced/display size': 0, - 'advanced/double click live': False, - 'advanced/enable exit confirmation': True, - 'advanced/expand service item': False, - 'advanced/hide mouse': True, - 'advanced/is portable': False, - 'advanced/max recent files': 20, - 'advanced/print file meta data': False, - 'advanced/print notes': False, - 'advanced/print slide text': False, - 'advanced/recent file count': 4, - 'advanced/save current plugin': False, - 'advanced/slide limits': SlideLimits.End, - 'advanced/single click preview': False, - 'advanced/x11 bypass wm': X11_BYPASS_DEFAULT, - 'crashreport/last directory': '', - 'formattingTags/html_tags': '', - 'core/audio repeat list': False, - 'core/auto open': False, - 'core/auto preview': False, - 'core/audio start paused': True, - 'core/auto unblank': False, - 'core/blank warning': False, - 'core/ccli number': '', - 'core/has run wizard': False, - 'core/language': '[en]', - 'core/last version test': '', - 'core/loop delay': 5, - 'core/recent files': [], - 'core/save prompt': False, - 'core/screen blank': False, - 'core/show splash': True, - 'core/songselect password': '', - 'core/songselect username': '', - 'core/update check': True, - 'core/view mode': 'default', - # The other display settings (display position and dimensions) are defined in the ScreenList class due to a - # circular dependency. - 'core/display on monitor': True, - 'core/override position': False, - 'images/background color': '#000000', - 'media/players': 'webkit', - 'media/override player': QtCore.Qt.Unchecked, - 'players/background color': '#000000', - 'servicemanager/last directory': '', - 'servicemanager/last file': '', - 'servicemanager/service theme': '', - 'SettingsImport/file_date_created': datetime.datetime.now().strftime("%Y-%m-%d %H:%M"), - 'SettingsImport/Make_Changes': 'At_Own_RISK', - 'SettingsImport/type': 'OpenLP_settings_export', - 'SettingsImport/version': '', - 'shortcuts/aboutItem': [QtGui.QKeySequence('Ctrl+F1')], - 'shortcuts/addToService': [], - 'shortcuts/audioPauseItem': [], - 'shortcuts/displayTagItem': [], - 'shortcuts/blankScreen': [QtGui.QKeySequence(QtCore.Qt.Key_Period)], - 'shortcuts/collapse': [QtGui.QKeySequence(QtCore.Qt.Key_Minus)], - 'shortcuts/desktopScreen': [QtGui.QKeySequence('D')], - 'shortcuts/delete': [], - 'shortcuts/down': [QtGui.QKeySequence(QtCore.Qt.Key_Down)], - 'shortcuts/editSong': [], - 'shortcuts/escapeItem': [QtGui.QKeySequence(QtCore.Qt.Key_Escape)], - 'shortcuts/expand': [QtGui.QKeySequence(QtCore.Qt.Key_Plus)], - 'shortcuts/exportThemeItem': [], - 'shortcuts/fileNewItem': [QtGui.QKeySequence('Ctrl+N')], - 'shortcuts/fileSaveAsItem': [QtGui.QKeySequence('Ctrl+Shift+S')], - 'shortcuts/fileExitItem': [QtGui.QKeySequence('Alt+F4')], - 'shortcuts/fileSaveItem': [QtGui.QKeySequence('Ctrl+S')], - 'shortcuts/fileOpenItem': [QtGui.QKeySequence('Ctrl+O')], - 'shortcuts/goLive': [], - 'shortcuts/importThemeItem': [], - 'shortcuts/importBibleItem': [], - 'shortcuts/listViewBiblesDeleteItem': [QtGui.QKeySequence(QtCore.Qt.Key_Delete)], - 'shortcuts/listViewBiblesPreviewItem': [QtGui.QKeySequence(QtCore.Qt.Key_Enter), - QtGui.QKeySequence(QtCore.Qt.Key_Return)], - 'shortcuts/listViewBiblesLiveItem': [QtGui.QKeySequence(QtCore.Qt.ShiftModifier | QtCore.Qt.Key_Enter), - QtGui.QKeySequence(QtCore.Qt.ShiftModifier | QtCore.Qt.Key_Return)], - 'shortcuts/listViewBiblesServiceItem': [QtGui.QKeySequence(QtCore.Qt.Key_Plus), - QtGui.QKeySequence(QtCore.Qt.Key_Equal)], - 'shortcuts/listViewCustomDeleteItem': [QtGui.QKeySequence(QtCore.Qt.Key_Delete)], - 'shortcuts/listViewCustomPreviewItem': [QtGui.QKeySequence(QtCore.Qt.Key_Enter), - QtGui.QKeySequence(QtCore.Qt.Key_Return)], - 'shortcuts/listViewCustomLiveItem': [QtGui.QKeySequence(QtCore.Qt.ShiftModifier | QtCore.Qt.Key_Enter), - QtGui.QKeySequence(QtCore.Qt.ShiftModifier | QtCore.Qt.Key_Return)], - 'shortcuts/listViewCustomServiceItem': [QtGui.QKeySequence(QtCore.Qt.Key_Plus), - QtGui.QKeySequence(QtCore.Qt.Key_Equal)], - 'shortcuts/listViewImagesDeleteItem': [QtGui.QKeySequence(QtCore.Qt.Key_Delete)], - 'shortcuts/listViewImagesPreviewItem': [QtGui.QKeySequence(QtCore.Qt.Key_Enter), - QtGui.QKeySequence(QtCore.Qt.Key_Return)], - 'shortcuts/listViewImagesLiveItem': [QtGui.QKeySequence(QtCore.Qt.ShiftModifier | QtCore.Qt.Key_Enter), - QtGui.QKeySequence(QtCore.Qt.ShiftModifier | QtCore.Qt.Key_Return)], - 'shortcuts/listViewImagesServiceItem': [QtGui.QKeySequence(QtCore.Qt.Key_Plus), - QtGui.QKeySequence(QtCore.Qt.Key_Equal)], - 'shortcuts/listViewMediaDeleteItem': [QtGui.QKeySequence(QtCore.Qt.Key_Delete)], - 'shortcuts/listViewMediaPreviewItem': [QtGui.QKeySequence(QtCore.Qt.Key_Enter), - QtGui.QKeySequence(QtCore.Qt.Key_Return)], - 'shortcuts/listViewMediaLiveItem': [QtGui.QKeySequence(QtCore.Qt.ShiftModifier | QtCore.Qt.Key_Enter), - QtGui.QKeySequence(QtCore.Qt.ShiftModifier | QtCore.Qt.Key_Return)], - 'shortcuts/listViewMediaServiceItem': [QtGui.QKeySequence(QtCore.Qt.Key_Plus), - QtGui.QKeySequence(QtCore.Qt.Key_Equal)], - 'shortcuts/listViewPresentationsDeleteItem': [QtGui.QKeySequence(QtCore.Qt.Key_Delete)], - 'shortcuts/listViewPresentationsPreviewItem': [QtGui.QKeySequence(QtCore.Qt.Key_Enter), - QtGui.QKeySequence(QtCore.Qt.Key_Return)], - 'shortcuts/listViewPresentationsLiveItem': [QtGui.QKeySequence(QtCore.Qt.ShiftModifier | QtCore.Qt.Key_Enter), - QtGui.QKeySequence(QtCore.Qt.ShiftModifier | QtCore.Qt.Key_Return)], - 'shortcuts/listViewPresentationsServiceItem': [QtGui.QKeySequence(QtCore.Qt.Key_Plus), - QtGui.QKeySequence(QtCore.Qt.Key_Equal)], - 'shortcuts/listViewSongsDeleteItem': [QtGui.QKeySequence(QtCore.Qt.Key_Delete)], - 'shortcuts/listViewSongsPreviewItem': [QtGui.QKeySequence(QtCore.Qt.Key_Enter), - QtGui.QKeySequence(QtCore.Qt.Key_Return)], - 'shortcuts/listViewSongsLiveItem': [QtGui.QKeySequence(QtCore.Qt.ShiftModifier | QtCore.Qt.Key_Enter), - QtGui.QKeySequence(QtCore.Qt.ShiftModifier | QtCore.Qt.Key_Return)], - 'shortcuts/listViewSongsServiceItem': [QtGui.QKeySequence(QtCore.Qt.Key_Plus), - QtGui.QKeySequence(QtCore.Qt.Key_Equal)], - 'shortcuts/lockPanel': [], - 'shortcuts/modeDefaultItem': [], - 'shortcuts/modeLiveItem': [], - 'shortcuts/make_live': [QtGui.QKeySequence(QtCore.Qt.Key_Enter), QtGui.QKeySequence(QtCore.Qt.Key_Return)], - 'shortcuts/moveUp': [QtGui.QKeySequence(QtCore.Qt.Key_PageUp)], - 'shortcuts/moveTop': [QtGui.QKeySequence(QtCore.Qt.Key_Home)], - 'shortcuts/modeSetupItem': [], - 'shortcuts/moveBottom': [QtGui.QKeySequence(QtCore.Qt.Key_End)], - 'shortcuts/moveDown': [QtGui.QKeySequence(QtCore.Qt.Key_PageDown)], - 'shortcuts/nextTrackItem': [], - 'shortcuts/nextItem_live': [QtGui.QKeySequence(QtCore.Qt.Key_Down), - QtGui.QKeySequence(QtCore.Qt.Key_PageDown)], - 'shortcuts/nextItem_preview': [], - 'shortcuts/nextService': [QtGui.QKeySequence(QtCore.Qt.Key_Right)], - 'shortcuts/newService': [], - 'shortcuts/offlineHelpItem': [], - 'shortcuts/onlineHelpItem': [QtGui.QKeySequence('Alt+F1')], - 'shortcuts/openService': [], - 'shortcuts/saveService': [], - 'shortcuts/previousItem_live': [QtGui.QKeySequence(QtCore.Qt.Key_Up), - QtGui.QKeySequence(QtCore.Qt.Key_PageUp)], - 'shortcuts/playbackPause': [], - 'shortcuts/playbackPlay': [], - 'shortcuts/playbackStop': [], - 'shortcuts/playSlidesLoop': [], - 'shortcuts/playSlidesOnce': [], - 'shortcuts/previousService': [QtGui.QKeySequence(QtCore.Qt.Key_Left)], - 'shortcuts/previousItem_preview': [], - 'shortcuts/printServiceItem': [QtGui.QKeySequence('Ctrl+P')], - 'shortcuts/songExportItem': [], - 'shortcuts/songUsageStatus': [QtGui.QKeySequence(QtCore.Qt.Key_F4)], - 'shortcuts/searchShortcut': [QtGui.QKeySequence('Ctrl+F')], - 'shortcuts/settingsShortcutsItem': [], - 'shortcuts/settingsImportItem': [], - 'shortcuts/settingsPluginListItem': [QtGui.QKeySequence('Alt+F7')], - 'shortcuts/songUsageDelete': [], - 'shortcuts/settingsConfigureItem': [], - 'shortcuts/shortcutAction_B': [QtGui.QKeySequence('B')], - 'shortcuts/shortcutAction_C': [QtGui.QKeySequence('C')], - 'shortcuts/shortcutAction_E': [QtGui.QKeySequence('E')], - 'shortcuts/shortcutAction_I': [QtGui.QKeySequence('I')], - 'shortcuts/shortcutAction_O': [QtGui.QKeySequence('O')], - 'shortcuts/shortcutAction_P': [QtGui.QKeySequence('P')], - 'shortcuts/shortcutAction_V': [QtGui.QKeySequence('V')], - 'shortcuts/shortcutAction_0': [QtGui.QKeySequence('0')], - 'shortcuts/shortcutAction_1': [QtGui.QKeySequence('1')], - 'shortcuts/shortcutAction_2': [QtGui.QKeySequence('2')], - 'shortcuts/shortcutAction_3': [QtGui.QKeySequence('3')], - 'shortcuts/shortcutAction_4': [QtGui.QKeySequence('4')], - 'shortcuts/shortcutAction_5': [QtGui.QKeySequence('5')], - 'shortcuts/shortcutAction_6': [QtGui.QKeySequence('6')], - 'shortcuts/shortcutAction_7': [QtGui.QKeySequence('7')], - 'shortcuts/shortcutAction_8': [QtGui.QKeySequence('8')], - 'shortcuts/shortcutAction_9': [QtGui.QKeySequence('9')], - 'shortcuts/settingsExportItem': [], - 'shortcuts/songUsageReport': [], - 'shortcuts/songImportItem': [], - 'shortcuts/themeScreen': [QtGui.QKeySequence('T')], - 'shortcuts/toolsReindexItem': [], - 'shortcuts/toolsFindDuplicates': [], - 'shortcuts/toolsAlertItem': [QtGui.QKeySequence('F7')], - 'shortcuts/toolsFirstTimeWizard': [], - 'shortcuts/toolsOpenDataFolder': [], - 'shortcuts/toolsAddToolItem': [], - 'shortcuts/updateThemeImages': [], - 'shortcuts/up': [QtGui.QKeySequence(QtCore.Qt.Key_Up)], - 'shortcuts/viewThemeManagerItem': [QtGui.QKeySequence('F10')], - 'shortcuts/viewMediaManagerItem': [QtGui.QKeySequence('F8')], - 'shortcuts/viewPreviewPanel': [QtGui.QKeySequence('F11')], - 'shortcuts/viewLivePanel': [QtGui.QKeySequence('F12')], - 'shortcuts/viewServiceManagerItem': [QtGui.QKeySequence('F9')], - 'shortcuts/webSiteItem': [], - 'themes/global theme': '', - 'themes/last directory': '', - 'themes/last directory export': '', - 'themes/last directory import': '', - 'themes/theme level': ThemeLevel.Song, - 'user interface/live panel': True, - 'user interface/live splitter geometry': QtCore.QByteArray(), - 'user interface/lock panel': False, - 'user interface/main window geometry': QtCore.QByteArray(), - 'user interface/main window position': QtCore.QPoint(0, 0), - 'user interface/main window splitter geometry': QtCore.QByteArray(), - 'user interface/main window state': QtCore.QByteArray(), - 'user interface/preview panel': True, - 'user interface/preview splitter geometry': QtCore.QByteArray() - } - __file_path__ = '' - __obsolete_settings__ = [ - # Changed during 1.9.x development. - ('bibles/bookname language', 'bibles/book name language', []), - ('general/enable slide loop', 'advanced/slide limits', [(SlideLimits.Wrap, True), (SlideLimits.End, False)]), - ('songs/ccli number', 'core/ccli number', []), - ('media/use phonon', '', []), - # Changed during 2.1.x development. - ('advanced/stylesheet fix', '', []), - ('bibles/last directory 1', 'bibles/last directory import', []), - ('media/background color', 'players/background color', []), - ('themes/last directory', 'themes/last directory import', []), - ('themes/last directory 1', 'themes/last directory export', []), - ('songs/last directory 1', 'songs/last directory import', []), - ('songusage/last directory 1', 'songusage/last directory export', []), - ('user interface/mainwindow splitter geometry', 'user interface/main window splitter geometry', []), - ('shortcuts/makeLive', 'shortcuts/make_live', []), - ('general/audio repeat list', 'core/audio repeat list', []), - ('general/auto open', 'core/auto open', []), - ('general/auto preview', 'core/auto preview', []), - ('general/audio start paused', 'core/audio start paused', []), - ('general/auto unblank', 'core/auto unblank', []), - ('general/blank warning', 'core/blank warning', []), - ('general/ccli number', 'core/ccli number', []), - ('general/has run wizard', 'core/has run wizard', []), - ('general/language', 'core/language', []), - ('general/last version test', 'core/last version test', []), - ('general/loop delay', 'core/loop delay', []), - ('general/recent files', 'core/recent files', []), - ('general/save prompt', 'core/save prompt', []), - ('general/screen blank', 'core/screen blank', []), - ('general/show splash', 'core/show splash', []), - ('general/songselect password', 'core/songselect password', []), - ('general/songselect username', 'core/songselect username', []), - ('general/update check', 'core/update check', []), - ('general/view mode', 'core/view mode', []), - ('general/display on monitor', 'core/display on monitor', []), - ('general/override position', 'core/override position', []), - ('general/x position', 'core/x position', []), - ('general/y position', 'core/y position', []), - ('general/monitor', 'core/monitor', []), - ('general/height', 'core/height', []), - ('general/monitor', 'core/monitor', []), - ('general/width', 'core/width', []) - ] - - @staticmethod - def extend_default_settings(default_values): - """ - Static method to merge the given ``default_values`` with the ``Settings.__default_settings__``. - - ``default_values`` - A dict with setting keys and their default values. - """ - Settings.__default_settings__ = dict(list(default_values.items()) + list(Settings.__default_settings__.items())) - - @staticmethod - def set_filename(ini_file): - """ - Sets the complete path to an Ini file to be used by Settings objects. - - Does not affect existing Settings objects. - """ - Settings.__file_path__ = ini_file - - @staticmethod - def set_up_default_values(): - """ - This static method is called on start up. It is used to perform any operation on the __default_settings__ dict. - """ - # Make sure the string is translated (when building the dict the string is not translated because the translate - # function was not set up as this stage). - Settings.__default_settings__['advanced/default service name'] = UiStrings().DefaultServiceName - - def __init__(self, *args): - """ - Constructor which checks if this should be a native settings object, or an INI file. - """ - if not args and Settings.__file_path__ and Settings.defaultFormat() == Settings.IniFormat: - QtCore.QSettings.__init__(self, Settings.__file_path__, Settings.IniFormat) - else: - QtCore.QSettings.__init__(self, *args) - - def get_default_value(self, key): - """ - Get the default value of the given key - """ - if self.group(): - key = self.group() + '/' + key - return Settings.__default_settings__[key] - - def remove_obsolete_settings(self): - """ - This method is only called to clean up the config. It removes old settings and it renames settings. See - ``__obsolete_settings__`` for more details. - """ - for old_key, new_key, rules in Settings.__obsolete_settings__: - # Once removed we don't have to do this again. - if self.contains(old_key): - if new_key: - # Get the value of the old_key. - old_value = super(Settings, self).value(old_key) - # When we want to convert the value, we have to figure out the default value (because we cannot get - # the default value from the central settings dict. - if rules: - default_value = rules[0][1] - old_value = self._convert_value(old_value, default_value) - # Iterate over our rules and check what the old_value should be "converted" to. - for new, old in rules: - # If the value matches with the condition (rule), then use the provided value. This is used to - # convert values. E. g. an old value 1 results in True, and 0 in False. - if old == old_value: - old_value = new - break - self.setValue(new_key, old_value) - self.remove(old_key) - - def value(self, key): - """ - Returns the value for the given ``key``. The returned ``value`` is of the same type as the default value in the - *Settings.__default_settings__* dict. - - ``key`` - The key to return the value from. - """ - # if group() is not empty the group has not been specified together with the key. - if self.group(): - default_value = Settings.__default_settings__[self.group() + '/' + key] - else: - default_value = Settings.__default_settings__[key] - setting = super(Settings, self).value(key, default_value) - return self._convert_value(setting, default_value) - - def _convert_value(self, setting, default_value): - """ - This converts the given ``setting`` to the type of the given ``default_value``. - - ``setting`` - The setting to convert. This could be ``true`` for example.Settings() - - ``default_value`` - Indication the type the setting should be converted to. For example ``True`` (type is boolean), meaning that - we convert the string ``true`` to a python boolean. - - **Note**, this method only converts a few types and might need to be extended if a certain type is missing! - """ - # On OS X (and probably on other platforms too) empty value from QSettings is represented as type - # PyQt4.QtCore.QPyNullVariant. This type has to be converted to proper 'None' Python type. - if isinstance(setting, QtCore.QPyNullVariant) and setting.isNull(): - setting = None - # Handle 'None' type (empty value) properly. - if setting is None: - # An empty string saved to the settings results in a None type being returned. - # Convert it to empty unicode string. - if isinstance(default_value, str): - return '' - # An empty list saved to the settings results in a None type being returned. - else: - return [] - # Convert the setting to the correct type. - if isinstance(default_value, bool): - if isinstance(setting, bool): - return setting - # Sometimes setting is string instead of a boolean. - return setting == 'true' - if isinstance(default_value, int): - return int(setting) - return setting - - def get_files_from_config(self, plugin): - """ - This removes the settings needed for old way we saved files (e. g. the image paths for the image plugin). A list - of file paths are returned. - - **Note**: Only a list of paths is returned; this does not convert anything! - - ``plugin`` - The Plugin object.The caller has to convert/save the list himself; o - """ - files_list = [] - # We need QSettings instead of Settings here to bypass our central settings dict. - # Do NOT do this anywhere else! - settings = QtCore.QSettings(self.fileName(), Settings.IniFormat) - settings.beginGroup(plugin.settings_section) - if settings.contains('%s count' % plugin.name): - # Get the count. - list_count = int(settings.value('%s count' % plugin.name, 0)) - if list_count: - for counter in range(list_count): - # The keys were named e. g.: "image 0" - item = settings.value('%s %d' % (plugin.name, counter), '') - if item: - files_list.append(item) - settings.remove('%s %d' % (plugin.name, counter)) - settings.remove('%s count' % plugin.name) - settings.endGroup() - return files_list diff --git a/openlp/core/lib/theme.py b/openlp/core/lib/theme.py index c10a59560..98e98ac6e 100644 --- a/openlp/core/lib/theme.py +++ b/openlp/core/lib/theme.py @@ -43,15 +43,6 @@ from openlp.core.lib import str_to_bool, ScreenList, get_text_file_string log = logging.getLogger(__name__) -class ThemeLevel(object): - """ - Provides an enumeration for the level a theme applies to - """ - Global = 1 - Service = 2 - Song = 3 - - class BackgroundType(object): """ Type enumeration for backgrounds. diff --git a/openlp/core/lib/ui.py b/openlp/core/lib/ui.py index 14ffdc2e8..631187505 100644 --- a/openlp/core/lib/ui.py +++ b/openlp/core/lib/ui.py @@ -33,7 +33,8 @@ import logging from PyQt4 import QtCore, QtGui -from openlp.core.lib import Registry, UiStrings, build_icon, translate +from openlp.core.common import UiStrings, translate +from openlp.core.lib import Registry, build_icon from openlp.core.utils.actions import ActionList diff --git a/openlp/core/ui/aboutdialog.py b/openlp/core/ui/aboutdialog.py index e96553803..f825e9a63 100644 --- a/openlp/core/ui/aboutdialog.py +++ b/openlp/core/ui/aboutdialog.py @@ -29,7 +29,8 @@ from PyQt4 import QtGui -from openlp.core.lib import UiStrings, build_icon, translate +from openlp.core.common import UiStrings, translate +from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button, create_button_box diff --git a/openlp/core/ui/advancedtab.py b/openlp/core/ui/advancedtab.py index fbd44a9f8..50bdc57fa 100644 --- a/openlp/core/ui/advancedtab.py +++ b/openlp/core/ui/advancedtab.py @@ -36,10 +36,9 @@ import sys from PyQt4 import QtCore, QtGui -from openlp.core.common import AppLocation -from openlp.core.lib import SettingsTab, Settings, UiStrings, translate, build_icon +from openlp.core.common import AppLocation, Settings, SlideLimits, UiStrings, translate +from openlp.core.lib import SettingsTab, build_icon from openlp.core.utils import format_time, get_images_filter -from openlp.core.lib import SlideLimits log = logging.getLogger(__name__) diff --git a/openlp/core/ui/exceptionform.py b/openlp/core/ui/exceptionform.py index 2dc034f71..b2427e009 100644 --- a/openlp/core/ui/exceptionform.py +++ b/openlp/core/ui/exceptionform.py @@ -85,7 +85,7 @@ try: except ImportError: VLC_VERSION = '-' -from openlp.core.lib import UiStrings, Settings, translate +from openlp.core.common import Settings, UiStrings, translate from openlp.core.utils import get_application_version from .exceptiondialog import Ui_ExceptionDialog diff --git a/openlp/core/ui/firsttimeform.py b/openlp/core/ui/firsttimeform.py index cbb5434f9..31d9cf198 100644 --- a/openlp/core/ui/firsttimeform.py +++ b/openlp/core/ui/firsttimeform.py @@ -41,8 +41,8 @@ from configparser import SafeConfigParser from PyQt4 import QtCore, QtGui -from openlp.core.common import AppLocation, check_directory_exists -from openlp.core.lib import PluginStatus, Settings, Registry, build_icon, translate +from openlp.core.common import AppLocation, Settings, check_directory_exists +from openlp.core.lib import PluginStatus, Registry, build_icon, translate from openlp.core.utils import get_web_page from .firsttimewizard import Ui_FirstTimeWizard, FirstTimePage diff --git a/openlp/core/ui/formattingtagdialog.py b/openlp/core/ui/formattingtagdialog.py index 6d7dd2453..f7855e470 100644 --- a/openlp/core/ui/formattingtagdialog.py +++ b/openlp/core/ui/formattingtagdialog.py @@ -31,7 +31,8 @@ The UI widgets for the formatting tags window. """ from PyQt4 import QtCore, QtGui -from openlp.core.lib import UiStrings, translate, build_icon +from openlp.core.common import UiStrings, translate +from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box diff --git a/openlp/core/ui/generaltab.py b/openlp/core/ui/generaltab.py index 42bba94d3..2e758ed34 100644 --- a/openlp/core/ui/generaltab.py +++ b/openlp/core/ui/generaltab.py @@ -33,7 +33,8 @@ import logging from PyQt4 import QtCore, QtGui -from openlp.core.lib import Registry, Settings, SettingsTab, ScreenList, UiStrings, translate +from openlp.core.common import Settings, UiStrings, translate +from openlp.core.lib import Registry, SettingsTab, ScreenList log = logging.getLogger(__name__) diff --git a/openlp/core/ui/maindisplay.py b/openlp/core/ui/maindisplay.py index f2c1033ed..541e5002a 100644 --- a/openlp/core/ui/maindisplay.py +++ b/openlp/core/ui/maindisplay.py @@ -44,7 +44,8 @@ import sys from PyQt4 import QtCore, QtGui, QtWebKit, QtOpenGL from PyQt4.phonon import Phonon -from openlp.core.lib import ServiceItem, Settings, ImageSource, Registry, build_html, expand_tags, \ +from openlp.core.common import Settings +from openlp.core.lib import ServiceItem, ImageSource, Registry, build_html, expand_tags, \ image_to_byte, translate from openlp.core.lib.theme import BackgroundType diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index 4110c5e04..f397e3306 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -42,12 +42,12 @@ from datetime import datetime from PyQt4 import QtCore, QtGui from openlp.core.lib import Renderer, OpenLPDockWidget, PluginManager, ImageManager, PluginStatus, Registry, \ - Settings, ScreenList, build_icon, translate + ScreenList, build_icon, translate from openlp.core.lib.ui import UiStrings, create_action from openlp.core.ui import AboutForm, SettingsForm, ServiceManager, ThemeManager, SlideController, PluginForm, \ MediaDockManager, ShortcutListForm, FormattingTagForm -from openlp.core.common import AppLocation, check_directory_exists +from openlp.core.common import AppLocation, Settings, check_directory_exists from openlp.core.ui.media import MediaController from openlp.core.utils import LanguageManager, add_actions, get_application_version from openlp.core.utils.actions import ActionList, CategoryOrder diff --git a/openlp/core/ui/media/__init__.py b/openlp/core/ui/media/__init__.py index 19771862f..02c22fc68 100644 --- a/openlp/core/ui/media/__init__.py +++ b/openlp/core/ui/media/__init__.py @@ -31,7 +31,7 @@ The :mod:`~openlp.core.ui.media` module contains classes and objects for media p """ import logging -from openlp.core.lib import Settings +from openlp.core.common import Settings from PyQt4 import QtCore diff --git a/openlp/core/ui/media/mediacontroller.py b/openlp/core/ui/media/mediacontroller.py index 83b9630fc..eb2d932ee 100644 --- a/openlp/core/ui/media/mediacontroller.py +++ b/openlp/core/ui/media/mediacontroller.py @@ -35,7 +35,8 @@ import os import datetime from PyQt4 import QtCore, QtGui -from openlp.core.lib import OpenLPToolbar, Settings, Registry, UiStrings, translate +from openlp.core.common import Settings, UiStrings, translate +from openlp.core.lib import OpenLPToolbar, Registry from openlp.core.lib.ui import critical_error_message_box from openlp.core.ui.media import MediaState, MediaInfo, MediaType, get_media_players, set_media_players from openlp.core.ui.media.mediaplayer import MediaPlayer diff --git a/openlp/core/ui/media/phononplayer.py b/openlp/core/ui/media/phononplayer.py index 0ea0bf2ff..fadd8a694 100644 --- a/openlp/core/ui/media/phononplayer.py +++ b/openlp/core/ui/media/phononplayer.py @@ -36,7 +36,8 @@ from datetime import datetime from PyQt4 import QtGui from PyQt4.phonon import Phonon -from openlp.core.lib import Settings, translate +from openlp.core.common import Settings +from openlp.core.lib import translate from openlp.core.ui.media import MediaState from openlp.core.ui.media.mediaplayer import MediaPlayer diff --git a/openlp/core/ui/media/playertab.py b/openlp/core/ui/media/playertab.py index b01f37ac7..4aa9feb0f 100644 --- a/openlp/core/ui/media/playertab.py +++ b/openlp/core/ui/media/playertab.py @@ -31,7 +31,8 @@ The :mod:`~openlp.core.ui.media.playertab` module holds the configuration tab fo """ from PyQt4 import QtCore, QtGui -from openlp.core.lib import Registry, SettingsTab, Settings, UiStrings, translate +from openlp.core.common import Settings, UiStrings, translate +from openlp.core.lib import Registry, SettingsTab from openlp.core.lib.ui import create_button from openlp.core.ui.media import get_media_players, set_media_players diff --git a/openlp/core/ui/media/vlcplayer.py b/openlp/core/ui/media/vlcplayer.py index 2055f287b..139751603 100644 --- a/openlp/core/ui/media/vlcplayer.py +++ b/openlp/core/ui/media/vlcplayer.py @@ -37,7 +37,8 @@ import sys from PyQt4 import QtGui -from openlp.core.lib import Settings, translate +from openlp.core.common import Settings +from openlp.core.lib import translate from openlp.core.ui.media import MediaState from openlp.core.ui.media.mediaplayer import MediaPlayer diff --git a/openlp/core/ui/media/webkitplayer.py b/openlp/core/ui/media/webkitplayer.py index 3983a1e8d..c8cae2d13 100644 --- a/openlp/core/ui/media/webkitplayer.py +++ b/openlp/core/ui/media/webkitplayer.py @@ -33,7 +33,8 @@ from PyQt4 import QtGui import logging -from openlp.core.lib import Settings, translate +from openlp.core.common import Settings +from openlp.core.lib import translate from openlp.core.ui.media import MediaState from openlp.core.ui.media.mediaplayer import MediaPlayer diff --git a/openlp/core/ui/plugindialog.py b/openlp/core/ui/plugindialog.py index d0bc0f103..fe7a185d6 100644 --- a/openlp/core/ui/plugindialog.py +++ b/openlp/core/ui/plugindialog.py @@ -31,7 +31,7 @@ The UI widgets of the plugin view dialog #""" from PyQt4 import QtCore, QtGui -from openlp.core.lib import UiStrings, translate +from openlp.core.common import UiStrings, translate from openlp.core.lib.ui import create_button_box diff --git a/openlp/core/ui/printservicedialog.py b/openlp/core/ui/printservicedialog.py index 6f007cf64..13308d6aa 100644 --- a/openlp/core/ui/printservicedialog.py +++ b/openlp/core/ui/printservicedialog.py @@ -31,7 +31,8 @@ The UI widgets of the print service dialog. """ from PyQt4 import QtCore, QtGui -from openlp.core.lib import SpellTextEdit, UiStrings, build_icon, translate +from openlp.core.common import UiStrings, translate +from openlp.core.lib import SpellTextEdit, build_icon class ZoomSize(object): diff --git a/openlp/core/ui/printserviceform.py b/openlp/core/ui/printserviceform.py index 153b6bed9..6c2dab2be 100644 --- a/openlp/core/ui/printserviceform.py +++ b/openlp/core/ui/printserviceform.py @@ -36,7 +36,8 @@ import os from PyQt4 import QtCore, QtGui from lxml import html -from openlp.core.lib import Settings, UiStrings, Registry, translate, get_text_file_string +from openlp.core.common import Settings, UiStrings, translate +from openlp.core.lib import Registry, get_text_file_string from openlp.core.ui.printservicedialog import Ui_PrintServiceDialog, ZoomSize from openlp.core.common import AppLocation diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index ee4fa850f..20989bb90 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -42,10 +42,10 @@ log = logging.getLogger(__name__) from PyQt4 import QtCore, QtGui -from openlp.core.common import AppLocation, check_directory_exists -from openlp.core.lib import OpenLPToolbar, ServiceItem, ItemCapabilities, Settings, PluginStatus, Registry, \ - UiStrings, build_icon, translate -from openlp.core.lib.theme import ThemeLevel +from openlp.core.common import AppLocation, Settings, check_directory_exists, UiStrings, translate +from openlp.core.lib import OpenLPToolbar, ServiceItem, ItemCapabilities, PluginStatus, Registry, \ + build_icon +from openlp.core.common import ThemeLevel from openlp.core.lib.ui import critical_error_message_box, create_widget_action, find_and_set_in_combo_box from openlp.core.ui import ServiceNoteForm, ServiceItemEditForm, StartTimeForm from openlp.core.ui.printserviceform import PrintServiceForm diff --git a/openlp/core/ui/shortcutlistform.py b/openlp/core/ui/shortcutlistform.py index f49b66678..efe876e3e 100644 --- a/openlp/core/ui/shortcutlistform.py +++ b/openlp/core/ui/shortcutlistform.py @@ -33,8 +33,8 @@ import re from PyQt4 import QtCore, QtGui -from openlp.core.lib import Registry, Settings -from openlp.core.utils import translate +from openlp.core.lib import Registry +from openlp.core.common import Settings, translate from openlp.core.utils.actions import ActionList from .shortcutlistdialog import Ui_ShortcutListDialog diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index a04af6525..171a24f16 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -37,8 +37,9 @@ from collections import deque from PyQt4 import QtCore, QtGui -from openlp.core.lib import OpenLPToolbar, ItemCapabilities, ServiceItem, ImageSource, SlideLimits, \ - ServiceItemAction, Settings, Registry, UiStrings, ScreenList, build_icon, build_html, translate +from openlp.core.common import Settings, SlideLimits, UiStrings, translate +from openlp.core.lib import OpenLPToolbar, ItemCapabilities, ServiceItem, ImageSource, \ + ServiceItemAction, Registry, ScreenList, build_icon, build_html from openlp.core.ui import HideMode, MainDisplay, Display, DisplayControllerType from openlp.core.lib.ui import create_action from openlp.core.utils.actions import ActionList, CategoryOrder diff --git a/openlp/core/ui/starttimedialog.py b/openlp/core/ui/starttimedialog.py index dfd794f26..24e11c14e 100644 --- a/openlp/core/ui/starttimedialog.py +++ b/openlp/core/ui/starttimedialog.py @@ -31,7 +31,7 @@ The UI widgets for the time dialog """ from PyQt4 import QtCore, QtGui -from openlp.core.lib import UiStrings, translate +from openlp.core.common import UiStrings, translate from openlp.core.lib.ui import create_button_box diff --git a/openlp/core/ui/starttimeform.py b/openlp/core/ui/starttimeform.py index 0a0867b3f..308453978 100644 --- a/openlp/core/ui/starttimeform.py +++ b/openlp/core/ui/starttimeform.py @@ -33,7 +33,8 @@ from PyQt4 import QtGui from .starttimedialog import Ui_StartTimeDialog -from openlp.core.lib import UiStrings, Registry, translate +from openlp.core.common import UiStrings, translate +from openlp.core.lib import Registry from openlp.core.lib.ui import critical_error_message_box diff --git a/openlp/core/ui/themeform.py b/openlp/core/ui/themeform.py index fe92d679b..46f632827 100644 --- a/openlp/core/ui/themeform.py +++ b/openlp/core/ui/themeform.py @@ -34,7 +34,8 @@ import os from PyQt4 import QtCore, QtGui -from openlp.core.lib import UiStrings, Registry, translate +from openlp.core.common import UiStrings, translate +from openlp.core.lib import Registry from openlp.core.lib.theme import BackgroundType, BackgroundGradientType from openlp.core.lib.ui import critical_error_message_box from openlp.core.ui import ThemeLayoutForm diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py index 8dd0727ea..daee4c320 100644 --- a/openlp/core/ui/thememanager.py +++ b/openlp/core/ui/thememanager.py @@ -38,9 +38,9 @@ import re from xml.etree.ElementTree import ElementTree, XML from PyQt4 import QtCore, QtGui -from openlp.core.common import AppLocation, check_directory_exists -from openlp.core.lib import ImageSource, OpenLPToolbar, Registry, Settings, UiStrings, get_text_file_string, \ - build_icon, translate, check_item_selected, create_thumb, validate_thumb +from openlp.core.common import AppLocation, Settings, check_directory_exists, UiStrings, translate +from openlp.core.lib import ImageSource, OpenLPToolbar, Registry, get_text_file_string, \ + build_icon, check_item_selected, create_thumb, validate_thumb from openlp.core.lib.theme import ThemeXML, BackgroundType from openlp.core.lib.ui import critical_error_message_box, create_widget_action from openlp.core.ui import FileRenameForm, ThemeForm, ThemeManagerHelper diff --git a/openlp/core/ui/themestab.py b/openlp/core/ui/themestab.py index c3c93f3af..be06f6ffc 100644 --- a/openlp/core/ui/themestab.py +++ b/openlp/core/ui/themestab.py @@ -33,8 +33,8 @@ The Themes configuration tab from PyQt4 import QtCore, QtGui -from openlp.core.lib import Registry, Settings, SettingsTab, UiStrings, translate -from openlp.core.lib.theme import ThemeLevel +from openlp.core.common import Settings, ThemeLevel, UiStrings, translate +from openlp.core.lib import Registry, SettingsTab from openlp.core.lib.ui import find_and_set_in_combo_box diff --git a/openlp/core/ui/themewizard.py b/openlp/core/ui/themewizard.py index f0674e924..d681a4428 100644 --- a/openlp/core/ui/themewizard.py +++ b/openlp/core/ui/themewizard.py @@ -31,7 +31,8 @@ The Create/Edit theme wizard """ from PyQt4 import QtCore, QtGui -from openlp.core.lib import UiStrings, build_icon, translate +from openlp.core.common import UiStrings, translate +from openlp.core.lib import build_icon from openlp.core.lib.theme import HorizontalType, BackgroundType, BackgroundGradientType from openlp.core.lib.ui import add_welcome_page, create_valign_selection_widgets diff --git a/openlp/core/ui/wizard.py b/openlp/core/ui/wizard.py index 3a8669b1c..255695a65 100644 --- a/openlp/core/ui/wizard.py +++ b/openlp/core/ui/wizard.py @@ -32,9 +32,10 @@ The :mod:``wizard`` module provides generic wizard tools for OpenLP. import logging import os -from PyQt4 import QtCore, QtGui +from PyQt4 import QtGui -from openlp.core.lib import Registry, Settings, UiStrings, build_icon, translate +from openlp.core.common import Settings, UiStrings, translate +from openlp.core.lib import Registry, build_icon from openlp.core.lib.ui import add_welcome_page log = logging.getLogger(__name__) diff --git a/openlp/core/utils/__init__.py b/openlp/core/utils/__init__.py index d07153a2a..c75e8782a 100644 --- a/openlp/core/utils/__init__.py +++ b/openlp/core/utils/__init__.py @@ -43,8 +43,8 @@ import urllib.parse from PyQt4 import QtGui, QtCore -from openlp.core.common import AppLocation -from openlp.core.lib import Registry, Settings +from openlp.core.common import AppLocation, Settings +from openlp.core.lib import Registry if sys.platform != 'win32' and sys.platform != 'darwin': diff --git a/openlp/core/utils/actions.py b/openlp/core/utils/actions.py index 6feeda276..82619a6e7 100644 --- a/openlp/core/utils/actions.py +++ b/openlp/core/utils/actions.py @@ -34,7 +34,7 @@ import logging from PyQt4 import QtCore, QtGui -from openlp.core.lib import Settings +from openlp.core.common import Settings log = logging.getLogger(__name__) diff --git a/openlp/core/utils/languagemanager.py b/openlp/core/utils/languagemanager.py index d6b3cd668..63f2e6f24 100644 --- a/openlp/core/utils/languagemanager.py +++ b/openlp/core/utils/languagemanager.py @@ -35,8 +35,8 @@ import sys from PyQt4 import QtCore, QtGui -from openlp.core.common import AppLocation -from openlp.core.lib import Settings, translate +from openlp.core.common import AppLocation, Settings +from openlp.core.lib import translate log = logging.getLogger(__name__) diff --git a/openlp/plugins/alerts/alertsplugin.py b/openlp/plugins/alerts/alertsplugin.py index b13230196..1dcdb13c7 100644 --- a/openlp/plugins/alerts/alertsplugin.py +++ b/openlp/plugins/alerts/alertsplugin.py @@ -31,7 +31,8 @@ import logging from PyQt4 import QtGui -from openlp.core.lib import Plugin, Settings, StringContent, build_icon, translate +from openlp.core.common import Settings +from openlp.core.lib import Plugin, StringContent, build_icon, translate from openlp.core.lib.db import Manager from openlp.core.lib.ui import create_action, UiStrings from openlp.core.lib.theme import VerticalType diff --git a/openlp/plugins/alerts/lib/alertstab.py b/openlp/plugins/alerts/lib/alertstab.py index f57caaf7c..5a1512c4b 100644 --- a/openlp/plugins/alerts/lib/alertstab.py +++ b/openlp/plugins/alerts/lib/alertstab.py @@ -29,7 +29,8 @@ from PyQt4 import QtGui -from openlp.core.lib import SettingsTab, Settings, UiStrings, translate +from openlp.core.common import Settings, UiStrings, translate +from openlp.core.lib import SettingsTab from openlp.core.lib.ui import create_valign_selection_widgets diff --git a/openlp/plugins/bibles/forms/bibleimportform.py b/openlp/plugins/bibles/forms/bibleimportform.py index 93bb5d565..f99d8138b 100644 --- a/openlp/plugins/bibles/forms/bibleimportform.py +++ b/openlp/plugins/bibles/forms/bibleimportform.py @@ -34,8 +34,7 @@ import os from PyQt4 import QtGui -from openlp.core.common import AppLocation -from openlp.core.lib import Settings, UiStrings, translate +from openlp.core.common import AppLocation, Settings, UiStrings, translate from openlp.core.lib.db import delete_database from openlp.core.lib.ui import critical_error_message_box from openlp.core.ui.wizard import OpenLPWizard, WizardStrings diff --git a/openlp/plugins/bibles/forms/bibleupgradeform.py b/openlp/plugins/bibles/forms/bibleupgradeform.py index ca74d6666..cc164363b 100644 --- a/openlp/plugins/bibles/forms/bibleupgradeform.py +++ b/openlp/plugins/bibles/forms/bibleupgradeform.py @@ -36,8 +36,8 @@ from tempfile import gettempdir from PyQt4 import QtCore, QtGui -from openlp.core.common import AppLocation, check_directory_exists -from openlp.core.lib import Registry, Settings, UiStrings, translate +from openlp.core.common import AppLocation, UiStrings, Settings, check_directory_exists, translate +from openlp.core.lib import Registry from openlp.core.lib.ui import critical_error_message_box from openlp.core.ui.wizard import OpenLPWizard, WizardStrings from openlp.core.utils import delete_file diff --git a/openlp/plugins/bibles/forms/booknamedialog.py b/openlp/plugins/bibles/forms/booknamedialog.py index 90a5bae40..605a4fb95 100644 --- a/openlp/plugins/bibles/forms/booknamedialog.py +++ b/openlp/plugins/bibles/forms/booknamedialog.py @@ -29,7 +29,7 @@ from PyQt4 import QtCore, QtGui -from openlp.core.lib import translate +from openlp.core.common import translate from openlp.core.lib.ui import create_button_box class Ui_BookNameDialog(object): diff --git a/openlp/plugins/bibles/forms/booknameform.py b/openlp/plugins/bibles/forms/booknameform.py index 04a0e2fe7..1fb359c86 100644 --- a/openlp/plugins/bibles/forms/booknameform.py +++ b/openlp/plugins/bibles/forms/booknameform.py @@ -36,7 +36,7 @@ import re from PyQt4.QtGui import QDialog from PyQt4 import QtCore -from openlp.core.lib import translate +from openlp.core.common import translate from openlp.core.lib.ui import critical_error_message_box from openlp.plugins.bibles.forms.booknamedialog import Ui_BookNameDialog from openlp.plugins.bibles.lib import BibleStrings diff --git a/openlp/plugins/bibles/forms/editbibledialog.py b/openlp/plugins/bibles/forms/editbibledialog.py index 6e608d8df..6c832f5ee 100644 --- a/openlp/plugins/bibles/forms/editbibledialog.py +++ b/openlp/plugins/bibles/forms/editbibledialog.py @@ -29,7 +29,8 @@ from PyQt4 import QtCore, QtGui -from openlp.core.lib import build_icon, translate +from openlp.core.common import translate +from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box from openlp.plugins.bibles.lib import LanguageSelection, BibleStrings from openlp.plugins.bibles.lib.db import BiblesResourcesDB diff --git a/openlp/plugins/bibles/forms/editbibleform.py b/openlp/plugins/bibles/forms/editbibleform.py index e0163a8b8..5e2c94f79 100644 --- a/openlp/plugins/bibles/forms/editbibleform.py +++ b/openlp/plugins/bibles/forms/editbibleform.py @@ -33,7 +33,8 @@ import re from PyQt4 import QtGui -from openlp.core.lib import Registry, UiStrings, translate +from openlp.core.common import UiStrings, translate +from openlp.core.lib import Registry from openlp.core.lib.ui import critical_error_message_box from .editbibledialog import Ui_EditBibleDialog from openlp.plugins.bibles.lib import BibleStrings diff --git a/openlp/plugins/bibles/forms/languagedialog.py b/openlp/plugins/bibles/forms/languagedialog.py index 533848187..c69eb8828 100644 --- a/openlp/plugins/bibles/forms/languagedialog.py +++ b/openlp/plugins/bibles/forms/languagedialog.py @@ -29,7 +29,7 @@ from PyQt4 import QtGui -from openlp.core.lib import translate +from openlp.core.common import translate from openlp.core.lib.ui import create_button_box class Ui_LanguageDialog(object): diff --git a/openlp/plugins/bibles/forms/languageform.py b/openlp/plugins/bibles/forms/languageform.py index 88d9906cd..dcfb14462 100644 --- a/openlp/plugins/bibles/forms/languageform.py +++ b/openlp/plugins/bibles/forms/languageform.py @@ -34,7 +34,7 @@ import logging from PyQt4.QtGui import QDialog -from openlp.core.lib import translate +from openlp.core.common import translate from openlp.core.lib.ui import critical_error_message_box from openlp.plugins.bibles.forms.languagedialog import \ Ui_LanguageDialog diff --git a/openlp/plugins/bibles/lib/__init__.py b/openlp/plugins/bibles/lib/__init__.py index df816d436..38575a974 100644 --- a/openlp/plugins/bibles/lib/__init__.py +++ b/openlp/plugins/bibles/lib/__init__.py @@ -33,7 +33,8 @@ plugin. import logging import re -from openlp.core.lib import Settings, translate +from openlp.core.common import Settings +from openlp.core.lib import translate log = logging.getLogger(__name__) diff --git a/openlp/plugins/bibles/lib/biblestab.py b/openlp/plugins/bibles/lib/biblestab.py index dd95d9b33..807cb2195 100644 --- a/openlp/plugins/bibles/lib/biblestab.py +++ b/openlp/plugins/bibles/lib/biblestab.py @@ -31,7 +31,8 @@ import logging from PyQt4 import QtCore, QtGui -from openlp.core.lib import Registry, SettingsTab, Settings, UiStrings, translate +from openlp.core.common import Settings, UiStrings, translate +from openlp.core.lib import Registry, SettingsTab from openlp.core.lib.ui import find_and_set_in_combo_box from openlp.plugins.bibles.lib import LayoutStyle, DisplayStyle, update_reference_separators, \ get_reference_separator, LanguageSelection diff --git a/openlp/plugins/bibles/lib/manager.py b/openlp/plugins/bibles/lib/manager.py index a05d0c301..fc7c31d15 100644 --- a/openlp/plugins/bibles/lib/manager.py +++ b/openlp/plugins/bibles/lib/manager.py @@ -30,8 +30,8 @@ import logging import os -from openlp.core.common import AppLocation -from openlp.core.lib import Registry, Settings, translate +from openlp.core.common import AppLocation, Settings +from openlp.core.lib import Registry, translate from openlp.core.utils import delete_file from openlp.plugins.bibles.lib import parse_reference, get_reference_separator, LanguageSelection from openlp.plugins.bibles.lib.db import BibleDB, BibleMeta diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index 4ccd37df1..437dfd65e 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -31,8 +31,9 @@ import logging from PyQt4 import QtCore, QtGui -from openlp.core.lib import Registry, MediaManagerItem, ItemCapabilities, ServiceItemContext, Settings, UiStrings, \ - create_separated_list, translate +from openlp.core.common import Settings, UiStrings, translate +from openlp.core.lib import Registry, MediaManagerItem, ItemCapabilities, ServiceItemContext, \ + create_separated_list from openlp.core.lib.searchedit import SearchEdit from openlp.core.lib.ui import set_case_insensitive_completer, create_horizontal_adjusting_combo_box, \ critical_error_message_box, find_and_set_in_combo_box, build_icon diff --git a/openlp/plugins/custom/forms/editcustomdialog.py b/openlp/plugins/custom/forms/editcustomdialog.py index 20adefa9a..106b3e339 100644 --- a/openlp/plugins/custom/forms/editcustomdialog.py +++ b/openlp/plugins/custom/forms/editcustomdialog.py @@ -29,7 +29,8 @@ from PyQt4 import QtGui -from openlp.core.lib import UiStrings, build_icon, translate +from openlp.core.common import UiStrings, translate +from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box, create_button diff --git a/openlp/plugins/custom/forms/editcustomslidedialog.py b/openlp/plugins/custom/forms/editcustomslidedialog.py index 80b3c8cc9..bf000d308 100644 --- a/openlp/plugins/custom/forms/editcustomslidedialog.py +++ b/openlp/plugins/custom/forms/editcustomslidedialog.py @@ -29,7 +29,8 @@ from PyQt4 import QtGui -from openlp.core.lib import SpellTextEdit, UiStrings, translate +from openlp.core.common import UiStrings, translate +from openlp.core.lib import SpellTextEdit from openlp.core.lib.ui import create_button, create_button_box class Ui_CustomSlideEditDialog(object): diff --git a/openlp/plugins/custom/forms/editcustomslideform.py b/openlp/plugins/custom/forms/editcustomslideform.py index 65a8deb68..181f6c6af 100644 --- a/openlp/plugins/custom/forms/editcustomslideform.py +++ b/openlp/plugins/custom/forms/editcustomslideform.py @@ -30,7 +30,7 @@ import logging -from PyQt4 import QtCore, QtGui +from PyQt4 import QtGui from .editcustomslidedialog import Ui_CustomSlideEditDialog diff --git a/openlp/plugins/custom/lib/customtab.py b/openlp/plugins/custom/lib/customtab.py index f62c4547d..c66711f49 100644 --- a/openlp/plugins/custom/lib/customtab.py +++ b/openlp/plugins/custom/lib/customtab.py @@ -33,7 +33,8 @@ for the Custom Slides plugin, which is inserted into the configuration dialog. from PyQt4 import QtCore, QtGui -from openlp.core.lib import SettingsTab, Settings, translate +from openlp.core.common import Settings, translate +from openlp.core.lib import SettingsTab class CustomTab(SettingsTab): diff --git a/openlp/plugins/custom/lib/mediaitem.py b/openlp/plugins/custom/lib/mediaitem.py index f5b518ce8..386045b40 100644 --- a/openlp/plugins/custom/lib/mediaitem.py +++ b/openlp/plugins/custom/lib/mediaitem.py @@ -32,8 +32,9 @@ import logging from PyQt4 import QtCore, QtGui from sqlalchemy.sql import or_, func, and_ -from openlp.core.lib import Registry, MediaManagerItem, ItemCapabilities, ServiceItemContext, Settings, PluginStatus,\ - UiStrings, check_item_selected, translate +from openlp.core.common import Settings, UiStrings, translate +from openlp.core.lib import Registry, MediaManagerItem, ItemCapabilities, ServiceItemContext, PluginStatus,\ + check_item_selected from openlp.plugins.custom.forms.editcustomform import EditCustomForm from openlp.plugins.custom.lib import CustomXMLParser, CustomXMLBuilder from openlp.plugins.custom.lib.db import CustomSlide diff --git a/openlp/plugins/images/imageplugin.py b/openlp/plugins/images/imageplugin.py index a6712d941..b82cfa184 100644 --- a/openlp/plugins/images/imageplugin.py +++ b/openlp/plugins/images/imageplugin.py @@ -31,10 +31,11 @@ from PyQt4 import QtGui import logging -from openlp.core.lib import Plugin, StringContent, Registry, ImageSource, Settings, build_icon, translate +from openlp.core.common import Settings +from openlp.core.lib import Plugin, StringContent, Registry, ImageSource, build_icon, translate from openlp.core.lib.db import Manager from openlp.plugins.images.lib import ImageMediaItem, ImageTab -from openlp.plugins.images.lib.db import init_schema, ImageFilenames +from openlp.plugins.images.lib.db import init_schema log = logging.getLogger(__name__) diff --git a/openlp/plugins/images/lib/imagetab.py b/openlp/plugins/images/lib/imagetab.py index b408c1361..20e810b7e 100644 --- a/openlp/plugins/images/lib/imagetab.py +++ b/openlp/plugins/images/lib/imagetab.py @@ -29,7 +29,8 @@ from PyQt4 import QtGui -from openlp.core.lib import Registry, SettingsTab, Settings, UiStrings, translate +from openlp.core.common import Settings, UiStrings, translate +from openlp.core.lib import SettingsTab class ImageTab(SettingsTab): diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index ce7d55e17..976a3d47e 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -32,9 +32,9 @@ import os from PyQt4 import QtCore, QtGui -from openlp.core.common import AppLocation, check_directory_exists -from openlp.core.lib import ItemCapabilities, MediaManagerItem, Registry, ServiceItemContext, Settings, \ - StringContent, TreeWidgetWithDnD, UiStrings, build_icon, check_item_selected, create_thumb, translate, \ +from openlp.core.common import AppLocation, Settings, UiStrings, check_directory_exists, translate +from openlp.core.lib import ItemCapabilities, MediaManagerItem, Registry, ServiceItemContext, \ + StringContent, TreeWidgetWithDnD, build_icon, check_item_selected, create_thumb, \ validate_thumb from openlp.core.lib.ui import create_widget_action, critical_error_message_box from openlp.core.utils import delete_file, get_locale_key, get_images_filter diff --git a/openlp/plugins/media/lib/mediaitem.py b/openlp/plugins/media/lib/mediaitem.py index dc6b1456f..3d2d5b26e 100644 --- a/openlp/plugins/media/lib/mediaitem.py +++ b/openlp/plugins/media/lib/mediaitem.py @@ -32,9 +32,9 @@ import os from PyQt4 import QtCore, QtGui -from openlp.core.common import AppLocation, check_directory_exists +from openlp.core.common import AppLocation, Settings, check_directory_exists, UiStrings, translate from openlp.core.lib import ItemCapabilities, MediaManagerItem,MediaType, Registry, ServiceItem, ServiceItemContext, \ - Settings, UiStrings, build_icon, check_item_selected, translate + build_icon, check_item_selected from openlp.core.lib.ui import critical_error_message_box, create_horizontal_adjusting_combo_box from openlp.core.ui import DisplayController, Display, DisplayControllerType from openlp.core.ui.media import get_media_players, set_media_players diff --git a/openlp/plugins/media/lib/mediatab.py b/openlp/plugins/media/lib/mediatab.py index 7798b2cf3..40b6a1ea8 100644 --- a/openlp/plugins/media/lib/mediatab.py +++ b/openlp/plugins/media/lib/mediatab.py @@ -29,7 +29,8 @@ from PyQt4 import QtGui -from openlp.core.lib import Settings, SettingsTab, UiStrings, translate +from openlp.core.common import Settings, UiStrings, translate +from openlp.core.lib import SettingsTab class MediaQ_check_box(QtGui.QCheckBox): diff --git a/openlp/plugins/media/mediaplugin.py b/openlp/plugins/media/mediaplugin.py index 896ff95d1..cd432c54c 100644 --- a/openlp/plugins/media/mediaplugin.py +++ b/openlp/plugins/media/mediaplugin.py @@ -31,7 +31,7 @@ import logging from PyQt4 import QtCore -from openlp.core.lib import Plugin, Registry, StringContent, Settings, build_icon, translate +from openlp.core.lib import Plugin, Registry, StringContent, build_icon, translate from openlp.plugins.media.lib import MediaMediaItem, MediaTab diff --git a/openlp/plugins/presentations/lib/mediaitem.py b/openlp/plugins/presentations/lib/mediaitem.py index 695baddc5..ce0325c7e 100644 --- a/openlp/plugins/presentations/lib/mediaitem.py +++ b/openlp/plugins/presentations/lib/mediaitem.py @@ -32,8 +32,9 @@ import os from PyQt4 import QtCore, QtGui -from openlp.core.lib import MediaManagerItem, Registry, ItemCapabilities, ServiceItemContext, Settings, UiStrings, \ - build_icon, check_item_selected, create_thumb, translate, validate_thumb +from openlp.core.common import Settings, UiStrings, translate +from openlp.core.lib import MediaManagerItem, Registry, ItemCapabilities, ServiceItemContext, \ + build_icon, check_item_selected, create_thumb, validate_thumb from openlp.core.lib.ui import critical_error_message_box, create_horizontal_adjusting_combo_box from openlp.core.utils import get_locale_key from openlp.plugins.presentations.lib import MessageListener diff --git a/openlp/plugins/presentations/lib/presentationcontroller.py b/openlp/plugins/presentations/lib/presentationcontroller.py index 08584038e..17911606c 100644 --- a/openlp/plugins/presentations/lib/presentationcontroller.py +++ b/openlp/plugins/presentations/lib/presentationcontroller.py @@ -33,8 +33,8 @@ import shutil from PyQt4 import QtCore -from openlp.core.common import AppLocation, check_directory_exists -from openlp.core.lib import Registry, Settings, create_thumb, validate_thumb +from openlp.core.common import AppLocation, Settings, check_directory_exists +from openlp.core.lib import Registry, create_thumb, validate_thumb log = logging.getLogger(__name__) diff --git a/openlp/plugins/presentations/lib/presentationtab.py b/openlp/plugins/presentations/lib/presentationtab.py index fed02ce75..50b066850 100644 --- a/openlp/plugins/presentations/lib/presentationtab.py +++ b/openlp/plugins/presentations/lib/presentationtab.py @@ -29,7 +29,8 @@ from PyQt4 import QtGui -from openlp.core.lib import Settings, SettingsTab, UiStrings, translate +from openlp.core.common import Settings, UiStrings, translate +from openlp.core.lib import SettingsTab class PresentationTab(SettingsTab): diff --git a/openlp/plugins/remotes/lib/httprouter.py b/openlp/plugins/remotes/lib/httprouter.py index 42b45abc6..ce13ed812 100644 --- a/openlp/plugins/remotes/lib/httprouter.py +++ b/openlp/plugins/remotes/lib/httprouter.py @@ -124,9 +124,8 @@ from urllib.parse import urlparse, parse_qs from mako.template import Template from PyQt4 import QtCore -from openlp.core.common import AppLocation -from openlp.core.lib import Registry, Settings, PluginStatus, StringContent, image_to_byte -from openlp.core.utils import translate +from openlp.core.common import AppLocation, Settings, translate +from openlp.core.lib import Registry, PluginStatus, StringContent, image_to_byte log = logging.getLogger(__name__) diff --git a/openlp/plugins/remotes/lib/httpserver.py b/openlp/plugins/remotes/lib/httpserver.py index a8453fb2b..0ac77115c 100644 --- a/openlp/plugins/remotes/lib/httpserver.py +++ b/openlp/plugins/remotes/lib/httpserver.py @@ -40,8 +40,7 @@ import logging from PyQt4 import QtCore -from openlp.core.common import AppLocation -from openlp.core.lib import Settings +from openlp.core.common import AppLocation, Settings from openlp.plugins.remotes.lib import HttpRouter diff --git a/openlp/plugins/remotes/lib/remotetab.py b/openlp/plugins/remotes/lib/remotetab.py index d7ae97342..3953d777f 100644 --- a/openlp/plugins/remotes/lib/remotetab.py +++ b/openlp/plugins/remotes/lib/remotetab.py @@ -31,8 +31,8 @@ import os.path from PyQt4 import QtCore, QtGui, QtNetwork -from openlp.core.common import AppLocation -from openlp.core.lib import Settings, SettingsTab, translate +from openlp.core.common import AppLocation, Settings, translate +from openlp.core.lib import SettingsTab ZERO_URL = '0.0.0.0' diff --git a/openlp/plugins/songs/forms/editsongdialog.py b/openlp/plugins/songs/forms/editsongdialog.py index 16e680587..e867aca37 100644 --- a/openlp/plugins/songs/forms/editsongdialog.py +++ b/openlp/plugins/songs/forms/editsongdialog.py @@ -29,7 +29,8 @@ from PyQt4 import QtCore, QtGui -from openlp.core.lib import UiStrings, build_icon, translate +from openlp.core.common import UiStrings, translate +from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box, create_button from openlp.plugins.songs.lib.ui import SongStrings diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index 83b4c8d04..ed4676929 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -38,8 +38,8 @@ import shutil from PyQt4 import QtCore, QtGui -from openlp.core.common import AppLocation, check_directory_exists -from openlp.core.lib import Registry, PluginStatus, MediaType, UiStrings, translate, create_separated_list +from openlp.core.common import AppLocation, UiStrings, check_directory_exists, translate +from openlp.core.lib import Registry, PluginStatus, MediaType, create_separated_list from openlp.core.lib.ui import set_case_insensitive_completer, critical_error_message_box, find_and_set_in_combo_box from openlp.plugins.songs.lib import VerseType, clean_song from openlp.plugins.songs.lib.db import Book, Song, Author, Topic, MediaFile diff --git a/openlp/plugins/songs/forms/songexportform.py b/openlp/plugins/songs/forms/songexportform.py index 26c07395c..f0b9262a7 100644 --- a/openlp/plugins/songs/forms/songexportform.py +++ b/openlp/plugins/songs/forms/songexportform.py @@ -34,7 +34,8 @@ import logging from PyQt4 import QtCore, QtGui -from openlp.core.lib import Registry, UiStrings, create_separated_list, build_icon, translate +from openlp.core.common import UiStrings, translate +from openlp.core.lib import Registry, create_separated_list, build_icon from openlp.core.lib.ui import critical_error_message_box from openlp.core.ui.wizard import OpenLPWizard, WizardStrings from openlp.plugins.songs.lib.db import Song diff --git a/openlp/plugins/songs/forms/songimportform.py b/openlp/plugins/songs/forms/songimportform.py index f1f63ffb5..2105e5e35 100644 --- a/openlp/plugins/songs/forms/songimportform.py +++ b/openlp/plugins/songs/forms/songimportform.py @@ -35,7 +35,9 @@ import os from PyQt4 import QtCore, QtGui -from openlp.core.lib import Registry, Settings, UiStrings, translate +from openlp.core.common import UiStrings, translate +from openlp.core.common import Settings +from openlp.core.lib import Registry from openlp.core.lib.ui import critical_error_message_box from openlp.core.ui.wizard import OpenLPWizard, WizardStrings from openlp.plugins.songs.lib.importer import SongFormat, SongFormatSelect diff --git a/openlp/plugins/songs/forms/songmaintenancedialog.py b/openlp/plugins/songs/forms/songmaintenancedialog.py index ea908fb0f..3e0363772 100644 --- a/openlp/plugins/songs/forms/songmaintenancedialog.py +++ b/openlp/plugins/songs/forms/songmaintenancedialog.py @@ -29,7 +29,8 @@ from PyQt4 import QtCore, QtGui -from openlp.core.lib import UiStrings, build_icon +from openlp.core.common import UiStrings +from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box from openlp.plugins.songs.lib.ui import SongStrings diff --git a/openlp/plugins/songs/forms/songmaintenanceform.py b/openlp/plugins/songs/forms/songmaintenanceform.py index 67d9ee3ad..142cca1e7 100644 --- a/openlp/plugins/songs/forms/songmaintenanceform.py +++ b/openlp/plugins/songs/forms/songmaintenanceform.py @@ -32,7 +32,8 @@ import os from PyQt4 import QtGui, QtCore from sqlalchemy.sql import and_ -from openlp.core.lib import Registry, UiStrings, translate +from openlp.core.common import UiStrings, translate +from openlp.core.lib import Registry from openlp.core.lib.ui import critical_error_message_box from openlp.plugins.songs.forms.authorsform import AuthorsForm from openlp.plugins.songs.forms.topicsform import TopicsForm diff --git a/openlp/plugins/songs/lib/importer.py b/openlp/plugins/songs/lib/importer.py index 8e7a9f36e..acdfddae7 100644 --- a/openlp/plugins/songs/lib/importer.py +++ b/openlp/plugins/songs/lib/importer.py @@ -32,7 +32,7 @@ The :mod:`importer` modules provides the general song import functionality. import os import logging -from openlp.core.lib import translate, UiStrings +from openlp.core.common import translate, UiStrings from openlp.core.ui.wizard import WizardStrings from .opensongimport import OpenSongImport from .easyslidesimport import EasySlidesImport diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index a708948ae..b8b1b5dcd 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -35,9 +35,9 @@ import shutil from PyQt4 import QtCore, QtGui from sqlalchemy.sql import or_ -from openlp.core.common import AppLocation, check_directory_exists -from openlp.core.lib import Registry, MediaManagerItem, ItemCapabilities, PluginStatus, ServiceItemContext, Settings, \ - UiStrings, translate, check_item_selected, create_separated_list +from openlp.core.common import AppLocation, Settings, check_directory_exists, UiStrings, translate +from openlp.core.lib import Registry, 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 from openlp.plugins.songs.forms.songmaintenanceform import SongMaintenanceForm diff --git a/openlp/plugins/songs/lib/olpimport.py b/openlp/plugins/songs/lib/olpimport.py index e95f78232..19078c9ec 100644 --- a/openlp/plugins/songs/lib/olpimport.py +++ b/openlp/plugins/songs/lib/olpimport.py @@ -36,7 +36,7 @@ from sqlalchemy import create_engine, MetaData, Table from sqlalchemy.orm import class_mapper, mapper, relation, scoped_session, sessionmaker from sqlalchemy.orm.exc import UnmappedClassError -from openlp.core.lib import translate +from openlp.core.common import translate from openlp.core.lib.db import BaseModel from openlp.core.ui.wizard import WizardStrings from openlp.plugins.songs.lib import clean_song diff --git a/openlp/plugins/songs/lib/oooimport.py b/openlp/plugins/songs/lib/oooimport.py index f74c022a7..bb7654d01 100644 --- a/openlp/plugins/songs/lib/oooimport.py +++ b/openlp/plugins/songs/lib/oooimport.py @@ -51,6 +51,7 @@ except ImportError: PAGE_AFTER = 5 PAGE_BOTH = 6 + class OooImport(SongImport): """ Import songs from Impress/Powerpoint docs using Impress diff --git a/openlp/plugins/songs/lib/openlyricsexport.py b/openlp/plugins/songs/lib/openlyricsexport.py index ef62a64f0..8f927dd8c 100644 --- a/openlp/plugins/songs/lib/openlyricsexport.py +++ b/openlp/plugins/songs/lib/openlyricsexport.py @@ -35,8 +35,8 @@ import os from lxml import etree -from openlp.core.common import check_directory_exists -from openlp.core.lib import Registry, translate +from openlp.core.common import check_directory_exists, translate +from openlp.core.lib import Registry from openlp.core.utils import clean_filename from openlp.plugins.songs.lib.xml import OpenLyrics diff --git a/openlp/plugins/songs/lib/opensongimport.py b/openlp/plugins/songs/lib/opensongimport.py index 669f861e3..3e60c9994 100644 --- a/openlp/plugins/songs/lib/opensongimport.py +++ b/openlp/plugins/songs/lib/opensongimport.py @@ -33,13 +33,14 @@ import re from lxml import objectify from lxml.etree import Error, LxmlError -from openlp.core.lib import translate +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.ui import SongStrings log = logging.getLogger(__name__) + class OpenSongImport(SongImport): """ Import songs exported from OpenSong diff --git a/openlp/plugins/songs/lib/powersongimport.py b/openlp/plugins/songs/lib/powersongimport.py index 7ab802505..88cea0b72 100644 --- a/openlp/plugins/songs/lib/powersongimport.py +++ b/openlp/plugins/songs/lib/powersongimport.py @@ -34,7 +34,7 @@ import logging import fnmatch import os -from openlp.core.lib import translate +from openlp.core.common import translate from openlp.plugins.songs.lib.songimport import SongImport log = logging.getLogger(__name__) diff --git a/openlp/plugins/songs/lib/songimport.py b/openlp/plugins/songs/lib/songimport.py index 378f18272..ead897e0e 100644 --- a/openlp/plugins/songs/lib/songimport.py +++ b/openlp/plugins/songs/lib/songimport.py @@ -34,8 +34,8 @@ import os from PyQt4 import QtCore -from openlp.core.common import AppLocation, check_directory_exists -from openlp.core.lib import Registry, translate +from openlp.core.common import AppLocation, check_directory_exists, translate +from openlp.core.lib import Registry 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 diff --git a/openlp/plugins/songs/lib/songproimport.py b/openlp/plugins/songs/lib/songproimport.py index 5673eaa34..a7384afd3 100644 --- a/openlp/plugins/songs/lib/songproimport.py +++ b/openlp/plugins/songs/lib/songproimport.py @@ -35,6 +35,7 @@ import re from openlp.plugins.songs.lib import strip_rtf from openlp.plugins.songs.lib.songimport import SongImport + class SongProImport(SongImport): """ The :class:`SongProImport` class provides the ability to import song files diff --git a/openlp/plugins/songs/lib/songshowplusimport.py b/openlp/plugins/songs/lib/songshowplusimport.py index 35cd44b8a..a82ae0c98 100644 --- a/openlp/plugins/songs/lib/songshowplusimport.py +++ b/openlp/plugins/songs/lib/songshowplusimport.py @@ -27,8 +27,8 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### """ -The :mod:`songshowplusimport` module provides the functionality for importing -SongShow Plus songs into the OpenLP database. +The :mod:`songshowplusimport` module provides the functionality for importing SongShow Plus songs into the OpenLP +database. """ import chardet import os @@ -56,6 +56,7 @@ CUSTOM_VERSE = 37 log = logging.getLogger(__name__) + class SongShowPlusImport(SongImport): """ The :class:`SongShowPlusImport` class provides the ability to import song files from SongShow Plus. diff --git a/openlp/plugins/songs/lib/songstab.py b/openlp/plugins/songs/lib/songstab.py index f2a36e0c8..c422f1231 100644 --- a/openlp/plugins/songs/lib/songstab.py +++ b/openlp/plugins/songs/lib/songstab.py @@ -29,7 +29,8 @@ from PyQt4 import QtCore, QtGui -from openlp.core.lib import Settings, SettingsTab, translate +from openlp.core.common import Settings, translate +from openlp.core.lib import SettingsTab class SongsTab(SettingsTab): diff --git a/openlp/plugins/songs/lib/ui.py b/openlp/plugins/songs/lib/ui.py index de864b470..ce876fe81 100644 --- a/openlp/plugins/songs/lib/ui.py +++ b/openlp/plugins/songs/lib/ui.py @@ -32,6 +32,7 @@ for the songs plugin. """ from openlp.core.lib import translate + class SongStrings(object): """ Provide standard strings for use throughout the songs plugin. diff --git a/openlp/plugins/songs/lib/worshipcenterproimport.py b/openlp/plugins/songs/lib/worshipcenterproimport.py index b1de90634..a58ace982 100644 --- a/openlp/plugins/songs/lib/worshipcenterproimport.py +++ b/openlp/plugins/songs/lib/worshipcenterproimport.py @@ -34,7 +34,7 @@ import logging import pyodbc -from openlp.core.lib import translate +from openlp.core.common import translate from openlp.plugins.songs.lib.songimport import SongImport log = logging.getLogger(__name__) diff --git a/openlp/plugins/songs/lib/wowimport.py b/openlp/plugins/songs/lib/wowimport.py index 3f06b4df8..8e9023e2f 100644 --- a/openlp/plugins/songs/lib/wowimport.py +++ b/openlp/plugins/songs/lib/wowimport.py @@ -33,7 +33,7 @@ Worship songs into the OpenLP database. import os import logging -from openlp.core.lib import translate +from openlp.core.common import translate from openlp.plugins.songs.lib.songimport import SongImport BLOCK_TYPES = ('V', 'C', 'B') diff --git a/openlp/plugins/songs/lib/xml.py b/openlp/plugins/songs/lib/xml.py index e788948b6..de1d33a22 100644 --- a/openlp/plugins/songs/lib/xml.py +++ b/openlp/plugins/songs/lib/xml.py @@ -68,7 +68,8 @@ import re from lxml import etree, objectify -from openlp.core.lib import FormattingTags, translate +from openlp.core.common import translate +from openlp.core.lib import FormattingTags from openlp.plugins.songs.lib import VerseType, clean_song from openlp.plugins.songs.lib.db import Author, Book, Song, Topic from openlp.core.utils import get_application_version diff --git a/openlp/plugins/songs/lib/zionworximport.py b/openlp/plugins/songs/lib/zionworximport.py index 50839f832..315f99708 100644 --- a/openlp/plugins/songs/lib/zionworximport.py +++ b/openlp/plugins/songs/lib/zionworximport.py @@ -33,7 +33,7 @@ ZionWorx songs into the OpenLP database. import csv import logging -from openlp.core.lib import translate +from openlp.core.common import translate from openlp.plugins.songs.lib.songimport import SongImport log = logging.getLogger(__name__) diff --git a/openlp/plugins/songs/songsplugin.py b/openlp/plugins/songs/songsplugin.py index 3418dd7e4..6e6f7ea77 100644 --- a/openlp/plugins/songs/songsplugin.py +++ b/openlp/plugins/songs/songsplugin.py @@ -38,7 +38,8 @@ import sqlite3 from PyQt4 import QtCore, QtGui -from openlp.core.lib import Plugin, StringContent, UiStrings, build_icon, translate +from openlp.core.common import UiStrings, translate +from openlp.core.lib import Plugin, StringContent, build_icon from openlp.core.lib.db import Manager from openlp.core.lib.ui import create_action from openlp.core.utils.actions import ActionList diff --git a/openlp/plugins/songusage/forms/songusagedeletedialog.py b/openlp/plugins/songusage/forms/songusagedeletedialog.py index a47afb9f4..4c7b57285 100644 --- a/openlp/plugins/songusage/forms/songusagedeletedialog.py +++ b/openlp/plugins/songusage/forms/songusagedeletedialog.py @@ -29,7 +29,7 @@ from PyQt4 import QtCore, QtGui -from openlp.core.lib import translate +from openlp.core.common import translate from openlp.core.lib.ui import create_button_box diff --git a/openlp/plugins/songusage/forms/songusagedeleteform.py b/openlp/plugins/songusage/forms/songusagedeleteform.py index 4ae9756b3..babd9d178 100644 --- a/openlp/plugins/songusage/forms/songusagedeleteform.py +++ b/openlp/plugins/songusage/forms/songusagedeleteform.py @@ -27,9 +27,10 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### -from PyQt4 import QtCore, QtGui +from PyQt4 import QtGui -from openlp.core.lib import Registry, translate +from openlp.core.common import translate +from openlp.core.lib import Registry from openlp.plugins.songusage.lib.db import SongUsageItem from .songusagedeletedialog import Ui_SongUsageDeleteDialog diff --git a/openlp/plugins/songusage/forms/songusagedetaildialog.py b/openlp/plugins/songusage/forms/songusagedetaildialog.py index efe84a7c8..278feebf8 100644 --- a/openlp/plugins/songusage/forms/songusagedetaildialog.py +++ b/openlp/plugins/songusage/forms/songusagedetaildialog.py @@ -29,7 +29,8 @@ from PyQt4 import QtCore, QtGui -from openlp.core.lib import build_icon, translate +from openlp.core.common import translate +from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box diff --git a/openlp/plugins/songusage/forms/songusagedetailform.py b/openlp/plugins/songusage/forms/songusagedetailform.py index e5925da78..6ff73b068 100644 --- a/openlp/plugins/songusage/forms/songusagedetailform.py +++ b/openlp/plugins/songusage/forms/songusagedetailform.py @@ -33,8 +33,8 @@ import os from PyQt4 import QtGui from sqlalchemy.sql import and_ -from openlp.core.common import check_directory_exists -from openlp.core.lib import Registry, Settings, translate +from openlp.core.common import Settings, check_directory_exists, translate +from openlp.core.lib import Registry from openlp.plugins.songusage.lib.db import SongUsageItem from .songusagedetaildialog import Ui_SongUsageDetailDialog diff --git a/openlp/plugins/songusage/songusageplugin.py b/openlp/plugins/songusage/songusageplugin.py index bed0e5be3..077b81155 100644 --- a/openlp/plugins/songusage/songusageplugin.py +++ b/openlp/plugins/songusage/songusageplugin.py @@ -32,7 +32,8 @@ from datetime import datetime from PyQt4 import QtCore, QtGui -from openlp.core.lib import Plugin, Registry, Settings, StringContent, build_icon, translate +from openlp.core.common import Settings, translate +from openlp.core.lib import Plugin, Registry, StringContent, build_icon from openlp.core.lib.db import Manager from openlp.core.lib.ui import create_action from openlp.core.utils.actions import ActionList diff --git a/tests/functional/openlp_core_common/test_applocation.py b/tests/functional/openlp_core_common/test_applocation.py index 7ce891bd9..28db96e43 100644 --- a/tests/functional/openlp_core_common/test_applocation.py +++ b/tests/functional/openlp_core_common/test_applocation.py @@ -46,7 +46,7 @@ class TestAppLocation(TestCase): """ Test the AppLocation.get_data_path() method """ - with patch('openlp.core.lib.Settings') as mocked_class, \ + with patch('openlp.core.common.Settings') as mocked_class, \ patch('openlp.core.common.AppLocation.get_directory') as mocked_get_directory, \ patch('openlp.core.common.applocation.check_directory_exists') as mocked_check_directory_exists, \ patch('openlp.core.common.applocation.os') as mocked_os: @@ -59,6 +59,7 @@ class TestAppLocation(TestCase): # WHEN: we call AppLocation.get_data_path() data_path = AppLocation.get_data_path() + print(data_path) # THEN: check that all the correct methods were called, and the result is correct mocked_settings.contains.assert_called_with('advanced/data path') @@ -70,7 +71,7 @@ class TestAppLocation(TestCase): """ Test the AppLocation.get_data_path() method when a custom location is set in the settings """ - with patch('openlp.core.lib.Settings') as mocked_class,\ + with patch('openlp.core.common.Settings') as mocked_class,\ patch('openlp.core.common.applocation.os') as mocked_os: # GIVEN: A mocked out Settings class which returns a custom data location mocked_settings = mocked_class.return_value diff --git a/tests/functional/openlp_core_lib/test_settings.py b/tests/functional/openlp_core_common/test_settings.py similarity index 99% rename from tests/functional/openlp_core_lib/test_settings.py rename to tests/functional/openlp_core_common/test_settings.py index 25647a6e1..f51cb7379 100644 --- a/tests/functional/openlp_core_lib/test_settings.py +++ b/tests/functional/openlp_core_common/test_settings.py @@ -35,7 +35,7 @@ from tempfile import mkstemp from PyQt4 import QtGui -from openlp.core.lib import Settings +from openlp.core.common import Settings class TestSettings(TestCase): diff --git a/tests/functional/openlp_core_lib/test_uistrings.py b/tests/functional/openlp_core_common/test_uistrings.py similarity index 98% rename from tests/functional/openlp_core_lib/test_uistrings.py rename to tests/functional/openlp_core_common/test_uistrings.py index fbfe07c78..b7f5ecc23 100644 --- a/tests/functional/openlp_core_lib/test_uistrings.py +++ b/tests/functional/openlp_core_common/test_uistrings.py @@ -31,7 +31,7 @@ Package to test the openlp.core.lib.uistrings package. """ from unittest import TestCase -from openlp.core.lib import UiStrings +from openlp.core.common import UiStrings class TestUiStrings(TestCase): diff --git a/tests/functional/openlp_core_lib/test_formattingtags.py b/tests/functional/openlp_core_lib/test_formattingtags.py index a200318ff..53d7519c7 100644 --- a/tests/functional/openlp_core_lib/test_formattingtags.py +++ b/tests/functional/openlp_core_lib/test_formattingtags.py @@ -59,7 +59,7 @@ class TestFormattingTags(TestCase): Test the FormattingTags class' get_html_tags static method. """ with patch('openlp.core.lib.translate') as mocked_translate, \ - patch('openlp.core.lib.settings') as mocked_settings, \ + patch('openlp.core.common.settings') as mocked_settings, \ patch('openlp.core.lib.formattingtags.json') as mocked_json: # GIVEN: Our mocked modules and functions. mocked_translate.side_effect = lambda module, string_to_translate, comment: string_to_translate @@ -80,7 +80,7 @@ class TestFormattingTags(TestCase): FormattingTags class - test the get_html_tags(), add_html_tags() and remove_html_tag() methods. """ with patch('openlp.core.lib.translate') as mocked_translate, \ - patch('openlp.core.lib.settings') as mocked_settings, \ + patch('openlp.core.common.settings') as mocked_settings, \ patch('openlp.core.lib.formattingtags.json') as mocked_json: # GIVEN: Our mocked modules and functions. mocked_translate.side_effect = lambda module, string_to_translate: string_to_translate diff --git a/tests/functional/openlp_core_lib/test_lib.py b/tests/functional/openlp_core_lib/test_lib.py index 6ba7eddbb..361b8d2cb 100644 --- a/tests/functional/openlp_core_lib/test_lib.py +++ b/tests/functional/openlp_core_lib/test_lib.py @@ -36,8 +36,8 @@ from datetime import datetime, timedelta from PyQt4 import QtCore, QtGui -from openlp.core.common import check_directory_exists -from openlp.core.lib import str_to_bool, create_thumb, translate, get_text_file_string, \ +from openlp.core.common import check_directory_exists, translate +from openlp.core.lib import str_to_bool, create_thumb, get_text_file_string, \ build_icon, image_to_byte, check_item_selected, validate_thumb, create_separated_list, clean_tags, expand_tags from tests.functional import MagicMock, patch diff --git a/tests/functional/openlp_core_lib/test_pluginmanager.py b/tests/functional/openlp_core_lib/test_pluginmanager.py index eb6d80f8c..725efd29e 100644 --- a/tests/functional/openlp_core_lib/test_pluginmanager.py +++ b/tests/functional/openlp_core_lib/test_pluginmanager.py @@ -31,8 +31,9 @@ Package to test the openlp.core.lib.pluginmanager package. """ from unittest import TestCase +from openlp.core.common import Settings from openlp.core.lib.pluginmanager import PluginManager -from openlp.core.lib import Settings, Registry, PluginStatus +from openlp.core.lib import Registry, PluginStatus from tests.functional import MagicMock diff --git a/tests/functional/openlp_core_utils/test_actions.py b/tests/functional/openlp_core_utils/test_actions.py index 42a7c7079..6b4972b37 100644 --- a/tests/functional/openlp_core_utils/test_actions.py +++ b/tests/functional/openlp_core_utils/test_actions.py @@ -35,7 +35,7 @@ from unittest import TestCase from PyQt4 import QtGui, QtCore -from openlp.core.lib import Settings +from openlp.core.common import Settings from openlp.core.utils import ActionList diff --git a/tests/functional/openlp_plugins/remotes/test_remotetab.py b/tests/functional/openlp_plugins/remotes/test_remotetab.py index cd32479d4..067c5cff1 100644 --- a/tests/functional/openlp_plugins/remotes/test_remotetab.py +++ b/tests/functional/openlp_plugins/remotes/test_remotetab.py @@ -36,7 +36,7 @@ from tempfile import mkstemp from PyQt4 import QtGui -from openlp.core.lib import Settings +from openlp.core.common import Settings from openlp.plugins.remotes.lib.remotetab import RemoteTab from tests.functional import patch @@ -105,7 +105,7 @@ class TestRemoteTab(TestCase): Test the set_urls function with standard defaults """ # GIVEN: A mocked location - with patch('openlp.core.lib.Settings') as mocked_class, \ + with patch('openlp.core.common.Settings') as mocked_class, \ patch('openlp.core.utils.AppLocation.get_directory') as mocked_get_directory, \ patch('openlp.core.common.check_directory_exists') as mocked_check_directory_exists, \ patch('openlp.core.common.applocation.os') as mocked_os: @@ -133,7 +133,7 @@ class TestRemoteTab(TestCase): Test the set_urls function with certificate available """ # GIVEN: A mocked location - with patch('openlp.core.lib.Settings') as mocked_class, \ + with patch('openlp.core.common.Settings') as mocked_class, \ patch('openlp.core.utils.AppLocation.get_directory') as mocked_get_directory, \ patch('openlp.core.common.check_directory_exists') as mocked_check_directory_exists, \ patch('openlp.core.common.applocation.os') as mocked_os: diff --git a/tests/functional/openlp_plugins/remotes/test_router.py b/tests/functional/openlp_plugins/remotes/test_router.py index 9b1b1dbb3..a9ba16bf8 100644 --- a/tests/functional/openlp_plugins/remotes/test_router.py +++ b/tests/functional/openlp_plugins/remotes/test_router.py @@ -35,7 +35,7 @@ from tempfile import mkstemp from PyQt4 import QtGui -from openlp.core.lib import Settings +from openlp.core.common import Settings from openlp.plugins.remotes.lib.httpserver import HttpRouter from tests.functional import MagicMock diff --git a/tests/functional/openlp_plugins/songs/test_mediaitem.py b/tests/functional/openlp_plugins/songs/test_mediaitem.py index 39f3146de..45c62469c 100644 --- a/tests/functional/openlp_plugins/songs/test_mediaitem.py +++ b/tests/functional/openlp_plugins/songs/test_mediaitem.py @@ -7,7 +7,8 @@ from unittest import TestCase from PyQt4 import QtCore, QtGui -from openlp.core.lib import Registry, ServiceItem, Settings +from openlp.core.common import Settings +from openlp.core.lib import Registry, ServiceItem from openlp.plugins.songs.lib.mediaitem import SongMediaItem from tests.functional import patch, MagicMock diff --git a/tests/interfaces/openlp_core_lib/test_pluginmanager.py b/tests/interfaces/openlp_core_lib/test_pluginmanager.py index dcff55e19..184b04808 100644 --- a/tests/interfaces/openlp_core_lib/test_pluginmanager.py +++ b/tests/interfaces/openlp_core_lib/test_pluginmanager.py @@ -10,8 +10,9 @@ from unittest import TestCase from mock import MagicMock from PyQt4 import QtGui +from openlp.core.common import Settings from openlp.core.lib.pluginmanager import PluginManager -from openlp.core.lib import Registry, Settings +from openlp.core.lib import Registry class TestPluginManager(TestCase): From b9e2a2cdc1313f5505f124eb11f6d92efd78dfb6 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sun, 13 Oct 2013 22:07:28 +0100 Subject: [PATCH 89/93] Missed imports --- openlp/core/lib/db.py | 3 +-- openlp/core/lib/serviceitem.py | 4 ++-- openlp/core/lib/theme.py | 1 - openlp/core/ui/filerenameform.py | 3 ++- openlp/core/ui/firsttimeform.py | 4 ++-- openlp/core/ui/firsttimelanguagedialog.py | 2 +- openlp/core/ui/firsttimewizard.py | 2 +- openlp/core/ui/formattingtagcontroller.py | 4 ++-- openlp/core/ui/formattingtagform.py | 3 ++- openlp/core/ui/maindisplay.py | 5 ++--- openlp/core/ui/mainwindow.py | 4 ++-- openlp/core/ui/pluginform.py | 3 ++- openlp/core/ui/serviceitemeditdialog.py | 2 +- openlp/core/ui/serviceitemeditform.py | 2 +- openlp/core/ui/servicemanager.py | 6 ++---- openlp/core/ui/servicenoteform.py | 3 ++- openlp/core/ui/settingsdialog.py | 3 ++- openlp/core/ui/shortcutlistdialog.py | 3 ++- openlp/core/ui/slidecontroller.py | 4 ++-- openlp/core/ui/themelayoutdialog.py | 2 +- openlp/core/ui/thememanager.py | 4 ++-- openlp/core/utils/__init__.py | 2 +- openlp/core/utils/languagemanager.py | 3 +-- openlp/plugins/alerts/alertsplugin.py | 4 ++-- openlp/plugins/alerts/forms/alertdialog.py | 3 ++- openlp/plugins/alerts/forms/alertform.py | 2 +- openlp/plugins/alerts/lib/alertsmanager.py | 3 ++- openlp/plugins/bibles/lib/csvbible.py | 2 +- openlp/plugins/bibles/lib/db.py | 4 ++-- openlp/plugins/bibles/lib/http.py | 3 ++- openlp/plugins/bibles/lib/manager.py | 4 ++-- openlp/plugins/bibles/lib/mediaitem.py | 3 +-- openlp/plugins/bibles/lib/opensong.py | 2 +- openlp/plugins/bibles/lib/osis.py | 3 +-- openlp/plugins/bibles/lib/versereferencelist.py | 1 + openlp/plugins/images/forms/addgroupdialog.py | 2 +- openlp/plugins/images/forms/addgroupform.py | 2 +- openlp/plugins/images/forms/choosegroupdialog.py | 2 +- openlp/plugins/images/imageplugin.py | 4 ++-- openlp/plugins/images/lib/mediaitem.py | 3 +-- openlp/plugins/media/mediaplugin.py | 3 ++- openlp/plugins/presentations/lib/mediaitem.py | 2 +- openlp/plugins/presentations/presentationplugin.py | 4 ++-- 43 files changed, 65 insertions(+), 63 deletions(-) diff --git a/openlp/core/lib/db.py b/openlp/core/lib/db.py index 6bbca9b5c..429c30262 100644 --- a/openlp/core/lib/db.py +++ b/openlp/core/lib/db.py @@ -41,8 +41,7 @@ from sqlalchemy.pool import NullPool from alembic.migration import MigrationContext from alembic.operations import Operations -from openlp.core.common import AppLocation, Settings -from openlp.core.lib import translate +from openlp.core.common import AppLocation, Settings, translate from openlp.core.lib.ui import critical_error_message_box from openlp.core.utils import delete_file diff --git a/openlp/core/lib/serviceitem.py b/openlp/core/lib/serviceitem.py index c1438840b..789854c78 100644 --- a/openlp/core/lib/serviceitem.py +++ b/openlp/core/lib/serviceitem.py @@ -39,8 +39,8 @@ import uuid from PyQt4 import QtGui -from openlp.core.common import Settings -from openlp.core.lib import ImageSource, Registry, build_icon, clean_tags, expand_tags, translate +from openlp.core.common import Settings, translate +from openlp.core.lib import ImageSource, Registry, build_icon, clean_tags, expand_tags log = logging.getLogger(__name__) diff --git a/openlp/core/lib/theme.py b/openlp/core/lib/theme.py index 98e98ac6e..e8918e4d1 100644 --- a/openlp/core/lib/theme.py +++ b/openlp/core/lib/theme.py @@ -510,7 +510,6 @@ class ThemeXML(object): """ Create the attributes with the correct data types and name format """ - #print(master, element, value) reject, master, element, value = self._translate_tags(master, element, value) if reject: return diff --git a/openlp/core/ui/filerenameform.py b/openlp/core/ui/filerenameform.py index 79268f560..90fb8c648 100644 --- a/openlp/core/ui/filerenameform.py +++ b/openlp/core/ui/filerenameform.py @@ -34,7 +34,8 @@ from PyQt4 import QtGui from .filerenamedialog import Ui_FileRenameDialog -from openlp.core.lib import translate, Registry +from openlp.core.common import translate +from openlp.core.lib import Registry class FileRenameForm(QtGui.QDialog, Ui_FileRenameDialog): diff --git a/openlp/core/ui/firsttimeform.py b/openlp/core/ui/firsttimeform.py index 31d9cf198..e48395000 100644 --- a/openlp/core/ui/firsttimeform.py +++ b/openlp/core/ui/firsttimeform.py @@ -41,8 +41,8 @@ from configparser import SafeConfigParser from PyQt4 import QtCore, QtGui -from openlp.core.common import AppLocation, Settings, check_directory_exists -from openlp.core.lib import PluginStatus, Registry, build_icon, translate +from openlp.core.common import AppLocation, Settings, check_directory_exists, translate +from openlp.core.lib import PluginStatus, Registry, build_icon from openlp.core.utils import get_web_page from .firsttimewizard import Ui_FirstTimeWizard, FirstTimePage diff --git a/openlp/core/ui/firsttimelanguagedialog.py b/openlp/core/ui/firsttimelanguagedialog.py index 59dd95053..793d0adab 100644 --- a/openlp/core/ui/firsttimelanguagedialog.py +++ b/openlp/core/ui/firsttimelanguagedialog.py @@ -31,7 +31,7 @@ The UI widgets of the language selection dialog. """ from PyQt4 import QtGui -from openlp.core.lib import translate +from openlp.core.common import translate from openlp.core.lib.ui import create_button_box diff --git a/openlp/core/ui/firsttimewizard.py b/openlp/core/ui/firsttimewizard.py index 35a27d494..ac51de955 100644 --- a/openlp/core/ui/firsttimewizard.py +++ b/openlp/core/ui/firsttimewizard.py @@ -33,7 +33,7 @@ from PyQt4 import QtCore, QtGui import sys -from openlp.core.lib import translate +from openlp.core.common import translate from openlp.core.lib.ui import add_welcome_page diff --git a/openlp/core/ui/formattingtagcontroller.py b/openlp/core/ui/formattingtagcontroller.py index 9b891849b..f2c081c51 100644 --- a/openlp/core/ui/formattingtagcontroller.py +++ b/openlp/core/ui/formattingtagcontroller.py @@ -33,8 +33,8 @@ cannot be changed. """ import re - -from openlp.core.lib import FormattingTags, translate +from openlp.core.common import translate +from openlp.core.lib import FormattingTags class FormattingTagController(object): diff --git a/openlp/core/ui/formattingtagform.py b/openlp/core/ui/formattingtagform.py index c6906fc95..420a9c2a2 100644 --- a/openlp/core/ui/formattingtagform.py +++ b/openlp/core/ui/formattingtagform.py @@ -34,7 +34,8 @@ Base Tags cannot be changed. from PyQt4 import QtGui -from openlp.core.lib import FormattingTags, translate +from openlp.core.common import translate +from openlp.core.lib import FormattingTags from openlp.core.ui.formattingtagdialog import Ui_FormattingTagDialog from openlp.core.ui.formattingtagcontroller import FormattingTagController diff --git a/openlp/core/ui/maindisplay.py b/openlp/core/ui/maindisplay.py index 541e5002a..919087569 100644 --- a/openlp/core/ui/maindisplay.py +++ b/openlp/core/ui/maindisplay.py @@ -44,9 +44,8 @@ import sys from PyQt4 import QtCore, QtGui, QtWebKit, QtOpenGL from PyQt4.phonon import Phonon -from openlp.core.common import Settings -from openlp.core.lib import ServiceItem, ImageSource, Registry, build_html, expand_tags, \ - image_to_byte, translate +from openlp.core.common import Settings, translate +from openlp.core.lib import ServiceItem, ImageSource, Registry, build_html, expand_tags, image_to_byte from openlp.core.lib.theme import BackgroundType from openlp.core.lib import ScreenList diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index f397e3306..3382d281d 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -42,12 +42,12 @@ from datetime import datetime from PyQt4 import QtCore, QtGui from openlp.core.lib import Renderer, OpenLPDockWidget, PluginManager, ImageManager, PluginStatus, Registry, \ - ScreenList, build_icon, translate + ScreenList, build_icon from openlp.core.lib.ui import UiStrings, create_action from openlp.core.ui import AboutForm, SettingsForm, ServiceManager, ThemeManager, SlideController, PluginForm, \ MediaDockManager, ShortcutListForm, FormattingTagForm -from openlp.core.common import AppLocation, Settings, check_directory_exists +from openlp.core.common import AppLocation, Settings, check_directory_exists, translate from openlp.core.ui.media import MediaController from openlp.core.utils import LanguageManager, add_actions, get_application_version from openlp.core.utils.actions import ActionList, CategoryOrder diff --git a/openlp/core/ui/pluginform.py b/openlp/core/ui/pluginform.py index f0c7dfaf7..504f04d59 100644 --- a/openlp/core/ui/pluginform.py +++ b/openlp/core/ui/pluginform.py @@ -34,7 +34,8 @@ import os from PyQt4 import QtGui -from openlp.core.lib import PluginStatus, Registry, translate +from openlp.core.common import translate +from openlp.core.lib import PluginStatus, Registry from .plugindialog import Ui_PluginViewDialog log = logging.getLogger(__name__) diff --git a/openlp/core/ui/serviceitemeditdialog.py b/openlp/core/ui/serviceitemeditdialog.py index 72b48d8f5..938366d6d 100644 --- a/openlp/core/ui/serviceitemeditdialog.py +++ b/openlp/core/ui/serviceitemeditdialog.py @@ -31,7 +31,7 @@ The UI widgets for the service item edit dialog """ from PyQt4 import QtGui -from openlp.core.lib import translate +from openlp.core.common import translate from openlp.core.lib.ui import create_button_box, create_button diff --git a/openlp/core/ui/serviceitemeditform.py b/openlp/core/ui/serviceitemeditform.py index ad4168bfd..3e4b053cc 100644 --- a/openlp/core/ui/serviceitemeditform.py +++ b/openlp/core/ui/serviceitemeditform.py @@ -29,7 +29,7 @@ """ The service item edit dialog """ -from PyQt4 import QtCore, QtGui +from PyQt4 import QtGui from openlp.core.lib import Registry from .serviceitemeditdialog import Ui_ServiceItemEditDialog diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 20989bb90..58904fd98 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -42,10 +42,8 @@ log = logging.getLogger(__name__) from PyQt4 import QtCore, QtGui -from openlp.core.common import AppLocation, Settings, check_directory_exists, UiStrings, translate -from openlp.core.lib import OpenLPToolbar, ServiceItem, ItemCapabilities, PluginStatus, Registry, \ - build_icon -from openlp.core.common import ThemeLevel +from openlp.core.common import AppLocation, Settings, ThemeLevel, check_directory_exists, UiStrings, translate +from openlp.core.lib import OpenLPToolbar, ServiceItem, ItemCapabilities, PluginStatus, Registry, build_icon from openlp.core.lib.ui import critical_error_message_box, create_widget_action, find_and_set_in_combo_box from openlp.core.ui import ServiceNoteForm, ServiceItemEditForm, StartTimeForm from openlp.core.ui.printserviceform import PrintServiceForm diff --git a/openlp/core/ui/servicenoteform.py b/openlp/core/ui/servicenoteform.py index fed016bed..18998c78d 100644 --- a/openlp/core/ui/servicenoteform.py +++ b/openlp/core/ui/servicenoteform.py @@ -31,7 +31,8 @@ The :mod:`~openlp.core.ui.servicenoteform` module contains the `ServiceNoteForm` """ from PyQt4 import QtGui -from openlp.core.lib import SpellTextEdit, Registry, translate +from openlp.core.common import translate +from openlp.core.lib import SpellTextEdit, Registry from openlp.core.lib.ui import create_button_box diff --git a/openlp/core/ui/settingsdialog.py b/openlp/core/ui/settingsdialog.py index 87923630a..31b5c841c 100644 --- a/openlp/core/ui/settingsdialog.py +++ b/openlp/core/ui/settingsdialog.py @@ -31,7 +31,8 @@ The UI widgets of the settings dialog. """ from PyQt4 import QtCore, QtGui -from openlp.core.lib import translate, build_icon +from openlp.core.common import translate +from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box diff --git a/openlp/core/ui/shortcutlistdialog.py b/openlp/core/ui/shortcutlistdialog.py index 7e2c091c8..c70311a4b 100644 --- a/openlp/core/ui/shortcutlistdialog.py +++ b/openlp/core/ui/shortcutlistdialog.py @@ -31,7 +31,8 @@ The list of shortcuts within a dialog. """ from PyQt4 import QtCore, QtGui -from openlp.core.lib import translate, build_icon +from openlp.core.common import translate +from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button_box diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index 171a24f16..043838f36 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -38,8 +38,8 @@ from collections import deque from PyQt4 import QtCore, QtGui from openlp.core.common import Settings, SlideLimits, UiStrings, translate -from openlp.core.lib import OpenLPToolbar, ItemCapabilities, ServiceItem, ImageSource, \ - ServiceItemAction, Registry, ScreenList, build_icon, build_html +from openlp.core.lib import OpenLPToolbar, ItemCapabilities, ServiceItem, ImageSource, ServiceItemAction, Registry, \ + ScreenList, build_icon, build_html from openlp.core.ui import HideMode, MainDisplay, Display, DisplayControllerType from openlp.core.lib.ui import create_action from openlp.core.utils.actions import ActionList, CategoryOrder diff --git a/openlp/core/ui/themelayoutdialog.py b/openlp/core/ui/themelayoutdialog.py index ae492941a..41b2dabd0 100644 --- a/openlp/core/ui/themelayoutdialog.py +++ b/openlp/core/ui/themelayoutdialog.py @@ -31,7 +31,7 @@ The layout of the theme """ from PyQt4 import QtGui -from openlp.core.lib import translate +from openlp.core.common import translate from openlp.core.lib.ui import create_button_box diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py index daee4c320..8e1838d5d 100644 --- a/openlp/core/ui/thememanager.py +++ b/openlp/core/ui/thememanager.py @@ -39,8 +39,8 @@ from xml.etree.ElementTree import ElementTree, XML from PyQt4 import QtCore, QtGui from openlp.core.common import AppLocation, Settings, check_directory_exists, UiStrings, translate -from openlp.core.lib import ImageSource, OpenLPToolbar, Registry, get_text_file_string, \ - build_icon, check_item_selected, create_thumb, validate_thumb +from openlp.core.lib import ImageSource, OpenLPToolbar, Registry, get_text_file_string, build_icon, \ + check_item_selected, create_thumb, validate_thumb from openlp.core.lib.theme import ThemeXML, BackgroundType from openlp.core.lib.ui import critical_error_message_box, create_widget_action from openlp.core.ui import FileRenameForm, ThemeForm, ThemeManagerHelper diff --git a/openlp/core/utils/__init__.py b/openlp/core/utils/__init__.py index c75e8782a..6ceb592f4 100644 --- a/openlp/core/utils/__init__.py +++ b/openlp/core/utils/__init__.py @@ -54,7 +54,7 @@ if sys.platform != 'win32' and sys.platform != 'darwin': except ImportError: XDG_BASE_AVAILABLE = False -from openlp.core.lib import translate +from openlp.core.common import translate log = logging.getLogger(__name__) APPLICATION_VERSION = {} diff --git a/openlp/core/utils/languagemanager.py b/openlp/core/utils/languagemanager.py index 63f2e6f24..8208435d1 100644 --- a/openlp/core/utils/languagemanager.py +++ b/openlp/core/utils/languagemanager.py @@ -35,8 +35,7 @@ import sys from PyQt4 import QtCore, QtGui -from openlp.core.common import AppLocation, Settings -from openlp.core.lib import translate +from openlp.core.common import AppLocation, Settings, translate log = logging.getLogger(__name__) diff --git a/openlp/plugins/alerts/alertsplugin.py b/openlp/plugins/alerts/alertsplugin.py index 1dcdb13c7..f41b3490c 100644 --- a/openlp/plugins/alerts/alertsplugin.py +++ b/openlp/plugins/alerts/alertsplugin.py @@ -31,8 +31,8 @@ import logging from PyQt4 import QtGui -from openlp.core.common import Settings -from openlp.core.lib import Plugin, StringContent, build_icon, translate +from openlp.core.common import Settings, translate +from openlp.core.lib import Plugin, StringContent, build_icon from openlp.core.lib.db import Manager from openlp.core.lib.ui import create_action, UiStrings from openlp.core.lib.theme import VerticalType diff --git a/openlp/plugins/alerts/forms/alertdialog.py b/openlp/plugins/alerts/forms/alertdialog.py index 6f189237f..02f41c7f8 100644 --- a/openlp/plugins/alerts/forms/alertdialog.py +++ b/openlp/plugins/alerts/forms/alertdialog.py @@ -29,7 +29,8 @@ from PyQt4 import QtGui -from openlp.core.lib import build_icon, translate +from openlp.core.common import translate +from openlp.core.lib import build_icon from openlp.core.lib.ui import create_button, create_button_box diff --git a/openlp/plugins/alerts/forms/alertform.py b/openlp/plugins/alerts/forms/alertform.py index 97a8a5efa..d0f78e4cb 100644 --- a/openlp/plugins/alerts/forms/alertform.py +++ b/openlp/plugins/alerts/forms/alertform.py @@ -29,7 +29,7 @@ from PyQt4 import QtGui, QtCore -from openlp.core.lib import translate +from openlp.core.common import translate from openlp.plugins.alerts.lib.db import AlertItem from .alertdialog import Ui_AlertDialog diff --git a/openlp/plugins/alerts/lib/alertsmanager.py b/openlp/plugins/alerts/lib/alertsmanager.py index 047f91674..88341b77a 100644 --- a/openlp/plugins/alerts/lib/alertsmanager.py +++ b/openlp/plugins/alerts/lib/alertsmanager.py @@ -35,7 +35,8 @@ import logging from PyQt4 import QtCore -from openlp.core.lib import Registry, translate +from openlp.core.common import translate +from openlp.core.lib import Registry log = logging.getLogger(__name__) diff --git a/openlp/plugins/bibles/lib/csvbible.py b/openlp/plugins/bibles/lib/csvbible.py index 69ba12062..40ae13359 100644 --- a/openlp/plugins/bibles/lib/csvbible.py +++ b/openlp/plugins/bibles/lib/csvbible.py @@ -60,7 +60,7 @@ import logging import chardet import csv -from openlp.core.lib import translate +from openlp.core.common import translate from openlp.plugins.bibles.lib.db import BibleDB, BiblesResourcesDB diff --git a/openlp/plugins/bibles/lib/db.py b/openlp/plugins/bibles/lib/db.py index 89ff8a61a..8aefcfa8b 100644 --- a/openlp/plugins/bibles/lib/db.py +++ b/openlp/plugins/bibles/lib/db.py @@ -38,8 +38,8 @@ from sqlalchemy import Column, ForeignKey, Table, or_, types, func from sqlalchemy.orm import class_mapper, mapper, relation from sqlalchemy.orm.exc import UnmappedClassError -from openlp.core.common import AppLocation -from openlp.core.lib import Registry, translate +from openlp.core.common import AppLocation, translate +from openlp.core.lib import Registry from openlp.core.lib.db import BaseModel, init_db, Manager from openlp.core.lib.ui import critical_error_message_box from openlp.core.utils import clean_filename diff --git a/openlp/plugins/bibles/lib/http.py b/openlp/plugins/bibles/lib/http.py index 0fee09265..22ff6185a 100644 --- a/openlp/plugins/bibles/lib/http.py +++ b/openlp/plugins/bibles/lib/http.py @@ -38,7 +38,8 @@ from html.parser import HTMLParseError from bs4 import BeautifulSoup, NavigableString, Tag -from openlp.core.lib import Registry, translate +from openlp.core.common import translate +from openlp.core.lib import Registry from openlp.core.lib.ui import critical_error_message_box from openlp.core.utils import get_web_page from openlp.plugins.bibles.lib import SearchResults diff --git a/openlp/plugins/bibles/lib/manager.py b/openlp/plugins/bibles/lib/manager.py index fc7c31d15..f2feb238f 100644 --- a/openlp/plugins/bibles/lib/manager.py +++ b/openlp/plugins/bibles/lib/manager.py @@ -30,8 +30,8 @@ import logging import os -from openlp.core.common import AppLocation, Settings -from openlp.core.lib import Registry, translate +from openlp.core.common import AppLocation, Settings, translate +from openlp.core.lib import Registry from openlp.core.utils import delete_file from openlp.plugins.bibles.lib import parse_reference, get_reference_separator, LanguageSelection from openlp.plugins.bibles.lib.db import BibleDB, BibleMeta diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index 437dfd65e..d0414d49c 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -32,8 +32,7 @@ import logging from PyQt4 import QtCore, QtGui from openlp.core.common import Settings, UiStrings, translate -from openlp.core.lib import Registry, MediaManagerItem, ItemCapabilities, ServiceItemContext, \ - create_separated_list +from openlp.core.lib import Registry, MediaManagerItem, ItemCapabilities, ServiceItemContext, create_separated_list from openlp.core.lib.searchedit import SearchEdit from openlp.core.lib.ui import set_case_insensitive_completer, create_horizontal_adjusting_combo_box, \ critical_error_message_box, find_and_set_in_combo_box, build_icon diff --git a/openlp/plugins/bibles/lib/opensong.py b/openlp/plugins/bibles/lib/opensong.py index d2b156345..efe0044ef 100644 --- a/openlp/plugins/bibles/lib/opensong.py +++ b/openlp/plugins/bibles/lib/opensong.py @@ -30,7 +30,7 @@ import logging from lxml import etree, objectify -from openlp.core.lib import translate +from openlp.core.common import translate from openlp.core.lib.ui import critical_error_message_box from openlp.plugins.bibles.lib.db import BibleDB, BiblesResourcesDB diff --git a/openlp/plugins/bibles/lib/osis.py b/openlp/plugins/bibles/lib/osis.py index c2ce24da6..a0aabb5a2 100644 --- a/openlp/plugins/bibles/lib/osis.py +++ b/openlp/plugins/bibles/lib/osis.py @@ -33,8 +33,7 @@ import chardet import codecs import re -from openlp.core.common import AppLocation -from openlp.core.lib import translate +from openlp.core.common import AppLocation, translate from openlp.plugins.bibles.lib.db import BibleDB, BiblesResourcesDB log = logging.getLogger(__name__) diff --git a/openlp/plugins/bibles/lib/versereferencelist.py b/openlp/plugins/bibles/lib/versereferencelist.py index 1f2d761e0..e8daada46 100644 --- a/openlp/plugins/bibles/lib/versereferencelist.py +++ b/openlp/plugins/bibles/lib/versereferencelist.py @@ -27,6 +27,7 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### + class VerseReferenceList(object): """ The VerseReferenceList class encapsulates a list of verse references, but maintains the order in which they were diff --git a/openlp/plugins/images/forms/addgroupdialog.py b/openlp/plugins/images/forms/addgroupdialog.py index e76332287..95e2118f4 100644 --- a/openlp/plugins/images/forms/addgroupdialog.py +++ b/openlp/plugins/images/forms/addgroupdialog.py @@ -29,7 +29,7 @@ from PyQt4 import QtGui -from openlp.core.lib import translate +from openlp.core.common import translate from openlp.core.lib.ui import create_button_box diff --git a/openlp/plugins/images/forms/addgroupform.py b/openlp/plugins/images/forms/addgroupform.py index 29bad676d..fafac53aa 100644 --- a/openlp/plugins/images/forms/addgroupform.py +++ b/openlp/plugins/images/forms/addgroupform.py @@ -29,7 +29,7 @@ from PyQt4 import QtGui -from openlp.core.lib import translate +from openlp.core.common import translate from openlp.core.lib.ui import critical_error_message_box from openlp.plugins.images.forms.addgroupdialog import Ui_AddGroupDialog diff --git a/openlp/plugins/images/forms/choosegroupdialog.py b/openlp/plugins/images/forms/choosegroupdialog.py index ec87df2bb..76b0d7849 100644 --- a/openlp/plugins/images/forms/choosegroupdialog.py +++ b/openlp/plugins/images/forms/choosegroupdialog.py @@ -29,7 +29,7 @@ from PyQt4 import QtCore, QtGui -from openlp.core.lib import translate +from openlp.core.common import translate from openlp.core.lib.ui import create_button_box diff --git a/openlp/plugins/images/imageplugin.py b/openlp/plugins/images/imageplugin.py index b82cfa184..137610c1f 100644 --- a/openlp/plugins/images/imageplugin.py +++ b/openlp/plugins/images/imageplugin.py @@ -31,8 +31,8 @@ from PyQt4 import QtGui import logging -from openlp.core.common import Settings -from openlp.core.lib import Plugin, StringContent, Registry, ImageSource, build_icon, translate +from openlp.core.common import Settings, translate +from openlp.core.lib import Plugin, StringContent, Registry, ImageSource, build_icon from openlp.core.lib.db import Manager from openlp.plugins.images.lib import ImageMediaItem, ImageTab from openlp.plugins.images.lib.db import init_schema diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index 976a3d47e..f1e0bfdb4 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -34,8 +34,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.common import AppLocation, Settings, UiStrings, check_directory_exists, translate from openlp.core.lib import ItemCapabilities, MediaManagerItem, Registry, ServiceItemContext, \ - StringContent, TreeWidgetWithDnD, build_icon, check_item_selected, create_thumb, \ - validate_thumb + StringContent, TreeWidgetWithDnD, build_icon, check_item_selected, create_thumb, validate_thumb from openlp.core.lib.ui import create_widget_action, critical_error_message_box from openlp.core.utils import delete_file, get_locale_key, get_images_filter from openlp.plugins.images.forms import AddGroupForm, ChooseGroupForm diff --git a/openlp/plugins/media/mediaplugin.py b/openlp/plugins/media/mediaplugin.py index cd432c54c..d6a943832 100644 --- a/openlp/plugins/media/mediaplugin.py +++ b/openlp/plugins/media/mediaplugin.py @@ -31,7 +31,8 @@ import logging from PyQt4 import QtCore -from openlp.core.lib import Plugin, Registry, StringContent, build_icon, translate +from openlp.core.common import translate +from openlp.core.lib import Plugin, Registry, StringContent, build_icon from openlp.plugins.media.lib import MediaMediaItem, MediaTab diff --git a/openlp/plugins/presentations/lib/mediaitem.py b/openlp/plugins/presentations/lib/mediaitem.py index ce0325c7e..e5aea43c5 100644 --- a/openlp/plugins/presentations/lib/mediaitem.py +++ b/openlp/plugins/presentations/lib/mediaitem.py @@ -33,7 +33,7 @@ import os from PyQt4 import QtCore, QtGui from openlp.core.common import Settings, UiStrings, translate -from openlp.core.lib import MediaManagerItem, Registry, ItemCapabilities, ServiceItemContext, \ +from openlp.core.lib import MediaManagerItem, Registry, ItemCapabilities, ServiceItemContext,\ build_icon, check_item_selected, create_thumb, validate_thumb from openlp.core.lib.ui import critical_error_message_box, create_horizontal_adjusting_combo_box from openlp.core.utils import get_locale_key diff --git a/openlp/plugins/presentations/presentationplugin.py b/openlp/plugins/presentations/presentationplugin.py index e37312095..c6565100c 100644 --- a/openlp/plugins/presentations/presentationplugin.py +++ b/openlp/plugins/presentations/presentationplugin.py @@ -35,8 +35,8 @@ import logging from PyQt4 import QtCore -from openlp.core.common import AppLocation -from openlp.core.lib import Plugin, StringContent, build_icon, translate +from openlp.core.common import AppLocation, translate +from openlp.core.lib import Plugin, StringContent, build_icon from openlp.plugins.presentations.lib import PresentationController, PresentationMediaItem, PresentationTab From 4f3291b2ee0d596c89e9fb43c8dd768ee774489a Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Mon, 14 Oct 2013 06:01:01 +0100 Subject: [PATCH 90/93] Fixes to tests --- tests/functional/openlp_core_common/test_applocation.py | 6 +++--- tests/functional/openlp_core_lib/test_theme.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/functional/openlp_core_common/test_applocation.py b/tests/functional/openlp_core_common/test_applocation.py index 28db96e43..0dcb2e6b1 100644 --- a/tests/functional/openlp_core_common/test_applocation.py +++ b/tests/functional/openlp_core_common/test_applocation.py @@ -46,7 +46,7 @@ class TestAppLocation(TestCase): """ Test the AppLocation.get_data_path() method """ - with patch('openlp.core.common.Settings') as mocked_class, \ + with patch('openlp.core.common.applocation.Settings') as mocked_class, \ patch('openlp.core.common.AppLocation.get_directory') as mocked_get_directory, \ patch('openlp.core.common.applocation.check_directory_exists') as mocked_check_directory_exists, \ patch('openlp.core.common.applocation.os') as mocked_os: @@ -71,7 +71,7 @@ class TestAppLocation(TestCase): """ Test the AppLocation.get_data_path() method when a custom location is set in the settings """ - with patch('openlp.core.common.Settings') as mocked_class,\ + with patch('openlp.core.common.applocation.Settings') as mocked_class,\ patch('openlp.core.common.applocation.os') as mocked_os: # GIVEN: A mocked out Settings class which returns a custom data location mocked_settings = mocked_class.return_value @@ -200,4 +200,4 @@ class TestAppLocation(TestCase): frozen_path = get_frozen_path('frozen', 'not frozen') # THEN: The frozen parameter is returned - self.assertEqual('frozen', frozen_path, 'Should return "frozen"') \ No newline at end of file + self.assertEqual('frozen', frozen_path, 'Should return "frozen"') diff --git a/tests/functional/openlp_core_lib/test_theme.py b/tests/functional/openlp_core_lib/test_theme.py index e533b1b8d..5f6bbc4e9 100644 --- a/tests/functional/openlp_core_lib/test_theme.py +++ b/tests/functional/openlp_core_lib/test_theme.py @@ -62,4 +62,4 @@ class TestTheme(TestCase): # THEN: We should get some default behaviours self.assertTrue(default_theme.background_border_color == '#000000', 'The theme should have a black border') - self.assertTrue(default_theme.background_type == 'solid', 'There theme should have a solid backgrounds') \ No newline at end of file + self.assertTrue(default_theme.background_type == 'solid', 'There theme should have a solid backgrounds') From 0f183af84dfef47709689d1d29d2cc46fb8b5dea Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Tue, 15 Oct 2013 20:17:53 +0100 Subject: [PATCH 91/93] Missed file --- openlp/core/common/settings.py | 498 +++++++++++++++++++++++++++++++++ 1 file changed, 498 insertions(+) create mode 100644 openlp/core/common/settings.py diff --git a/openlp/core/common/settings.py b/openlp/core/common/settings.py new file mode 100644 index 000000000..0b5aebc69 --- /dev/null +++ b/openlp/core/common/settings.py @@ -0,0 +1,498 @@ +# -*- 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 # +############################################################################### +""" +This class contains the core default settings. +""" +import datetime +import logging +import os +import sys + +from PyQt4 import QtCore, QtGui + +from openlp.core.common import ThemeLevel, SlideLimits, UiStrings + + +log = logging.getLogger(__name__) + + +# Fix for bug #1014422. +X11_BYPASS_DEFAULT = True +if sys.platform.startswith('linux'): + # Default to False on Gnome. + X11_BYPASS_DEFAULT = bool(not os.environ.get('GNOME_DESKTOP_SESSION_ID')) + # Default to False on Xfce. + if os.environ.get('DESKTOP_SESSION') == 'xfce': + X11_BYPASS_DEFAULT = False + + +class Settings(QtCore.QSettings): + """ + Class to wrap QSettings. + + * Exposes all the methods of QSettings. + * Adds functionality for OpenLP Portable. If the ``defaultFormat`` is set to + ``IniFormat``, and the path to the Ini file is set using ``set_filename``, + then the Settings constructor (without any arguments) will create a Settings + object for accessing settings stored in that Ini file. + + ``__default_settings__`` + This dict contains all core settings with their default values. + + ``__obsolete_settings__`` + Each entry is structured in the following way:: + + (u'general/enable slide loop', u'advanced/slide limits', + [(SlideLimits.Wrap, True), (SlideLimits.End, False)]) + + The first entry is the *old key*; it will be removed. + + The second entry is the *new key*; we will add it to the config. If this is just an empty string, we just remove + the old key. + + The last entry is a list containing two-pair tuples. If the list is empty, no conversion is made. Otherwise each + pair describes how to convert the old setting's value:: + + (SlideLimits.Wrap, True) + + This means, that if the value of ``general/enable slide loop`` is equal (``==``) ``True`` then we set + ``advanced/slide limits`` to ``SlideLimits.Wrap``. **NOTE**, this means that the rules have to cover all cases! + So, if the type of the old value is bool, then there must be two rules. + """ + __default_settings__ = { + 'advanced/add page break': False, + 'advanced/alternate rows': not sys.platform.startswith('win'), + 'advanced/current media plugin': -1, + 'advanced/data path': '', + 'advanced/default color': '#ffffff', + 'advanced/default image': ':/graphics/openlp-splash-screen.png', + # 7 stands for now, 0 to 6 is Monday to Sunday. + 'advanced/default service day': 7, + 'advanced/default service enabled': True, + 'advanced/default service hour': 11, + 'advanced/default service minute': 0, + 'advanced/default service name': UiStrings().DefaultServiceName, + 'advanced/display size': 0, + 'advanced/double click live': False, + 'advanced/enable exit confirmation': True, + 'advanced/expand service item': False, + 'advanced/hide mouse': True, + 'advanced/is portable': False, + 'advanced/max recent files': 20, + 'advanced/print file meta data': False, + 'advanced/print notes': False, + 'advanced/print slide text': False, + 'advanced/recent file count': 4, + 'advanced/save current plugin': False, + 'advanced/slide limits': SlideLimits.End, + 'advanced/single click preview': False, + 'advanced/x11 bypass wm': X11_BYPASS_DEFAULT, + 'crashreport/last directory': '', + 'formattingTags/html_tags': '', + 'core/audio repeat list': False, + 'core/auto open': False, + 'core/auto preview': False, + 'core/audio start paused': True, + 'core/auto unblank': False, + 'core/blank warning': False, + 'core/ccli number': '', + 'core/has run wizard': False, + 'core/language': '[en]', + 'core/last version test': '', + 'core/loop delay': 5, + 'core/recent files': [], + 'core/save prompt': False, + 'core/screen blank': False, + 'core/show splash': True, + 'core/songselect password': '', + 'core/songselect username': '', + 'core/update check': True, + 'core/view mode': 'default', + # The other display settings (display position and dimensions) are defined in the ScreenList class due to a + # circular dependency. + 'core/display on monitor': True, + 'core/override position': False, + 'images/background color': '#000000', + 'media/players': 'webkit', + 'media/override player': QtCore.Qt.Unchecked, + 'players/background color': '#000000', + 'servicemanager/last directory': '', + 'servicemanager/last file': '', + 'servicemanager/service theme': '', + 'SettingsImport/file_date_created': datetime.datetime.now().strftime("%Y-%m-%d %H:%M"), + 'SettingsImport/Make_Changes': 'At_Own_RISK', + 'SettingsImport/type': 'OpenLP_settings_export', + 'SettingsImport/version': '', + 'shortcuts/aboutItem': [QtGui.QKeySequence('Ctrl+F1')], + 'shortcuts/addToService': [], + 'shortcuts/audioPauseItem': [], + 'shortcuts/displayTagItem': [], + 'shortcuts/blankScreen': [QtGui.QKeySequence(QtCore.Qt.Key_Period)], + 'shortcuts/collapse': [QtGui.QKeySequence(QtCore.Qt.Key_Minus)], + 'shortcuts/desktopScreen': [QtGui.QKeySequence('D')], + 'shortcuts/delete': [], + 'shortcuts/down': [QtGui.QKeySequence(QtCore.Qt.Key_Down)], + 'shortcuts/editSong': [], + 'shortcuts/escapeItem': [QtGui.QKeySequence(QtCore.Qt.Key_Escape)], + 'shortcuts/expand': [QtGui.QKeySequence(QtCore.Qt.Key_Plus)], + 'shortcuts/exportThemeItem': [], + 'shortcuts/fileNewItem': [QtGui.QKeySequence('Ctrl+N')], + 'shortcuts/fileSaveAsItem': [QtGui.QKeySequence('Ctrl+Shift+S')], + 'shortcuts/fileExitItem': [QtGui.QKeySequence('Alt+F4')], + 'shortcuts/fileSaveItem': [QtGui.QKeySequence('Ctrl+S')], + 'shortcuts/fileOpenItem': [QtGui.QKeySequence('Ctrl+O')], + 'shortcuts/goLive': [], + 'shortcuts/importThemeItem': [], + 'shortcuts/importBibleItem': [], + 'shortcuts/listViewBiblesDeleteItem': [QtGui.QKeySequence(QtCore.Qt.Key_Delete)], + 'shortcuts/listViewBiblesPreviewItem': [QtGui.QKeySequence(QtCore.Qt.Key_Enter), + QtGui.QKeySequence(QtCore.Qt.Key_Return)], + 'shortcuts/listViewBiblesLiveItem': [QtGui.QKeySequence(QtCore.Qt.ShiftModifier | QtCore.Qt.Key_Enter), + QtGui.QKeySequence(QtCore.Qt.ShiftModifier | QtCore.Qt.Key_Return)], + 'shortcuts/listViewBiblesServiceItem': [QtGui.QKeySequence(QtCore.Qt.Key_Plus), + QtGui.QKeySequence(QtCore.Qt.Key_Equal)], + 'shortcuts/listViewCustomDeleteItem': [QtGui.QKeySequence(QtCore.Qt.Key_Delete)], + 'shortcuts/listViewCustomPreviewItem': [QtGui.QKeySequence(QtCore.Qt.Key_Enter), + QtGui.QKeySequence(QtCore.Qt.Key_Return)], + 'shortcuts/listViewCustomLiveItem': [QtGui.QKeySequence(QtCore.Qt.ShiftModifier | QtCore.Qt.Key_Enter), + QtGui.QKeySequence(QtCore.Qt.ShiftModifier | QtCore.Qt.Key_Return)], + 'shortcuts/listViewCustomServiceItem': [QtGui.QKeySequence(QtCore.Qt.Key_Plus), + QtGui.QKeySequence(QtCore.Qt.Key_Equal)], + 'shortcuts/listViewImagesDeleteItem': [QtGui.QKeySequence(QtCore.Qt.Key_Delete)], + 'shortcuts/listViewImagesPreviewItem': [QtGui.QKeySequence(QtCore.Qt.Key_Enter), + QtGui.QKeySequence(QtCore.Qt.Key_Return)], + 'shortcuts/listViewImagesLiveItem': [QtGui.QKeySequence(QtCore.Qt.ShiftModifier | QtCore.Qt.Key_Enter), + QtGui.QKeySequence(QtCore.Qt.ShiftModifier | QtCore.Qt.Key_Return)], + 'shortcuts/listViewImagesServiceItem': [QtGui.QKeySequence(QtCore.Qt.Key_Plus), + QtGui.QKeySequence(QtCore.Qt.Key_Equal)], + 'shortcuts/listViewMediaDeleteItem': [QtGui.QKeySequence(QtCore.Qt.Key_Delete)], + 'shortcuts/listViewMediaPreviewItem': [QtGui.QKeySequence(QtCore.Qt.Key_Enter), + QtGui.QKeySequence(QtCore.Qt.Key_Return)], + 'shortcuts/listViewMediaLiveItem': [QtGui.QKeySequence(QtCore.Qt.ShiftModifier | QtCore.Qt.Key_Enter), + QtGui.QKeySequence(QtCore.Qt.ShiftModifier | QtCore.Qt.Key_Return)], + 'shortcuts/listViewMediaServiceItem': [QtGui.QKeySequence(QtCore.Qt.Key_Plus), + QtGui.QKeySequence(QtCore.Qt.Key_Equal)], + 'shortcuts/listViewPresentationsDeleteItem': [QtGui.QKeySequence(QtCore.Qt.Key_Delete)], + 'shortcuts/listViewPresentationsPreviewItem': [QtGui.QKeySequence(QtCore.Qt.Key_Enter), + QtGui.QKeySequence(QtCore.Qt.Key_Return)], + 'shortcuts/listViewPresentationsLiveItem': [QtGui.QKeySequence(QtCore.Qt.ShiftModifier | QtCore.Qt.Key_Enter), + QtGui.QKeySequence(QtCore.Qt.ShiftModifier | QtCore.Qt.Key_Return)], + 'shortcuts/listViewPresentationsServiceItem': [QtGui.QKeySequence(QtCore.Qt.Key_Plus), + QtGui.QKeySequence(QtCore.Qt.Key_Equal)], + 'shortcuts/listViewSongsDeleteItem': [QtGui.QKeySequence(QtCore.Qt.Key_Delete)], + 'shortcuts/listViewSongsPreviewItem': [QtGui.QKeySequence(QtCore.Qt.Key_Enter), + QtGui.QKeySequence(QtCore.Qt.Key_Return)], + 'shortcuts/listViewSongsLiveItem': [QtGui.QKeySequence(QtCore.Qt.ShiftModifier | QtCore.Qt.Key_Enter), + QtGui.QKeySequence(QtCore.Qt.ShiftModifier | QtCore.Qt.Key_Return)], + 'shortcuts/listViewSongsServiceItem': [QtGui.QKeySequence(QtCore.Qt.Key_Plus), + QtGui.QKeySequence(QtCore.Qt.Key_Equal)], + 'shortcuts/lockPanel': [], + 'shortcuts/modeDefaultItem': [], + 'shortcuts/modeLiveItem': [], + 'shortcuts/make_live': [QtGui.QKeySequence(QtCore.Qt.Key_Enter), QtGui.QKeySequence(QtCore.Qt.Key_Return)], + 'shortcuts/moveUp': [QtGui.QKeySequence(QtCore.Qt.Key_PageUp)], + 'shortcuts/moveTop': [QtGui.QKeySequence(QtCore.Qt.Key_Home)], + 'shortcuts/modeSetupItem': [], + 'shortcuts/moveBottom': [QtGui.QKeySequence(QtCore.Qt.Key_End)], + 'shortcuts/moveDown': [QtGui.QKeySequence(QtCore.Qt.Key_PageDown)], + 'shortcuts/nextTrackItem': [], + 'shortcuts/nextItem_live': [QtGui.QKeySequence(QtCore.Qt.Key_Down), + QtGui.QKeySequence(QtCore.Qt.Key_PageDown)], + 'shortcuts/nextItem_preview': [], + 'shortcuts/nextService': [QtGui.QKeySequence(QtCore.Qt.Key_Right)], + 'shortcuts/newService': [], + 'shortcuts/offlineHelpItem': [], + 'shortcuts/onlineHelpItem': [QtGui.QKeySequence('Alt+F1')], + 'shortcuts/openService': [], + 'shortcuts/saveService': [], + 'shortcuts/previousItem_live': [QtGui.QKeySequence(QtCore.Qt.Key_Up), + QtGui.QKeySequence(QtCore.Qt.Key_PageUp)], + 'shortcuts/playbackPause': [], + 'shortcuts/playbackPlay': [], + 'shortcuts/playbackStop': [], + 'shortcuts/playSlidesLoop': [], + 'shortcuts/playSlidesOnce': [], + 'shortcuts/previousService': [QtGui.QKeySequence(QtCore.Qt.Key_Left)], + 'shortcuts/previousItem_preview': [], + 'shortcuts/printServiceItem': [QtGui.QKeySequence('Ctrl+P')], + 'shortcuts/songExportItem': [], + 'shortcuts/songUsageStatus': [QtGui.QKeySequence(QtCore.Qt.Key_F4)], + 'shortcuts/searchShortcut': [QtGui.QKeySequence('Ctrl+F')], + 'shortcuts/settingsShortcutsItem': [], + 'shortcuts/settingsImportItem': [], + 'shortcuts/settingsPluginListItem': [QtGui.QKeySequence('Alt+F7')], + 'shortcuts/songUsageDelete': [], + 'shortcuts/settingsConfigureItem': [], + 'shortcuts/shortcutAction_B': [QtGui.QKeySequence('B')], + 'shortcuts/shortcutAction_C': [QtGui.QKeySequence('C')], + 'shortcuts/shortcutAction_E': [QtGui.QKeySequence('E')], + 'shortcuts/shortcutAction_I': [QtGui.QKeySequence('I')], + 'shortcuts/shortcutAction_O': [QtGui.QKeySequence('O')], + 'shortcuts/shortcutAction_P': [QtGui.QKeySequence('P')], + 'shortcuts/shortcutAction_V': [QtGui.QKeySequence('V')], + 'shortcuts/shortcutAction_0': [QtGui.QKeySequence('0')], + 'shortcuts/shortcutAction_1': [QtGui.QKeySequence('1')], + 'shortcuts/shortcutAction_2': [QtGui.QKeySequence('2')], + 'shortcuts/shortcutAction_3': [QtGui.QKeySequence('3')], + 'shortcuts/shortcutAction_4': [QtGui.QKeySequence('4')], + 'shortcuts/shortcutAction_5': [QtGui.QKeySequence('5')], + 'shortcuts/shortcutAction_6': [QtGui.QKeySequence('6')], + 'shortcuts/shortcutAction_7': [QtGui.QKeySequence('7')], + 'shortcuts/shortcutAction_8': [QtGui.QKeySequence('8')], + 'shortcuts/shortcutAction_9': [QtGui.QKeySequence('9')], + 'shortcuts/settingsExportItem': [], + 'shortcuts/songUsageReport': [], + 'shortcuts/songImportItem': [], + 'shortcuts/themeScreen': [QtGui.QKeySequence('T')], + 'shortcuts/toolsReindexItem': [], + 'shortcuts/toolsFindDuplicates': [], + 'shortcuts/toolsAlertItem': [QtGui.QKeySequence('F7')], + 'shortcuts/toolsFirstTimeWizard': [], + 'shortcuts/toolsOpenDataFolder': [], + 'shortcuts/toolsAddToolItem': [], + 'shortcuts/updateThemeImages': [], + 'shortcuts/up': [QtGui.QKeySequence(QtCore.Qt.Key_Up)], + 'shortcuts/viewThemeManagerItem': [QtGui.QKeySequence('F10')], + 'shortcuts/viewMediaManagerItem': [QtGui.QKeySequence('F8')], + 'shortcuts/viewPreviewPanel': [QtGui.QKeySequence('F11')], + 'shortcuts/viewLivePanel': [QtGui.QKeySequence('F12')], + 'shortcuts/viewServiceManagerItem': [QtGui.QKeySequence('F9')], + 'shortcuts/webSiteItem': [], + 'themes/global theme': '', + 'themes/last directory': '', + 'themes/last directory export': '', + 'themes/last directory import': '', + 'themes/theme level': ThemeLevel.Song, + 'user interface/live panel': True, + 'user interface/live splitter geometry': QtCore.QByteArray(), + 'user interface/lock panel': False, + 'user interface/main window geometry': QtCore.QByteArray(), + 'user interface/main window position': QtCore.QPoint(0, 0), + 'user interface/main window splitter geometry': QtCore.QByteArray(), + 'user interface/main window state': QtCore.QByteArray(), + 'user interface/preview panel': True, + 'user interface/preview splitter geometry': QtCore.QByteArray() + } + __file_path__ = '' + __obsolete_settings__ = [ + # Changed during 1.9.x development. + ('bibles/bookname language', 'bibles/book name language', []), + ('general/enable slide loop', 'advanced/slide limits', [(SlideLimits.Wrap, True), (SlideLimits.End, False)]), + ('songs/ccli number', 'core/ccli number', []), + ('media/use phonon', '', []), + # Changed during 2.1.x development. + ('advanced/stylesheet fix', '', []), + ('bibles/last directory 1', 'bibles/last directory import', []), + ('media/background color', 'players/background color', []), + ('themes/last directory', 'themes/last directory import', []), + ('themes/last directory 1', 'themes/last directory export', []), + ('songs/last directory 1', 'songs/last directory import', []), + ('songusage/last directory 1', 'songusage/last directory export', []), + ('user interface/mainwindow splitter geometry', 'user interface/main window splitter geometry', []), + ('shortcuts/makeLive', 'shortcuts/make_live', []), + ('general/audio repeat list', 'core/audio repeat list', []), + ('general/auto open', 'core/auto open', []), + ('general/auto preview', 'core/auto preview', []), + ('general/audio start paused', 'core/audio start paused', []), + ('general/auto unblank', 'core/auto unblank', []), + ('general/blank warning', 'core/blank warning', []), + ('general/ccli number', 'core/ccli number', []), + ('general/has run wizard', 'core/has run wizard', []), + ('general/language', 'core/language', []), + ('general/last version test', 'core/last version test', []), + ('general/loop delay', 'core/loop delay', []), + ('general/recent files', 'core/recent files', []), + ('general/save prompt', 'core/save prompt', []), + ('general/screen blank', 'core/screen blank', []), + ('general/show splash', 'core/show splash', []), + ('general/songselect password', 'core/songselect password', []), + ('general/songselect username', 'core/songselect username', []), + ('general/update check', 'core/update check', []), + ('general/view mode', 'core/view mode', []), + ('general/display on monitor', 'core/display on monitor', []), + ('general/override position', 'core/override position', []), + ('general/x position', 'core/x position', []), + ('general/y position', 'core/y position', []), + ('general/monitor', 'core/monitor', []), + ('general/height', 'core/height', []), + ('general/monitor', 'core/monitor', []), + ('general/width', 'core/width', []) + ] + + @staticmethod + def extend_default_settings(default_values): + """ + Static method to merge the given ``default_values`` with the ``Settings.__default_settings__``. + + ``default_values`` + A dict with setting keys and their default values. + """ + Settings.__default_settings__ = dict(list(default_values.items()) + list(Settings.__default_settings__.items())) + + @staticmethod + def set_filename(ini_file): + """ + Sets the complete path to an Ini file to be used by Settings objects. + + Does not affect existing Settings objects. + """ + Settings.__file_path__ = ini_file + + @staticmethod + def set_up_default_values(): + """ + This static method is called on start up. It is used to perform any operation on the __default_settings__ dict. + """ + # Make sure the string is translated (when building the dict the string is not translated because the translate + # function was not set up as this stage). + Settings.__default_settings__['advanced/default service name'] = UiStrings().DefaultServiceName + + def __init__(self, *args): + """ + Constructor which checks if this should be a native settings object, or an INI file. + """ + if not args and Settings.__file_path__ and Settings.defaultFormat() == Settings.IniFormat: + QtCore.QSettings.__init__(self, Settings.__file_path__, Settings.IniFormat) + else: + QtCore.QSettings.__init__(self, *args) + + def get_default_value(self, key): + """ + Get the default value of the given key + """ + if self.group(): + key = self.group() + '/' + key + return Settings.__default_settings__[key] + + def remove_obsolete_settings(self): + """ + This method is only called to clean up the config. It removes old settings and it renames settings. See + ``__obsolete_settings__`` for more details. + """ + for old_key, new_key, rules in Settings.__obsolete_settings__: + # Once removed we don't have to do this again. + if self.contains(old_key): + if new_key: + # Get the value of the old_key. + old_value = super(Settings, self).value(old_key) + # When we want to convert the value, we have to figure out the default value (because we cannot get + # the default value from the central settings dict. + if rules: + default_value = rules[0][1] + old_value = self._convert_value(old_value, default_value) + # Iterate over our rules and check what the old_value should be "converted" to. + for new, old in rules: + # If the value matches with the condition (rule), then use the provided value. This is used to + # convert values. E. g. an old value 1 results in True, and 0 in False. + if old == old_value: + old_value = new + break + self.setValue(new_key, old_value) + self.remove(old_key) + + def value(self, key): + """ + Returns the value for the given ``key``. The returned ``value`` is of the same type as the default value in the + *Settings.__default_settings__* dict. + + ``key`` + The key to return the value from. + """ + # if group() is not empty the group has not been specified together with the key. + if self.group(): + default_value = Settings.__default_settings__[self.group() + '/' + key] + else: + default_value = Settings.__default_settings__[key] + setting = super(Settings, self).value(key, default_value) + return self._convert_value(setting, default_value) + + def _convert_value(self, setting, default_value): + """ + This converts the given ``setting`` to the type of the given ``default_value``. + + ``setting`` + The setting to convert. This could be ``true`` for example.Settings() + + ``default_value`` + Indication the type the setting should be converted to. For example ``True`` (type is boolean), meaning that + we convert the string ``true`` to a python boolean. + + **Note**, this method only converts a few types and might need to be extended if a certain type is missing! + """ + # On OS X (and probably on other platforms too) empty value from QSettings is represented as type + # PyQt4.QtCore.QPyNullVariant. This type has to be converted to proper 'None' Python type. + if isinstance(setting, QtCore.QPyNullVariant) and setting.isNull(): + setting = None + # Handle 'None' type (empty value) properly. + if setting is None: + # An empty string saved to the settings results in a None type being returned. + # Convert it to empty unicode string. + if isinstance(default_value, str): + return '' + # An empty list saved to the settings results in a None type being returned. + else: + return [] + # Convert the setting to the correct type. + if isinstance(default_value, bool): + if isinstance(setting, bool): + return setting + # Sometimes setting is string instead of a boolean. + return setting == 'true' + if isinstance(default_value, int): + return int(setting) + return setting + + def get_files_from_config(self, plugin): + """ + This removes the settings needed for old way we saved files (e. g. the image paths for the image plugin). A list + of file paths are returned. + + **Note**: Only a list of paths is returned; this does not convert anything! + + ``plugin`` + The Plugin object.The caller has to convert/save the list himself; o + """ + files_list = [] + # We need QSettings instead of Settings here to bypass our central settings dict. + # Do NOT do this anywhere else! + settings = QtCore.QSettings(self.fileName(), Settings.IniFormat) + settings.beginGroup(plugin.settings_section) + if settings.contains('%s count' % plugin.name): + # Get the count. + list_count = int(settings.value('%s count' % plugin.name, 0)) + if list_count: + for counter in range(list_count): + # The keys were named e. g.: "image 0" + item = settings.value('%s %d' % (plugin.name, counter), '') + if item: + files_list.append(item) + settings.remove('%s %d' % (plugin.name, counter)) + settings.remove('%s count' % plugin.name) + settings.endGroup() + return files_list From ba0808f551a18892557f35b7a2e6dda190565832 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Fri, 18 Oct 2013 19:10:47 +0100 Subject: [PATCH 92/93] Fix up json --- openlp/core/lib/json/theme.json | 102 ++++++++++-------- openlp/core/lib/theme.py | 23 +++- .../functional/openlp_core_lib/test_theme.py | 6 ++ 3 files changed, 83 insertions(+), 48 deletions(-) diff --git a/openlp/core/lib/json/theme.json b/openlp/core/lib/json/theme.json index 9991f2b53..e8862d0b4 100644 --- a/openlp/core/lib/json/theme.json +++ b/openlp/core/lib/json/theme.json @@ -1,49 +1,59 @@ { - "background_border_color": "#000000", - "background_color": "#000000", - "background_direction": "vertical", - "background_end_color": "#000000", - "background_filename": "", - "background_start_color": "#000000", - "background_type": "solid", - "display_horizontal_align": 0, - "display_slide_transition": false, - "display_vertical_align": 0, - "font_footer_bold": false, - "font_footer_color": "#FFFFFF", - "font_footer_height": 78, - "font_footer_italics": false, - "font_footer_line_adjustment": 0, - "font_footer_location": "", - "font_footer_name": "Arial", - "font_footer_outline": false, - "font_footer_outline_color": "#000000", - "font_footer_outline_size": 2, - "font_footer_override": false, - "font_footer_shadow": true, - "font_footer_shadow_color": "#000000", - "font_footer_shadow_size": 5, - "font_footer_size": 12, - "font_footer_width": 1004, - "font_footer_x": 10, - "font_footer_y": 690, - "font_main_bold": false, - "font_main_color": "#FFFFFF", - "font_main_height": 690, - "font_main_italics": false, - "font_main_line_adjustment": 0, - "font_main_location": "", - "font_main_name": "Arial", - "font_main_outline": false, - "font_main_outline_color": "#000000", - "font_main_outline_size": 2, - "font_main_override": false, - "font_main_shadow": true, - "font_main_shadow_color": "#000000", - "font_main_shadow_size": 5, - "font_main_size": 40, - "font_main_width": 1004, - "font_main_x": 10, - "font_main_y": 10, + "background" : { + "border_color": "#000000", + "color": "#000000", + "direction": "vertical", + "end_color": "#000000", + "filename": "", + "start_color": "#000000", + "type": "solid" + }, + "display" :{ + "horizontal_align": 0, + "slide_transition": false, + "vertical_align": 0 + }, + "font": { + "footer": { + "bold": false, + "color": "#FFFFFF", + "height": 78, + "italics": false, + "line_adjustment": 0, + "location": "", + "name": "Arial", + "outline": false, + "outline_color": "#000000", + "outline_size": 2, + "override": false, + "shadow": true, + "shadow_color": "#000000", + "shadow_size": 5, + "size": 12, + "width": 1004, + "x": 10, + "y": 690 + }, + "main": { + "bold": false, + "color": "#FFFFFF", + "height": 690, + "italics": false, + "line_adjustment": 0, + "location": "", + "name": "Arial", + "outline": false, + "outline_color": "#000000", + "outline_size": 2, + "override": false, + "shadow": true, + "shadow_color": "#000000", + "shadow_size": 5, + "size": 40, + "width": 1004, + "x": 10, + "y": 10 + } + }, "theme_name": "" } diff --git a/openlp/core/lib/theme.py b/openlp/core/lib/theme.py index e8918e4d1..86b126ed8 100644 --- a/openlp/core/lib/theme.py +++ b/openlp/core/lib/theme.py @@ -169,8 +169,27 @@ class ThemeXML(object): json_file = os.path.join(json_dir, 'theme.json') jsn = get_text_file_string(json_file) jsn = json.loads(jsn) - for key, value in jsn.items(): - setattr(self, key, value) + self.expand_json(jsn) + + def expand_json(self, var, prev=None): + """ + Expand the json objects and make into variables. + + ``var`` + The array list to be processed. + + ``prev`` + The preceding string to add to the key to make the variable. + """ + for key, value in var.items(): + if prev: + key = prev + "_" + key + else: + key = key + if isinstance(value, dict): + self.expand_json(value, key) + else: + setattr(self, key, value) def extend_image_filename(self, path): """ diff --git a/tests/functional/openlp_core_lib/test_theme.py b/tests/functional/openlp_core_lib/test_theme.py index 5f6bbc4e9..27097ca29 100644 --- a/tests/functional/openlp_core_lib/test_theme.py +++ b/tests/functional/openlp_core_lib/test_theme.py @@ -63,3 +63,9 @@ class TestTheme(TestCase): # THEN: We should get some default behaviours self.assertTrue(default_theme.background_border_color == '#000000', 'The theme should have a black border') self.assertTrue(default_theme.background_type == 'solid', 'There theme should have a solid backgrounds') + self.assertTrue(default_theme.background_type == 'solid', 'There theme should have a solid backgrounds') + self.assertTrue(default_theme.display_vertical_align == 0, + 'There theme should have display_vertical_align of 0') + self.assertTrue(default_theme.font_footer_name == "Arial", + 'There theme should has font_footer_name of Arial') + self.assertTrue(default_theme.font_main_bold is False, 'There theme should has font_main_bold of false') \ No newline at end of file From 28a94a06fb44d06d0d497600cf3ab2f51f972a76 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Fri, 18 Oct 2013 19:11:17 +0100 Subject: [PATCH 93/93] Duplicate line --- tests/functional/openlp_core_lib/test_theme.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/functional/openlp_core_lib/test_theme.py b/tests/functional/openlp_core_lib/test_theme.py index 27097ca29..1ece64b34 100644 --- a/tests/functional/openlp_core_lib/test_theme.py +++ b/tests/functional/openlp_core_lib/test_theme.py @@ -63,7 +63,6 @@ class TestTheme(TestCase): # THEN: We should get some default behaviours self.assertTrue(default_theme.background_border_color == '#000000', 'The theme should have a black border') self.assertTrue(default_theme.background_type == 'solid', 'There theme should have a solid backgrounds') - self.assertTrue(default_theme.background_type == 'solid', 'There theme should have a solid backgrounds') self.assertTrue(default_theme.display_vertical_align == 0, 'There theme should have display_vertical_align of 0') self.assertTrue(default_theme.font_footer_name == "Arial",