Merge lp:~phill-ridout/openlp/saved_bible_verses into lp:openlp

Proposed by Phill on 2017-05-26
Status: Merged
Merged at revision: 2743
Proposed branch: lp:~phill-ridout/openlp/saved_bible_verses
Merge into: lp:openlp
Diff against target: 1076 lines (+324/-202)
12 files modified
openlp/core/ui/lib/listwidgetwithdnd.py (+10/-4)
openlp/core/ui/lib/pathedit.py (+4/-4)
openlp/plugins/bibles/lib/__init__.py (+3/-3)
openlp/plugins/bibles/lib/db.py (+13/-0)
openlp/plugins/bibles/lib/manager.py (+15/-43)
openlp/plugins/bibles/lib/mediaitem.py (+178/-63)
openlp/plugins/presentations/presentationplugin.py (+1/-1)
openlp/plugins/songusage/forms/songusagedetaildialog.py (+1/-1)
resources/images/openlp-2.qrc (+2/-4)
tests/functional/openlp_core_ui_lib/test_listwidgetwithdnd.py (+33/-31)
tests/functional/openlp_plugins/bibles/test_mediaitem.py (+63/-47)
tests/interfaces/openlp_plugins/bibles/test_lib_parse_reference.py (+1/-1)
To merge this branch: bzr merge lp:~phill-ridout/openlp/saved_bible_verses
Reviewer Review Type Date Requested Status
Tomas Groth Approve on 2017-05-30
Tim Bentley 2017-05-26 Pending
Review via email: mp+324674@code.launchpad.net

This proposal supersedes a proposal from 2017-05-07.

Description of the change

To post a comment you must log in.
Tim Bentley (trb143) wrote : Posted in a previous version of this proposal

Please can you remove the resource file as this makes it difficult to see the changes.
The resource file can be merged in the next request,

review: Needs Fixing
Tim Bentley (trb143) wrote : Posted in a previous version of this proposal

Traceback (most recent call last):
  File "/home/tim/Projects/OpenLP/openlp/saved_bible_verses/openlp/plugins/bibles/lib/mediaitem.py", line 789, in on_search_timer_timeout
    self.text_search()
  File "/home/tim/Projects/OpenLP/openlp/saved_bible_verses/openlp/plugins/bibles/lib/mediaitem.py", line 765, in text_search
    self.display_results()
  File "/home/tim/Projects/OpenLP/openlp/saved_bible_verses/openlp/plugins/bibles/lib/mediaitem.py", line 798, in display_results
    self.current_results = self.build_display_results(self.bible, self.second_bible, self.search_results)
  File "/home/tim/Projects/OpenLP/openlp/saved_bible_verses/openlp/plugins/bibles/lib/mediaitem.py", line 814, in build_display_results
    version = self.plugin.manager.get_meta_data(self.bible.name, 'name').value
AttributeError: 'NoneType' object has no attribute 'name'

downloaded and started to no bibles loaded !!!!!!!

Loaded a new bible and started to type. Search as type fired and downloaded the chapter and gave me a message saying search as you type is not available!

Then a traceback
Traceback (most recent call last):
  File "/home/tim/Projects/OpenLP/openlp/saved_bible_verses/openlp/plugins/bibles/lib/mediaitem.py", line 789, in on_search_timer_timeout
    self.text_search()
  File "/home/tim/Projects/OpenLP/openlp/saved_bible_verses/openlp/plugins/bibles/lib/mediaitem.py", line 768, in text_search
    self.on_text_search(text)
  File "/home/tim/Projects/OpenLP/openlp/saved_bible_verses/openlp/plugins/bibles/lib/mediaitem.py", line 732, in on_text_search
    self.display_results()
  File "/home/tim/Projects/OpenLP/openlp/saved_bible_verses/openlp/plugins/bibles/lib/mediaitem.py", line 798, in display_results
    self.current_results = self.build_display_results(self.bible, self.second_bible, self.search_results)
  File "/home/tim/Projects/OpenLP/openlp/saved_bible_verses/openlp/plugins/bibles/lib/mediaitem.py", line 828, in build_display_results
    for count, verse in enumerate(search_results):
TypeError: 'NoneType' object is not iterable

On the select UI I only have 2 lines of search as the options is large and takes up most the the space.

review: Needs Fixing
Tim Bentley (trb143) wrote : Posted in a previous version of this proposal

Traceback (most recent call last):
  File "./openlp.py", line 44, in <module>
    main()
  File "/home/tim/Projects/OpenLP/openlp/saved_bible_verses/openlp/core/__init__.py", line 439, in main
    sys.exit(application.run(qt_args))
  File "/home/tim/Projects/OpenLP/openlp/saved_bible_verses/openlp/core/__init__.py", line 145, in run
    self.main_window.show()
  File "/home/tim/Projects/OpenLP/openlp/saved_bible_verses/openlp/core/ui/mainwindow.py", line 626, in show
    self.service_manager_contents.load_last_file()
  File "/home/tim/Projects/OpenLP/openlp/saved_bible_verses/openlp/core/ui/servicemanager.py", line 841, in load_last_file
    self.load_file(file_name)
  File "/home/tim/Projects/OpenLP/openlp/saved_bible_verses/openlp/core/ui/servicemanager.py", line 770, in load_file
    self.process_service_items(items)
  File "/home/tim/Projects/OpenLP/openlp/saved_bible_verses/openlp/core/ui/servicemanager.py", line 829, in process_service_items
    new_item = Registry().get(service_item.name).service_load(service_item)
  File "/home/tim/Projects/OpenLP/openlp/saved_bible_verses/openlp/plugins/songs/lib/mediaitem.py", line 712, in service_load
    self.generate_footer(item, song)
  File "/home/tim/Projects/OpenLP/openlp/saved_bible_verses/openlp/plugins/songs/lib/mediaitem.py", line 663, in generate_footer
    if self.display_copyright_symbol:
AttributeError: 'SongMediaItem' object has no attribute 'display_copyright_symbol'

on start up!

review: Needs Fixing
Tim Bentley (trb143) : Posted in a previous version of this proposal
review: Approve
Tomas Groth (tomasgroth) wrote :

I just did a quick test, where I using ESV:
 * Searched for "wrath", saved the verses.
 * Searched for "wrath of" and again saved the verses.

This causes the a lot of duplicates in the saved verse lists since the second list of saved verses seems to be appended to the existing list, without any duplicate checks. Is this intended behavior? Maybe removing duplicates and inserting new saved verses into list so that they are chronologically ordered, should be considered?

Phill (phill-ridout) wrote :

> I just did a quick test, where I using ESV:
> * Searched for "wrath", saved the verses.
> * Searched for "wrath of" and again saved the verses.
>
> This causes the a lot of duplicates in the saved verse lists since the second
> list of saved verses seems to be appended to the existing list, without any
> duplicate checks. Is this intended behavior? Maybe removing duplicates and
> inserting new saved verses into list so that they are chronologically ordered,
> should be considered?

Only verses that are selected should be saved. The idea being is that you could build up a list of verses, possibly from different places in the bible, and present them as one item.

I think it should be down to the user to decided if they want to add duplicate verses. How often would you add all the search results as a service item?

Tomas Groth (tomasgroth) wrote :

Ok, I'm convinced :)

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'openlp/core/ui/lib/listwidgetwithdnd.py' (properties changed: -x to +x)
2--- openlp/core/ui/lib/listwidgetwithdnd.py 2017-02-18 07:23:15 +0000
3+++ openlp/core/ui/lib/listwidgetwithdnd.py 2017-05-26 13:55:01 +0000
4@@ -44,7 +44,6 @@
5 self.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
6 self.setAlternatingRowColors(True)
7 self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
8- self.locked = False
9
10 def activateDnD(self):
11 """
12@@ -54,15 +53,13 @@
13 self.setDragDropMode(QtWidgets.QAbstractItemView.DragDrop)
14 Registry().register_function(('%s_dnd' % self.mime_data_text), self.parent().load_file)
15
16- def clear(self, search_while_typing=False, override_lock=False):
17+ def clear(self, search_while_typing=False):
18 """
19 Re-implement clear, so that we can customise feedback when using 'Search as you type'
20
21 :param search_while_typing: True if we want to display the customised message
22 :return: None
23 """
24- if self.locked and not override_lock:
25- return
26 if search_while_typing:
27 self.no_results_text = UiStrings().ShortResults
28 else:
29@@ -128,6 +125,15 @@
30 else:
31 event.ignore()
32
33+ def allItems(self):
34+ """
35+ An generator to list all the items in the widget
36+
37+ :return: a generator
38+ """
39+ for row in range(self.count()):
40+ yield self.item(row)
41+
42 def paintEvent(self, event):
43 """
44 Re-implement paintEvent so that we can add 'No Results' text when the listWidget is empty.
45
46=== modified file 'openlp/core/ui/lib/pathedit.py'
47--- openlp/core/ui/lib/pathedit.py 2017-05-22 18:22:43 +0000
48+++ openlp/core/ui/lib/pathedit.py 2017-05-26 13:55:01 +0000
49@@ -46,16 +46,16 @@
50
51 :param parent: The parent of the widget. This is just passed to the super method.
52 :type parent: QWidget or None
53-
54+
55 :param dialog_caption: Used to customise the caption in the QFileDialog.
56 :param dialog_caption: str
57-
58+
59 :param default_path: The default path. This is set as the path when the revert button is clicked
60 :type default_path: str
61
62 :param show_revert: Used to determin if the 'revert button' should be visible.
63 :type show_revert: bool
64-
65+
66 :return: None
67 :rtype: None
68 """
69@@ -72,7 +72,7 @@
70 Set up the widget
71 :param show_revert: Show or hide the revert button
72 :type show_revert: bool
73-
74+
75 :return: None
76 :rtype: None
77 """
78
79=== modified file 'openlp/plugins/bibles/lib/__init__.py'
80--- openlp/plugins/bibles/lib/__init__.py 2017-02-18 07:23:15 +0000
81+++ openlp/plugins/bibles/lib/__init__.py 2017-05-26 13:55:01 +0000
82@@ -341,10 +341,10 @@
83 if not book_ref_id:
84 book_ref_id = bible.get_book_ref_id_by_localised_name(book, language_selection)
85 elif not bible.get_book_by_book_ref_id(book_ref_id):
86- return False
87+ return []
88 # We have not found the book so do not continue
89 if not book_ref_id:
90- return False
91+ return []
92 ranges = match.group('ranges')
93 range_list = get_reference_match('range_separator').split(ranges)
94 ref_list = []
95@@ -403,7 +403,7 @@
96 return ref_list
97 else:
98 log.debug('Invalid reference: {text}'.format(text=reference))
99- return None
100+ return []
101
102
103 class SearchResults(object):
104
105=== modified file 'openlp/plugins/bibles/lib/db.py'
106--- openlp/plugins/bibles/lib/db.py 2017-02-18 07:23:15 +0000
107+++ openlp/plugins/bibles/lib/db.py 2017-05-26 13:55:01 +0000
108@@ -158,6 +158,7 @@
109 self.get_name()
110 if 'path' in kwargs:
111 self.path = kwargs['path']
112+ self._is_web_bible = None
113
114 def get_name(self):
115 """
116@@ -426,6 +427,18 @@
117 return 0
118 return count
119
120+ @property
121+ def is_web_bible(self):
122+ """
123+ A read only property indicating if the bible is a 'web bible'
124+
125+ :return: If the bible is a web bible.
126+ :rtype: bool
127+ """
128+ if self._is_web_bible is None:
129+ self._is_web_bible = bool(self.get_object(BibleMeta, 'download_source'))
130+ return self._is_web_bible
131+
132 def dump_bible(self):
133 """
134 Utility debugging method to dump the contents of a bible.
135
136=== modified file 'openlp/plugins/bibles/lib/manager.py'
137--- openlp/plugins/bibles/lib/manager.py 2017-03-15 19:51:10 +0000
138+++ openlp/plugins/bibles/lib/manager.py 2017-05-26 13:55:01 +0000
139@@ -142,8 +142,8 @@
140 log.debug('Bible Name: "{name}"'.format(name=name))
141 self.db_cache[name] = bible
142 # Look to see if lazy load bible exists and get create getter.
143- source = self.db_cache[name].get_object(BibleMeta, 'download_source')
144- if source:
145+ if self.db_cache[name].is_web_bible:
146+ source = self.db_cache[name].get_object(BibleMeta, 'download_source')
147 download_name = self.db_cache[name].get_object(BibleMeta, 'download_name').value
148 meta_proxy = self.db_cache[name].get_object(BibleMeta, 'proxy_server')
149 web_bible = HTTPBible(self.parent, path=self.path, file=filename, download_source=source.value,
150@@ -278,7 +278,7 @@
151 :param show_error:
152 """
153 if not bible or not ref_list:
154- return None
155+ return []
156 return self.db_cache[bible].get_verses(ref_list, show_error)
157
158 def get_language_selection(self, bible):
159@@ -305,11 +305,17 @@
160 """
161 Does a verse search for the given bible and text.
162
163- :param bible: The bible to search in (unicode).
164- :param second_bible: The second bible (unicode). We do not search in this bible.
165- :param text: The text to search for (unicode).
166+ :param bible: The bible to search
167+ :type bible: str
168+ :param text: The text to search for
169+ :type text: str
170+
171+ :return: The search results if valid, or None if the search is invalid.
172+ :rtype: None, list
173 """
174 log.debug('BibleManager.verse_search("{bible}", "{text}")'.format(bible=bible, text=text))
175+ if not text:
176+ return None
177 # If no bibles are installed, message is given.
178 if not bible:
179 self.main_window.information_message(
180@@ -317,8 +323,7 @@
181 UiStrings().BibleNoBibles)
182 return None
183 # Check if the bible or second_bible is a web bible.
184- web_bible = self.db_cache[bible].get_object(BibleMeta, 'download_source')
185- if web_bible:
186+ if self.db_cache[bible].is_web_bible:
187 # If either Bible is Web, cursor is reset to normal and message is given.
188 self.application.set_normal_cursor()
189 self.main_window.information_message(
190@@ -328,41 +333,8 @@
191 'This means that the currently selected Bible is a Web Bible.')
192 )
193 return None
194- # Shorter than 3 char searches break OpenLP with very long search times, thus they are blocked.
195- if len(text) - text.count(' ') < 3:
196- return None
197- # Fetch the results from db. If no results are found, return None, no message is given for this.
198- elif text:
199- return self.db_cache[bible].verse_search(text)
200- else:
201- return None
202-
203- def verse_search_while_typing(self, bible, second_bible, text):
204- """
205- Does a verse search for the given bible and text.
206- This is used during "Search while typing"
207- It's the same thing as the normal text search, but it does not show the web Bible error.
208- (It would result in the error popping every time a char is entered or removed)
209- It also does not have a minimum text len, this is set in mediaitem.py
210-
211- :param bible: The bible to search in (unicode).
212- :param second_bible: The second bible (unicode). We do not search in this bible.
213- :param text: The text to search for (unicode).
214- """
215- # If no bibles are installed, message is given.
216- if not bible:
217- return None
218- # Check if the bible or second_bible is a web bible.
219- web_bible = self.db_cache[bible].get_object(BibleMeta, 'download_source')
220- second_web_bible = ''
221- if second_bible:
222- second_web_bible = self.db_cache[second_bible].get_object(BibleMeta, 'download_source')
223- if web_bible or second_web_bible:
224- # If either Bible is Web, cursor is reset to normal and search ends w/o any message.
225- self.application.set_normal_cursor()
226- return None
227- # Fetch the results from db. If no results are found, return None, no message is given for this.
228- elif text:
229+ # Fetch the results from db. If no results are found, return None, no message is given for this.
230+ if text:
231 return self.db_cache[bible].verse_search(text)
232 else:
233 return None
234
235=== modified file 'openlp/plugins/bibles/lib/mediaitem.py' (properties changed: -x to +x)
236--- openlp/plugins/bibles/lib/mediaitem.py 2017-02-18 20:22:47 +0000
237+++ openlp/plugins/bibles/lib/mediaitem.py 2017-05-26 13:55:01 +0000
238@@ -22,6 +22,7 @@
239
240 import logging
241 import re
242+from enum import IntEnum, unique
243
244 from PyQt5 import QtCore, QtWidgets
245
246@@ -48,15 +49,45 @@
247 'list': get_reference_separator('sep_l_display')}
248
249
250-class BibleSearch(object):
251+@unique
252+class BibleSearch(IntEnum):
253 """
254- Enumeration class for the different search methods for the "Search" tab.
255+ Enumeration class for the different search types for the "Search" tab.
256 """
257 Reference = 1
258 Text = 2
259 Combined = 3
260
261
262+@unique
263+class ResultsTab(IntEnum):
264+ """
265+ Enumeration class for the different tabs for the results list.
266+ """
267+ Saved = 0
268+ Search = 1
269+
270+
271+@unique
272+class SearchStatus(IntEnum):
273+ """
274+ Enumeration class for the different search methods.
275+ """
276+ SearchButton = 0
277+ SearchAsYouType = 1
278+ NotEnoughText = 2
279+
280+
281+@unique
282+class SearchTabs(IntEnum):
283+ """
284+ Enumeration class for the tabs on the media item.
285+ """
286+ Search = 0
287+ Select = 1
288+ Options = 2
289+
290+
291 class BibleMediaItem(MediaManagerItem):
292 """
293 This is the custom media manager item for Bibles.
294@@ -73,11 +104,13 @@
295 :param kwargs: Keyword arguments to pass to the super method. (dict)
296 """
297 self.clear_icon = build_icon(':/bibles/bibles_search_clear.png')
298- self.lock_icon = build_icon(':/bibles/bibles_search_lock.png')
299- self.unlock_icon = build_icon(':/bibles/bibles_search_unlock.png')
300+ self.save_results_icon = build_icon(':/bibles/bibles_save_results.png')
301 self.sort_icon = build_icon(':/bibles/bibles_book_sort.png')
302 self.bible = None
303 self.second_bible = None
304+ self.saved_results = []
305+ self.current_results = []
306+ self.search_status = SearchStatus.SearchButton
307 # TODO: Make more central and clean up after!
308 self.search_timer = QtCore.QTimer()
309 self.search_timer.setInterval(200)
310@@ -162,8 +195,10 @@
311 self.select_tab.setVisible(False)
312 self.page_layout.addWidget(self.select_tab)
313 # General Search Opions
314- self.options_widget = QtWidgets.QGroupBox(translate('BiblesPlugin.MediaItem', 'Options'), self)
315- self.general_bible_layout = QtWidgets.QFormLayout(self.options_widget)
316+ self.options_tab = QtWidgets.QWidget()
317+ self.options_tab.setSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum)
318+ self.search_tab_bar.addTab(translate('BiblesPlugin.MediaItem', 'Options'))
319+ self.general_bible_layout = QtWidgets.QFormLayout(self.options_tab)
320 self.version_combo_box = create_horizontal_adjusting_combo_box(self, 'version_combo_box')
321 self.general_bible_layout.addRow('{version}:'.format(version=UiStrings().Version), self.version_combo_box)
322 self.second_combo_box = create_horizontal_adjusting_combo_box(self, 'second_combo_box')
323@@ -171,20 +206,28 @@
324 self.style_combo_box = create_horizontal_adjusting_combo_box(self, 'style_combo_box')
325 self.style_combo_box.addItems(['', '', ''])
326 self.general_bible_layout.addRow(UiStrings().LayoutStyle, self.style_combo_box)
327- self.search_button_layout = QtWidgets.QHBoxLayout()
328+ self.options_tab.setVisible(False)
329+ self.page_layout.addWidget(self.options_tab)
330+ # This widget is the easier way to reset the spacing of search_button_layout. (Because page_layout has had its
331+ # spacing set to 0)
332+ self.search_button_widget = QtWidgets.QWidget()
333+ self.search_button_layout = QtWidgets.QHBoxLayout(self.search_button_widget)
334 self.search_button_layout.addStretch()
335 # Note: If we use QPushButton instead of the QToolButton, the icon will be larger than the Lock icon.
336- self.clear_button = QtWidgets.QToolButton(self)
337+ self.clear_button = QtWidgets.QPushButton()
338 self.clear_button.setIcon(self.clear_icon)
339- self.lock_button = QtWidgets.QToolButton(self)
340- self.lock_button.setIcon(self.unlock_icon)
341- self.lock_button.setCheckable(True)
342+ self.save_results_button = QtWidgets.QPushButton()
343+ self.save_results_button.setIcon(self.save_results_icon)
344 self.search_button_layout.addWidget(self.clear_button)
345- self.search_button_layout.addWidget(self.lock_button)
346+ self.search_button_layout.addWidget(self.save_results_button)
347 self.search_button = QtWidgets.QPushButton(self)
348 self.search_button_layout.addWidget(self.search_button)
349- self.general_bible_layout.addRow(self.search_button_layout)
350- self.page_layout.addWidget(self.options_widget)
351+ self.page_layout.addWidget(self.search_button_widget)
352+ self.results_view_tab = QtWidgets.QTabBar(self)
353+ self.results_view_tab.addTab('')
354+ self.results_view_tab.addTab('')
355+ self.results_view_tab.setCurrentIndex(ResultsTab.Search)
356+ self.page_layout.addWidget(self.results_view_tab)
357
358 def setupUi(self):
359 super().setupUi()
360@@ -211,12 +254,15 @@
361 # Buttons
362 self.book_order_button.toggled.connect(self.on_book_order_button_toggled)
363 self.clear_button.clicked.connect(self.on_clear_button_clicked)
364- self.lock_button.toggled.connect(self.on_lock_button_toggled)
365+ self.save_results_button.clicked.connect(self.on_save_results_button_clicked)
366 self.search_button.clicked.connect(self.on_search_button_clicked)
367 # Other stuff
368 self.search_edit.returnPressed.connect(self.on_search_button_clicked)
369 self.search_tab_bar.currentChanged.connect(self.on_search_tab_bar_current_changed)
370+ self.results_view_tab.currentChanged.connect(self.on_results_view_tab_current_changed)
371 self.search_edit.textChanged.connect(self.on_search_edit_text_changed)
372+ self.on_results_view_tab_total_update(ResultsTab.Saved)
373+ self.on_results_view_tab_total_update(ResultsTab.Search)
374
375 def retranslateUi(self):
376 log.debug('retranslateUi')
377@@ -225,9 +271,9 @@
378 self.style_combo_box.setItemText(LayoutStyle.VersePerSlide, UiStrings().VersePerSlide)
379 self.style_combo_box.setItemText(LayoutStyle.VersePerLine, UiStrings().VersePerLine)
380 self.style_combo_box.setItemText(LayoutStyle.Continuous, UiStrings().Continuous)
381- self.clear_button.setToolTip(translate('BiblesPlugin.MediaItem', 'Clear the search results.'))
382- self.lock_button.setToolTip(
383- translate('BiblesPlugin.MediaItem', 'Toggle to keep or clear the previous results.'))
384+ self.clear_button.setToolTip(translate('BiblesPlugin.MediaItem', 'Clear the results on the current tab.'))
385+ self.save_results_button.setToolTip(
386+ translate('BiblesPlugin.MediaItem', 'Add the search results to the saved list.'))
387 self.search_button.setText(UiStrings().Search)
388
389 def on_focus(self):
390@@ -241,8 +287,10 @@
391 if self.search_tab.isVisible():
392 self.search_edit.setFocus()
393 self.search_edit.selectAll()
394- else:
395+ if self.select_tab.isVisible():
396 self.select_book_combo_box.setFocus()
397+ if self.options_tab.isVisible():
398+ self.version_combo_box.setFocus()
399
400 def config_update(self):
401 """
402@@ -415,14 +463,48 @@
403 """
404 Show the selected tab and set focus to it
405
406- :param index: The tab selected (int)
407+ :param index: The tab selected
408+ :type index: int
409 :return: None
410 """
411- search_tab = index == 0
412- self.search_tab.setVisible(search_tab)
413- self.select_tab.setVisible(not search_tab)
414+ if index == SearchTabs.Search or index == SearchTabs.Select:
415+ self.search_button.setEnabled(True)
416+ else:
417+ self.search_button.setEnabled(False)
418+ self.search_tab.setVisible(index == SearchTabs.Search)
419+ self.select_tab.setVisible(index == SearchTabs.Select)
420+ self.options_tab.setVisible(index == SearchTabs.Options)
421 self.on_focus()
422
423+ def on_results_view_tab_current_changed(self, index):
424+ """
425+ Update list_widget with the contents of the selected list
426+
427+ :param index: The index of the tab that has been changed to. (int)
428+ :return: None
429+ """
430+ if index == ResultsTab.Saved:
431+ self.add_built_results_to_list_widget(self.saved_results)
432+ elif index == ResultsTab.Search:
433+ self.add_built_results_to_list_widget(self.current_results)
434+
435+ def on_results_view_tab_total_update(self, index):
436+ """
437+ Update the result total count on the tab with the given index.
438+
439+ :param index: Index of the tab to update (int)
440+ :return: None
441+ """
442+ string = ''
443+ count = 0
444+ if index == ResultsTab.Saved:
445+ string = translate('BiblesPlugin.MediaItem', 'Saved ({result_count})')
446+ count = len(self.saved_results)
447+ elif index == ResultsTab.Search:
448+ string = translate('BiblesPlugin.MediaItem', 'Results ({result_count})')
449+ count = len(self.current_results)
450+ self.results_view_tab.setTabText(index, string.format(result_count=count))
451+
452 def on_book_order_button_toggled(self, checked):
453 """
454 Change the sort order of the book names
455@@ -442,22 +524,25 @@
456
457 :return: None
458 """
459- self.list_view.clear()
460- self.search_edit.clear()
461- self.on_focus()
462+ current_index = self.results_view_tab.currentIndex()
463+ for item in self.list_view.selectedItems():
464+ self.list_view.takeItem(self.list_view.row(item))
465+ results = [item.data(QtCore.Qt.UserRole) for item in self.list_view.allItems()]
466+ if current_index == ResultsTab.Saved:
467+ self.saved_results = results
468+ elif current_index == ResultsTab.Search:
469+ self.current_results = results
470+ self.on_results_view_tab_total_update(current_index)
471
472- def on_lock_button_toggled(self, checked):
473+ def on_save_results_button_clicked(self):
474 """
475- Toggle the lock button, if Search tab is used, set focus to search field.
476+ Add the selected verses to the saved_results list.
477
478- :param checked: The state of the toggle button. (bool)
479 :return: None
480 """
481- self.list_view.locked = checked
482- if checked:
483- self.sender().setIcon(self.lock_icon)
484- else:
485- self.sender().setIcon(self.unlock_icon)
486+ for verse in self.list_view.selectedItems():
487+ self.saved_results.append(verse.data(QtCore.Qt.UserRole))
488+ self.on_results_view_tab_total_update(ResultsTab.Saved)
489
490 def on_style_combo_box_index_changed(self, index):
491 """
492@@ -490,16 +575,17 @@
493 :return: None
494 """
495 new_selection = self.second_combo_box.currentData()
496- if self.list_view.count():
497+ if self.saved_results:
498 # Exclusive or (^) the new and previous selections to detect if the user has switched between single and
499 # dual bible mode
500 if (new_selection is None) ^ (self.second_bible is None):
501 if critical_error_message_box(
502 message=translate('BiblesPlugin.MediaItem',
503 'OpenLP cannot combine single and dual Bible verse search results. '
504- 'Do you want to clear your search results and start a new search?'),
505+ 'Do you want to clear your saved results?'),
506 parent=self, question=True) == QtWidgets.QMessageBox.Yes:
507- self.list_view.clear(override_lock=True)
508+ self.saved_results = []
509+ self.on_results_view_tab_total_update(ResultsTab.Saved)
510 else:
511 self.second_combo_box.setCurrentIndex(self.second_combo_box.findData(self.second_bible))
512 return
513@@ -525,7 +611,8 @@
514 log.warning('Not enough chapters in %s', book_ref_id)
515 critical_error_message_box(message=translate('BiblesPlugin.MediaItem', 'Bible not fully loaded.'))
516 else:
517- self.search_button.setEnabled(True)
518+ if self.select_tab.isVisible():
519+ self.search_button.setEnabled(True)
520 self.adjust_combo_box(1, self.chapter_count, self.from_chapter)
521 self.adjust_combo_box(1, self.chapter_count, self.to_chapter)
522 self.adjust_combo_box(1, verse_count, self.from_verse)
523@@ -602,6 +689,8 @@
524
525 :return: None
526 """
527+ self.search_timer.stop()
528+ self.search_status = SearchStatus.SearchButton
529 if not self.bible:
530 self.main_window.information_message(UiStrings().BibleNoBiblesTitle, UiStrings().BibleNoBibles)
531 return
532@@ -613,6 +702,7 @@
533 elif self.select_tab.isVisible():
534 self.select_search()
535 self.search_button.setEnabled(True)
536+ self.results_view_tab.setCurrentIndex(ResultsTab.Search)
537 self.application.set_normal_cursor()
538
539 def select_search(self):
540@@ -636,18 +726,21 @@
541
542 :return: None
543 """
544+ self.search_results = []
545 verse_refs = self.plugin.manager.parse_ref(self.bible.name, search_text)
546 self.search_results = self.plugin.manager.get_verses(self.bible.name, verse_refs, True)
547 if self.second_bible and self.search_results:
548 self.search_results = self.plugin.manager.get_verses(self.second_bible.name, verse_refs, True)
549 self.display_results()
550
551- def on_text_search(self, text, search_while_type=False):
552+ def on_text_search(self, text):
553 """
554 We are doing a 'Text Search'.
555 This search is called on def text_search by 'Search' Text and Combined Searches.
556 """
557 self.search_results = self.plugin.manager.verse_search(self.bible.name, text)
558+ if self.search_results is None:
559+ return
560 if self.second_bible and self.search_results:
561 filtered_search_results = []
562 not_found_count = 0
563@@ -663,7 +756,7 @@
564 verse=verse.verse, bible_name=self.second_bible.name))
565 not_found_count += 1
566 self.search_results = filtered_search_results
567- if not_found_count != 0 and not search_while_type:
568+ if not_found_count != 0 and self.search_status == SearchStatus.SearchButton:
569 self.main_window.information_message(
570 translate('BiblesPlugin.MediaItem', 'Verses not found'),
571 translate('BiblesPlugin.MediaItem',
572@@ -673,22 +766,23 @@
573 ).format(second_name=self.second_bible.name, name=self.bible.name, count=not_found_count))
574 self.display_results()
575
576- def text_search(self, search_while_type=False):
577+ def text_search(self):
578 """
579 This triggers the proper 'Search' search based on which search type is used.
580 "Eg. "Reference Search", "Text Search" or "Combined search".
581 """
582+ self.search_results = []
583 log.debug('text_search called')
584 text = self.search_edit.text()
585 if text == '':
586- self.list_view.clear()
587+ self.display_results()
588 return
589- self.list_view.clear(search_while_typing=search_while_type)
590+ self.on_results_view_tab_total_update(ResultsTab.Search)
591 if self.search_edit.current_search_type() == BibleSearch.Reference:
592 if get_reference_match('full').match(text):
593 # Valid reference found. Do reference search.
594 self.text_reference_search(text)
595- elif not search_while_type:
596+ elif self.search_status == SearchStatus.SearchButton:
597 self.main_window.information_message(
598 translate('BiblesPlugin.BibleManager', 'Scripture Reference Error'),
599 translate('BiblesPlugin.BibleManager',
600@@ -700,10 +794,12 @@
601 self.text_reference_search(text)
602 else:
603 # It can only be a 'Combined' search without a valid reference, or a 'Text' search
604- if search_while_type:
605- if len(text) > 8 and VALID_TEXT_SEARCH.search(text):
606- self.on_text_search(text, True)
607- elif VALID_TEXT_SEARCH.search(text):
608+ if self.search_status == SearchStatus.SearchAsYouType:
609+ if len(text) <= 8:
610+ self.search_status = SearchStatus.NotEnoughText
611+ self.display_results()
612+ return
613+ if VALID_TEXT_SEARCH.search(text):
614 self.on_text_search(text)
615
616 def on_search_edit_text_changed(self):
617@@ -713,9 +809,12 @@
618
619 :return: None
620 """
621- if Settings().value('bibles/is search while typing enabled'):
622- if not self.search_timer.isActive():
623- self.search_timer.start()
624+ if not Settings().value('bibles/is search while typing enabled') or \
625+ not self.bible or self.bible.is_web_bible or \
626+ (self.second_bible and self.bible.is_web_bible):
627+ return
628+ if not self.search_timer.isActive():
629+ self.search_timer.start()
630
631 def on_search_timer_timeout(self):
632 """
633@@ -724,7 +823,9 @@
634
635 :return: None
636 """
637- self.text_search(True)
638+ self.search_status = SearchStatus.SearchAsYouType
639+ self.text_search()
640+ self.results_view_tab.setCurrentIndex(ResultsTab.Search)
641
642 def display_results(self):
643 """
644@@ -732,14 +833,16 @@
645
646 :return: None
647 """
648- self.list_view.clear()
649- if self.search_results:
650- items = self.build_display_results(self.bible, self.second_bible, self.search_results)
651- for item in items:
652- self.list_view.addItem(item)
653- self.list_view.selectAll()
654+ self.current_results = self.build_display_results(self.bible, self.second_bible, self.search_results)
655 self.search_results = []
656- self.second_search_results = []
657+ self.add_built_results_to_list_widget(self.current_results)
658+
659+ def add_built_results_to_list_widget(self, results):
660+ self.list_view.clear(self.search_status == SearchStatus.NotEnoughText)
661+ for item in self.build_list_widget_items(results):
662+ self.list_view.addItem(item)
663+ self.list_view.selectAll()
664+ self.on_results_view_tab_total_update(ResultsTab.Search)
665
666 def build_display_results(self, bible, second_bible, search_results):
667 """
668@@ -789,10 +892,17 @@
669 bible_text = '{book} {chapter:d}{sep}{verse:d} ({version}, {second_version})'
670 else:
671 bible_text = '{book} {chapter:d}{sep}{verse:d} ({version})'
672- bible_verse = QtWidgets.QListWidgetItem(bible_text.format(sep=verse_separator, **data))
673+ data['item_title'] = bible_text.format(sep=verse_separator, **data)
674+ items.append(data)
675+ return items
676+
677+ def build_list_widget_items(self, items):
678+ list_widget_items = []
679+ for data in items:
680+ bible_verse = QtWidgets.QListWidgetItem(data['item_title'])
681 bible_verse.setData(QtCore.Qt.UserRole, data)
682- items.append(bible_verse)
683- return items
684+ list_widget_items.append(bible_verse)
685+ return list_widget_items
686
687 def generate_slide_data(self, service_item, item=None, xml_version=False, remote=False,
688 context=ServiceItemContext.Service):
689@@ -897,6 +1007,8 @@
690 """
691 Search for some Bible verses (by reference).
692 """
693+ if self.bible is None:
694+ return []
695 reference = self.plugin.manager.parse_ref(self.bible.name, string)
696 search_results = self.plugin.manager.get_verses(self.bible.name, reference, showError)
697 if search_results:
698@@ -908,6 +1020,9 @@
699 """
700 Create a media item from an item id.
701 """
702+ if self.bible is None:
703+ return []
704 reference = self.plugin.manager.parse_ref(self.bible.name, item_id)
705 search_results = self.plugin.manager.get_verses(self.bible.name, reference, False)
706- return self.build_display_results(self.bible, None, search_results)
707+ items = self.build_display_results(self.bible, None, search_results)
708+ return self.build_list_widget_items(items)
709
710=== modified file 'openlp/plugins/presentations/presentationplugin.py'
711--- openlp/plugins/presentations/presentationplugin.py 2017-05-22 18:27:40 +0000
712+++ openlp/plugins/presentations/presentationplugin.py 2017-05-26 13:55:01 +0000
713@@ -1,4 +1,4 @@
714- # -*- coding: utf-8 -*-
715+# -*- coding: utf-8 -*-
716 # vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
717
718 ###############################################################################
719
720=== modified file 'openlp/plugins/songusage/forms/songusagedetaildialog.py'
721--- openlp/plugins/songusage/forms/songusagedetaildialog.py 2017-05-22 18:22:43 +0000
722+++ openlp/plugins/songusage/forms/songusagedetaildialog.py 2017-05-26 13:55:01 +0000
723@@ -69,7 +69,7 @@
724 self.file_horizontal_layout.setSpacing(8)
725 self.file_horizontal_layout.setContentsMargins(8, 8, 8, 8)
726 self.file_horizontal_layout.setObjectName('file_horizontal_layout')
727- self.report_path_edit = PathEdit(self.file_group_box, path_type = PathType.Directories, show_revert=False)
728+ self.report_path_edit = PathEdit(self.file_group_box, path_type=PathType.Directories, show_revert=False)
729 self.file_horizontal_layout.addWidget(self.report_path_edit)
730 self.vertical_layout.addWidget(self.file_group_box)
731 self.button_box = create_button_box(song_usage_detail_dialog, 'button_box', ['cancel', 'ok'])
732
733=== added file 'resources/images/bibles_save_results.png'
734Binary files resources/images/bibles_save_results.png 1970-01-01 00:00:00 +0000 and resources/images/bibles_save_results.png 2017-05-26 13:55:01 +0000 differ
735=== removed file 'resources/images/bibles_search_lock.png'
736Binary files resources/images/bibles_search_lock.png 2011-05-11 23:59:37 +0000 and resources/images/bibles_search_lock.png 1970-01-01 00:00:00 +0000 differ
737=== removed file 'resources/images/bibles_search_unlock.png'
738Binary files resources/images/bibles_search_unlock.png 2011-05-11 23:59:37 +0000 and resources/images/bibles_search_unlock.png 1970-01-01 00:00:00 +0000 differ
739=== removed file 'resources/images/network_ssl.png'
740Binary files resources/images/network_ssl.png 2014-04-14 18:09:47 +0000 and resources/images/network_ssl.png 1970-01-01 00:00:00 +0000 differ
741=== modified file 'resources/images/openlp-2.qrc'
742--- resources/images/openlp-2.qrc 2016-12-18 14:11:31 +0000
743+++ resources/images/openlp-2.qrc 2017-05-26 13:55:01 +0000
744@@ -34,8 +34,7 @@
745 <file>bibles_search_text.png</file>
746 <file>bibles_search_reference.png</file>
747 <file>bibles_search_clear.png</file>
748- <file>bibles_search_unlock.png</file>
749- <file>bibles_search_lock.png</file>
750+ <file>bibles_save_results.png</file>
751 </qresource>
752 <qresource prefix="plugins">
753 <file>plugin_alerts.png</file>
754@@ -144,7 +143,6 @@
755 </qresource>
756 <qresource prefix="remote">
757 <file>network_server.png</file>
758- <file>network_ssl.png</file>
759 <file>network_auth.png</file>
760 </qresource>
761 <qresource prefix="songusage">
762@@ -188,4 +186,4 @@
763 <file>android_app_qr.png</file>
764 <file>ios_app_qr.png</file>
765 </qresource>
766-</RCC>
767\ No newline at end of file
768+</RCC>
769
770=== modified file 'tests/functional/openlp_core_ui_lib/test_listwidgetwithdnd.py' (properties changed: -x to +x)
771--- tests/functional/openlp_core_ui_lib/test_listwidgetwithdnd.py 2017-02-18 07:23:15 +0000
772+++ tests/functional/openlp_core_ui_lib/test_listwidgetwithdnd.py 2017-05-26 13:55:01 +0000
773@@ -23,6 +23,7 @@
774 This module contains tests for the openlp.core.lib.listwidgetwithdnd module
775 """
776 from unittest import TestCase
777+from types import GeneratorType
778
779 from openlp.core.common.uistrings import UiStrings
780 from openlp.core.ui.lib.listwidgetwithdnd import ListWidgetWithDnD
781@@ -33,37 +34,6 @@
782 """
783 Test the :class:`~openlp.core.lib.listwidgetwithdnd.ListWidgetWithDnD` class
784 """
785- def test_clear_locked(self):
786- """
787- Test the clear method the list is 'locked'
788- """
789- with patch('openlp.core.ui.lib.listwidgetwithdnd.QtWidgets.QListWidget.clear') as mocked_clear_super_method:
790- # GIVEN: An instance of ListWidgetWithDnD
791- widget = ListWidgetWithDnD()
792-
793- # WHEN: The list is 'locked' and clear has been called
794- widget.locked = True
795- widget.clear()
796-
797- # THEN: The super method should not have been called (i.e. The list not cleared)
798- self.assertFalse(mocked_clear_super_method.called)
799-
800- def test_clear_overide_locked(self):
801- """
802- Test the clear method the list is 'locked', but clear is called with 'override_lock' set to True
803- """
804- with patch('openlp.core.ui.lib.listwidgetwithdnd.QtWidgets.QListWidget.clear') as mocked_clear_super_method:
805- # GIVEN: An instance of ListWidgetWithDnD
806- widget = ListWidgetWithDnD()
807-
808- # WHEN: The list is 'locked' and clear has been called with override_lock se to True
809- widget.locked = True
810- widget.clear(override_lock=True)
811-
812- # THEN: The super method should have been called (i.e. The list is cleared regardless whether it is locked
813- # or not)
814- mocked_clear_super_method.assert_called_once_with()
815-
816 def test_clear(self):
817 """
818 Test the clear method when called without any arguments.
819@@ -90,6 +60,38 @@
820 # THEN: The results text should be the 'short results' text.
821 self.assertEqual(widget.no_results_text, UiStrings().ShortResults)
822
823+ def test_all_items_no_list_items(self):
824+ """
825+ Test allItems when there are no items in the list widget
826+ """
827+ # GIVEN: An instance of ListWidgetWithDnD
828+ widget = ListWidgetWithDnD()
829+ with patch.object(widget, 'count', return_value=0), \
830+ patch.object(widget, 'item', side_effect=lambda x: [][x]):
831+
832+ # WHEN: Calling allItems
833+ result = widget.allItems()
834+
835+ # THEN: An instance of a Generator object should be returned. The generator should not yeild any results
836+ self.assertIsInstance(result, GeneratorType)
837+ self.assertEqual(list(result), [])
838+
839+ def test_all_items_list_items(self):
840+ """
841+ Test allItems when the list widget contains some items.
842+ """
843+ # GIVEN: An instance of ListWidgetWithDnD
844+ widget = ListWidgetWithDnD()
845+ with patch.object(widget, 'count', return_value=2), \
846+ patch.object(widget, 'item', side_effect=lambda x: [5, 3][x]):
847+
848+ # WHEN: Calling allItems
849+ result = widget.allItems()
850+
851+ # THEN: An instance of a Generator object should be returned. The generator should not yeild any results
852+ self.assertIsInstance(result, GeneratorType)
853+ self.assertEqual(list(result), [5, 3])
854+
855 def test_paint_event(self):
856 """
857 Test the paintEvent method when the list is not empty
858
859=== modified file 'tests/functional/openlp_plugins/bibles/test_mediaitem.py' (properties changed: -x to +x)
860--- tests/functional/openlp_plugins/bibles/test_mediaitem.py 2017-02-18 07:23:15 +0000
861+++ tests/functional/openlp_plugins/bibles/test_mediaitem.py 2017-05-26 13:55:01 +0000
862@@ -31,7 +31,8 @@
863
864 from openlp.core.common import Registry
865 from openlp.core.lib import MediaManagerItem
866-from openlp.plugins.bibles.lib.mediaitem import BibleMediaItem, BibleSearch, get_reference_separators, VALID_TEXT_SEARCH
867+from openlp.plugins.bibles.lib.mediaitem import BibleMediaItem, BibleSearch, ResultsTab, SearchStatus, \
868+ get_reference_separators, VALID_TEXT_SEARCH
869
870
871 class TestBibleMediaItemModulefunctions(TestCase):
872@@ -143,6 +144,7 @@
873 self.media_item = BibleMediaItem(None, self.mocked_plugin)
874
875 self.media_item.settings_section = 'bibles'
876+ self.media_item.results_view_tab = MagicMock()
877
878 self.mocked_book_1 = MagicMock(**{'get_name.return_value': 'Book 1', 'book_reference_id': 1})
879 self.mocked_book_2 = MagicMock(**{'get_name.return_value': 'Book 2', 'book_reference_id': 2})
880@@ -658,56 +660,65 @@
881 # THEN: The select_book_combo_box model sort should have been reset
882 self.media_item.select_book_combo_box.model().sort.assert_called_once_with(-1)
883
884- def test_on_clear_button_clicked(self):
885- """
886- Test on_clear_button_clicked
887+ def test_on_clear_button_clicked_saved_tab(self):
888+ """
889+ Test on_clear_button_clicked when the saved tab is selected
890+ """
891+ # GIVEN: An instance of :class:`MediaManagerItem` and mocked out saved_tab and select_tab and a mocked out
892+ # list_view and search_edit
893+ self.media_item.list_view = MagicMock()
894+ self.media_item.search_edit = MagicMock()
895+ self.media_item.results_view_tab = MagicMock(**{'currentIndex.return_value': ResultsTab.Saved})
896+ self.media_item.saved_results = ['Some', 'Results']
897+ with patch.object(self.media_item, 'on_focus'):
898+
899+ # WHEN: Calling on_clear_button_clicked
900+ self.media_item.on_clear_button_clicked()
901+
902+ # THEN: The list_view should be cleared
903+ self.assertEqual(self.media_item.saved_results, [])
904+ self.media_item.list_view.clear.assert_called_once_with()
905+
906+ def test_on_clear_button_clicked_search_tab(self):
907+ """
908+ Test on_clear_button_clicked when the search tab is selected
909 """
910 # GIVEN: An instance of :class:`MediaManagerItem` and mocked out search_tab and select_tab and a mocked out
911 # list_view and search_edit
912 self.media_item.list_view = MagicMock()
913 self.media_item.search_edit = MagicMock()
914+ self.media_item.results_view_tab = MagicMock(**{'currentIndex.return_value': ResultsTab.Search})
915+ self.media_item.current_results = ['Some', 'Results']
916 with patch.object(self.media_item, 'on_focus'):
917
918 # WHEN: Calling on_clear_button_clicked
919 self.media_item.on_clear_button_clicked()
920
921 # THEN: The list_view and the search_edit should be cleared
922+ self.assertEqual(self.media_item.current_results, [])
923 self.media_item.list_view.clear.assert_called_once_with()
924 self.media_item.search_edit.clear.assert_called_once_with()
925
926- def test_on_lock_button_toggled_search_tab_lock_icon(self):
927- """
928- Test that "on_lock_button_toggled" toggles the lock properly.
929- """
930- # GIVEN: An instance of :class:`MediaManagerItem` a mocked sender and list_view
931- self.media_item.list_view = MagicMock()
932- self.media_item.lock_icon = 'lock icon'
933- mocked_sender_instance = MagicMock()
934- with patch.object(self.media_item, 'sender', return_value=mocked_sender_instance):
935-
936- # WHEN: When the lock_button is checked
937- self.media_item.on_lock_button_toggled(True)
938-
939- # THEN: list_view should be 'locked' and the lock icon set
940- self.assertTrue(self.media_item.list_view.locked)
941- mocked_sender_instance.setIcon.assert_called_once_with('lock icon')
942-
943- def test_on_lock_button_toggled_unlock_icon(self):
944- """
945- Test that "on_lock_button_toggled" toggles the lock properly.
946- """
947- # GIVEN: An instance of :class:`MediaManagerItem` a mocked sender and list_view
948- self.media_item.list_view = MagicMock()
949- self.media_item.unlock_icon = 'unlock icon'
950- mocked_sender_instance = MagicMock()
951- with patch.object(self.media_item, 'sender', return_value=mocked_sender_instance):
952-
953- # WHEN: When the lock_button is unchecked
954- self.media_item.on_lock_button_toggled(False)
955-
956- # THEN: list_view should be 'unlocked' and the unlock icon set
957- self.assertFalse(self.media_item.list_view.locked)
958- mocked_sender_instance.setIcon.assert_called_once_with('unlock icon')
959+ def test_on_save_results_button_clicked(self):
960+ """
961+ Test that "on_save_results_button_clicked" saves the results.
962+ """
963+ # GIVEN: An instance of :class:`MediaManagerItem` and a mocked list_view
964+ result_1 = MagicMock(**{'data.return_value': 'R1'})
965+ result_2 = MagicMock(**{'data.return_value': 'R2'})
966+ result_3 = MagicMock(**{'data.return_value': 'R3'})
967+ self.media_item.list_view = MagicMock(**{'selectedItems.return_value': [result_1, result_2, result_3]})
968+
969+ with patch.object(self.media_item, 'on_results_view_tab_total_update') as \
970+ mocked_on_results_view_tab_total_update:
971+
972+ # WHEN: When the save_results_button is clicked
973+ self.media_item.on_save_results_button_clicked()
974+
975+ # THEN: The selected results in the list_view should be added to the 'saved_results' list. And the saved_tab
976+ # total should be updated.
977+ self.assertEqual(self.media_item.saved_results, ['R1', 'R2', 'R3'])
978+ mocked_on_results_view_tab_total_update.assert_called_once_with(ResultsTab.Saved)
979
980 def test_on_style_combo_box_changed(self):
981 """
982@@ -815,7 +826,9 @@
983 self.media_item.list_view = MagicMock(**{'count.return_value': 5})
984 self.media_item.style_combo_box = MagicMock()
985 self.media_item.select_book_combo_box = MagicMock()
986+ self.media_item.search_results = ['list', 'of', 'results']
987 with patch.object(self.media_item, 'initialise_advanced_bible') as mocked_initialise_advanced_bible, \
988+ patch.object(self.media_item, 'display_results'), \
989 patch('openlp.plugins.bibles.lib.mediaitem.critical_error_message_box',
990 return_value=QtWidgets.QMessageBox.Yes) as mocked_critical_error_message_box:
991
992@@ -825,9 +838,8 @@
993 self.media_item.second_combo_box = MagicMock(**{'currentData.return_value': self.mocked_bible_1})
994 self.media_item.on_second_combo_box_index_changed(5)
995
996- # THEN: The list_view should be cleared and the selected bible should be set as the current bible
997+ # THEN: The selected bible should be set as the current bible
998 self.assertTrue(mocked_critical_error_message_box.called)
999- self.media_item.list_view.clear.assert_called_once_with(override_lock=True)
1000 self.media_item.style_combo_box.setEnabled.assert_called_once_with(False)
1001 self.assertTrue(mocked_initialise_advanced_bible.called)
1002 self.assertEqual(self.media_item.second_bible, self.mocked_bible_1)
1003@@ -841,7 +853,9 @@
1004 self.media_item.list_view = MagicMock(**{'count.return_value': 5})
1005 self.media_item.style_combo_box = MagicMock()
1006 self.media_item.select_book_combo_box = MagicMock()
1007+ self.media_item.search_results = ['list', 'of', 'results']
1008 with patch.object(self.media_item, 'initialise_advanced_bible') as mocked_initialise_advanced_bible, \
1009+ patch.object(self.media_item, 'display_results'), \
1010 patch('openlp.plugins.bibles.lib.mediaitem.critical_error_message_box',
1011 return_value=QtWidgets.QMessageBox.Yes) as mocked_critical_error_message_box:
1012 # WHEN: The previously is a bible new selection is None and the user selects yes
1013@@ -850,9 +864,8 @@
1014 self.media_item.second_combo_box = MagicMock(**{'currentData.return_value': None})
1015 self.media_item.on_second_combo_box_index_changed(0)
1016
1017- # THEN: The list_view should be cleared and the selected bible should be set as the current bible
1018+ # THEN: The selected bible should be set as the current bible
1019 self.assertTrue(mocked_critical_error_message_box.called)
1020- self.media_item.list_view.clear.assert_called_once_with(override_lock=True)
1021 self.media_item.style_combo_box.setEnabled.assert_called_once_with(True)
1022 self.assertFalse(mocked_initialise_advanced_bible.called)
1023 self.assertEqual(self.media_item.second_bible, None)
1024@@ -1388,8 +1401,9 @@
1025 # WHEN: Calling on_search_timer_timeout
1026 self.media_item.on_search_timer_timeout()
1027
1028- # THEN: The text_search method should have been called with True
1029- mocked_text_search.assert_called_once_with(True)
1030+ # THEN: The search_status should be set to SearchAsYouType and text_search should have been called
1031+ self.assertEqual(self.media_item.search_status, SearchStatus().SearchAsYouType)
1032+ mocked_text_search.assert_called_once_with()
1033
1034 def test_display_results_no_results(self):
1035 """
1036@@ -1407,7 +1421,6 @@
1037 self.media_item.display_results()
1038
1039 # THEN: No items should be added to the list
1040- self.media_item.list_view.clear.assert_called_once_with()
1041 self.assertFalse(self.media_item.list_view.addItem.called)
1042
1043 def test_display_results_results(self):
1044@@ -1415,7 +1428,10 @@
1045 Test the display_results method when there are items to display
1046 """
1047 # GIVEN: An instance of BibleMediaItem and a mocked build_display_results which returns a list of results
1048- with patch.object(self.media_item, 'build_display_results', return_value=['list', 'items']):
1049+ with patch.object(self.media_item, 'build_display_results', return_value=[
1050+ {'item_title': 'Title 1'}, {'item_title': 'Title 2'}]), \
1051+ patch.object(self.media_item, 'add_built_results_to_list_widget') as \
1052+ mocked_add_built_results_to_list_widget:
1053 self.media_item.search_results = ['results']
1054 self.media_item.list_view = MagicMock()
1055
1056@@ -1423,5 +1439,5 @@
1057 self.media_item.display_results()
1058
1059 # THEN: addItem should have been with the display items
1060- self.media_item.list_view.clear.assert_called_once_with()
1061- self.assertEqual(self.media_item.list_view.addItem.call_args_list, [call('list'), call('items')])
1062+ mocked_add_built_results_to_list_widget.assert_called_once_with(
1063+ [{'item_title': 'Title 1'}, {'item_title': 'Title 2'}])
1064
1065=== modified file 'tests/interfaces/openlp_plugins/bibles/test_lib_parse_reference.py'
1066--- tests/interfaces/openlp_plugins/bibles/test_lib_parse_reference.py 2017-04-24 05:17:55 +0000
1067+++ tests/interfaces/openlp_plugins/bibles/test_lib_parse_reference.py 2017-05-26 13:55:01 +0000
1068@@ -108,7 +108,7 @@
1069 # WHEN asking to parse the bible reference
1070 results = parse_reference('Raoul 1', self.manager.db_cache['tests'], MagicMock())
1071 # THEN a verse array should be returned
1072- self.assertEqual(False, results, "The bible Search should return False")
1073+ self.assertEqual([], results, "The bible Search should return an empty list")
1074
1075 def test_parse_reference_five(self):
1076 """