From ed29edec3abf0c7f1dae4388fadf5850e3d2b361 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Fri, 21 Mar 2014 21:04:53 +0100 Subject: [PATCH 01/20] imported duplicate check speed --- .../songs/forms/duplicatesongremovalform.py | 54 +++++++++++++------ openlp/plugins/songs/lib/songcompare.py | 11 ++-- 2 files changed, 44 insertions(+), 21 deletions(-) diff --git a/openlp/plugins/songs/forms/duplicatesongremovalform.py b/openlp/plugins/songs/forms/duplicatesongremovalform.py index 2da9113d6..7f65bf3ac 100644 --- a/openlp/plugins/songs/forms/duplicatesongremovalform.py +++ b/openlp/plugins/songs/forms/duplicatesongremovalform.py @@ -31,6 +31,7 @@ The duplicate song removal logic for OpenLP. """ import logging +import multiprocessing import os from PyQt4 import QtCore, QtGui @@ -45,6 +46,17 @@ from openlp.plugins.songs.lib.songcompare import songs_probably_equal log = logging.getLogger(__name__) +class SongIterator(object): + def __init__(self, songs): + self.songs = songs + + def __iter__(self): + for outer_song_counter in range(len(self.songs) - 1): + for inner_song_counter in range(outer_song_counter + 1, len(self.songs)): + yield (self.songs[outer_song_counter], self.songs[inner_song_counter]) + + + class DuplicateSongRemovalForm(OpenLPWizard): """ This is the Duplicate Song Removal Wizard. It provides functionality to search for and remove duplicate songs @@ -167,24 +179,32 @@ class DuplicateSongRemovalForm(OpenLPWizard): max_progress_count = max_songs * (max_songs - 1) // 2 self.duplicate_search_progress_bar.setMaximum(max_progress_count) songs = self.plugin.manager.get_all_objects(Song) - for outer_song_counter in range(max_songs - 1): - for inner_song_counter in range(outer_song_counter + 1, max_songs): - if songs_probably_equal(songs[outer_song_counter], songs[inner_song_counter]): - duplicate_added = self.add_duplicates_to_song_list( - songs[outer_song_counter], songs[inner_song_counter]) - if duplicate_added: - self.found_duplicates_edit.appendPlainText( - songs[outer_song_counter].title + " = " + songs[inner_song_counter].title) - self.duplicate_search_progress_bar.setValue(self.duplicate_search_progress_bar.value() + 1) - # The call to process_events() will keep the GUI responsive. - self.application.process_events() - if self.break_search: - return - self.review_total_count = len(self.duplicate_song_list) - if self.review_total_count == 0: - self.notify_no_duplicates() - else: + # Create a worker/process pool to check the songs. + process_number = max(1, multiprocessing.cpu_count() - 1) + pool = multiprocessing.Pool(process_number) + song_list = SongIterator(songs) + #song_list = [(songs[outer_song_counter], songs[inner_song_counter]) for outer_song_counter in range(max_songs - 1) for inner_song_counter in range(outer_song_counter + 1, max_songs)] + result = pool.imap_unordered(songs_probably_equal, song_list, 30) + # Do not accept any further tasks. Also this closes the processes if all tasks are done. + pool.close() + # While the processes are still working, start to look at the results. + for song_tuple in result: + self.duplicate_search_progress_bar.setValue(self.duplicate_search_progress_bar.value() + 1) + # The call to process_events() will keep the GUI responsive. + self.application.process_events() + if self.break_search: + pool.terminate() + return + if song_tuple is None: + continue + song1, song2 = song_tuple + duplicate_added = self.add_duplicates_to_song_list(song1, song2) + if duplicate_added: + self.found_duplicates_edit.appendPlainText(song1.title + " = " + song2.title) + if self.duplicate_song_list: self.button(QtGui.QWizard.NextButton).show() + else: + self.notify_no_duplicates() finally: self.application.set_normal_cursor() elif page_id == self.review_page_id: diff --git a/openlp/plugins/songs/lib/songcompare.py b/openlp/plugins/songs/lib/songcompare.py index 99a04beb2..09dacd649 100644 --- a/openlp/plugins/songs/lib/songcompare.py +++ b/openlp/plugins/songs/lib/songcompare.py @@ -52,13 +52,15 @@ MIN_BLOCK_SIZE = 70 MAX_TYPO_SIZE = 3 -def songs_probably_equal(song1, song2): +def songs_probably_equal(song1, song2=None): """ Calculate and return whether two songs are probably equal. :param song1: The first song to compare. :param song2: The second song to compare. """ + if song2 is None: + song1, song2 = song1 if len(song1.search_lyrics) < len(song2.search_lyrics): small = song1.search_lyrics large = song2.search_lyrics @@ -75,8 +77,9 @@ def songs_probably_equal(song1, song2): for element in diff_no_typos: if element[0] == "equal" and _op_length(element) >= MIN_BLOCK_SIZE: length_of_equal_blocks += _op_length(element) + if length_of_equal_blocks >= MIN_BLOCK_SIZE: - return True + return song1, song2 # Check 2: Similarity based on the relative length of the longest equal block. # Calculate the length of the largest equal block of the diff set. length_of_longest_equal_block = 0 @@ -84,9 +87,9 @@ def songs_probably_equal(song1, song2): if element[0] == "equal" and _op_length(element) > length_of_longest_equal_block: length_of_longest_equal_block = _op_length(element) if length_of_equal_blocks >= MIN_BLOCK_SIZE or length_of_longest_equal_block > len(small) * 2 // 3: - return True + return song1, song2 # Both checks failed. We assume the songs are not equal. - return False + return None def _op_length(opcode): From 394e5a5be2cc55877e4fc04743ee86678d2ecf67 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sat, 22 Mar 2014 09:52:49 +0100 Subject: [PATCH 02/20] removed code, commment --- openlp/plugins/songs/forms/duplicatesongremovalform.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openlp/plugins/songs/forms/duplicatesongremovalform.py b/openlp/plugins/songs/forms/duplicatesongremovalform.py index 5b8ccebfe..54778de03 100644 --- a/openlp/plugins/songs/forms/duplicatesongremovalform.py +++ b/openlp/plugins/songs/forms/duplicatesongremovalform.py @@ -47,6 +47,9 @@ log = logging.getLogger(__name__) class SongIterator(object): + """ + This class implements an iterator for the song duplicate finder. The iterator returns a tuple of two songs. + """ def __init__(self, songs): self.songs = songs @@ -182,7 +185,6 @@ class DuplicateSongRemovalForm(OpenLPWizard, RegistryProperties): process_number = max(1, multiprocessing.cpu_count() - 1) pool = multiprocessing.Pool(process_number) song_list = SongIterator(songs) - #song_list = [(songs[outer_song_counter], songs[inner_song_counter]) for outer_song_counter in range(max_songs - 1) for inner_song_counter in range(outer_song_counter + 1, max_songs)] result = pool.imap_unordered(songs_probably_equal, song_list, 30) # Do not accept any further tasks. Also this closes the processes if all tasks are done. pool.close() From 11fe2045e50d3750a394c9a7376fc46c27d2f02b Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sat, 22 Mar 2014 10:24:52 +0100 Subject: [PATCH 03/20] fixed tests --- .../songs/forms/duplicatesongremovalform.py | 3 ++- .../functional/openlp_plugins/songs/test_lib.py | 16 ++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/openlp/plugins/songs/forms/duplicatesongremovalform.py b/openlp/plugins/songs/forms/duplicatesongremovalform.py index 54778de03..d63391c08 100644 --- a/openlp/plugins/songs/forms/duplicatesongremovalform.py +++ b/openlp/plugins/songs/forms/duplicatesongremovalform.py @@ -48,7 +48,8 @@ log = logging.getLogger(__name__) class SongIterator(object): """ - This class implements an iterator for the song duplicate finder. The iterator returns a tuple of two songs. + This class implements an iterator for the song duplicate finder. The iterator returns a tuple of two songs. When + completely iterated then all songs have once been returned combined with any other songs. """ def __init__(self, songs): self.songs = songs diff --git a/tests/functional/openlp_plugins/songs/test_lib.py b/tests/functional/openlp_plugins/songs/test_lib.py index beff6d0d5..2683770c6 100644 --- a/tests/functional/openlp_plugins/songs/test_lib.py +++ b/tests/functional/openlp_plugins/songs/test_lib.py @@ -96,10 +96,10 @@ class TestLib(TestCase): self.song2.search_lyrics = self.full_lyrics # WHEN: We compare those songs for equality. - result = songs_probably_equal(self.song1, self.song2) + result = songs_probably_equal((self.song1, self.song2)) # THEN: The result should be True. - assert result == True, 'The result should be True' + assert result == (self.song1, self.song2), 'The result should be the tuble of songs' def songs_probably_equal_short_song_test(self): """ @@ -110,10 +110,10 @@ class TestLib(TestCase): self.song2.search_lyrics = self.short_lyrics # WHEN: We compare those songs for equality. - result = songs_probably_equal(self.song1, self.song2) + result = songs_probably_equal((self.song1, self.song2)) # THEN: The result should be True. - assert result == True, 'The result should be True' + assert result == (self.song1, self.song2), 'The result should be the tuble of songs' def songs_probably_equal_error_song_test(self): """ @@ -124,10 +124,10 @@ class TestLib(TestCase): self.song2.search_lyrics = self.error_lyrics # WHEN: We compare those songs for equality. - result = songs_probably_equal(self.song1, self.song2) + result = songs_probably_equal((self.song1, self.song2)) # THEN: The result should be True. - assert result == True, 'The result should be True' + assert result == (self.song1, self.song2), 'The result should be the tuble of songs' def songs_probably_equal_different_song_test(self): """ @@ -138,10 +138,10 @@ class TestLib(TestCase): self.song2.search_lyrics = self.different_lyrics # WHEN: We compare those songs for equality. - result = songs_probably_equal(self.song1, self.song2) + result = songs_probably_equal((self.song1, self.song2)) # THEN: The result should be False. - assert result == False, 'The result should be False' + assert result is None, 'The result should be None' def remove_typos_beginning_test(self): """ From cc0b736ef627ea1c18594d5c588b22abbb1ec8cc Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sat, 22 Mar 2014 20:35:24 +0100 Subject: [PATCH 04/20] condition already checked above --- openlp/plugins/songs/lib/songcompare.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/plugins/songs/lib/songcompare.py b/openlp/plugins/songs/lib/songcompare.py index 399672fbe..512ad2ad5 100644 --- a/openlp/plugins/songs/lib/songcompare.py +++ b/openlp/plugins/songs/lib/songcompare.py @@ -84,7 +84,7 @@ def songs_probably_equal(song_tupel): for element in diff_no_typos: if element[0] == "equal" and _op_length(element) > length_of_longest_equal_block: length_of_longest_equal_block = _op_length(element) - if length_of_equal_blocks >= MIN_BLOCK_SIZE or length_of_longest_equal_block > len(small) * 2 // 3: + if length_of_longest_equal_block > len(small) * 2 // 3: return song1, song2 # Both checks failed. We assume the songs are not equal. return None From c64723371af2589b5916074d07631eca3fdd64ce Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 31 Mar 2014 16:47:43 +0200 Subject: [PATCH 05/20] test of ci script --- tests/interfaces/openlp_core_ui/test_filerenamedialog.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/interfaces/openlp_core_ui/test_filerenamedialog.py b/tests/interfaces/openlp_core_ui/test_filerenamedialog.py index 905f167e9..c618e680e 100644 --- a/tests/interfaces/openlp_core_ui/test_filerenamedialog.py +++ b/tests/interfaces/openlp_core_ui/test_filerenamedialog.py @@ -62,6 +62,7 @@ class TestStartFileRenameForm(TestCase, TestMixin): """ Test the windowTitle of the FileRenameDialog """ + assert False, "reason" # GIVEN: A mocked QDialog.exec_() method with patch('PyQt4.QtGui.QDialog.exec_') as mocked_exec: From d77c6fe7c0f6d53fc3e33b683dbda6b9fff0f82a Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 31 Mar 2014 17:17:40 +0200 Subject: [PATCH 06/20] added renderer test case --- .../openlp_core_lib/test_renderer.py | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/tests/functional/openlp_core_lib/test_renderer.py b/tests/functional/openlp_core_lib/test_renderer.py index d0aa82b74..e4e7f0051 100644 --- a/tests/functional/openlp_core_lib/test_renderer.py +++ b/tests/functional/openlp_core_lib/test_renderer.py @@ -49,7 +49,7 @@ class TestRenderer(TestCase): def setUp(self): """ - Set up the components need for all tests. + Set up the components need for all tests """ # Mocked out desktop object self.desktop = MagicMock() @@ -67,7 +67,7 @@ class TestRenderer(TestCase): def initial_renderer_test(self): """ - Test the initial renderer state . + Test the initial renderer state """ # GIVEN: A new renderer instance. renderer = Renderer() @@ -77,7 +77,7 @@ class TestRenderer(TestCase): def default_screen_layout_test(self): """ - Test the default layout calculations. + Test the default layout calculations """ # GIVEN: A new renderer instance. renderer = Renderer() @@ -86,4 +86,20 @@ class TestRenderer(TestCase): self.assertEqual(renderer.width, 1024, 'The base renderer should be a live controller') self.assertEqual(renderer.height, 768, 'The base renderer should be a live controller') self.assertEqual(renderer.screen_ratio, 0.75, 'The base renderer should be a live controller') - self.assertEqual(renderer.footer_start, 691, 'The base renderer should be a live controller') \ No newline at end of file + self.assertEqual(renderer.footer_start, 691, 'The base renderer should be a live controller') + + def _get_start_tags_test(self): + """ + Test the _get_start_tags() method + """ + # GIVEN: A new renderer instance. + renderer = Renderer() + given_raw_text = '{st}{r}Text text text' + expected_tuple = ('{st}{r}Text text text{/r}{/st}', '{st}{r}', + '') + + # WHEN: + result = renderer._get_start_tags(given_raw_text) + + # THEN: + self.assertEqual(result, expected_tuple) \ No newline at end of file From b391072e9f74f5ed765a164a24ff1bff2ad806a6 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 31 Mar 2014 17:20:17 +0200 Subject: [PATCH 07/20] removed test code --- tests/functional/openlp_core_lib/test_renderer.py | 2 +- tests/interfaces/openlp_core_ui/test_filerenamedialog.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/functional/openlp_core_lib/test_renderer.py b/tests/functional/openlp_core_lib/test_renderer.py index e4e7f0051..aa27f93e7 100644 --- a/tests/functional/openlp_core_lib/test_renderer.py +++ b/tests/functional/openlp_core_lib/test_renderer.py @@ -92,7 +92,7 @@ class TestRenderer(TestCase): """ Test the _get_start_tags() method """ - # GIVEN: A new renderer instance. + # GIVEN: A new renderer instance. Broken raw_text (missing closing tags). renderer = Renderer() given_raw_text = '{st}{r}Text text text' expected_tuple = ('{st}{r}Text text text{/r}{/st}', '{st}{r}', diff --git a/tests/interfaces/openlp_core_ui/test_filerenamedialog.py b/tests/interfaces/openlp_core_ui/test_filerenamedialog.py index c618e680e..905f167e9 100644 --- a/tests/interfaces/openlp_core_ui/test_filerenamedialog.py +++ b/tests/interfaces/openlp_core_ui/test_filerenamedialog.py @@ -62,7 +62,6 @@ class TestStartFileRenameForm(TestCase, TestMixin): """ Test the windowTitle of the FileRenameDialog """ - assert False, "reason" # GIVEN: A mocked QDialog.exec_() method with patch('PyQt4.QtGui.QDialog.exec_') as mocked_exec: From 3f038ad5c5c82ecfd329fa845aa8e6ac44156f41 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 31 Mar 2014 17:21:41 +0200 Subject: [PATCH 08/20] break --- tests/functional/openlp_core_lib/test_renderer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/openlp_core_lib/test_renderer.py b/tests/functional/openlp_core_lib/test_renderer.py index aa27f93e7..a17ddb88f 100644 --- a/tests/functional/openlp_core_lib/test_renderer.py +++ b/tests/functional/openlp_core_lib/test_renderer.py @@ -96,7 +96,7 @@ class TestRenderer(TestCase): renderer = Renderer() given_raw_text = '{st}{r}Text text text' expected_tuple = ('{st}{r}Text text text{/r}{/st}', '{st}{r}', - '') + '') # WHEN: result = renderer._get_start_tags(given_raw_text) From 9dd5f3025411ade09fde61d8c51896e05e5af38e Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 31 Mar 2014 17:24:31 +0200 Subject: [PATCH 09/20] be a bit more talky --- tests/functional/openlp_core_lib/test_renderer.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/functional/openlp_core_lib/test_renderer.py b/tests/functional/openlp_core_lib/test_renderer.py index a17ddb88f..cd07ad9a2 100644 --- a/tests/functional/openlp_core_lib/test_renderer.py +++ b/tests/functional/openlp_core_lib/test_renderer.py @@ -96,10 +96,11 @@ class TestRenderer(TestCase): renderer = Renderer() given_raw_text = '{st}{r}Text text text' expected_tuple = ('{st}{r}Text text text{/r}{/st}', '{st}{r}', - '') + '') # WHEN: result = renderer._get_start_tags(given_raw_text) - # THEN: - self.assertEqual(result, expected_tuple) \ No newline at end of file + # THEN: Check if the correct tuple is returned. + self.assertEqual(result, expected_tuple), 'A tuple should be returned ' + '(fixed-text, opening tags, html opening tags).' From 3260de7b7c351b9d3a134211a78b9264bbc1095e Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 31 Mar 2014 17:31:05 +0200 Subject: [PATCH 10/20] added another test --- tests/functional/openlp_core_lib/test_renderer.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/functional/openlp_core_lib/test_renderer.py b/tests/functional/openlp_core_lib/test_renderer.py index cd07ad9a2..22b7c4a7b 100644 --- a/tests/functional/openlp_core_lib/test_renderer.py +++ b/tests/functional/openlp_core_lib/test_renderer.py @@ -104,3 +104,18 @@ class TestRenderer(TestCase): # THEN: Check if the correct tuple is returned. self.assertEqual(result, expected_tuple), 'A tuple should be returned ' '(fixed-text, opening tags, html opening tags).' + + def _word_split_test(self): + """ + Test the _word_split() method + """ + # GIVEN: A line of text + renderer = Renderer() + given_line = 'beginning asdf \n end asdf' + expected_words = ['beginning', 'asdf', 'end', 'asdf'] + + # WHEN: Split the line + result_words = renderer._words_split(given_line) + + # THEN: The word lists should be the same. + self.assertListEqual(result_words, expected_words) From 4da90f0c6a2d830783655cf029cbb314e33924ee Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 31 Mar 2014 17:36:11 +0200 Subject: [PATCH 11/20] comments: updated comments --- tests/functional/openlp_plugins/songs/test_lib.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/functional/openlp_plugins/songs/test_lib.py b/tests/functional/openlp_plugins/songs/test_lib.py index 2683770c6..67d54d3f0 100644 --- a/tests/functional/openlp_plugins/songs/test_lib.py +++ b/tests/functional/openlp_plugins/songs/test_lib.py @@ -98,7 +98,7 @@ class TestLib(TestCase): # WHEN: We compare those songs for equality. result = songs_probably_equal((self.song1, self.song2)) - # THEN: The result should be True. + # THEN: The result should be a tuple.. assert result == (self.song1, self.song2), 'The result should be the tuble of songs' def songs_probably_equal_short_song_test(self): @@ -112,7 +112,7 @@ class TestLib(TestCase): # WHEN: We compare those songs for equality. result = songs_probably_equal((self.song1, self.song2)) - # THEN: The result should be True. + # THEN: The result should be a tuple.. assert result == (self.song1, self.song2), 'The result should be the tuble of songs' def songs_probably_equal_error_song_test(self): @@ -126,7 +126,7 @@ class TestLib(TestCase): # WHEN: We compare those songs for equality. result = songs_probably_equal((self.song1, self.song2)) - # THEN: The result should be True. + # THEN: The result should be a tuple of songs.. assert result == (self.song1, self.song2), 'The result should be the tuble of songs' def songs_probably_equal_different_song_test(self): @@ -140,7 +140,7 @@ class TestLib(TestCase): # WHEN: We compare those songs for equality. result = songs_probably_equal((self.song1, self.song2)) - # THEN: The result should be False. + # THEN: The result should be Nonw. assert result is None, 'The result should be None' def remove_typos_beginning_test(self): From 729edf1d21ba81e936f5f1114837f4fb61575fa4 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 31 Mar 2014 17:44:18 +0200 Subject: [PATCH 12/20] pep8 failure test --- tests/functional/openlp_core_lib/test_renderer.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/functional/openlp_core_lib/test_renderer.py b/tests/functional/openlp_core_lib/test_renderer.py index 22b7c4a7b..4a3e54c0d 100644 --- a/tests/functional/openlp_core_lib/test_renderer.py +++ b/tests/functional/openlp_core_lib/test_renderer.py @@ -119,3 +119,5 @@ class TestRenderer(TestCase): # THEN: The word lists should be the same. self.assertListEqual(result_words, expected_words) + + pep_error = 'sdfjalksdjfl kajsdlfj lkasdjflkjaslkdjlkasjdljklsdjflkajsdljalksdflkajsdlfj laskdflkjsdlfkjaslkdflksajdlfk' From a49854e20b7689b532c28effb2d8981d172882ff Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Tue, 8 Apr 2014 13:07:52 +0200 Subject: [PATCH 13/20] Update verse order when verse names change --- openlp/plugins/songs/lib/xml.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/openlp/plugins/songs/lib/xml.py b/openlp/plugins/songs/lib/xml.py index 667afebdd..d516b5e02 100644 --- a/openlp/plugins/songs/lib/xml.py +++ b/openlp/plugins/songs/lib/xml.py @@ -675,6 +675,7 @@ class OpenLyrics(object): sxml = SongXML() verses = {} verse_def_list = [] + verse_order = self._text(properties.verseOrder).split(' ') if hasattr(properties, 'verseOrder') else [] try: lyrics = song_xml.lyrics except AttributeError: @@ -717,13 +718,17 @@ class OpenLyrics(object): else: verses[(verse_tag, verse_number, lang, translit, verse_part)] = text verse_def_list.append((verse_tag, verse_number, lang, translit, verse_part)) + # Update verse order when the verse name has changed + if verse_def != verse_tag + verse_number + verse_part: + for i in range(len(verse_order)): + if verse_order[i] == verse_def: + verse_order[i] = verse_tag + verse_number + verse_part # We have to use a list to keep the order, as dicts are not sorted. for verse in verse_def_list: sxml.add_verse_to_lyrics(verse[0], verse[1], verses[verse], verse[2]) song_obj.lyrics = str(sxml.extract_xml(), 'utf-8') # Process verse order - if hasattr(properties, 'verseOrder'): - song_obj.verse_order = self._text(properties.verseOrder) + song_obj.verse_order = ' '.join(verse_order) def _process_songbooks(self, properties, song): """ From 2b8586bf99a9168d9e8daf3ef43ceba578a56fdc Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Tue, 8 Apr 2014 13:19:12 +0200 Subject: [PATCH 14/20] Test --- tests/functional/openlp_core_lib/test_ui.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/functional/openlp_core_lib/test_ui.py b/tests/functional/openlp_core_lib/test_ui.py index babc94a81..9ea94719f 100644 --- a/tests/functional/openlp_core_lib/test_ui.py +++ b/tests/functional/openlp_core_lib/test_ui.py @@ -81,3 +81,18 @@ class TestUi(TestCase): self.assertIsInstance(btnbox, QtGui.QDialogButtonBox) self.assertEqual(1, len(btnbox.buttons())) self.assertEqual(QtGui.QDialogButtonBox.HelpRole, btnbox.buttonRole(btnbox.buttons()[0])) + + def test_create_horizontal_adjusting_combo_box(self): + """ + Test creating a horizontal adjusting combo box + """ + # GIVEN: A dialog + dialog = QtGui.QDialog() + + # WHEN: We create the combobox + combo = create_horizontal_adjusting_combo_box(dialog, 'combo1') + + # THEN: We should get a ComboBox + self.assertIsInstance(combo, QtGui.QComboBox) + self.assertEqual('combo1', combo.objectName()) + self.assertEqual(QtGui.QComboBox.AdjustToMinimumContentsLength, combo.sizeAdjustPolicy()) From 49ff37c530dd6ab7875a7e4fd37e09ffab8a3d1d Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sun, 13 Apr 2014 09:54:34 +0200 Subject: [PATCH 15/20] fixed long line; use generator --- openlp/core/ui/servicemanager.py | 3 ++- .../songs/forms/duplicatesongremovalform.py | 21 ++++++++----------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 4489360da..09d7908c1 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -235,7 +235,8 @@ class Ui_ServiceManager(object): self.edit_action = create_widget_action(self.menu, text=translate('OpenLP.ServiceManager', '&Edit Item'), icon=':/general/general_edit.png', triggers=self.remote_edit) self.rename_action = create_widget_action(self.menu, text=translate('OpenLP.ServiceManager', '&Rename...'), - icon=':/general/general_edit.png', triggers=self.on_service_item_rename) + icon=':/general/general_edit.png', + triggers=self.on_service_item_rename) self.maintain_action = create_widget_action(self.menu, text=translate('OpenLP.ServiceManager', '&Reorder Item'), icon=':/general/general_edit.png', triggers=self.on_service_item_edit_form) diff --git a/openlp/plugins/songs/forms/duplicatesongremovalform.py b/openlp/plugins/songs/forms/duplicatesongremovalform.py index 1fd81ef07..35eccaeb4 100644 --- a/openlp/plugins/songs/forms/duplicatesongremovalform.py +++ b/openlp/plugins/songs/forms/duplicatesongremovalform.py @@ -46,18 +46,16 @@ from openlp.plugins.songs.lib.songcompare import songs_probably_equal log = logging.getLogger(__name__) -class SongIterator(object): +def song_generator(songs): """ - This class implements an iterator for the song duplicate finder. The iterator returns a tuple of two songs. When - completely iterated then all songs have once been returned combined with any other songs. - """ - def __init__(self, songs): - self.songs = songs + This is a generator function to return tuples of two songs. When completed then all songs have once been returned + combined with any other songs. - def __iter__(self): - for outer_song_counter in range(len(self.songs) - 1): - for inner_song_counter in range(outer_song_counter + 1, len(self.songs)): - yield (self.songs[outer_song_counter], self.songs[inner_song_counter]) + :param songs: All songs in the database. + """ + for outer_song_counter in range(len(songs) - 1): + for inner_song_counter in range(outer_song_counter + 1, len(songs)): + yield (songs[outer_song_counter], songs[inner_song_counter]) class DuplicateSongRemovalForm(OpenLPWizard, RegistryProperties): @@ -185,8 +183,7 @@ class DuplicateSongRemovalForm(OpenLPWizard, RegistryProperties): # Create a worker/process pool to check the songs. process_number = max(1, multiprocessing.cpu_count() - 1) pool = multiprocessing.Pool(process_number) - song_list = SongIterator(songs) - result = pool.imap_unordered(songs_probably_equal, song_list, 30) + result = pool.imap_unordered(songs_probably_equal, song_generator(songs), 30) # Do not accept any further tasks. Also this closes the processes if all tasks are done. pool.close() # While the processes are still working, start to look at the results. From 7e064246fbfa7c106e7a518f0519bf712325128a Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sun, 13 Apr 2014 10:21:30 +0200 Subject: [PATCH 16/20] fixed counter --- openlp/plugins/songs/forms/duplicatesongremovalform.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openlp/plugins/songs/forms/duplicatesongremovalform.py b/openlp/plugins/songs/forms/duplicatesongremovalform.py index 35eccaeb4..930e8cf37 100644 --- a/openlp/plugins/songs/forms/duplicatesongremovalform.py +++ b/openlp/plugins/songs/forms/duplicatesongremovalform.py @@ -200,6 +200,7 @@ class DuplicateSongRemovalForm(OpenLPWizard, RegistryProperties): duplicate_added = self.add_duplicates_to_song_list(song1, song2) if duplicate_added: self.found_duplicates_edit.appendPlainText(song1.title + " = " + song2.title) + self.review_total_count = len(self.duplicate_song_list) if self.duplicate_song_list: self.button(QtGui.QWizard.NextButton).show() else: From 2c8d163b09bf84f4db5c59f7508d45baf379841f Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 14 Apr 2014 20:59:28 +0200 Subject: [PATCH 17/20] fixed assertion message --- tests/functional/openlp_core_lib/test_renderer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/functional/openlp_core_lib/test_renderer.py b/tests/functional/openlp_core_lib/test_renderer.py index 22b7c4a7b..8814a21a0 100644 --- a/tests/functional/openlp_core_lib/test_renderer.py +++ b/tests/functional/openlp_core_lib/test_renderer.py @@ -102,8 +102,8 @@ class TestRenderer(TestCase): result = renderer._get_start_tags(given_raw_text) # THEN: Check if the correct tuple is returned. - self.assertEqual(result, expected_tuple), 'A tuple should be returned ' - '(fixed-text, opening tags, html opening tags).' + self.assertEqual(result, expected_tuple), 'A tuple should be returned containing the text with correct ' \ + 'tags, the opening tags, and the opening html tags.' def _word_split_test(self): """ From a07a97f78b1924a62772f6738bacc4d0f0ef73b3 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 14 Apr 2014 21:07:01 +0200 Subject: [PATCH 18/20] strange pyqt fix --- openlp/core/ui/servicemanager.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 1796ddc11..70cbd6141 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -1489,9 +1489,11 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ServiceManage if new_item: self.add_service_item(new_item, replace=True) - def on_service_item_rename(self): + def on_service_item_rename(self, field=None): """ Opens a dialog to rename the service item. + + :param field: Not used, but PyQt needs this. """ item = self.find_service_item()[0] if not self.service_items[item]['service_item'].is_capable(ItemCapabilities.CanEditTitle): From 0c2112091d0819f90d5f7a00f785aeaa0d63ee2c Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 14 Apr 2014 21:36:07 +0200 Subject: [PATCH 19/20] fixed spelling --- tests/functional/openlp_plugins/songs/test_lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/openlp_plugins/songs/test_lib.py b/tests/functional/openlp_plugins/songs/test_lib.py index 2b7f107e8..2ab808bc9 100644 --- a/tests/functional/openlp_plugins/songs/test_lib.py +++ b/tests/functional/openlp_plugins/songs/test_lib.py @@ -141,7 +141,7 @@ class TestLib(TestCase): # WHEN: We compare those songs for equality. result = songs_probably_equal((self.song1, self.song2)) - # THEN: The result should be Nonw. + # THEN: The result should be None. assert result is None, 'The result should be None' def remove_typos_beginning_test(self):