Merge lp:~phill-ridout/openlp/even-more-refactors into lp:openlp

Proposed by Phill
Status: Merged
Approved by: Tim Bentley
Approved revision: 2707
Merged at revision: 2696
Proposed branch: lp:~phill-ridout/openlp/even-more-refactors
Merge into: lp:openlp
Diff against target: 3154 lines (+1623/-771)
24 files modified
openlp/core/common/openlpmixin.py (+6/-0)
openlp/plugins/bibles/bibleplugin.py (+4/-4)
openlp/plugins/bibles/forms/bibleimportform.py (+24/-16)
openlp/plugins/bibles/lib/__init__.py (+2/-2)
openlp/plugins/bibles/lib/bibleimport.py (+150/-29)
openlp/plugins/bibles/lib/db.py (+2/-62)
openlp/plugins/bibles/lib/importers/csvbible.py (+15/-31)
openlp/plugins/bibles/lib/importers/opensong.py (+112/-95)
openlp/plugins/bibles/lib/importers/osis.py (+93/-97)
openlp/plugins/bibles/lib/importers/zefania.py (+4/-3)
openlp/plugins/bibles/lib/manager.py (+3/-3)
openlp/plugins/bibles/lib/upgrade.py (+2/-164)
tests/functional/openlp_core_ui/test_exceptionform.py (+1/-6)
tests/functional/openlp_plugins/bibles/test_bibleimport.py (+437/-34)
tests/functional/openlp_plugins/bibles/test_csvimport.py (+11/-77)
tests/functional/openlp_plugins/bibles/test_db.py (+1/-55)
tests/functional/openlp_plugins/bibles/test_opensongimport.py (+343/-31)
tests/functional/openlp_plugins/bibles/test_osisimport.py (+367/-14)
tests/functional/openlp_plugins/bibles/test_swordimport.py (+1/-1)
tests/functional/openlp_plugins/bibles/test_zefaniaimport.py (+5/-7)
tests/resources/bibles/dk1933.json (+10/-10)
tests/resources/bibles/kjv.json (+10/-10)
tests/resources/bibles/rst.json (+10/-10)
tests/resources/bibles/web.json (+10/-10)
To merge this branch: bzr merge lp:~phill-ridout/openlp/even-more-refactors
Reviewer Review Type Date Requested Status
Tim Bentley Approve
Tomas Groth Approve
Review via email: mp+305388@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Tim Bentley (trb143) wrote :

Sorry one small change needed.

review: Needs Fixing
Revision history for this message
Phill (phill-ridout) wrote :

> Sorry one small change needed.

Any chance you can let this one slide? I'm not going to be in a position to correct this for a week. I'll sort it in my next round of Bible refractors if that's ok with you? Unless tgc or superfly pick up on something else!

Revision history for this message
Tim Bentley (trb143) wrote :

Ok it was a real nit pick!

Revision history for this message
Tomas Groth (tomasgroth) :
review: Approve
Revision history for this message
Tim Bentley (trb143) wrote :

Adding approval

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'openlp/core/common/openlpmixin.py'
2--- openlp/core/common/openlpmixin.py 2016-05-14 04:24:46 +0000
3+++ openlp/core/common/openlpmixin.py 2016-09-09 21:59:44 +0000
4@@ -71,6 +71,12 @@
5 """
6 self.logger.info(message)
7
8+ def log_warning(self, message):
9+ """
10+ Common log warning handler
11+ """
12+ self.logger.warning(message)
13+
14 def log_error(self, message):
15 """
16 Common log error handler which prints the calling path
17
18=== modified file 'openlp/plugins/bibles/bibleplugin.py'
19--- openlp/plugins/bibles/bibleplugin.py 2016-08-08 18:11:32 +0000
20+++ openlp/plugins/bibles/bibleplugin.py 2016-09-09 21:59:44 +0000
21@@ -140,10 +140,10 @@
22
23 def uses_theme(self, theme):
24 """
25- Called to find out if the bible plugin is currently using a theme. Returns ``1`` if the theme is being used,
26- otherwise returns ``0``.
27+ Called to find out if the bible plugin is currently using a theme.
28
29 :param theme: The theme
30+ :return: 1 if the theme is being used, otherwise returns 0
31 """
32 if str(self.settings_tab.bible_theme) == theme:
33 return 1
34@@ -151,11 +151,11 @@
35
36 def rename_theme(self, old_theme, new_theme):
37 """
38- Rename the theme the bible plugin is using making the plugin use the
39- new name.
40+ Rename the theme the bible plugin is using, making the plugin use the new name.
41
42 :param old_theme: The name of the theme the plugin should stop using. Unused for this particular plugin.
43 :param new_theme: The new name the plugin should now use.
44+ :return: None
45 """
46 self.settings_tab.bible_theme = new_theme
47 self.settings_tab.save()
48
49=== modified file 'openlp/plugins/bibles/forms/bibleimportform.py'
50--- openlp/plugins/bibles/forms/bibleimportform.py 2016-08-09 20:45:25 +0000
51+++ openlp/plugins/bibles/forms/bibleimportform.py 2016-09-09 21:59:44 +0000
52@@ -25,6 +25,7 @@
53 import logging
54 import os
55 import urllib.error
56+from lxml import etree
57
58 from PyQt5 import QtWidgets
59 try:
60@@ -33,14 +34,15 @@
61 except:
62 PYSWORD_AVAILABLE = False
63
64-from openlp.core.common import AppLocation, Settings, UiStrings, translate, clean_filename
65+from openlp.core.common import AppLocation, Settings, UiStrings, trace_error_handler, translate
66+from openlp.core.common.languagemanager import get_locale_key
67 from openlp.core.lib.db import delete_database
68+from openlp.core.lib.exceptions import ValidationError
69 from openlp.core.lib.ui import critical_error_message_box
70 from openlp.core.ui.lib.wizard import OpenLPWizard, WizardStrings
71-from openlp.core.common.languagemanager import get_locale_key
72-from openlp.plugins.bibles.lib.manager import BibleFormat
73 from openlp.plugins.bibles.lib.db import clean_filename
74 from openlp.plugins.bibles.lib.importers.http import CWExtract, BGExtract, BSExtract
75+from openlp.plugins.bibles.lib.manager import BibleFormat
76
77 log = logging.getLogger(__name__)
78
79@@ -809,16 +811,22 @@
80 sword_path=self.field('sword_zip_path'),
81 sword_key=self.sword_zipbible_combo_box.itemData(
82 self.sword_zipbible_combo_box.currentIndex()))
83- if importer.do_import(license_version):
84- self.manager.save_meta_data(license_version, license_version, license_copyright, license_permissions)
85- self.manager.reload_bibles()
86- if bible_type == BibleFormat.WebDownload:
87- self.progress_label.setText(
88- translate('BiblesPlugin.ImportWizardForm', 'Registered Bible. Please note, that verses will be '
89- 'downloaded on demand and thus an internet connection is required.'))
90- else:
91- self.progress_label.setText(WizardStrings.FinishedImport)
92- else:
93- self.progress_label.setText(translate('BiblesPlugin.ImportWizardForm', 'Your Bible import failed.'))
94- del self.manager.db_cache[importer.name]
95- delete_database(self.plugin.settings_section, importer.file)
96+
97+ try:
98+ if importer.do_import(license_version) and not importer.stop_import_flag:
99+ self.manager.save_meta_data(license_version, license_version, license_copyright, license_permissions)
100+ self.manager.reload_bibles()
101+ if bible_type == BibleFormat.WebDownload:
102+ self.progress_label.setText(
103+ translate('BiblesPlugin.ImportWizardForm', 'Registered Bible. Please note, that verses will be '
104+ 'downloaded on demand and thus an internet connection is required.'))
105+ else:
106+ self.progress_label.setText(WizardStrings.FinishedImport)
107+ return
108+ except (AttributeError, ValidationError, etree.XMLSyntaxError):
109+ log.exception('Importing bible failed')
110+ trace_error_handler(log)
111+
112+ self.progress_label.setText(translate('BiblesPlugin.ImportWizardForm', 'Your Bible import failed.'))
113+ del self.manager.db_cache[importer.name]
114+ delete_database(self.plugin.settings_section, importer.file)
115
116=== modified file 'openlp/plugins/bibles/lib/__init__.py'
117--- openlp/plugins/bibles/lib/__init__.py 2016-05-05 15:41:48 +0000
118+++ openlp/plugins/bibles/lib/__init__.py 2016-09-09 21:59:44 +0000
119@@ -173,7 +173,7 @@
120
121 def update_reference_separators():
122 """
123- Updates separators and matches for parsing and formating scripture references.
124+ Updates separators and matches for parsing and formatting scripture references.
125 """
126 default_separators = [
127 '|'.join([
128@@ -215,7 +215,7 @@
129 # escape reserved characters
130 for character in '\\.^$*+?{}[]()':
131 source_string = source_string.replace(character, '\\' + character)
132- # add various unicode alternatives
133+ # add various Unicode alternatives
134 source_string = source_string.replace('-', '(?:[-\u00AD\u2010\u2011\u2012\u2014\u2014\u2212\uFE63\uFF0D])')
135 source_string = source_string.replace(',', '(?:[,\u201A])')
136 REFERENCE_SEPARATORS['sep_{role}'.format(role=role)] = '\s*(?:{source})\s*'.format(source=source_string)
137
138=== modified file 'openlp/plugins/bibles/lib/bibleimport.py'
139--- openlp/plugins/bibles/lib/bibleimport.py 2016-08-09 19:32:29 +0000
140+++ openlp/plugins/bibles/lib/bibleimport.py 2016-09-09 21:59:44 +0000
141@@ -20,25 +20,84 @@
142 # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
143 ###############################################################################
144
145-import logging
146-
147 from lxml import etree, objectify
148+from zipfile import is_zipfile
149
150-from openlp.core.common import OpenLPMixin, languages
151+from openlp.core.common import OpenLPMixin, Registry, RegistryProperties, languages, translate
152 from openlp.core.lib import ValidationError
153-from openlp.plugins.bibles.lib.db import BibleDB, BiblesResourcesDB
154-
155-log = logging.getLogger(__name__)
156-
157-
158-class BibleImport(OpenLPMixin, BibleDB):
159+from openlp.core.lib.ui import critical_error_message_box
160+from openlp.plugins.bibles.lib.db import AlternativeBookNamesDB, BibleDB, BiblesResourcesDB
161+
162+
163+class BibleImport(OpenLPMixin, RegistryProperties, BibleDB):
164 """
165 Helper class to import bibles from a third party source into OpenLP
166 """
167- # TODO: Test
168 def __init__(self, *args, **kwargs):
169 super().__init__(*args, **kwargs)
170 self.filename = kwargs['filename'] if 'filename' in kwargs else None
171+ self.wizard = None
172+ self.stop_import_flag = False
173+ Registry().register_function('openlp_stop_wizard', self.stop_import)
174+
175+ @staticmethod
176+ def is_compressed(file):
177+ """
178+ Check if the supplied file is compressed
179+
180+ :param file: A path to the file to check
181+ """
182+ if is_zipfile(file):
183+ critical_error_message_box(
184+ message=translate('BiblesPlugin.BibleImport',
185+ 'The file "{file}" you supplied is compressed. You must decompress it before import.'
186+ ).format(file=file))
187+ return True
188+ return False
189+
190+ def get_book_ref_id_by_name(self, book, maxbooks=66, language_id=None):
191+ """
192+ Find the book id from the name or abbreviation of the book. If it doesn't currently exist, ask the user.
193+
194+ :param book: The name or abbreviation of the book
195+ :param maxbooks: The number of books in the bible
196+ :param language_id: The language_id of the bible
197+ :return: The id of the bible, or None
198+ """
199+ self.log_debug('BibleDB.get_book_ref_id_by_name:("{book}", "{lang}")'.format(book=book, lang=language_id))
200+ book_temp = BiblesResourcesDB.get_book(book, True)
201+ if book_temp:
202+ return book_temp['id']
203+ book_id = BiblesResourcesDB.get_alternative_book_name(book)
204+ if book_id:
205+ return book_id
206+ book_id = AlternativeBookNamesDB.get_book_reference_id(book)
207+ if book_id:
208+ return book_id
209+ from openlp.plugins.bibles.forms import BookNameForm
210+ book_name = BookNameForm(self.wizard)
211+ if book_name.exec(book, self.get_books(), maxbooks) and book_name.book_id:
212+ AlternativeBookNamesDB.create_alternative_book_name(book, book_name.book_id, language_id)
213+ return book_name.book_id
214+
215+ def get_language(self, bible_name=None):
216+ """
217+ If no language is given it calls a dialog window where the user could select the bible language.
218+ Return the language id of a bible.
219+
220+ :param bible_name: The language the bible is.
221+ """
222+ self.log_debug('BibleImpoer.get_language()')
223+ from openlp.plugins.bibles.forms import LanguageForm
224+ language_id = None
225+ language_form = LanguageForm(self.wizard)
226+ if language_form.exec(bible_name):
227+ combo_box = language_form.language_combo_box
228+ language_id = combo_box.itemData(combo_box.currentIndex())
229+ if not language_id:
230+ return None
231+ self.save_meta('language_id', language_id)
232+ return language_id
233
234 def get_language_id(self, file_language=None, bible_name=None):
235 """
236@@ -58,8 +117,8 @@
237 language_id = self.get_language(bible_name)
238 if not language_id:
239 # User cancelled get_language dialog
240- log.error('Language detection failed when importing from "{name}". User aborted language selection.'
241- .format(name=bible_name))
242+ self.log_error('Language detection failed when importing from "{name}". User aborted language selection.'
243+ .format(name=bible_name))
244 return None
245 self.save_meta('language_id', language_id)
246 return language_id
247@@ -77,7 +136,7 @@
248 if name:
249 book_ref_id = self.get_book_ref_id_by_name(name, no_of_books, language_id)
250 else:
251- log.debug('No book name supplied. Falling back to guess_id')
252+ self.log_debug('No book name supplied. Falling back to guess_id')
253 book_ref_id = guess_id
254 if not book_ref_id:
255 raise ValidationError(msg='Could not resolve book_ref_id in "{}"'.format(self.filename))
256@@ -87,8 +146,7 @@
257 'importing {file}'.format(book_ref=book_ref_id, file=self.filename))
258 return self.create_book(name, book_ref_id, book_details['testament_id'])
259
260- @staticmethod
261- def parse_xml(filename, use_objectify=False, elements=None, tags=None):
262+ def parse_xml(self, filename, use_objectify=False, elements=None, tags=None):
263 """
264 Parse and clean the supplied file by removing any elements or tags we don't use.
265 :param filename: The filename of the xml file to parse. Str
266@@ -97,17 +155,80 @@
267 :param tags: A tuple of element names (Str) to remove, preserving their content.
268 :return: The root element of the xml document
269 """
270- with open(filename, 'rb') as import_file:
271- # NOTE: We don't need to do any of the normal encoding detection here, because lxml does it's own encoding
272- # detection, and the two mechanisms together interfere with each other.
273- if not use_objectify:
274- tree = etree.parse(import_file, parser=etree.XMLParser(recover=True))
275- else:
276- tree = objectify.parse(import_file, parser=objectify.makeparser(recover=True))
277- if elements:
278- # Strip tags we don't use - remove content
279- etree.strip_elements(tree, elements, with_tail=False)
280- if tags:
281- # Strip tags we don't use - keep content
282- etree.strip_tags(tree, tags)
283- return tree.getroot()
284+ try:
285+ with open(filename, 'rb') as import_file:
286+ # NOTE: We don't need to do any of the normal encoding detection here, because lxml does it's own
287+ # encoding detection, and the two mechanisms together interfere with each other.
288+ if not use_objectify:
289+ tree = etree.parse(import_file, parser=etree.XMLParser(recover=True))
290+ else:
291+ tree = objectify.parse(import_file, parser=objectify.makeparser(recover=True))
292+ if elements or tags:
293+ self.wizard.increment_progress_bar(
294+ translate('BiblesPlugin.OsisImport', 'Removing unused tags (this may take a few minutes)...'))
295+ if elements:
296+ # Strip tags we don't use - remove content
297+ etree.strip_elements(tree, elements, with_tail=False)
298+ if tags:
299+ # Strip tags we don't use - keep content
300+ etree.strip_tags(tree, tags)
301+ return tree.getroot()
302+ except OSError as e:
303+ self.log_exception('Opening {file_name} failed.'.format(file_name=e.filename))
304+ critical_error_message_box(
305+ title='An Error Occured When Opening A File',
306+ message='The following error occurred when trying to open\n{file_name}:\n\n{error}'
307+ .format(file_name=e.filename, error=e.strerror))
308+ return None
309+
310+ def register(self, wizard):
311+ """
312+ This method basically just initialises the database. It is called from the Bible Manager when a Bible is
313+ imported. Descendant classes may want to override this method to supply their own custom
314+ initialisation as well.
315+
316+ :param wizard: The actual Qt wizard form.
317+ """
318+ self.wizard = wizard
319+ return self.name
320+
321+ def set_current_chapter(self, book_name, chapter_name):
322+ self.wizard.increment_progress_bar(translate('BiblesPlugin.OsisImport', 'Importing {book} {chapter}...')
323+ .format(book=book_name, chapter=chapter_name))
324+
325+ def stop_import(self):
326+ """
327+ Stops the import of the Bible.
328+ """
329+ self.log_debug('Stopping import')
330+ self.stop_import_flag = True
331+
332+ def validate_xml_file(self, filename, tag):
333+ """
334+ Validate the supplied file
335+
336+ :param filename: The supplied file
337+ :param tag: The expected root tag type
338+ :return: True if valid. ValidationError is raised otherwise.
339+ """
340+ if BibleImport.is_compressed(filename):
341+ raise ValidationError(msg='Compressed file')
342+ bible = self.parse_xml(filename, use_objectify=True)
343+ if bible is None:
344+ raise ValidationError(msg='Error when opening file')
345+ root_tag = bible.tag.lower()
346+ bible_type = translate('BiblesPlugin.BibleImport', 'unknown type of',
347+ 'This looks like an unknown type of XML bible.')
348+ if root_tag == tag:
349+ return True
350+ elif root_tag == 'bible':
351+ bible_type = "OpenSong"
352+ elif root_tag == '{http://www.bibletechnologies.net/2003/osis/namespace}osis':
353+ bible_type = 'OSIS'
354+ elif root_tag == 'xmlbible':
355+ bible_type = 'Zefania'
356+ critical_error_message_box(
357+ message=translate('BiblesPlugin.BibleImport',
358+ 'Incorrect Bible file type supplied. This looks like an {bible_type} XML bible.'
359+ .format(bible_type=bible_type)))
360+ raise ValidationError(msg='Invalid xml.')
361
362=== modified file 'openlp/plugins/bibles/lib/db.py'
363--- openlp/plugins/bibles/lib/db.py 2016-08-07 10:15:43 +0000
364+++ openlp/plugins/bibles/lib/db.py 2016-09-09 21:59:44 +0000
365@@ -33,7 +33,7 @@
366 from sqlalchemy.orm import class_mapper, mapper, relation
367 from sqlalchemy.orm.exc import UnmappedClassError
368
369-from openlp.core.common import Registry, RegistryProperties, AppLocation, translate, clean_filename
370+from openlp.core.common import AppLocation, translate, clean_filename
371 from openlp.core.lib.db import BaseModel, init_db, Manager
372 from openlp.core.lib.ui import critical_error_message_box
373 from openlp.plugins.bibles.lib import upgrade
374@@ -106,7 +106,7 @@
375 return session
376
377
378-class BibleDB(Manager, RegistryProperties):
379+class BibleDB(Manager):
380 """
381 This class represents a database-bound Bible. It is used as a base class for all the custom importers, so that
382 the can implement their own import methods, but benefit from the database methods in here via inheritance,
383@@ -140,7 +140,6 @@
384 raise KeyError('Missing keyword argument "path".')
385 if 'name' not in kwargs and 'file' not in kwargs:
386 raise KeyError('Missing keyword argument "name" or "file".')
387- self.stop_import_flag = False
388 if 'name' in kwargs:
389 self.name = kwargs['name']
390 if not isinstance(self.name, str):
391@@ -153,15 +152,6 @@
392 self.get_name()
393 if 'path' in kwargs:
394 self.path = kwargs['path']
395- self.wizard = None
396- Registry().register_function('openlp_stop_wizard', self.stop_import)
397-
398- def stop_import(self):
399- """
400- Stops the import of the Bible.
401- """
402- log.debug('Stopping import')
403- self.stop_import_flag = True
404
405 def get_name(self):
406 """
407@@ -171,17 +161,6 @@
408 self.name = version_name.value if version_name else None
409 return self.name
410
411- def register(self, wizard):
412- """
413- This method basically just initialises the database. It is called from the Bible Manager when a Bible is
414- imported. Descendant classes may want to override this method to supply their own custom
415- initialisation as well.
416-
417- :param wizard: The actual Qt wizard form.
418- """
419- self.wizard = wizard
420- return self.name
421-
422 def create_book(self, name, bk_ref_id, testament=1):
423 """
424 Add a book to the database.
425@@ -306,26 +285,6 @@
426 log.debug('BibleDB.get_book_by_book_ref_id("{ref}")'.format(ref=ref_id))
427 return self.get_object_filtered(Book, Book.book_reference_id.like(ref_id))
428
429- def get_book_ref_id_by_name(self, book, maxbooks, language_id=None):
430- log.debug('BibleDB.get_book_ref_id_by_name:("{book}", "{lang}")'.format(book=book, lang=language_id))
431- book_id = None
432- if BiblesResourcesDB.get_book(book, True):
433- book_temp = BiblesResourcesDB.get_book(book, True)
434- book_id = book_temp['id']
435- elif BiblesResourcesDB.get_alternative_book_name(book):
436- book_id = BiblesResourcesDB.get_alternative_book_name(book)
437- elif AlternativeBookNamesDB.get_book_reference_id(book):
438- book_id = AlternativeBookNamesDB.get_book_reference_id(book)
439- else:
440- from openlp.plugins.bibles.forms import BookNameForm
441- book_name = BookNameForm(self.wizard)
442- if book_name.exec(book, self.get_books(), maxbooks):
443- book_id = book_name.book_id
444- if book_id:
445- AlternativeBookNamesDB.create_alternative_book_name(
446- book, book_id, language_id)
447- return book_id
448-
449 def get_book_ref_id_by_localised_name(self, book, language_selection):
450 """
451 Return the id of a named book.
452@@ -462,25 +421,6 @@
453 return 0
454 return count
455
456- def get_language(self, bible_name=None):
457- """
458- If no language is given it calls a dialog window where the user could select the bible language.
459- Return the language id of a bible.
460-
461- :param bible_name: The language the bible is.
462- """
463- log.debug('BibleDB.get_language()')
464- from openlp.plugins.bibles.forms import LanguageForm
465- language_id = None
466- language_form = LanguageForm(self.wizard)
467- if language_form.exec(bible_name):
468- combo_box = language_form.language_combo_box
469- language_id = combo_box.itemData(combo_box.currentIndex())
470- if not language_id:
471- return None
472- self.save_meta('language_id', language_id)
473- return language_id
474-
475 def dump_bible(self):
476 """
477 Utility debugging method to dump the contents of a bible.
478
479=== modified file 'openlp/plugins/bibles/lib/importers/csvbible.py'
480--- openlp/plugins/bibles/lib/importers/csvbible.py 2016-08-09 20:45:25 +0000
481+++ openlp/plugins/bibles/lib/importers/csvbible.py 2016-09-09 21:59:44 +0000
482@@ -50,7 +50,6 @@
483 All CSV files are expected to use a comma (',') as the delimiter and double quotes ('"') as the quote symbol.
484 """
485 import csv
486-import logging
487 from collections import namedtuple
488
489 from openlp.core.common import get_file_encoding, translate
490@@ -58,8 +57,6 @@
491 from openlp.plugins.bibles.lib.bibleimport import BibleImport
492
493
494-log = logging.getLogger(__name__)
495-
496 Book = namedtuple('Book', 'id, testament_id, name, abbreviation')
497 Verse = namedtuple('Verse', 'book_id_name, chapter_number, number, text')
498
499@@ -68,15 +65,13 @@
500 """
501 This class provides a specialisation for importing of CSV Bibles.
502 """
503- log.info('CSVBible loaded')
504-
505 def __init__(self, *args, **kwargs):
506 """
507 Loads a Bible from a set of CSV files. This class assumes the files contain all the information and a clean
508 bible is being loaded.
509 """
510- log.info(self.__class__.__name__)
511 super().__init__(*args, **kwargs)
512+ self.log_info(self.__class__.__name__)
513 self.books_file = kwargs['booksfile']
514 self.verses_file = kwargs['versefile']
515
516@@ -123,12 +118,11 @@
517 number_of_books = len(books)
518 for book in books:
519 if self.stop_import_flag:
520- return None
521+ break
522 self.wizard.increment_progress_bar(
523 translate('BiblesPlugin.CSVBible', 'Importing books... {book}').format(book=book.name))
524 self.find_and_create_book(book.name, number_of_books, self.language_id)
525 book_list.update({int(book.id): book.name})
526- self.application.process_events()
527 return book_list
528
529 def process_verses(self, verses, books):
530@@ -142,7 +136,7 @@
531 book_ptr = None
532 for verse in verses:
533 if self.stop_import_flag:
534- return None
535+ break
536 verse_book = self.get_book_name(verse.book_id_name, books)
537 if book_ptr != verse_book:
538 book = self.get_book(verse_book)
539@@ -151,9 +145,7 @@
540 translate('BiblesPlugin.CSVBible', 'Importing verses from {book}...',
541 'Importing verses from <book name>...').format(book=book.name))
542 self.session.commit()
543- self.create_verse(book.id, verse.chapter_number, verse.number, verse.text)
544- self.wizard.increment_progress_bar(translate('BiblesPlugin.CSVBible', 'Importing verses... done.'))
545- self.application.process_events()
546+ self.create_verse(book.id, int(verse.chapter_number), int(verse.number), verse.text)
547 self.session.commit()
548
549 def do_import(self, bible_name=None):
550@@ -163,24 +155,16 @@
551 :param bible_name: Optional name of the bible being imported. Str or None
552 :return: True if the import was successful, False if it failed or was cancelled
553 """
554- try:
555- self.language_id = self.get_language(bible_name)
556- if not self.language_id:
557- raise ValidationError(msg='Invalid language selected')
558- books = self.parse_csv_file(self.books_file, Book)
559- self.wizard.progress_bar.setValue(0)
560- self.wizard.progress_bar.setMinimum(0)
561- self.wizard.progress_bar.setMaximum(len(books))
562- book_list = self.process_books(books)
563- if self.stop_import_flag:
564- return False
565- verses = self.parse_csv_file(self.verses_file, Verse)
566- self.wizard.progress_bar.setValue(0)
567- self.wizard.progress_bar.setMaximum(len(books) + 1)
568- self.process_verses(verses, book_list)
569- if self.stop_import_flag:
570- return False
571- except ValidationError:
572- log.exception('Could not import CSV bible')
573+ self.language_id = self.get_language(bible_name)
574+ if not self.language_id:
575 return False
576+ books = self.parse_csv_file(self.books_file, Book)
577+ self.wizard.progress_bar.setValue(0)
578+ self.wizard.progress_bar.setMinimum(0)
579+ self.wizard.progress_bar.setMaximum(len(books))
580+ book_list = self.process_books(books)
581+ verses = self.parse_csv_file(self.verses_file, Verse)
582+ self.wizard.progress_bar.setValue(0)
583+ self.wizard.progress_bar.setMaximum(len(books) + 1)
584+ self.process_verses(verses, book_list)
585 return True
586
587=== modified file 'openlp/plugins/bibles/lib/importers/opensong.py'
588--- openlp/plugins/bibles/lib/importers/opensong.py 2016-08-09 20:45:25 +0000
589+++ openlp/plugins/bibles/lib/importers/opensong.py 2016-09-09 21:59:44 +0000
590@@ -20,109 +20,126 @@
591 # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
592 ###############################################################################
593
594-import logging
595-from lxml import etree, objectify
596-
597-from openlp.core.common import translate, trace_error_handler
598-from openlp.core.lib.ui import critical_error_message_box
599 from openlp.plugins.bibles.lib.bibleimport import BibleImport
600-from openlp.plugins.bibles.lib.db import BibleDB, BiblesResourcesDB
601-
602-
603-log = logging.getLogger(__name__)
604+
605+
606+def get_text(element):
607+ """
608+ Recursively get all text in an objectify element and its child elements.
609+
610+ :param element: An objectify element to get the text from
611+ :return: The text content of the element (str)
612+ """
613+ verse_text = ''
614+ if element.text:
615+ verse_text = element.text
616+ for sub_element in element.iterchildren():
617+ verse_text += get_text(sub_element)
618+ if element.tail:
619+ verse_text += element.tail
620+ return verse_text
621+
622+
623+def parse_chapter_number(number, previous_number):
624+ """
625+ Parse the chapter number
626+
627+ :param number: The raw data from the xml
628+ :param previous_number: The previous chapter number
629+ :return: Number of current chapter. (Int)
630+ """
631+ if number:
632+ return int(number.split()[-1])
633+ return previous_number + 1
634
635
636 class OpenSongBible(BibleImport):
637 """
638 OpenSong Bible format importer class. This class is used to import Bibles from OpenSong's XML format.
639 """
640- def get_text(self, element):
641- """
642- Recursively get all text in an objectify element and its child elements.
643-
644- :param element: An objectify element to get the text from
645- """
646- verse_text = ''
647- if element.text:
648- verse_text = element.text
649- for sub_element in element.iterchildren():
650- verse_text += self.get_text(sub_element)
651- if element.tail:
652- verse_text += element.tail
653- return verse_text
654+
655+ def parse_verse_number(self, number, previous_number):
656+ """
657+ Parse the verse number retrieved from the xml
658+
659+ :param number: The raw data from the xml
660+ :param previous_number: The previous verse number
661+ :return: Number of current verse. (Int)
662+ """
663+ if not number:
664+ return previous_number + 1
665+ try:
666+ return int(number)
667+ except ValueError:
668+ verse_parts = number.split('-')
669+ if len(verse_parts) > 1:
670+ number = int(verse_parts[0])
671+ return number
672+ except TypeError:
673+ self.log_warning('Illegal verse number: {verse_no}'.format(verse_no=str(number)))
674+ return previous_number + 1
675+
676+ def process_books(self, books):
677+ """
678+ Extract and create the books from the objectified xml
679+
680+ :param books: Objectified xml
681+ :return: None
682+ """
683+ for book in books:
684+ if self.stop_import_flag:
685+ break
686+ db_book = self.find_and_create_book(str(book.attrib['n']), len(books), self.language_id)
687+ self.process_chapters(db_book, book.c)
688+ self.session.commit()
689+
690+ def process_chapters(self, book, chapters):
691+ """
692+ Extract and create the chapters from the objectified xml for the book `book`
693+
694+ :param book: A database Book object to add the chapters to
695+ :param chapters: Objectified xml containing chapters
696+ :return: None
697+ """
698+ chapter_number = 0
699+ for chapter in chapters:
700+ if self.stop_import_flag:
701+ break
702+ chapter_number = parse_chapter_number(chapter.attrib['n'], chapter_number)
703+ self.set_current_chapter(book.name, chapter_number)
704+ self.process_verses(book, chapter_number, chapter.v)
705+
706+ def process_verses(self, book, chapter_number, verses):
707+ """
708+ Extract and create the verses from the objectified xml
709+
710+ :param book: A database Book object
711+ :param chapter_number: The chapter number to add the verses to (int)
712+ :param verses: Objectified xml containing verses
713+ :return: None
714+ """
715+ verse_number = 0
716+ for verse in verses:
717+ if self.stop_import_flag:
718+ break
719+ verse_number = self.parse_verse_number(verse.attrib['n'], verse_number)
720+ self.create_verse(book.id, chapter_number, verse_number, get_text(verse))
721
722 def do_import(self, bible_name=None):
723 """
724- Loads a Bible from file.
725+ Loads an Open Song Bible from a file.
726+
727+ :param bible_name: The name of the bible being imported
728+ :return: True if import completed, False if import was unsuccessful
729 """
730- log.debug('Starting OpenSong import from "{name}"'.format(name=self.filename))
731- success = True
732- try:
733- bible = self.parse_xml(self.filename, use_objectify=True)
734- # Check that we're not trying to import a Zefania XML bible, it is sometimes refered to as 'OpenSong'
735- if bible.tag.upper() == 'XMLBIBLE':
736- critical_error_message_box(
737- message=translate('BiblesPlugin.OpenSongImport',
738- 'Incorrect Bible file type supplied. This looks like a Zefania XML bible, '
739- 'please use the Zefania import option.'))
740- return False
741- # No language info in the opensong format, so ask the user
742- language_id = self.get_language_id(bible_name=self.filename)
743- if not language_id:
744- return False
745- for book in bible.b:
746- if self.stop_import_flag:
747- break
748- book_ref_id = self.get_book_ref_id_by_name(str(book.attrib['n']), len(bible.b), language_id)
749- if not book_ref_id:
750- log.error('Importing books from "{name}" failed'.format(name=self.filename))
751- return False
752- book_details = BiblesResourcesDB.get_book_by_id(book_ref_id)
753- db_book = self.create_book(book.attrib['n'], book_ref_id, book_details['testament_id'])
754- chapter_number = 0
755- for chapter in book.c:
756- if self.stop_import_flag:
757- break
758- number = chapter.attrib['n']
759- if number:
760- chapter_number = int(number.split()[-1])
761- else:
762- chapter_number += 1
763- verse_number = 0
764- for verse in chapter.v:
765- if self.stop_import_flag:
766- break
767- number = verse.attrib['n']
768- if number:
769- try:
770- number = int(number)
771- except ValueError:
772- verse_parts = number.split('-')
773- if len(verse_parts) > 1:
774- number = int(verse_parts[0])
775- except TypeError:
776- log.warning('Illegal verse number: {verse:d}'.format(verse=verse.attrib['n']))
777- verse_number = number
778- else:
779- verse_number += 1
780- self.create_verse(db_book.id, chapter_number, verse_number, self.get_text(verse))
781- self.wizard.increment_progress_bar(translate('BiblesPlugin.Opensong',
782- 'Importing {name} {chapter}...'
783- ).format(name=db_book.name, chapter=chapter_number))
784- self.session.commit()
785- self.application.process_events()
786- except etree.XMLSyntaxError as inst:
787- trace_error_handler(log)
788- critical_error_message_box(
789- message=translate('BiblesPlugin.OpenSongImport',
790- 'Incorrect Bible file type supplied. OpenSong Bibles may be '
791- 'compressed. You must decompress them before import.'))
792- log.exception(inst)
793- success = False
794- except (IOError, AttributeError):
795- log.exception('Loading Bible from OpenSong file failed')
796- success = False
797- if self.stop_import_flag:
798- return False
799- else:
800- return success
801+ self.log_debug('Starting OpenSong import from "{name}"'.format(name=self.filename))
802+ self.validate_xml_file(self.filename, 'bible')
803+ bible = self.parse_xml(self.filename, use_objectify=True)
804+ if bible is None:
805+ return False
806+ # No language info in the opensong format, so ask the user
807+ self.language_id = self.get_language_id(bible_name=self.filename)
808+ if not self.language_id:
809+ return False
810+ self.process_books(bible.b)
811+ return True
812
813=== modified file 'openlp/plugins/bibles/lib/importers/osis.py'
814--- openlp/plugins/bibles/lib/importers/osis.py 2016-08-10 19:08:09 +0000
815+++ openlp/plugins/bibles/lib/importers/osis.py 2016-09-09 21:59:44 +0000
816@@ -20,15 +20,9 @@
817 # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
818 ###############################################################################
819
820-import logging
821 from lxml import etree
822
823-from openlp.core.common import translate, trace_error_handler
824-from openlp.core.lib.ui import critical_error_message_box
825 from openlp.plugins.bibles.lib.bibleimport import BibleImport
826-from openlp.plugins.bibles.lib.db import BiblesResourcesDB
827-
828-log = logging.getLogger(__name__)
829
830 NS = {'ns': 'http://www.bibletechnologies.net/2003/OSIS/namespace'}
831 # Tags we don't use and can remove the content
832@@ -74,104 +68,106 @@
833 '{http://www.bibletechnologies.net/2003/OSIS/namespace}caption'
834 )
835
836-
837-def replacement(match):
838- return match.group(2).upper()
839+# Precompile a few xpath-querys
840+verse_in_chapter = etree.XPath('//ns:chapter[1]/ns:verse', namespaces=NS)
841+text_in_verse = etree.XPath('//ns:verse[1]/text()', namespaces=NS)
842
843
844 class OSISBible(BibleImport):
845 """
846 `OSIS <http://www.bibletechnologies.net/>`_ Bible format importer class.
847 """
848+ def process_books(self, bible_data):
849+ """
850+ Extract and create the bible books from the parsed xml
851+
852+ :param bible_data: parsed xml
853+ :return: None
854+ """
855+ # Find books in the bible
856+ bible_books = bible_data.xpath("//ns:div[@type='book']", namespaces=NS)
857+ no_of_books = len(bible_books)
858+ for book in bible_books:
859+ if self.stop_import_flag:
860+ break
861+ # Remove div-tags in the book
862+ etree.strip_tags(book, '{http://www.bibletechnologies.net/2003/OSIS/namespace}div')
863+ db_book = self.find_and_create_book(book.get('osisID'), no_of_books, self.language_id)
864+ self.process_chapters(db_book, book)
865+ self.session.commit()
866+
867+ def process_chapters(self, book, chapters):
868+ """
869+ Extract the chapters, and do some initial processing of the verses
870+
871+ :param book: An OpenLP bible database book object
872+ :param chapters: parsed chapters
873+ :return: None
874+ """
875+ # Find out if chapter-tags contains the verses, or if it is used as milestone/anchor
876+ if verse_in_chapter(chapters):
877+ # The chapter tags contains the verses
878+ for chapter in chapters:
879+ chapter_number = int(chapter.get("osisID").split('.')[1])
880+ self.set_current_chapter(book.name, chapter_number)
881+ # Find out if verse-tags contains the text, or if it is used as milestone/anchor
882+ if not text_in_verse(chapter):
883+ # verse-tags are used as milestone
884+ for verse in chapter:
885+ # If this tag marks the start of a verse, the verse text is between this tag and
886+ # the next tag, which the "tail" attribute gives us.
887+ self.process_verse(book, chapter_number, verse, use_milestones=True)
888+ else:
889+ # Verse-tags contains the text
890+ for verse in chapter:
891+ self.process_verse(book, chapter_number, verse)
892+ else:
893+ # The chapter tags is used as milestones. For now we assume verses is also milestones
894+ chapter_number = 0
895+ for element in chapters:
896+ if element.tag == '{http://www.bibletechnologies.net/2003/OSIS/namespace}chapter' \
897+ and element.get('sID'):
898+ chapter_number = int(element.get("osisID").split('.')[1])
899+ self.set_current_chapter(book.name, chapter_number)
900+ elif element.tag == '{http://www.bibletechnologies.net/2003/OSIS/namespace}verse':
901+ # If this tag marks the start of a verse, the verse text is between this tag and
902+ # the next tag, which the "tail" attribute gives us.
903+ self.process_verse(book, chapter_number, element, use_milestones=True)
904+
905+ def process_verse(self, book, chapter_number, element, use_milestones=False):
906+ """
907+ Process a verse element
908+ :param book: A database Book object
909+ :param chapter_number: The chapter number to add the verses to (int)
910+ :param element: The verse element to process. (etree element type)
911+ :param use_milestones: set to True to process a 'milestone' verse. Defaults to False
912+ :return: None
913+ """
914+ osis_id = element.get("osisID")
915+ if not osis_id:
916+ return None
917+ verse_number = int(osis_id.split('.')[2])
918+ verse_text = ''
919+ if use_milestones and element.get('sID'):
920+ verse_text = element.tail
921+ elif not use_milestones:
922+ verse_text = element.text
923+ if verse_text:
924+ self.create_verse(book.id, chapter_number, verse_number, verse_text.strip())
925+
926 def do_import(self, bible_name=None):
927 """
928 Loads a Bible from file.
929 """
930- log.debug('Starting OSIS import from "{name}"'.format(name=self.filename))
931- success = True
932- try:
933- self.wizard.increment_progress_bar(translate('BiblesPlugin.OsisImport',
934- 'Removing unused tags (this may take a few minutes)...'))
935- osis_bible_tree = self.parse_xml(self.filename, elements=REMOVABLE_ELEMENTS, tags=REMOVABLE_TAGS)
936- # Find bible language]
937- language = osis_bible_tree.xpath("//ns:osisText/@xml:lang", namespaces=NS)
938- language_id = self.get_language_id(language[0] if language else None, bible_name=self.filename)
939- if not language_id:
940- return False
941- num_books = int(osis_bible_tree.xpath("count(//ns:div[@type='book'])", namespaces=NS))
942- # Precompile a few xpath-querys
943- verse_in_chapter = etree.XPath('count(//ns:chapter[1]/ns:verse)', namespaces=NS)
944- text_in_verse = etree.XPath('count(//ns:verse[1]/text())', namespaces=NS)
945- # Find books in the bible
946- bible_books = osis_bible_tree.xpath("//ns:div[@type='book']", namespaces=NS)
947- for book in bible_books:
948- if self.stop_import_flag:
949- break
950- # Remove div-tags in the book
951- etree.strip_tags(book, ('{http://www.bibletechnologies.net/2003/OSIS/namespace}div'))
952- book_ref_id = self.get_book_ref_id_by_name(book.get('osisID'), num_books, language_id)
953- if not book_ref_id:
954- log.error('Importing books from "{name}" failed'.format(name=self.filename))
955- return False
956- book_details = BiblesResourcesDB.get_book_by_id(book_ref_id)
957- db_book = self.create_book(book_details['name'], book_ref_id, book_details['testament_id'])
958- # Find out if chapter-tags contains the verses, or if it is used as milestone/anchor
959- if int(verse_in_chapter(book)) > 0:
960- # The chapter tags contains the verses
961- for chapter in book:
962- chapter_number = chapter.get("osisID").split('.')[1]
963- # Find out if verse-tags contains the text, or if it is used as milestone/anchor
964- if int(text_in_verse(chapter)) == 0:
965- # verse-tags are used as milestone
966- for verse in chapter:
967- # If this tag marks the start of a verse, the verse text is between this tag and
968- # the next tag, which the "tail" attribute gives us.
969- if verse.get('sID'):
970- verse_number = verse.get("osisID").split('.')[2]
971- verse_text = verse.tail
972- if verse_text:
973- self.create_verse(db_book.id, chapter_number, verse_number, verse_text.strip())
974- else:
975- # Verse-tags contains the text
976- for verse in chapter:
977- verse_number = verse.get("osisID").split('.')[2]
978- if verse.text:
979- self.create_verse(db_book.id, chapter_number, verse_number, verse.text.strip())
980- self.wizard.increment_progress_bar(
981- translate('BiblesPlugin.OsisImport', 'Importing %(bookname)s %(chapter)s...') %
982- {'bookname': db_book.name, 'chapter': chapter_number})
983- else:
984- # The chapter tags is used as milestones. For now we assume verses is also milestones
985- chapter_number = 0
986- for element in book:
987- if element.tag == '{http://www.bibletechnologies.net/2003/OSIS/namespace}chapter' \
988- and element.get('sID'):
989- chapter_number = element.get("osisID").split('.')[1]
990- self.wizard.increment_progress_bar(
991- translate('BiblesPlugin.OsisImport', 'Importing %(bookname)s %(chapter)s...') %
992- {'bookname': db_book.name, 'chapter': chapter_number})
993- elif element.tag == '{http://www.bibletechnologies.net/2003/OSIS/namespace}verse' \
994- and element.get('sID'):
995- # If this tag marks the start of a verse, the verse text is between this tag and
996- # the next tag, which the "tail" attribute gives us.
997- verse_number = element.get("osisID").split('.')[2]
998- verse_text = element.tail
999- if verse_text:
1000- self.create_verse(db_book.id, chapter_number, verse_number, verse_text.strip())
1001- self.session.commit()
1002- self.application.process_events()
1003- except (ValueError, IOError):
1004- log.exception('Loading bible from OSIS file failed')
1005- trace_error_handler(log)
1006- success = False
1007- except etree.XMLSyntaxError as e:
1008- log.exception('Loading bible from OSIS file failed')
1009- trace_error_handler(log)
1010- success = False
1011- critical_error_message_box(message=translate('BiblesPlugin.OsisImport',
1012- 'The file is not a valid OSIS-XML file:'
1013- '\n{text}').format(text=e.msg))
1014- if self.stop_import_flag:
1015- return False
1016- else:
1017- return success
1018+ self.log_debug('Starting OSIS import from "{name}"'.format(name=self.filename))
1019+ self.validate_xml_file(self.filename, '{http://www.bibletechnologies.net/2003/osis/namespace}osis')
1020+ bible = self.parse_xml(self.filename, elements=REMOVABLE_ELEMENTS, tags=REMOVABLE_TAGS)
1021+ if bible is None:
1022+ return False
1023+ # Find bible language
1024+ language = bible.xpath("//ns:osisText/@xml:lang", namespaces=NS)
1025+ self.language_id = self.get_language_id(language[0] if language else None, bible_name=self.filename)
1026+ if not self.language_id:
1027+ return False
1028+ self.process_books(bible)
1029+ return True
1030
1031=== modified file 'openlp/plugins/bibles/lib/importers/zefania.py'
1032--- openlp/plugins/bibles/lib/importers/zefania.py 2016-08-09 20:45:25 +0000
1033+++ openlp/plugins/bibles/lib/importers/zefania.py 2016-09-09 21:59:44 +0000
1034@@ -54,7 +54,7 @@
1035 language_id = self.get_language_id(language[0] if language else None, bible_name=self.filename)
1036 if not language_id:
1037 return False
1038- num_books = int(xmlbible.xpath('count(//BIBLEBOOK)'))
1039+ no_of_books = int(xmlbible.xpath('count(//BIBLEBOOK)'))
1040 self.wizard.progress_bar.setMaximum(int(xmlbible.xpath('count(//CHAPTER)')))
1041 for BIBLEBOOK in xmlbible:
1042 if self.stop_import_flag:
1043@@ -64,7 +64,7 @@
1044 if not bname and not bnumber:
1045 continue
1046 if bname:
1047- book_ref_id = self.get_book_ref_id_by_name(bname, num_books, language_id)
1048+ book_ref_id = self.get_book_ref_id_by_name(bname, no_of_books, language_id)
1049 else:
1050 log.debug('Could not find a name, will use number, basically a guess.')
1051 book_ref_id = int(bnumber)
1052@@ -79,7 +79,8 @@
1053 chapter_number = CHAPTER.get("cnumber")
1054 for VERS in CHAPTER:
1055 verse_number = VERS.get("vnumber")
1056- self.create_verse(db_book.id, chapter_number, verse_number, VERS.text.replace('<BR/>', '\n'))
1057+ self.create_verse(
1058+ db_book.id, int(chapter_number), int(verse_number), VERS.text.replace('<BR/>', '\n'))
1059 self.wizard.increment_progress_bar(
1060 translate('BiblesPlugin.Zefnia',
1061 'Importing {book} {chapter}...').format(book=db_book.name,
1062
1063=== modified file 'openlp/plugins/bibles/lib/manager.py'
1064--- openlp/plugins/bibles/lib/manager.py 2016-08-12 17:26:54 +0000
1065+++ openlp/plugins/bibles/lib/manager.py 2016-09-09 21:59:44 +0000
1066@@ -23,8 +23,8 @@
1067 import logging
1068 import os
1069
1070-from openlp.core.common import RegistryProperties, AppLocation, Settings, translate, delete_file, UiStrings
1071-from openlp.plugins.bibles.lib import parse_reference, LanguageSelection
1072+from openlp.core.common import AppLocation, OpenLPMixin, RegistryProperties, Settings, translate, delete_file, UiStrings
1073+from openlp.plugins.bibles.lib import LanguageSelection, parse_reference
1074 from openlp.plugins.bibles.lib.db import BibleDB, BibleMeta
1075 from .importers.csvbible import CSVBible
1076 from .importers.http import HTTPBible
1077@@ -88,7 +88,7 @@
1078 ]
1079
1080
1081-class BibleManager(RegistryProperties):
1082+class BibleManager(OpenLPMixin, RegistryProperties):
1083 """
1084 The Bible manager which holds and manages all the Bibles.
1085 """
1086
1087=== modified file 'openlp/plugins/bibles/lib/upgrade.py'
1088--- openlp/plugins/bibles/lib/upgrade.py 2016-05-21 08:31:24 +0000
1089+++ openlp/plugins/bibles/lib/upgrade.py 2016-09-09 21:59:44 +0000
1090@@ -24,8 +24,6 @@
1091 """
1092 import logging
1093
1094-from sqlalchemy import delete, func, insert, select
1095-
1096 log = logging.getLogger(__name__)
1097 __version__ = 1
1098
1099@@ -35,166 +33,6 @@
1100 """
1101 Version 1 upgrade.
1102
1103- This upgrade renames a number of keys to a single naming convention.
1104+ This upgrade renamed a number of keys to a single naming convention.
1105 """
1106- metadata_table = metadata.tables['metadata']
1107- # Copy "Version" to "name" ("version" used by upgrade system)
1108- try:
1109- session.execute(insert(metadata_table).values(
1110- key='name',
1111- value=select(
1112- [metadata_table.c.value],
1113- metadata_table.c.key == 'Version'
1114- ).as_scalar()
1115- ))
1116- session.execute(delete(metadata_table).where(metadata_table.c.key == 'Version'))
1117- except:
1118- log.exception('Exception when upgrading Version')
1119- # Copy "Copyright" to "copyright"
1120- try:
1121- session.execute(insert(metadata_table).values(
1122- key='copyright',
1123- value=select(
1124- [metadata_table.c.value],
1125- metadata_table.c.key == 'Copyright'
1126- ).as_scalar()
1127- ))
1128- session.execute(delete(metadata_table).where(metadata_table.c.key == 'Copyright'))
1129- except:
1130- log.exception('Exception when upgrading Copyright')
1131- # Copy "Permissions" to "permissions"
1132- try:
1133- session.execute(insert(metadata_table).values(
1134- key='permissions',
1135- value=select(
1136- [metadata_table.c.value],
1137- metadata_table.c.key == 'Permissions'
1138- ).as_scalar()
1139- ))
1140- session.execute(delete(metadata_table).where(metadata_table.c.key == 'Permissions'))
1141- except:
1142- log.exception('Exception when upgrading Permissions')
1143- # Copy "Bookname language" to "book_name_language"
1144- try:
1145- value_count = session.execute(
1146- select(
1147- [func.count(metadata_table.c.value)],
1148- metadata_table.c.key == 'Bookname language'
1149- )
1150- ).scalar()
1151- if value_count > 0:
1152- session.execute(insert(metadata_table).values(
1153- key='book_name_language',
1154- value=select(
1155- [metadata_table.c.value],
1156- metadata_table.c.key == 'Bookname language'
1157- ).as_scalar()
1158- ))
1159- session.execute(delete(metadata_table).where(metadata_table.c.key == 'Bookname language'))
1160- except:
1161- log.exception('Exception when upgrading Bookname language')
1162- # Copy "download source" to "download_source"
1163- try:
1164- value_count = session.execute(
1165- select(
1166- [func.count(metadata_table.c.value)],
1167- metadata_table.c.key == 'download source'
1168- )
1169- ).scalar()
1170- log.debug('download source: {count}'.format(count=value_count))
1171- if value_count > 0:
1172- session.execute(insert(metadata_table).values(
1173- key='download_source',
1174- value=select(
1175- [metadata_table.c.value],
1176- metadata_table.c.key == 'download source'
1177- ).as_scalar()
1178- ))
1179- session.execute(delete(metadata_table).where(metadata_table.c.key == 'download source'))
1180- except:
1181- log.exception('Exception when upgrading download source')
1182- # Copy "download name" to "download_name"
1183- try:
1184- value_count = session.execute(
1185- select(
1186- [func.count(metadata_table.c.value)],
1187- metadata_table.c.key == 'download name'
1188- )
1189- ).scalar()
1190- log.debug('download name: {count}'.format(count=value_count))
1191- if value_count > 0:
1192- session.execute(insert(metadata_table).values(
1193- key='download_name',
1194- value=select(
1195- [metadata_table.c.value],
1196- metadata_table.c.key == 'download name'
1197- ).as_scalar()
1198- ))
1199- session.execute(delete(metadata_table).where(metadata_table.c.key == 'download name'))
1200- except:
1201- log.exception('Exception when upgrading download name')
1202- # Copy "proxy server" to "proxy_server"
1203- try:
1204- value_count = session.execute(
1205- select(
1206- [func.count(metadata_table.c.value)],
1207- metadata_table.c.key == 'proxy server'
1208- )
1209- ).scalar()
1210- log.debug('proxy server: {count}'.format(count=value_count))
1211- if value_count > 0:
1212- session.execute(insert(metadata_table).values(
1213- key='proxy_server',
1214- value=select(
1215- [metadata_table.c.value],
1216- metadata_table.c.key == 'proxy server'
1217- ).as_scalar()
1218- ))
1219- session.execute(delete(metadata_table).where(metadata_table.c.key == 'proxy server'))
1220- except:
1221- log.exception('Exception when upgrading proxy server')
1222- # Copy "proxy username" to "proxy_username"
1223- try:
1224- value_count = session.execute(
1225- select(
1226- [func.count(metadata_table.c.value)],
1227- metadata_table.c.key == 'proxy username'
1228- )
1229- ).scalar()
1230- log.debug('proxy username: {count}'.format(count=value_count))
1231- if value_count > 0:
1232- session.execute(insert(metadata_table).values(
1233- key='proxy_username',
1234- value=select(
1235- [metadata_table.c.value],
1236- metadata_table.c.key == 'proxy username'
1237- ).as_scalar()
1238- ))
1239- session.execute(delete(metadata_table).where(metadata_table.c.key == 'proxy username'))
1240- except:
1241- log.exception('Exception when upgrading proxy username')
1242- # Copy "proxy password" to "proxy_password"
1243- try:
1244- value_count = session.execute(
1245- select(
1246- [func.count(metadata_table.c.value)],
1247- metadata_table.c.key == 'proxy password'
1248- )
1249- ).scalar()
1250- log.debug('proxy password: {count}'.format(count=value_count))
1251- if value_count > 0:
1252- session.execute(insert(metadata_table).values(
1253- key='proxy_password',
1254- value=select(
1255- [metadata_table.c.value],
1256- metadata_table.c.key == 'proxy password'
1257- ).as_scalar()
1258- ))
1259- session.execute(delete(metadata_table).where(metadata_table.c.key == 'proxy password'))
1260- except:
1261- log.exception('Exception when upgrading proxy password')
1262- try:
1263- session.execute(delete(metadata_table).where(metadata_table.c.key == 'dbversion'))
1264- except:
1265- log.exception('Exception when deleting dbversion')
1266- session.commit()
1267+ log.info('No upgrades to perform')
1268
1269=== modified file 'tests/functional/openlp_core_ui/test_exceptionform.py'
1270--- tests/functional/openlp_core_ui/test_exceptionform.py 2016-06-25 14:41:06 +0000
1271+++ tests/functional/openlp_core_ui/test_exceptionform.py 2016-09-09 21:59:44 +0000
1272@@ -24,18 +24,13 @@
1273 """
1274
1275 import os
1276-import socket
1277 import tempfile
1278-import urllib
1279 from unittest import TestCase
1280 from unittest.mock import mock_open
1281
1282-from PyQt5.QtCore import QUrlQuery
1283-
1284 from openlp.core.common import Registry
1285-from openlp.core.ui.firsttimeform import FirstTimeForm
1286
1287-from tests.functional import MagicMock, patch
1288+from tests.functional import patch
1289 from tests.helpers.testmixin import TestMixin
1290
1291 from openlp.core.ui import exceptionform
1292
1293=== modified file 'tests/functional/openlp_plugins/bibles/test_bibleimport.py'
1294--- tests/functional/openlp_plugins/bibles/test_bibleimport.py 2016-08-07 11:20:53 +0000
1295+++ tests/functional/openlp_plugins/bibles/test_bibleimport.py 2016-09-09 21:59:44 +0000
1296@@ -27,9 +27,12 @@
1297 from lxml import etree, objectify
1298
1299 from unittest import TestCase
1300+from PyQt5.QtWidgets import QDialog
1301
1302 from openlp.core.common.languages import Language
1303+from openlp.core.lib.exceptions import ValidationError
1304 from openlp.plugins.bibles.lib.bibleimport import BibleImport
1305+from openlp.plugins.bibles.lib.db import BibleDB
1306 from tests.functional import MagicMock, patch
1307
1308
1309@@ -39,22 +42,103 @@
1310 """
1311
1312 def setUp(self):
1313- test_file = BytesIO(b'<?xml version="1.0" encoding="UTF-8" ?>\n'
1314- b'<root>\n'
1315- b' <data><div>Test<p>data</p><a>to</a>keep</div></data>\n'
1316- b' <data><unsupported>Test<x>data</x><y>to</y>discard</unsupported></data>\n'
1317- b'</root>')
1318- self.file_patcher = patch('builtins.open', return_value=test_file)
1319- self.log_patcher = patch('openlp.plugins.bibles.lib.bibleimport.log')
1320+ self.test_file = BytesIO(
1321+ b'<?xml version="1.0" encoding="UTF-8" ?>\n'
1322+ b'<root>\n'
1323+ b' <data><div>Test<p>data</p><a>to</a>keep</div></data>\n'
1324+ b' <data><unsupported>Test<x>data</x><y>to</y>discard</unsupported></data>\n'
1325+ b'</root>'
1326+ )
1327+ self.open_patcher = patch('builtins.open')
1328+ self.addCleanup(self.open_patcher.stop)
1329+ self.mocked_open = self.open_patcher.start()
1330+ self.critical_error_message_box_patcher = \
1331+ patch('openlp.plugins.bibles.lib.bibleimport.critical_error_message_box')
1332+ self.addCleanup(self.critical_error_message_box_patcher.stop)
1333+ self.mocked_critical_error_message_box = self.critical_error_message_box_patcher.start()
1334 self.setup_patcher = patch('openlp.plugins.bibles.lib.db.BibleDB._setup')
1335-
1336- self.addCleanup(self.file_patcher.stop)
1337- self.addCleanup(self.log_patcher.stop)
1338 self.addCleanup(self.setup_patcher.stop)
1339-
1340- self.file_patcher.start()
1341- self.mock_log = self.log_patcher.start()
1342 self.setup_patcher.start()
1343+ self.translate_patcher = patch('openlp.plugins.bibles.lib.bibleimport.translate',
1344+ side_effect=lambda module, string_to_translate, *args: string_to_translate)
1345+ self.addCleanup(self.translate_patcher.stop)
1346+ self.mocked_translate = self.translate_patcher.start()
1347+ self.registry_patcher = patch('openlp.plugins.bibles.lib.bibleimport.Registry')
1348+ self.addCleanup(self.registry_patcher.stop)
1349+ self.registry_patcher.start()
1350+
1351+ def init_kwargs_none_test(self):
1352+ """
1353+ Test the initialisation of the BibleImport Class when no key word arguments are supplied
1354+ """
1355+ # GIVEN: A patched BibleDB._setup, BibleImport class and mocked parent
1356+ # WHEN: Creating an instance of BibleImport with no key word arguments
1357+ instance = BibleImport(MagicMock())
1358+
1359+ # THEN: The filename attribute should be None
1360+ self.assertIsNone(instance.filename)
1361+ self.assertIsInstance(instance, BibleDB)
1362+
1363+ def init_kwargs_set_test(self):
1364+ """
1365+ Test the initialisation of the BibleImport Class when supplied with select keyword arguments
1366+ """
1367+ # GIVEN: A patched BibleDB._setup, BibleImport class and mocked parent
1368+ # WHEN: Creating an instance of BibleImport with selected key word arguments
1369+ kwargs = {'filename': 'bible.xml'}
1370+ instance = BibleImport(MagicMock(), **kwargs)
1371+
1372+ # THEN: The filename keyword should be set to bible.xml
1373+ self.assertEqual(instance.filename, 'bible.xml')
1374+ self.assertIsInstance(instance, BibleDB)
1375+
1376+ def get_language_canceled_test(self):
1377+ """
1378+ Test the BibleImport.get_language method when the user rejects the dialog box
1379+ """
1380+ # GIVEN: A mocked LanguageForm with an exec method which returns QtDialog.Rejected and an instance of BibleDB
1381+ with patch.object(BibleDB, '_setup'), patch('openlp.plugins.bibles.forms.LanguageForm') as mocked_language_form:
1382+
1383+ # The integer value of QtDialog.Rejected is 0. Using the enumeration causes a seg fault for some reason
1384+ mocked_language_form_instance = MagicMock(**{'exec.return_value': 0})
1385+ mocked_language_form.return_value = mocked_language_form_instance
1386+ instance = BibleImport(MagicMock())
1387+ mocked_wizard = MagicMock()
1388+ instance.wizard = mocked_wizard
1389+
1390+ # WHEN: Calling get_language()
1391+ result = instance.get_language()
1392+
1393+ # THEN: get_language() should return False
1394+ mocked_language_form.assert_called_once_with(mocked_wizard)
1395+ mocked_language_form_instance.exec.assert_called_once_with(None)
1396+ self.assertFalse(result, 'get_language() should return False if the user rejects the dialog box')
1397+
1398+ def get_language_accepted_test(self):
1399+ """
1400+ Test the BibleImport.get_language method when the user accepts the dialog box
1401+ """
1402+ # GIVEN: A mocked LanguageForm with an exec method which returns QtDialog.Accepted an instance of BibleDB and
1403+ # a combobox with the selected item data as 10
1404+ with patch.object(BibleDB, 'save_meta'), patch.object(BibleDB, '_setup'), \
1405+ patch('openlp.plugins.bibles.forms.LanguageForm') as mocked_language_form:
1406+
1407+ # The integer value of QtDialog.Accepted is 1. Using the enumeration causes a seg fault for some reason
1408+ mocked_language_form_instance = MagicMock(**{'exec.return_value': 1,
1409+ 'language_combo_box.itemData.return_value': 10})
1410+ mocked_language_form.return_value = mocked_language_form_instance
1411+ instance = BibleImport(MagicMock())
1412+ mocked_wizard = MagicMock()
1413+ instance.wizard = mocked_wizard
1414+
1415+ # WHEN: Calling get_language()
1416+ result = instance.get_language('Bible Name')
1417+
1418+ # THEN: get_language() should return the id of the selected language in the combo box
1419+ mocked_language_form.assert_called_once_with(mocked_wizard)
1420+ mocked_language_form_instance.exec.assert_called_once_with('Bible Name')
1421+ self.assertEqual(result, 10, 'get_language() should return the id of the language the user has chosen when '
1422+ 'they accept the dialog box')
1423
1424 def get_language_id_language_found_test(self):
1425 """
1426@@ -63,7 +147,7 @@
1427 # GIVEN: A mocked languages.get_language which returns language and an instance of BibleImport
1428 with patch('openlp.core.common.languages.get_language', return_value=Language(30, 'English', 'en')) \
1429 as mocked_languages_get_language, \
1430- patch('openlp.plugins.bibles.lib.db.BibleDB.get_language') as mocked_db_get_language:
1431+ patch.object(BibleImport, 'get_language') as mocked_db_get_language:
1432 instance = BibleImport(MagicMock())
1433 instance.save_meta = MagicMock()
1434
1435@@ -81,9 +165,8 @@
1436 Test get_language_id() when called with a name not found in the languages list
1437 """
1438 # GIVEN: A mocked languages.get_language which returns language and an instance of BibleImport
1439- with patch('openlp.core.common.languages.get_language', return_value=None) \
1440- as mocked_languages_get_language, \
1441- patch('openlp.plugins.bibles.lib.db.BibleDB.get_language', return_value=20) as mocked_db_get_language:
1442+ with patch('openlp.core.common.languages.get_language', return_value=None) as mocked_languages_get_language, \
1443+ patch.object(BibleImport, 'get_language', return_value=20) as mocked_db_get_language:
1444 instance = BibleImport(MagicMock())
1445 instance.save_meta = MagicMock()
1446
1447@@ -103,8 +186,8 @@
1448 # GIVEN: A mocked languages.get_language which returns None a mocked BibleDB.get_language which returns a
1449 # language id.
1450 with patch('openlp.core.common.languages.get_language', return_value=None) as mocked_languages_get_language, \
1451- patch('openlp.plugins.bibles.lib.db.BibleDB.get_language', return_value=40) as mocked_db_get_language:
1452- self.mock_log.error.reset_mock()
1453+ patch.object(BibleImport, 'get_language', return_value=40) as mocked_db_get_language, \
1454+ patch.object(BibleImport, 'log_error') as mocked_log_error:
1455 instance = BibleImport(MagicMock())
1456 instance.save_meta = MagicMock()
1457
1458@@ -114,7 +197,7 @@
1459 # THEN: The id of the language returned from BibleDB.get_language should be returned
1460 mocked_languages_get_language.assert_called_once_with('English')
1461 mocked_db_get_language.assert_called_once_with('KJV')
1462- self.assertFalse(self.mock_log.error.called)
1463+ self.assertFalse(mocked_log_error.error.called)
1464 instance.save_meta.assert_called_once_with('language_id', 40)
1465 self.assertEqual(result, 40)
1466
1467@@ -125,8 +208,8 @@
1468 # GIVEN: A mocked languages.get_language which returns None a mocked BibleDB.get_language which returns a
1469 # language id.
1470 with patch('openlp.core.common.languages.get_language', return_value=None) as mocked_languages_get_language, \
1471- patch('openlp.plugins.bibles.lib.db.BibleDB.get_language', return_value=None) as mocked_db_get_language:
1472- self.mock_log.error.reset_mock()
1473+ patch.object(BibleImport, 'get_language', return_value=None) as mocked_db_get_language, \
1474+ patch.object(BibleImport, 'log_error') as mocked_log_error:
1475 instance = BibleImport(MagicMock())
1476 instance.save_meta = MagicMock()
1477
1478@@ -136,18 +219,148 @@
1479 # THEN: None should be returned and an error should be logged
1480 mocked_languages_get_language.assert_called_once_with('Qwerty')
1481 mocked_db_get_language.assert_called_once_with('KJV')
1482- self.mock_log.error.assert_called_once_with('Language detection failed when importing from "KJV". '
1483- 'User aborted language selection.')
1484+ mocked_log_error.assert_called_once_with(
1485+ 'Language detection failed when importing from "KJV". User aborted language selection.')
1486 self.assertFalse(instance.save_meta.called)
1487 self.assertIsNone(result)
1488
1489+ def get_book_ref_id_by_name_get_book_test(self):
1490+ """
1491+ Test get_book_ref_id_by_name when the book is found as a book in BiblesResourcesDB
1492+ """
1493+ # GIVEN: An instance of BibleImport and a mocked BiblesResourcesDB which returns a book id when get_book is
1494+ # called
1495+ with patch.object(BibleImport, 'log_debug'), \
1496+ patch('openlp.plugins.bibles.lib.bibleimport.BiblesResourcesDB',
1497+ **{'get_book.return_value': {'id': 20}}):
1498+ instance = BibleImport(MagicMock())
1499+
1500+ # WHEN: Calling get_book_ref_id_by_name
1501+ result = instance.get_book_ref_id_by_name('Gen', 66, 4)
1502+
1503+ # THEN: The bible id should be returned
1504+ self.assertEqual(result, 20)
1505+
1506+ def get_book_ref_id_by_name_get_alternative_book_name_test(self):
1507+ """
1508+ Test get_book_ref_id_by_name when the book is found as an alternative book in BiblesResourcesDB
1509+ """
1510+ # GIVEN: An instance of BibleImport and a mocked BiblesResourcesDB which returns a book id when
1511+ # get_alternative_book_name is called
1512+ with patch.object(BibleImport, 'log_debug'), \
1513+ patch('openlp.plugins.bibles.lib.bibleimport.BiblesResourcesDB',
1514+ **{'get_book.return_value': None, 'get_alternative_book_name.return_value': 30}):
1515+ instance = BibleImport(MagicMock())
1516+
1517+ # WHEN: Calling get_book_ref_id_by_name
1518+ result = instance.get_book_ref_id_by_name('Gen', 66, 4)
1519+
1520+ # THEN: The bible id should be returned
1521+ self.assertEqual(result, 30)
1522+
1523+ def get_book_ref_id_by_name_get_book_reference_id_test(self):
1524+ """
1525+ Test get_book_ref_id_by_name when the book is found as a book in AlternativeBookNamesDB
1526+ """
1527+ # GIVEN: An instance of BibleImport and a mocked AlternativeBookNamesDB which returns a book id when
1528+ # get_book_reference_id is called
1529+ with patch.object(BibleImport, 'log_debug'), \
1530+ patch('openlp.plugins.bibles.lib.bibleimport.BiblesResourcesDB',
1531+ **{'get_book.return_value': None, 'get_alternative_book_name.return_value': None}), \
1532+ patch('openlp.plugins.bibles.lib.bibleimport.AlternativeBookNamesDB',
1533+ **{'get_book_reference_id.return_value': 40}):
1534+ instance = BibleImport(MagicMock())
1535+
1536+ # WHEN: Calling get_book_ref_id_by_name
1537+ result = instance.get_book_ref_id_by_name('Gen', 66, 4)
1538+
1539+ # THEN: The bible id should be returned
1540+ self.assertEqual(result, 40)
1541+
1542+ def get_book_ref_id_by_name_book_name_form_rejected_test(self):
1543+ """
1544+ Test get_book_ref_id_by_name when the user rejects the BookNameForm
1545+ """
1546+ # GIVEN: An instance of BibleImport and a mocked BookNameForm which simulates a user rejecting the dialog
1547+ with patch.object(BibleImport, 'log_debug'), patch.object(BibleImport, 'get_books'), \
1548+ patch('openlp.plugins.bibles.lib.bibleimport.BiblesResourcesDB',
1549+ **{'get_book.return_value': None, 'get_alternative_book_name.return_value': None}), \
1550+ patch('openlp.plugins.bibles.lib.bibleimport.AlternativeBookNamesDB',
1551+ **{'get_book_reference_id.return_value': None}), \
1552+ patch('openlp.plugins.bibles.forms.BookNameForm',
1553+ return_value=MagicMock(**{'exec.return_value': QDialog.Rejected})):
1554+ instance = BibleImport(MagicMock())
1555+
1556+ # WHEN: Calling get_book_ref_id_by_name
1557+ result = instance.get_book_ref_id_by_name('Gen', 66, 4)
1558+
1559+ # THEN: None should be returned
1560+ self.assertIsNone(result)
1561+
1562+ def get_book_ref_id_by_name_book_name_form_accepted_test(self):
1563+ """
1564+ Test get_book_ref_id_by_name when the user accepts the BookNameForm
1565+ """
1566+ # GIVEN: An instance of BibleImport and a mocked BookNameForm which simulates a user accepting the dialog
1567+ with patch.object(BibleImport, 'log_debug'), patch.object(BibleImport, 'get_books'), \
1568+ patch('openlp.plugins.bibles.lib.bibleimport.BiblesResourcesDB',
1569+ **{'get_book.return_value': None, 'get_alternative_book_name.return_value': None}), \
1570+ patch('openlp.plugins.bibles.lib.bibleimport.AlternativeBookNamesDB',
1571+ **{'get_book_reference_id.return_value': None}) as mocked_alternative_book_names_db, \
1572+ patch('openlp.plugins.bibles.forms.BookNameForm',
1573+ return_value=MagicMock(**{'exec.return_value': QDialog.Accepted, 'book_id': 50})):
1574+ instance = BibleImport(MagicMock())
1575+
1576+ # WHEN: Calling get_book_ref_id_by_name
1577+ result = instance.get_book_ref_id_by_name('Gen', 66, 4)
1578+
1579+ # THEN: An alternative book name should be created and a bible id should be returned
1580+ mocked_alternative_book_names_db.create_alternative_book_name.assert_called_once_with('Gen', 50, 4)
1581+ self.assertEqual(result, 50)
1582+
1583+ def is_compressed_compressed_test(self):
1584+ """
1585+ Test is_compressed when the 'file' being tested is compressed
1586+ """
1587+ # GIVEN: An instance of BibleImport and a mocked is_zipfile which returns True
1588+ with patch('openlp.plugins.bibles.lib.bibleimport.is_zipfile', return_value=True):
1589+ instance = BibleImport(MagicMock())
1590+
1591+ # WHEN: Calling is_compressed
1592+ result = instance.is_compressed('file.ext')
1593+
1594+ # THEN: Then critical_error_message_box should be called informing the user that the file is compressed and
1595+ # True should be returned
1596+ self.mocked_critical_error_message_box.assert_called_once_with(
1597+ message='The file "file.ext" you supplied is compressed. You must decompress it before import.')
1598+ self.assertTrue(result)
1599+
1600+ def is_compressed_not_compressed_test(self):
1601+ """
1602+ Test is_compressed when the 'file' being tested is not compressed
1603+ """
1604+ # GIVEN: An instance of BibleImport and a mocked is_zipfile which returns False
1605+ with patch('openlp.plugins.bibles.lib.bibleimport.is_zipfile', return_value=False):
1606+ instance = BibleImport(MagicMock())
1607+
1608+ # WHEN: Calling is_compressed
1609+ result = instance.is_compressed('file.ext')
1610+
1611+ # THEN: False should be returned and critical_error_message_box should not have been called
1612+ self.assertFalse(result)
1613+ self.assertFalse(self.mocked_critical_error_message_box.called)
1614+
1615 def parse_xml_etree_test(self):
1616 """
1617 Test BibleImport.parse_xml() when called with the use_objectify default value
1618 """
1619- # GIVEN: A sample "file" to parse
1620+ # GIVEN: A sample "file" to parse and an instance of BibleImport
1621+ self.mocked_open.return_value = self.test_file
1622+ instance = BibleImport(MagicMock())
1623+ instance.wizard = MagicMock()
1624+
1625 # WHEN: Calling parse_xml
1626- result = BibleImport.parse_xml('file.tst')
1627+ result = instance.parse_xml('file.tst')
1628
1629 # THEN: The result returned should contain the correct data, and should be an instance of eetree_Element
1630 self.assertEqual(etree.tostring(result),
1631@@ -159,9 +372,13 @@
1632 """
1633 Test BibleImport.parse_xml() when called with use_objectify set to True
1634 """
1635- # GIVEN: A sample "file" to parse
1636+ # GIVEN: A sample "file" to parse and an instance of BibleImport
1637+ self.mocked_open.return_value = self.test_file
1638+ instance = BibleImport(MagicMock())
1639+ instance.wizard = MagicMock()
1640+
1641 # WHEN: Calling parse_xml
1642- result = BibleImport.parse_xml('file.tst', use_objectify=True)
1643+ result = instance.parse_xml('file.tst', use_objectify=True)
1644
1645 # THEN: The result returned should contain the correct data, and should be an instance of ObjectifiedElement
1646 self.assertEqual(etree.tostring(result),
1647@@ -173,11 +390,14 @@
1648 """
1649 Test BibleImport.parse_xml() when given a tuple of elements to remove
1650 """
1651- # GIVEN: A tuple of elements to remove
1652+ # GIVEN: A tuple of elements to remove and an instance of BibleImport
1653+ self.mocked_open.return_value = self.test_file
1654 elements = ('unsupported', 'x', 'y')
1655+ instance = BibleImport(MagicMock())
1656+ instance.wizard = MagicMock()
1657
1658 # WHEN: Calling parse_xml, with a test file
1659- result = BibleImport.parse_xml('file.tst', elements=elements)
1660+ result = instance.parse_xml('file.tst', elements=elements)
1661
1662 # THEN: The result returned should contain the correct data
1663 self.assertEqual(etree.tostring(result),
1664@@ -187,11 +407,14 @@
1665 """
1666 Test BibleImport.parse_xml() when given a tuple of tags to remove
1667 """
1668- # GIVEN: A tuple of tags to remove
1669+ # GIVEN: A tuple of tags to remove and an instance of BibleImport
1670+ self.mocked_open.return_value = self.test_file
1671 tags = ('div', 'p', 'a')
1672+ instance = BibleImport(MagicMock())
1673+ instance.wizard = MagicMock()
1674
1675 # WHEN: Calling parse_xml, with a test file
1676- result = BibleImport.parse_xml('file.tst', tags=tags)
1677+ result = instance.parse_xml('file.tst', tags=tags)
1678
1679 # THEN: The result returned should contain the correct data
1680 self.assertEqual(etree.tostring(result), b'<root>\n <data>Testdatatokeep</data>\n <data><unsupported>Test'
1681@@ -201,12 +424,192 @@
1682 """
1683 Test BibleImport.parse_xml() when given a tuple of elements and of tags to remove
1684 """
1685- # GIVEN: A tuple of elements and of tags to remove
1686+ # GIVEN: A tuple of elements and of tags to remove and an instacne of BibleImport
1687+ self.mocked_open.return_value = self.test_file
1688 elements = ('unsupported', 'x', 'y')
1689 tags = ('div', 'p', 'a')
1690+ instance = BibleImport(MagicMock())
1691+ instance.wizard = MagicMock()
1692
1693 # WHEN: Calling parse_xml, with a test file
1694- result = BibleImport.parse_xml('file.tst', elements=elements, tags=tags)
1695+ result = instance.parse_xml('file.tst', elements=elements, tags=tags)
1696
1697 # THEN: The result returned should contain the correct data
1698 self.assertEqual(etree.tostring(result), b'<root>\n <data>Testdatatokeep</data>\n <data/>\n</root>')
1699+
1700+ def parse_xml_file_file_not_found_exception_test(self):
1701+ """
1702+ Test that parse_xml handles a FileNotFoundError exception correctly
1703+ """
1704+ with patch.object(BibleImport, 'log_exception') as mocked_log_exception:
1705+ # GIVEN: A mocked open which raises a FileNotFoundError and an instance of BibleImporter
1706+ exception = FileNotFoundError()
1707+ exception.filename = 'file.tst'
1708+ exception.strerror = 'No such file or directory'
1709+ self.mocked_open.side_effect = exception
1710+ importer = BibleImport(MagicMock(), path='.', name='.', filename='')
1711+
1712+ # WHEN: Calling parse_xml
1713+ result = importer.parse_xml('file.tst')
1714+
1715+ # THEN: parse_xml should have caught the error, informed the user and returned None
1716+ mocked_log_exception.assert_called_once_with('Opening file.tst failed.')
1717+ self.mocked_critical_error_message_box.assert_called_once_with(
1718+ title='An Error Occured When Opening A File',
1719+ message='The following error occurred when trying to open\nfile.tst:\n\nNo such file or directory')
1720+ self.assertIsNone(result)
1721+
1722+ def parse_xml_file_permission_error_exception_test(self):
1723+ """
1724+ Test that parse_xml handles a PermissionError exception correctly
1725+ """
1726+ with patch.object(BibleImport, 'log_exception') as mocked_log_exception:
1727+ # GIVEN: A mocked open which raises a PermissionError and an instance of BibleImporter
1728+ exception = PermissionError()
1729+ exception.filename = 'file.tst'
1730+ exception.strerror = 'Permission denied'
1731+ self.mocked_open.side_effect = exception
1732+ importer = BibleImport(MagicMock(), path='.', name='.', filename='')
1733+
1734+ # WHEN: Calling parse_xml
1735+ result = importer.parse_xml('file.tst')
1736+
1737+ # THEN: parse_xml should have caught the error, informed the user and returned None
1738+ mocked_log_exception.assert_called_once_with('Opening file.tst failed.')
1739+ self.mocked_critical_error_message_box.assert_called_once_with(
1740+ title='An Error Occured When Opening A File',
1741+ message='The following error occurred when trying to open\nfile.tst:\n\nPermission denied')
1742+ self.assertIsNone(result)
1743+
1744+ def set_current_chapter_test(self):
1745+ """
1746+ Test set_current_chapter
1747+ """
1748+ # GIVEN: An instance of BibleImport and a mocked wizard
1749+ importer = BibleImport(MagicMock(), path='.', name='.', filename='')
1750+ importer.wizard = MagicMock()
1751+
1752+ # WHEN: Calling set_current_chapter
1753+ importer.set_current_chapter('Book_Name', 'Chapter')
1754+
1755+ # THEN: Increment_progress_bar should have been called with a text string
1756+ importer.wizard.increment_progress_bar.assert_called_once_with('Importing Book_Name Chapter...')
1757+
1758+ def validate_xml_file_compressed_file_test(self):
1759+ """
1760+ Test that validate_xml_file raises a ValidationError when is_compressed returns True
1761+ """
1762+ # GIVEN: A mocked parse_xml which returns None
1763+ with patch.object(BibleImport, 'is_compressed', return_value=True):
1764+ importer = BibleImport(MagicMock(), path='.', name='.', filename='')
1765+
1766+ # WHEN: Calling is_compressed
1767+ # THEN: ValidationError should be raised, with the message 'Compressed file'
1768+ with self.assertRaises(ValidationError) as context:
1769+ importer.validate_xml_file('file.name', 'xbible')
1770+ self.assertEqual(context.exception.msg, 'Compressed file')
1771+
1772+ def validate_xml_file_parse_xml_fails_test(self):
1773+ """
1774+ Test that validate_xml_file raises a ValidationError when parse_xml returns None
1775+ """
1776+ # GIVEN: A mocked parse_xml which returns None
1777+ with patch.object(BibleImport, 'parse_xml', return_value=None), \
1778+ patch.object(BibleImport, 'is_compressed', return_value=False):
1779+ importer = BibleImport(MagicMock(), path='.', name='.', filename='')
1780+
1781+ # WHEN: Calling validate_xml_file
1782+ # THEN: ValidationError should be raised, with the message 'Error when opening file'
1783+ # the user that an OpenSong bible was found
1784+ with self.assertRaises(ValidationError) as context:
1785+ importer.validate_xml_file('file.name', 'xbible')
1786+ self.assertEqual(context.exception.msg, 'Error when opening file')
1787+
1788+ def validate_xml_file_success_test(self):
1789+ """
1790+ Test that validate_xml_file returns True with valid XML
1791+ """
1792+ # GIVEN: Some test data with an OpenSong Bible "bible" root tag
1793+ with patch.object(BibleImport, 'parse_xml', return_value=objectify.fromstring('<bible></bible>')), \
1794+ patch.object(BibleImport, 'is_compressed', return_value=False):
1795+ importer = BibleImport(MagicMock(), path='.', name='.', filename='')
1796+
1797+ # WHEN: Calling validate_xml_file
1798+ result = importer.validate_xml_file('file.name', 'bible')
1799+
1800+ # THEN: True should be returned
1801+ self.assertTrue(result)
1802+
1803+ def validate_xml_file_opensong_root_test(self):
1804+ """
1805+ Test that validate_xml_file raises a ValidationError with an OpenSong root tag
1806+ """
1807+ # GIVEN: Some test data with an Zefania root tag and an instance of BibleImport
1808+ with patch.object(BibleImport, 'parse_xml', return_value=objectify.fromstring('<bible></bible>')), \
1809+ patch.object(BibleImport, 'is_compressed', return_value=False):
1810+ importer = BibleImport(MagicMock(), path='.', name='.', filename='')
1811+
1812+ # WHEN: Calling validate_xml_file
1813+ # THEN: ValidationError should be raised, and the critical error message box should was called informing
1814+ # the user that an OpenSong bible was found
1815+ with self.assertRaises(ValidationError) as context:
1816+ importer.validate_xml_file('file.name', 'xbible')
1817+ self.assertEqual(context.exception.msg, 'Invalid xml.')
1818+ self.mocked_critical_error_message_box.assert_called_once_with(
1819+ message='Incorrect Bible file type supplied. This looks like an OpenSong XML bible.')
1820+
1821+ def validate_xml_file_osis_root_test(self):
1822+ """
1823+ Test that validate_xml_file raises a ValidationError with an OSIS root tag
1824+ """
1825+ # GIVEN: Some test data with an Zefania root tag and an instance of BibleImport
1826+ with patch.object(BibleImport, 'parse_xml', return_value=objectify.fromstring(
1827+ '<osis xmlns=\'http://www.bibletechnologies.net/2003/OSIS/namespace\'></osis>')), \
1828+ patch.object(BibleImport, 'is_compressed', return_value=False):
1829+ importer = BibleImport(MagicMock(), path='.', name='.', filename='')
1830+
1831+ # WHEN: Calling validate_xml_file
1832+ # THEN: ValidationError should be raised, and the critical error message box should was called informing
1833+ # the user that an OSIS bible was found
1834+ with self.assertRaises(ValidationError) as context:
1835+ importer.validate_xml_file('file.name', 'xbible')
1836+ self.assertEqual(context.exception.msg, 'Invalid xml.')
1837+ self.mocked_critical_error_message_box.assert_called_once_with(
1838+ message='Incorrect Bible file type supplied. This looks like an OSIS XML bible.')
1839+
1840+ def validate_xml_file_zefania_root_test(self):
1841+ """
1842+ Test that validate_xml_file raises a ValidationError with an Zefania root tag
1843+ """
1844+ # GIVEN: Some test data with an Zefania root tag and an instance of BibleImport
1845+ with patch.object(BibleImport, 'parse_xml', return_value=objectify.fromstring('<xmlbible></xmlbible>')), \
1846+ patch.object(BibleImport, 'is_compressed', return_value=False):
1847+ importer = BibleImport(MagicMock(), path='.', name='.', filename='')
1848+
1849+ # WHEN: Calling validate_xml_file
1850+ # THEN: ValidationError should be raised, and the critical error message box should was called informing
1851+ # the user that an Zefania bible was found
1852+ with self.assertRaises(ValidationError) as context:
1853+ importer.validate_xml_file('file.name', 'xbible')
1854+ self.assertEqual(context.exception.msg, 'Invalid xml.')
1855+ self.mocked_critical_error_message_box.assert_called_once_with(
1856+ message='Incorrect Bible file type supplied. This looks like an Zefania XML bible.')
1857+
1858+ def validate_xml_file_unknown_root_test(self):
1859+ """
1860+ Test that validate_xml_file raises a ValidationError with an unknown root tag
1861+ """
1862+ # GIVEN: Some test data with an unknown root tag and an instance of BibleImport
1863+ with patch.object(
1864+ BibleImport, 'parse_xml', return_value=objectify.fromstring('<unknownbible></unknownbible>')), \
1865+ patch.object(BibleImport, 'is_compressed', return_value=False):
1866+ importer = BibleImport(MagicMock(), path='.', name='.', filename='')
1867+
1868+ # WHEN: Calling validate_xml_file
1869+ # THEN: ValidationError should be raised, and the critical error message box should was called informing
1870+ # the user that a unknown xml bible was found
1871+ with self.assertRaises(ValidationError) as context:
1872+ importer.validate_xml_file('file.name', 'xbible')
1873+ self.assertEqual(context.exception.msg, 'Invalid xml.')
1874+ self.mocked_critical_error_message_box.assert_called_once_with(
1875+ message='Incorrect Bible file type supplied. This looks like an unknown type of XML bible.')
1876
1877=== renamed file 'tests/functional/openlp_plugins/bibles/test_http.py' => 'tests/functional/openlp_plugins/bibles/test_bibleserver.py'
1878=== modified file 'tests/functional/openlp_plugins/bibles/test_csvimport.py'
1879--- tests/functional/openlp_plugins/bibles/test_csvimport.py 2016-08-09 20:56:04 +0000
1880+++ tests/functional/openlp_plugins/bibles/test_csvimport.py 2016-09-09 21:59:44 +0000
1881@@ -46,10 +46,10 @@
1882
1883 def setUp(self):
1884 self.manager_patcher = patch('openlp.plugins.bibles.lib.db.Manager')
1885- self.registry_patcher = patch('openlp.plugins.bibles.lib.db.Registry')
1886 self.addCleanup(self.manager_patcher.stop)
1887+ self.manager_patcher.start()
1888+ self.registry_patcher = patch('openlp.plugins.bibles.lib.bibleimport.Registry')
1889 self.addCleanup(self.registry_patcher.stop)
1890- self.manager_patcher.start()
1891 self.registry_patcher.start()
1892
1893 def test_create_importer(self):
1894@@ -194,9 +194,9 @@
1895 # WHEN: Calling process_books
1896 result = importer.process_books(['Book 1'])
1897
1898- # THEN: increment_progress_bar should not be called and the return value should be None
1899+ # THEN: increment_progress_bar should not be called and the return value should be an empty dictionary
1900 self.assertFalse(importer.wizard.increment_progress_bar.called)
1901- self.assertIsNone(result)
1902+ self.assertEqual(result, {})
1903
1904 def process_books_test(self):
1905 """
1906@@ -207,7 +207,6 @@
1907 with patch('openlp.plugins.bibles.lib.db.BibleDB._setup'),\
1908 patch('openlp.plugins.bibles.lib.importers.csvbible.translate'):
1909 importer = CSVBible(mocked_manager, path='.', name='.', booksfile='books.csv', versefile='verse.csv')
1910- type(importer).application = PropertyMock()
1911 importer.find_and_create_book = MagicMock()
1912 importer.language_id = 10
1913 importer.stop_import_flag = False
1914@@ -222,7 +221,6 @@
1915 # The returned data should be a dictionary with both song's id and names.
1916 self.assertEqual(importer.find_and_create_book.mock_calls,
1917 [call('1. Mosebog', 2, 10), call('2. Mosebog', 2, 10)])
1918- importer.application.process_events.assert_called_once_with()
1919 self.assertDictEqual(result, {1: '1. Mosebog', 2: '2. Mosebog'})
1920
1921 def process_verses_stopped_import_test(self):
1922@@ -233,19 +231,16 @@
1923 mocked_manager = MagicMock()
1924 with patch('openlp.plugins.bibles.lib.db.BibleDB._setup'):
1925 importer = CSVBible(mocked_manager, path='.', name='.', booksfile='books.csv', versefile='verse.csv')
1926- type(importer).application = PropertyMock()
1927 importer.get_book_name = MagicMock()
1928 importer.session = MagicMock()
1929 importer.stop_import_flag = True
1930 importer.wizard = MagicMock()
1931
1932 # WHEN: Calling process_verses
1933- result = importer.process_verses([], [])
1934+ result = importer.process_verses(['Dummy Verse'], [])
1935
1936 # THEN: get_book_name should not be called and the return value should be None
1937 self.assertFalse(importer.get_book_name.called)
1938- importer.wizard.increment_progress_bar.assert_called_once_with('Importing verses... done.')
1939- importer.application.process_events.assert_called_once_with()
1940 self.assertIsNone(result)
1941
1942 def process_verses_successful_test(self):
1943@@ -257,7 +252,6 @@
1944 with patch('openlp.plugins.bibles.lib.db.BibleDB._setup'),\
1945 patch('openlp.plugins.bibles.lib.importers.csvbible.translate'):
1946 importer = CSVBible(mocked_manager, path='.', name='.', booksfile='books.csv', versefile='verse.csv')
1947- type(importer).application = PropertyMock()
1948 importer.create_verse = MagicMock()
1949 importer.get_book = MagicMock(return_value=Book('1', '1', '1. Mosebog', '1Mos'))
1950 importer.get_book_name = MagicMock(return_value='1. Mosebog')
1951@@ -280,7 +274,6 @@
1952 [call('1', 1, 1, 'I Begyndelsen skabte Gud Himmelen og Jorden.'),
1953 call('1', 1, 2, 'Og Jorden var øde og tom, og der var Mørke over Verdensdybet. '
1954 'Men Guds Ånd svævede over Vandene.')])
1955- importer.application.process_events.assert_called_once_with()
1956
1957 def do_import_invalid_language_id_test(self):
1958 """
1959@@ -288,73 +281,16 @@
1960 """
1961 # GIVEN: An instance of CSVBible and a mocked get_language which simulates the user cancelling the language box
1962 mocked_manager = MagicMock()
1963- with patch('openlp.plugins.bibles.lib.db.BibleDB._setup'),\
1964- patch('openlp.plugins.bibles.lib.importers.csvbible.log') as mocked_log:
1965+ with patch('openlp.plugins.bibles.lib.db.BibleDB._setup'):
1966 importer = CSVBible(mocked_manager, path='.', name='.', booksfile='books.csv', versefile='verse.csv')
1967 importer.get_language = MagicMock(return_value=None)
1968
1969 # WHEN: Calling do_import
1970 result = importer.do_import('Bible Name')
1971
1972- # THEN: The log.exception method should have been called to show that it reached the except clause.
1973- # False should be returned.
1974+ # THEN: The False should be returned.
1975 importer.get_language.assert_called_once_with('Bible Name')
1976- mocked_log.exception.assert_called_once_with('Could not import CSV bible')
1977- self.assertFalse(result)
1978-
1979- def do_import_stop_import_test(self):
1980- """
1981- Test do_import when the import is stopped
1982- """
1983- # GIVEN: An instance of CSVBible with stop_import set to True
1984- mocked_manager = MagicMock()
1985- with patch('openlp.plugins.bibles.lib.db.BibleDB._setup'),\
1986- patch('openlp.plugins.bibles.lib.importers.csvbible.log') as mocked_log:
1987- importer = CSVBible(mocked_manager, path='.', name='.', booksfile='books.csv', versefile='verse.csv')
1988- importer.get_language = MagicMock(return_value=10)
1989- importer.parse_csv_file = MagicMock(return_value=['Book 1', 'Book 2', 'Book 3'])
1990- importer.process_books = MagicMock()
1991- importer.stop_import_flag = True
1992- importer.wizard = MagicMock()
1993-
1994- # WHEN: Calling do_import
1995- result = importer.do_import('Bible Name')
1996-
1997- # THEN: log.exception should not be called, parse_csv_file should only be called once,
1998- # and False should be returned.
1999- self.assertFalse(mocked_log.exception.called)
2000- importer.parse_csv_file.assert_called_once_with('books.csv', Book)
2001- importer.process_books.assert_called_once_with(['Book 1', 'Book 2', 'Book 3'])
2002- self.assertFalse(result)
2003-
2004- def do_import_stop_import_2_test(self):
2005- """
2006- Test do_import when the import is stopped
2007- """
2008- # GIVEN: An instance of CSVBible with stop_import which is True the second time of calling
2009- mocked_manager = MagicMock()
2010- with patch('openlp.plugins.bibles.lib.db.BibleDB._setup'),\
2011- patch('openlp.plugins.bibles.lib.importers.csvbible.log') as mocked_log:
2012- CSVBible.stop_import_flag = PropertyMock(side_effect=[False, True])
2013- importer = CSVBible(mocked_manager, path='.', name='.', booksfile='books.csv', versefile='verses.csv')
2014- importer.get_language = MagicMock(return_value=10)
2015- importer.parse_csv_file = MagicMock(side_effect=[['Book 1'], ['Verse 1']])
2016- importer.process_books = MagicMock(return_value=['Book 1'])
2017- importer.process_verses = MagicMock(return_value=['Verse 1'])
2018- importer.wizard = MagicMock()
2019-
2020- # WHEN: Calling do_import
2021- result = importer.do_import('Bible Name')
2022-
2023- # THEN: log.exception should not be called, parse_csv_file should be called twice,
2024- # and False should be returned.
2025- self.assertFalse(mocked_log.exception.called)
2026- self.assertEqual(importer.parse_csv_file.mock_calls, [call('books.csv', Book), call('verses.csv', Verse)])
2027- importer.process_verses.assert_called_once_with(['Verse 1'], ['Book 1'])
2028- self.assertFalse(result)
2029-
2030- # Cleanup
2031- del CSVBible.stop_import_flag
2032+ self.assertFalse(result)
2033
2034 def do_import_success_test(self):
2035 """
2036@@ -362,8 +298,7 @@
2037 """
2038 # GIVEN: An instance of CSVBible
2039 mocked_manager = MagicMock()
2040- with patch('openlp.plugins.bibles.lib.db.BibleDB._setup'),\
2041- patch('openlp.plugins.bibles.lib.importers.csvbible.log') as mocked_log:
2042+ with patch('openlp.plugins.bibles.lib.db.BibleDB._setup'):
2043 importer = CSVBible(mocked_manager, path='.', name='.', booksfile='books.csv', versefile='verses.csv')
2044 importer.get_language = MagicMock(return_value=10)
2045 importer.parse_csv_file = MagicMock(side_effect=[['Book 1'], ['Verse 1']])
2046@@ -376,9 +311,8 @@
2047 # WHEN: Calling do_import
2048 result = importer.do_import('Bible Name')
2049
2050- # THEN: log.exception should not be called, parse_csv_file should be called twice,
2051+ # THEN: parse_csv_file should be called twice,
2052 # and True should be returned.
2053- self.assertFalse(mocked_log.exception.called)
2054 self.assertEqual(importer.parse_csv_file.mock_calls, [call('books.csv', Book), call('verses.csv', Verse)])
2055 importer.process_books.assert_called_once_with(['Book 1'])
2056 importer.process_verses.assert_called_once_with(['Verse 1'], ['Book 1'])
2057@@ -413,6 +347,6 @@
2058 # THEN: The create_verse() method should have been called with each verse in the file.
2059 self.assertTrue(importer.create_verse.called)
2060 for verse_tag, verse_text in test_data['verses']:
2061- importer.create_verse.assert_any_call(importer.get_book().id, '1', verse_tag, verse_text)
2062+ importer.create_verse.assert_any_call(importer.get_book().id, 1, verse_tag, verse_text)
2063 importer.create_book.assert_any_call('1. Mosebog', importer.get_book_ref_id_by_name(), 1)
2064 importer.create_book.assert_any_call('1. Krønikebog', importer.get_book_ref_id_by_name(), 1)
2065
2066=== modified file 'tests/functional/openlp_plugins/bibles/test_db.py'
2067--- tests/functional/openlp_plugins/bibles/test_db.py 2016-08-03 20:10:41 +0000
2068+++ tests/functional/openlp_plugins/bibles/test_db.py 2016-09-09 21:59:44 +0000
2069@@ -25,63 +25,9 @@
2070
2071 from unittest import TestCase
2072
2073-from openlp.plugins.bibles.lib.db import BibleDB
2074-from tests.functional import MagicMock, patch
2075-
2076
2077 class TestBibleDB(TestCase):
2078 """
2079 Test the functions in the BibleDB class.
2080 """
2081-
2082- def test_get_language_canceled(self):
2083- """
2084- Test the BibleDB.get_language method when the user rejects the dialog box
2085- """
2086- # GIVEN: A mocked LanguageForm with an exec method which returns QtDialog.Rejected and an instance of BibleDB
2087- with patch('openlp.plugins.bibles.lib.db.BibleDB._setup'),\
2088- patch('openlp.plugins.bibles.forms.LanguageForm') as mocked_language_form:
2089-
2090- # The integer value of QtDialog.Rejected is 0. Using the enumeration causes a seg fault for some reason
2091- mocked_language_form_instance = MagicMock(**{'exec.return_value': 0})
2092- mocked_language_form.return_value = mocked_language_form_instance
2093- mocked_parent = MagicMock()
2094- instance = BibleDB(mocked_parent)
2095- mocked_wizard = MagicMock()
2096- instance.wizard = mocked_wizard
2097-
2098- # WHEN: Calling get_language()
2099- result = instance.get_language()
2100-
2101- # THEN: get_language() should return False
2102- mocked_language_form.assert_called_once_with(mocked_wizard)
2103- mocked_language_form_instance.exec.assert_called_once_with(None)
2104- self.assertFalse(result, 'get_language() should return False if the user rejects the dialog box')
2105-
2106- def test_get_language_accepted(self):
2107- """
2108- Test the BibleDB.get_language method when the user accepts the dialog box
2109- """
2110- # GIVEN: A mocked LanguageForm with an exec method which returns QtDialog.Accepted an instance of BibleDB and
2111- # a combobox with the selected item data as 10
2112- with patch('openlp.plugins.bibles.lib.db.BibleDB._setup'), \
2113- patch('openlp.plugins.bibles.lib.db.BibleDB.save_meta'), \
2114- patch('openlp.plugins.bibles.forms.LanguageForm') as mocked_language_form:
2115-
2116- # The integer value of QtDialog.Accepted is 1. Using the enumeration causes a seg fault for some reason
2117- mocked_language_form_instance = MagicMock(**{'exec.return_value': 1,
2118- 'language_combo_box.itemData.return_value': 10})
2119- mocked_language_form.return_value = mocked_language_form_instance
2120- mocked_parent = MagicMock()
2121- instance = BibleDB(mocked_parent)
2122- mocked_wizard = MagicMock()
2123- instance.wizard = mocked_wizard
2124-
2125- # WHEN: Calling get_language()
2126- result = instance.get_language('Bible Name')
2127-
2128- # THEN: get_language() should return the id of the selected language in the combo box
2129- mocked_language_form.assert_called_once_with(mocked_wizard)
2130- mocked_language_form_instance.exec.assert_called_once_with('Bible Name')
2131- self.assertEqual(result, 10, 'get_language() should return the id of the language the user has chosen when '
2132- 'they accept the dialog box')
2133+ pass
2134
2135=== modified file 'tests/functional/openlp_plugins/bibles/test_opensongimport.py'
2136--- tests/functional/openlp_plugins/bibles/test_opensongimport.py 2016-08-09 20:45:25 +0000
2137+++ tests/functional/openlp_plugins/bibles/test_opensongimport.py 2016-09-09 21:59:44 +0000
2138@@ -23,32 +23,38 @@
2139 This module contains tests for the OpenSong Bible importer.
2140 """
2141
2142+import json
2143 import os
2144-import json
2145 from unittest import TestCase
2146
2147-from tests.functional import MagicMock, patch
2148-from openlp.plugins.bibles.lib.importers.opensong import OpenSongBible
2149-from openlp.plugins.bibles.lib.db import BibleDB
2150+from lxml import objectify
2151+
2152+from tests.functional import MagicMock, patch, call
2153+from tests.helpers.testmixin import TestMixin
2154+from openlp.core.common import Registry
2155+from openlp.plugins.bibles.lib.importers.opensong import OpenSongBible, get_text, parse_chapter_number
2156+from openlp.plugins.bibles.lib.bibleimport import BibleImport
2157
2158 TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__),
2159 '..', '..', '..', 'resources', 'bibles'))
2160
2161
2162-class TestOpenSongImport(TestCase):
2163+class TestOpenSongImport(TestCase, TestMixin):
2164 """
2165 Test the functions in the :mod:`opensongimport` module.
2166 """
2167
2168 def setUp(self):
2169- self.registry_patcher = patch('openlp.plugins.bibles.lib.db.Registry')
2170- self.registry_patcher.start()
2171+ self.find_and_create_book_patch = patch.object(BibleImport, 'find_and_create_book')
2172+ self.addCleanup(self.find_and_create_book_patch.stop)
2173+ self.mocked_find_and_create_book = self.find_and_create_book_patch.start()
2174 self.manager_patcher = patch('openlp.plugins.bibles.lib.db.Manager')
2175+ self.addCleanup(self.manager_patcher.stop)
2176 self.manager_patcher.start()
2177-
2178- def tearDown(self):
2179- self.registry_patcher.stop()
2180- self.manager_patcher.stop()
2181+ self.setup_application()
2182+ self.app.process_events = MagicMock()
2183+ Registry.create()
2184+ Registry().register('application', self.app)
2185
2186 def test_create_importer(self):
2187 """
2188@@ -61,7 +67,332 @@
2189 importer = OpenSongBible(mocked_manager, path='.', name='.', filename='')
2190
2191 # THEN: The importer should be an instance of BibleDB
2192- self.assertIsInstance(importer, BibleDB)
2193+ self.assertIsInstance(importer, BibleImport)
2194+
2195+ def get_text_no_text_test(self):
2196+ """
2197+ Test that get_text handles elements containing text in a combination of text and tail attributes
2198+ """
2199+ # GIVEN: Some test data which contains an empty element and an instance of OpenSongBible
2200+ test_data = objectify.fromstring('<element></element>')
2201+
2202+ # WHEN: Calling get_text
2203+ result = get_text(test_data)
2204+
2205+ # THEN: A blank string should be returned
2206+ self.assertEqual(result, '')
2207+
2208+ def get_text_text_test(self):
2209+ """
2210+ Test that get_text handles elements containing text in a combination of text and tail attributes
2211+ """
2212+ # GIVEN: Some test data which contains all possible permutation of text and tail text possible and an instance
2213+ # of OpenSongBible
2214+ test_data = objectify.fromstring('<element>Element text '
2215+ '<sub_text_tail>sub_text_tail text </sub_text_tail>sub_text_tail tail '
2216+ '<sub_text>sub_text text </sub_text>'
2217+ '<sub_tail></sub_tail>sub_tail tail</element>')
2218+
2219+ # WHEN: Calling get_text
2220+ result = get_text(test_data)
2221+
2222+ # THEN: The text returned should be as expected
2223+ self.assertEqual(result, 'Element text sub_text_tail text sub_text_tail tail sub_text text sub_tail tail')
2224+
2225+ def parse_chapter_number_test(self):
2226+ """
2227+ Test parse_chapter_number when supplied with chapter number and an instance of OpenSongBible
2228+ """
2229+ # GIVEN: The number 10 represented as a string
2230+ # WHEN: Calling parse_chapter_nnumber
2231+ result = parse_chapter_number('10', 0)
2232+
2233+ # THEN: The 10 should be returned as an Int
2234+ self.assertEqual(result, 10)
2235+
2236+ def parse_chapter_number_empty_attribute_test(self):
2237+ """
2238+ Testparse_chapter_number when the chapter number is an empty string. (Bug #1074727)
2239+ """
2240+ # GIVEN: An empty string, and the previous chapter number set as 12 and an instance of OpenSongBible
2241+ # WHEN: Calling parse_chapter_number
2242+ result = parse_chapter_number('', 12)
2243+
2244+ # THEN: parse_chapter_number should increment the previous verse number
2245+ self.assertEqual(result, 13)
2246+
2247+ def parse_verse_number_valid_verse_no_test(self):
2248+ """
2249+ Test parse_verse_number when supplied with a valid verse number
2250+ """
2251+ # GIVEN: An instance of OpenSongBible, the number 15 represented as a string and an instance of OpenSongBible
2252+ importer = OpenSongBible(MagicMock(), path='.', name='.', filename='')
2253+
2254+ # WHEN: Calling parse_verse_number
2255+ result = importer.parse_verse_number('15', 0)
2256+
2257+ # THEN: parse_verse_number should return the verse number
2258+ self.assertEqual(result, 15)
2259+
2260+ def parse_verse_number_verse_range_test(self):
2261+ """
2262+ Test parse_verse_number when supplied with a verse range
2263+ """
2264+ # GIVEN: An instance of OpenSongBible, and the range 24-26 represented as a string
2265+ importer = OpenSongBible(MagicMock(), path='.', name='.', filename='')
2266+
2267+ # WHEN: Calling parse_verse_number
2268+ result = importer.parse_verse_number('24-26', 0)
2269+
2270+ # THEN: parse_verse_number should return the first verse number in the range
2271+ self.assertEqual(result, 24)
2272+
2273+ def parse_verse_number_invalid_verse_no_test(self):
2274+ """
2275+ Test parse_verse_number when supplied with a invalid verse number
2276+ """
2277+ # GIVEN: An instance of OpenSongBible, a non numeric string represented as a string
2278+ importer = OpenSongBible(MagicMock(), path='.', name='.', filename='')
2279+
2280+ # WHEN: Calling parse_verse_number
2281+ result = importer.parse_verse_number('invalid', 41)
2282+
2283+ # THEN: parse_verse_number should increment the previous verse number
2284+ self.assertEqual(result, 42)
2285+
2286+ def parse_verse_number_empty_attribute_test(self):
2287+ """
2288+ Test parse_verse_number when the verse number is an empty string. (Bug #1074727)
2289+ """
2290+ # GIVEN: An instance of OpenSongBible, an empty string, and the previous verse number set as 14
2291+ importer = OpenSongBible(MagicMock(), path='.', name='.', filename='')
2292+ # WHEN: Calling parse_verse_number
2293+ result = importer.parse_verse_number('', 14)
2294+
2295+ # THEN: parse_verse_number should increment the previous verse number
2296+ self.assertEqual(result, 15)
2297+
2298+ def parse_verse_number_invalid_type_test(self):
2299+ """
2300+ Test parse_verse_number when the verse number is an invalid type)
2301+ """
2302+ with patch.object(OpenSongBible, 'log_warning')as mocked_log_warning:
2303+ # GIVEN: An instanceofOpenSongBible, a Tuple, and the previous verse number set as 12
2304+ importer = OpenSongBible(MagicMock(), path='.', name='.', filename='')
2305+
2306+ # WHEN: Calling parse_verse_number
2307+ result = importer.parse_verse_number((1, 2, 3), 12)
2308+
2309+ # THEN: parse_verse_number should log the verse number it was called with increment the previous verse
2310+ # number
2311+ mocked_log_warning.assert_called_once_with('Illegal verse number: (1, 2, 3)')
2312+ self.assertEqual(result, 13)
2313+
2314+ def process_books_stop_import_test(self):
2315+ """
2316+ Test process_books when stop_import is set to True
2317+ """
2318+ # GIVEN: An instance of OpenSongBible
2319+ importer = OpenSongBible(MagicMock(), path='.', name='.', filename='')
2320+
2321+ # WHEN: stop_import_flag is set to True
2322+ importer.stop_import_flag = True
2323+ importer.process_books(['Book'])
2324+
2325+ # THEN: find_and_create_book should not have been called
2326+ self.assertFalse(self.mocked_find_and_create_book.called)
2327+
2328+ def process_books_completes_test(self):
2329+ """
2330+ Test process_books when it processes all books
2331+ """
2332+ # GIVEN: An instance of OpenSongBible Importer and two mocked books
2333+ self.mocked_find_and_create_book.side_effect = ['db_book1', 'db_book2']
2334+ with patch.object(OpenSongBible, 'process_chapters') as mocked_process_chapters:
2335+ importer = OpenSongBible(MagicMock(), path='.', name='.', filename='')
2336+
2337+ book1 = MagicMock()
2338+ book1.attrib = {'n': 'Name1'}
2339+ book1.c = 'Chapter1'
2340+ book2 = MagicMock()
2341+ book2.attrib = {'n': 'Name2'}
2342+ book2.c = 'Chapter2'
2343+ importer.language_id = 10
2344+ importer.session = MagicMock()
2345+ importer.stop_import_flag = False
2346+
2347+ # WHEN: Calling process_books with the two books
2348+ importer.process_books([book1, book2])
2349+
2350+ # THEN: find_and_create_book and process_books should be called with the details from the mocked books
2351+ self.assertEqual(self.mocked_find_and_create_book.call_args_list,
2352+ [call('Name1', 2, 10), call('Name2', 2, 10)])
2353+ self.assertEqual(mocked_process_chapters.call_args_list,
2354+ [call('db_book1', 'Chapter1'), call('db_book2', 'Chapter2')])
2355+ self.assertEqual(importer.session.commit.call_count, 2)
2356+
2357+ def process_chapters_stop_import_test(self):
2358+ """
2359+ Test process_chapters when stop_import is set to True
2360+ """
2361+ # GIVEN: An isntance of OpenSongBible
2362+ importer = OpenSongBible(MagicMock(), path='.', name='.', filename='')
2363+ importer.parse_chapter_number = MagicMock()
2364+
2365+ # WHEN: stop_import_flag is set to True
2366+ importer.stop_import_flag = True
2367+ importer.process_chapters('Book', ['Chapter1'])
2368+
2369+ # THEN: importer.parse_chapter_number not have been called
2370+ self.assertFalse(importer.parse_chapter_number.called)
2371+
2372+ @patch('openlp.plugins.bibles.lib.importers.opensong.parse_chapter_number', **{'side_effect': [1, 2]})
2373+ def process_chapters_completes_test(self, mocked_parse_chapter_number):
2374+ """
2375+ Test process_chapters when it completes
2376+ """
2377+ # GIVEN: An instance of OpenSongBible
2378+ importer = OpenSongBible(MagicMock(), path='.', name='.', filename='')
2379+ importer.wizard = MagicMock()
2380+
2381+ # WHEN: called with some valid data
2382+ book = MagicMock()
2383+ book.name = "Book"
2384+ chapter1 = MagicMock()
2385+ chapter1.attrib = {'n': '1'}
2386+ chapter1.c = 'Chapter1'
2387+ chapter1.v = ['Chapter1 Verses']
2388+ chapter2 = MagicMock()
2389+ chapter2.attrib = {'n': '2'}
2390+ chapter2.c = 'Chapter2'
2391+ chapter2.v = ['Chapter2 Verses']
2392+
2393+ importer.process_verses = MagicMock()
2394+ importer.stop_import_flag = False
2395+ importer.process_chapters(book, [chapter1, chapter2])
2396+
2397+ # THEN: parse_chapter_number, process_verses and increment_process_bar should have been called
2398+ self.assertEqual(mocked_parse_chapter_number.call_args_list, [call('1', 0), call('2', 1)])
2399+ self.assertEqual(
2400+ importer.process_verses.call_args_list,
2401+ [call(book, 1, ['Chapter1 Verses']), call(book, 2, ['Chapter2 Verses'])])
2402+ self.assertEqual(importer.wizard.increment_progress_bar.call_args_list,
2403+ [call('Importing Book 1...'), call('Importing Book 2...')])
2404+
2405+ def process_verses_stop_import_test(self):
2406+ """
2407+ Test process_verses when stop_import is set to True
2408+ """
2409+ # GIVEN: An isntance of OpenSongBible
2410+ importer = OpenSongBible(MagicMock(), path='.', name='.', filename='')
2411+ importer.parse_verse_number = MagicMock()
2412+
2413+ # WHEN: stop_import_flag is set to True
2414+ importer.stop_import_flag = True
2415+ importer.process_verses('Book', 1, 'Verses')
2416+
2417+ # THEN: importer.parse_verse_number not have been called
2418+ self.assertFalse(importer.parse_verse_number.called)
2419+
2420+ def process_verses_completes_test(self):
2421+ """
2422+ Test process_verses when it completes
2423+ """
2424+ with patch('openlp.plugins.bibles.lib.importers.opensong.get_text',
2425+ **{'side_effect': ['Verse1 Text', 'Verse2 Text']}) as mocked_get_text, \
2426+ patch.object(OpenSongBible, 'parse_verse_number',
2427+ **{'side_effect': [1, 2]}) as mocked_parse_verse_number:
2428+ # GIVEN: An instance of OpenSongBible
2429+ importer = OpenSongBible(MagicMock(), path='.', name='.', filename='')
2430+ importer.wizard = MagicMock()
2431+
2432+ # WHEN: called with some valid data
2433+ book = MagicMock()
2434+ book.id = 1
2435+ verse1 = MagicMock()
2436+ verse1.attrib = {'n': '1'}
2437+ verse1.c = 'Chapter1'
2438+ verse1.v = ['Chapter1 Verses']
2439+ verse2 = MagicMock()
2440+ verse2.attrib = {'n': '2'}
2441+ verse2.c = 'Chapter2'
2442+ verse2.v = ['Chapter2 Verses']
2443+
2444+ importer.create_verse = MagicMock()
2445+ importer.stop_import_flag = False
2446+ importer.process_verses(book, 1, [verse1, verse2])
2447+
2448+ # THEN: parse_chapter_number, process_verses and increment_process_bar should have been called
2449+ self.assertEqual(mocked_parse_verse_number.call_args_list, [call('1', 0), call('2', 1)])
2450+ self.assertEqual(mocked_get_text.call_args_list, [call(verse1), call(verse2)])
2451+ self.assertEqual(
2452+ importer.create_verse.call_args_list,
2453+ [call(1, 1, 1, 'Verse1 Text'), call(1, 1, 2, 'Verse2 Text')])
2454+
2455+ def do_import_parse_xml_fails_test(self):
2456+ """
2457+ Test do_import when parse_xml fails (returns None)
2458+ """
2459+ # GIVEN: An instance of OpenSongBible and a mocked parse_xml which returns False
2460+ with patch.object(OpenSongBible, 'log_debug'), \
2461+ patch.object(OpenSongBible, 'validate_xml_file'), \
2462+ patch.object(OpenSongBible, 'parse_xml', return_value=None), \
2463+ patch.object(OpenSongBible, 'get_language_id') as mocked_language_id:
2464+ importer = OpenSongBible(MagicMock(), path='.', name='.', filename='')
2465+
2466+ # WHEN: Calling do_import
2467+ result = importer.do_import()
2468+
2469+ # THEN: do_import should return False and get_language_id should have not been called
2470+ self.assertFalse(result)
2471+ self.assertFalse(mocked_language_id.called)
2472+
2473+ def do_import_no_language_test(self):
2474+ """
2475+ Test do_import when the user cancels the language selection dialog
2476+ """
2477+ # GIVEN: An instance of OpenSongBible and a mocked get_language which returns False
2478+ with patch.object(OpenSongBible, 'log_debug'), \
2479+ patch.object(OpenSongBible, 'validate_xml_file'), \
2480+ patch.object(OpenSongBible, 'parse_xml'), \
2481+ patch.object(OpenSongBible, 'get_language_id', return_value=False), \
2482+ patch.object(OpenSongBible, 'process_books') as mocked_process_books:
2483+ importer = OpenSongBible(MagicMock(), path='.', name='.', filename='')
2484+
2485+ # WHEN: Calling do_import
2486+ result = importer.do_import()
2487+
2488+ # THEN: do_import should return False and process_books should have not been called
2489+ self.assertFalse(result)
2490+ self.assertFalse(mocked_process_books.called)
2491+
2492+ def do_import_completes_test(self):
2493+ """
2494+ Test do_import when it completes successfully
2495+ """
2496+ # GIVEN: An instance of OpenSongBible
2497+ with patch.object(OpenSongBible, 'log_debug'), \
2498+ patch.object(OpenSongBible, 'validate_xml_file'), \
2499+ patch.object(OpenSongBible, 'parse_xml'), \
2500+ patch.object(OpenSongBible, 'get_language_id', return_value=10), \
2501+ patch.object(OpenSongBible, 'process_books'):
2502+ importer = OpenSongBible(MagicMock(), path='.', name='.', filename='')
2503+
2504+ # WHEN: Calling do_import
2505+ result = importer.do_import()
2506+
2507+ # THEN: do_import should return True
2508+ self.assertTrue(result)
2509+
2510+
2511+class TestOpenSongImportFileImports(TestCase, TestMixin):
2512+ """
2513+ Test the functions in the :mod:`opensongimport` module.
2514+ """
2515+ def setUp(self):
2516+ self.manager_patcher = patch('openlp.plugins.bibles.lib.db.Manager')
2517+ self.addCleanup(self.manager_patcher.stop)
2518+ self.manager_patcher.start()
2519
2520 def test_file_import(self):
2521 """
2522@@ -92,22 +423,3 @@
2523 self.assertTrue(importer.create_verse.called)
2524 for verse_tag, verse_text in test_data['verses']:
2525 importer.create_verse.assert_any_call(importer.create_book().id, 1, int(verse_tag), verse_text)
2526-
2527- def test_zefania_import_error(self):
2528- """
2529- Test that we give an error message if trying to import a zefania bible
2530- """
2531- # GIVEN: A mocked out "manager" and mocked out critical_error_message_box and an import
2532- with patch('openlp.plugins.bibles.lib.importers.opensong.critical_error_message_box') as \
2533- mocked_critical_error_message_box:
2534- mocked_manager = MagicMock()
2535- importer = OpenSongBible(mocked_manager, path='.', name='.', filename='')
2536-
2537- # WHEN: An trying to import a zefania bible
2538- importer.filename = os.path.join(TEST_PATH, 'zefania-dk1933.xml')
2539- importer.do_import()
2540-
2541- # THEN: The importer should have "shown" an error message
2542- mocked_critical_error_message_box.assert_called_with(message='Incorrect Bible file type supplied. '
2543- 'This looks like a Zefania XML bible, '
2544- 'please use the Zefania import option.')
2545
2546=== modified file 'tests/functional/openlp_plugins/bibles/test_osisimport.py'
2547--- tests/functional/openlp_plugins/bibles/test_osisimport.py 2016-08-09 20:45:25 +0000
2548+++ tests/functional/openlp_plugins/bibles/test_osisimport.py 2016-09-09 21:59:44 +0000
2549@@ -27,29 +27,35 @@
2550 import json
2551 from unittest import TestCase
2552
2553-from tests.functional import MagicMock, patch
2554+from tests.functional import MagicMock, call, patch
2555+from openlp.plugins.bibles.lib.bibleimport import BibleImport
2556+from openlp.plugins.bibles.lib.db import BibleDB
2557 from openlp.plugins.bibles.lib.importers.osis import OSISBible
2558-from openlp.plugins.bibles.lib.db import BibleDB
2559
2560-TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__),
2561- '..', '..', '..', 'resources', 'bibles'))
2562+TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..', 'resources', 'bibles'))
2563
2564
2565 class TestOsisImport(TestCase):
2566 """
2567 Test the functions in the :mod:`osisimport` module.
2568 """
2569-
2570 def setUp(self):
2571- self.registry_patcher = patch('openlp.plugins.bibles.lib.db.Registry')
2572+ self.etree_patcher = patch('openlp.plugins.bibles.lib.importers.osis.etree')
2573+ self.addCleanup(self.etree_patcher.stop)
2574+ self.mocked_etree = self.etree_patcher.start()
2575+ self.create_verse_patcher = patch('openlp.plugins.bibles.lib.db.BibleDB.create_verse')
2576+ self.addCleanup(self.create_verse_patcher.stop)
2577+ self.mocked_create_verse = self.create_verse_patcher.start()
2578+ self.find_and_create_book_patch = patch.object(BibleImport, 'find_and_create_book')
2579+ self.addCleanup(self.find_and_create_book_patch.stop)
2580+ self.mocked_find_and_create_book = self.find_and_create_book_patch.start()
2581+ self.registry_patcher = patch('openlp.plugins.bibles.lib.bibleimport.Registry')
2582+ self.addCleanup(self.registry_patcher.stop)
2583 self.registry_patcher.start()
2584 self.manager_patcher = patch('openlp.plugins.bibles.lib.db.Manager')
2585+ self.addCleanup(self.manager_patcher.stop)
2586 self.manager_patcher.start()
2587
2588- def tearDown(self):
2589- self.registry_patcher.stop()
2590- self.manager_patcher.stop()
2591-
2592 def test_create_importer(self):
2593 """
2594 Test creating an instance of the OSIS file importer
2595@@ -63,6 +69,353 @@
2596 # THEN: The importer should be an instance of BibleDB
2597 self.assertIsInstance(importer, BibleDB)
2598
2599+ def process_books_stop_import_test(self):
2600+ """
2601+ Test process_books when stop_import is set to True
2602+ """
2603+ # GIVEN: An instance of OSISBible adn some mocked data
2604+ importer = OSISBible(MagicMock(), path='.', name='.', filename='')
2605+ mocked_data = MagicMock(**{'xpath.return_value': ['Book']})
2606+
2607+ # WHEN: stop_import_flag is set to True and process_books is called
2608+ importer.stop_import_flag = True
2609+ importer.process_books(mocked_data)
2610+
2611+ # THEN: find_and_create_book should not have been called
2612+ self.assertFalse(self.mocked_find_and_create_book.called)
2613+
2614+ def process_books_completes_test(self):
2615+ """
2616+ Test process_books when it processes all books
2617+ """
2618+ # GIVEN: An instance of OSISBible Importer and two mocked books
2619+ self.mocked_find_and_create_book.side_effect = ['db_book1', 'db_book2']
2620+ with patch.object(OSISBible, 'process_chapters') as mocked_process_chapters:
2621+ importer = OSISBible(MagicMock(), path='.', name='.', filename='')
2622+
2623+ book1 = MagicMock()
2624+ book1.get.return_value = 'Name1'
2625+ book2 = MagicMock()
2626+ book2.get.return_value = 'Name2'
2627+ mocked_data = MagicMock(**{'xpath.return_value': [book1, book2]})
2628+ importer.language_id = 10
2629+ importer.session = MagicMock()
2630+ importer.stop_import_flag = False
2631+
2632+ # WHEN: Calling process_books with the two books
2633+ importer.process_books(mocked_data)
2634+
2635+ # THEN: find_and_create_book and process_books should be called with the details from the mocked books
2636+ self.assertEqual(self.mocked_find_and_create_book.call_args_list,
2637+ [call('Name1', 2, 10), call('Name2', 2, 10)])
2638+ self.assertEqual(mocked_process_chapters.call_args_list,
2639+ [call('db_book1', book1), call('db_book2', book2)])
2640+ self.assertEqual(importer.session.commit.call_count, 2)
2641+
2642+ def process_chapters_verse_in_chapter_verse_text_test(self):
2643+ """
2644+ Test process_chapters when supplied with an etree element with a verse element nested in it
2645+ """
2646+ with patch('openlp.plugins.bibles.lib.importers.osis.verse_in_chapter', return_value=True), \
2647+ patch('openlp.plugins.bibles.lib.importers.osis.text_in_verse', return_value=True), \
2648+ patch.object(OSISBible, 'set_current_chapter') as mocked_set_current_chapter, \
2649+ patch.object(OSISBible, 'process_verse') as mocked_process_verse:
2650+
2651+ # GIVEN: Some test data and an instance of OSISBible
2652+ test_book = MagicMock()
2653+ test_verse = MagicMock()
2654+ test_verse.tail = '\n ' # Whitespace
2655+ test_verse.text = 'Verse Text'
2656+ test_chapter = MagicMock()
2657+ test_chapter.__iter__.return_value = [test_verse]
2658+ test_chapter.get.side_effect = lambda x: {'osisID': '1.2.4', 'sID': '999'}.get(x)
2659+ importer = OSISBible(MagicMock(), path='.', name='.', filename='')
2660+
2661+ # WHEN: Calling process_chapters
2662+ importer.process_chapters(test_book, [test_chapter])
2663+
2664+ # THEN: set_current_chapter and process_verse should have been called with the test data
2665+ mocked_set_current_chapter.assert_called_once_with(test_book.name, 2)
2666+ mocked_process_verse.assert_called_once_with(test_book, 2, test_verse)
2667+
2668+ def process_chapters_verse_in_chapter_verse_milestone_test(self):
2669+ """
2670+ Test process_chapters when supplied with an etree element with a verse element nested, when the verse system is
2671+ based on milestones
2672+ """
2673+ with patch('openlp.plugins.bibles.lib.importers.osis.verse_in_chapter', return_value=True), \
2674+ patch('openlp.plugins.bibles.lib.importers.osis.text_in_verse', return_value=False), \
2675+ patch.object(OSISBible, 'set_current_chapter') as mocked_set_current_chapter, \
2676+ patch.object(OSISBible, 'process_verse') as mocked_process_verse:
2677+
2678+ # GIVEN: Some test data and an instance of OSISBible
2679+ test_book = MagicMock()
2680+ test_verse = MagicMock()
2681+ test_verse.tail = '\n ' # Whitespace
2682+ test_verse.text = 'Verse Text'
2683+ test_chapter = MagicMock()
2684+ test_chapter.__iter__.return_value = [test_verse]
2685+ test_chapter.get.side_effect = lambda x: {'osisID': '1.2.4', 'sID': '999'}.get(x)
2686+ importer = OSISBible(MagicMock(), path='.', name='.', filename='')
2687+
2688+ # WHEN: Calling process_chapters
2689+ importer.process_chapters(test_book, [test_chapter])
2690+
2691+ # THEN: set_current_chapter and process_verse should have been called with the test data
2692+ mocked_set_current_chapter.assert_called_once_with(test_book.name, 2)
2693+ mocked_process_verse.assert_called_once_with(test_book, 2, test_verse, use_milestones=True)
2694+
2695+ def process_chapters_milestones_chapter_no_sid_test(self):
2696+ """
2697+ Test process_chapters when supplied with an etree element with a chapter and verse element in the milestone
2698+ configuration, where the chapter is the "closing" milestone. (Missing the sID attribute)
2699+ """
2700+ with patch('openlp.plugins.bibles.lib.importers.osis.verse_in_chapter', return_value=False), \
2701+ patch.object(OSISBible, 'set_current_chapter') as mocked_set_current_chapter, \
2702+ patch.object(OSISBible, 'process_verse') as mocked_process_verse:
2703+
2704+ # GIVEN: Some test data and an instance of OSISBible
2705+ test_book = MagicMock()
2706+ test_chapter = MagicMock()
2707+ test_chapter.tag = '{http://www.bibletechnologies.net/2003/OSIS/namespace}chapter'
2708+ test_chapter.get.side_effect = lambda x: {'osisID': '1.2.4'}.get(x)
2709+
2710+ # WHEN: Calling process_chapters
2711+ importer = OSISBible(MagicMock(), path='.', name='.', filename='')
2712+ importer.process_chapters(test_book, [test_chapter])
2713+
2714+ # THEN: neither set_current_chapter or process_verse should have been called
2715+ self.assertFalse(mocked_set_current_chapter.called)
2716+ self.assertFalse(mocked_process_verse.called)
2717+
2718+ def process_chapters_milestones_chapter_sid_test(self):
2719+ """
2720+ Test process_chapters when supplied with an etree element with a chapter and verse element in the milestone
2721+ configuration, where the chapter is the "opening" milestone. (Has the sID attribute)
2722+ """
2723+ with patch('openlp.plugins.bibles.lib.importers.osis.verse_in_chapter', return_value=False), \
2724+ patch.object(OSISBible, 'set_current_chapter') as mocked_set_current_chapter, \
2725+ patch.object(OSISBible, 'process_verse') as mocked_process_verse:
2726+
2727+ # GIVEN: Some test data and an instance of OSISBible
2728+ test_book = MagicMock()
2729+ test_chapter = MagicMock()
2730+ test_chapter.tag = '{http://www.bibletechnologies.net/2003/OSIS/namespace}chapter'
2731+ test_chapter.get.side_effect = lambda x: {'osisID': '1.2.4', 'sID': '999'}.get(x)
2732+ importer = OSISBible(MagicMock(), path='.', name='.', filename='')
2733+
2734+ # WHEN: Calling process_chapters
2735+ importer.process_chapters(test_book, [test_chapter])
2736+
2737+ # THEN: set_current_chapter should have been called with the test data
2738+ mocked_set_current_chapter.assert_called_once_with(test_book.name, 2)
2739+ self.assertFalse(mocked_process_verse.called)
2740+
2741+ def process_chapters_milestones_verse_tag_test(self):
2742+ """
2743+ Test process_chapters when supplied with an etree element with a chapter and verse element in the milestone
2744+ configuration, where the verse is the "opening" milestone. (Has the sID attribute)
2745+ """
2746+ with patch('openlp.plugins.bibles.lib.importers.osis.verse_in_chapter', return_value=False), \
2747+ patch.object(OSISBible, 'set_current_chapter') as mocked_set_current_chapter, \
2748+ patch.object(OSISBible, 'process_verse') as mocked_process_verse:
2749+
2750+ # GIVEN: Some test data and an instance of OSISBible
2751+ test_book = MagicMock()
2752+ test_verse = MagicMock()
2753+ test_verse.get.side_effect = lambda x: {'osisID': '1.2.4', 'sID': '999'}.get(x)
2754+ test_verse.tag = '{http://www.bibletechnologies.net/2003/OSIS/namespace}verse'
2755+ test_verse.tail = '\n ' # Whitespace
2756+ test_verse.text = 'Verse Text'
2757+
2758+ # WHEN: Calling process_chapters
2759+ importer = OSISBible(MagicMock(), path='.', name='.', filename='')
2760+ importer.process_chapters(test_book, [test_verse])
2761+
2762+ # THEN: process_verse should have been called with the test data
2763+ self.assertFalse(mocked_set_current_chapter.called)
2764+ mocked_process_verse.assert_called_once_with(test_book, 0, test_verse, use_milestones=True)
2765+
2766+ def process_verse_no_osis_id_test(self):
2767+ """
2768+ Test process_verse when the element supplied does not have and osisID attribute
2769+ """
2770+ # GIVEN: An instance of OSISBible, and some mocked test data
2771+ test_book = MagicMock()
2772+ test_verse = MagicMock()
2773+ test_verse.get.side_effect = lambda x: {}.get(x)
2774+ test_verse.tail = 'Verse Text'
2775+ test_verse.text = None
2776+ importer = OSISBible(MagicMock(), path='.', name='.', filename='')
2777+
2778+ # WHEN: Calling process_verse with the test data
2779+ importer.process_verse(test_book, 2, test_verse)
2780+
2781+ # THEN: create_verse should not have been called
2782+ self.assertFalse(self.mocked_create_verse.called)
2783+
2784+ def process_verse_use_milestones_no_s_id_test(self):
2785+ """
2786+ Test process_verse when called with use_milestones set to True, but the element supplied does not have and sID
2787+ attribute
2788+ """
2789+ # GIVEN: An instance of OSISBible, and some mocked test data
2790+ test_book = MagicMock()
2791+ test_verse = MagicMock()
2792+ test_verse.get.side_effect = lambda x: {}.get(x)
2793+ test_verse.tail = 'Verse Text'
2794+ test_verse.text = None
2795+ importer = OSISBible(MagicMock(), path='.', name='.', filename='')
2796+
2797+ # WHEN: Calling process_verse with the test data
2798+ importer.process_verse(test_book, 2, test_verse)
2799+
2800+ # THEN: create_verse should not have been called
2801+ self.assertFalse(self.mocked_create_verse.called)
2802+
2803+ def process_verse_use_milestones_no_tail_test(self):
2804+ """
2805+ Test process_verse when called with use_milestones set to True, but the element supplied does not have a 'tail'
2806+ """
2807+ # GIVEN: An instance of OSISBible, and some mocked test data
2808+ test_book = MagicMock()
2809+ test_verse = MagicMock()
2810+ test_verse.tail = None
2811+ test_verse.text = None
2812+ test_verse.get.side_effect = lambda x: {'osisID': '1.2.4', 'sID': '999'}.get(x)
2813+ importer = OSISBible(MagicMock(), path='.', name='.', filename='')
2814+
2815+ # WHEN: Calling process_verse with the test data
2816+ importer.process_verse(test_book, 2, test_verse, use_milestones=True)
2817+
2818+ # THEN: create_verse should not have been called
2819+ self.assertFalse(self.mocked_create_verse.called)
2820+
2821+ def process_verse_use_milestones_success_test(self):
2822+ """
2823+ Test process_verse when called with use_milestones set to True, and the verse element successfully imports
2824+ """
2825+ # GIVEN: An instance of OSISBible, and some mocked test data
2826+ test_book = MagicMock()
2827+ test_book.id = 1
2828+ test_verse = MagicMock()
2829+ test_verse.tail = 'Verse Text'
2830+ test_verse.text = None
2831+ test_verse.get.side_effect = lambda x: {'osisID': '1.2.4', 'sID': '999'}.get(x)
2832+ importer = OSISBible(MagicMock(), path='.', name='.', filename='')
2833+
2834+ # WHEN: Calling process_verse with the test data
2835+ importer.process_verse(test_book, 2, test_verse, use_milestones=True)
2836+
2837+ # THEN: create_verse should have been called with the test data
2838+ self.mocked_create_verse.assert_called_once_with(1, 2, 4, 'Verse Text')
2839+
2840+ def process_verse_no_text_test(self):
2841+ """
2842+ Test process_verse when called with an empty verse element
2843+ """
2844+ # GIVEN: An instance of OSISBible, and some mocked test data
2845+ test_book = MagicMock()
2846+ test_book.id = 1
2847+ test_verse = MagicMock()
2848+ test_verse.tail = '\n ' # Whitespace
2849+ test_verse.text = None
2850+ test_verse.get.side_effect = lambda x: {'osisID': '1.2.4', 'sID': '999'}.get(x)
2851+ importer = OSISBible(MagicMock(), path='.', name='.', filename='')
2852+
2853+ # WHEN: Calling process_verse with the test data
2854+ importer.process_verse(test_book, 2, test_verse)
2855+
2856+ # THEN: create_verse should not have been called
2857+ self.assertFalse(self.mocked_create_verse.called)
2858+
2859+ def process_verse_success_test(self):
2860+ """
2861+ Test process_verse when called with an element with text set
2862+ """
2863+ # GIVEN: An instance of OSISBible, and some mocked test data
2864+ test_book = MagicMock()
2865+ test_book.id = 1
2866+ test_verse = MagicMock()
2867+ test_verse.tail = '\n ' # Whitespace
2868+ test_verse.text = 'Verse Text'
2869+ test_verse.get.side_effect = lambda x: {'osisID': '1.2.4', 'sID': '999'}.get(x)
2870+ importer = OSISBible(MagicMock(), path='.', name='.', filename='')
2871+
2872+ # WHEN: Calling process_verse with the test data
2873+ importer.process_verse(test_book, 2, test_verse)
2874+
2875+ # THEN: create_verse should have been called with the test data
2876+ self.mocked_create_verse.assert_called_once_with(1, 2, 4, 'Verse Text')
2877+
2878+ def do_import_parse_xml_fails_test(self):
2879+ """
2880+ Test do_import when parse_xml fails (returns None)
2881+ """
2882+ # GIVEN: An instance of OpenSongBible and a mocked parse_xml which returns False
2883+ with patch.object(OSISBible, 'log_debug'), \
2884+ patch.object(OSISBible, 'validate_xml_file'), \
2885+ patch.object(OSISBible, 'parse_xml', return_value=None), \
2886+ patch.object(OSISBible, 'get_language_id') as mocked_language_id:
2887+ importer = OSISBible(MagicMock(), path='.', name='.', filename='')
2888+
2889+ # WHEN: Calling do_import
2890+ result = importer.do_import()
2891+
2892+ # THEN: do_import should return False and get_language_id should have not been called
2893+ self.assertFalse(result)
2894+ self.assertFalse(mocked_language_id.called)
2895+
2896+ def do_import_no_language_test(self):
2897+ """
2898+ Test do_import when the user cancels the language selection dialog
2899+ """
2900+ # GIVEN: An instance of OpenSongBible and a mocked get_language which returns False
2901+ with patch.object(OSISBible, 'log_debug'), \
2902+ patch.object(OSISBible, 'validate_xml_file'), \
2903+ patch.object(OSISBible, 'parse_xml'), \
2904+ patch.object(OSISBible, 'get_language_id', **{'return_value': False}), \
2905+ patch.object(OSISBible, 'process_books') as mocked_process_books:
2906+ importer = OSISBible(MagicMock(), path='.', name='.', filename='')
2907+
2908+ # WHEN: Calling do_import
2909+ result = importer.do_import()
2910+
2911+ # THEN: do_import should return False and process_books should have not been called
2912+ self.assertFalse(result)
2913+ self.assertFalse(mocked_process_books.called)
2914+
2915+ def do_import_completes_test(self):
2916+ """
2917+ Test do_import when it completes successfully
2918+ """
2919+ # GIVEN: An instance of OpenSongBible
2920+ with patch.object(OSISBible, 'log_debug'), \
2921+ patch.object(OSISBible, 'validate_xml_file'), \
2922+ patch.object(OSISBible, 'parse_xml'), \
2923+ patch.object(OSISBible, 'get_language_id', **{'return_value': 10}), \
2924+ patch.object(OSISBible, 'process_books'):
2925+ importer = OSISBible(MagicMock(), path='.', name='.', filename='')
2926+
2927+ # WHEN: Calling do_import
2928+ result = importer.do_import()
2929+
2930+ # THEN: do_import should return True
2931+ self.assertTrue(result)
2932+
2933+
2934+class TestOsisImportFileImports(TestCase):
2935+ """
2936+ Test the functions in the :mod:`osisimport` module.
2937+ """
2938+ def setUp(self):
2939+ self.registry_patcher = patch('openlp.plugins.bibles.lib.bibleimport.Registry')
2940+ self.addCleanup(self.registry_patcher.stop)
2941+ self.registry_patcher.start()
2942+ self.manager_patcher = patch('openlp.plugins.bibles.lib.db.Manager')
2943+ self.addCleanup(self.manager_patcher.stop)
2944+ self.manager_patcher.start()
2945+
2946 def test_file_import_nested_tags(self):
2947 """
2948 Test the actual import of OSIS Bible file, with nested chapter and verse tags
2949@@ -91,7 +444,7 @@
2950 # THEN: The create_verse() method should have been called with each verse in the file.
2951 self.assertTrue(importer.create_verse.called)
2952 for verse_tag, verse_text in test_data['verses']:
2953- importer.create_verse.assert_any_call(importer.create_book().id, '1', verse_tag, verse_text)
2954+ importer.create_verse.assert_any_call(importer.create_book().id, 1, verse_tag, verse_text)
2955
2956 def test_file_import_mixed_tags(self):
2957 """
2958@@ -121,7 +474,7 @@
2959 # THEN: The create_verse() method should have been called with each verse in the file.
2960 self.assertTrue(importer.create_verse.called)
2961 for verse_tag, verse_text in test_data['verses']:
2962- importer.create_verse.assert_any_call(importer.create_book().id, '1', verse_tag, verse_text)
2963+ importer.create_verse.assert_any_call(importer.create_book().id, 1, verse_tag, verse_text)
2964
2965 def test_file_import_milestone_tags(self):
2966 """
2967@@ -151,7 +504,7 @@
2968 # THEN: The create_verse() method should have been called with each verse in the file.
2969 self.assertTrue(importer.create_verse.called)
2970 for verse_tag, verse_text in test_data['verses']:
2971- importer.create_verse.assert_any_call(importer.create_book().id, '1', verse_tag, verse_text)
2972+ importer.create_verse.assert_any_call(importer.create_book().id, 1, verse_tag, verse_text)
2973
2974 def test_file_import_empty_verse_tags(self):
2975 """
2976@@ -181,4 +534,4 @@
2977 # THEN: The create_verse() method should have been called with each verse in the file.
2978 self.assertTrue(importer.create_verse.called)
2979 for verse_tag, verse_text in test_data['verses']:
2980- importer.create_verse.assert_any_call(importer.create_book().id, '1', verse_tag, verse_text)
2981+ importer.create_verse.assert_any_call(importer.create_book().id, 1, verse_tag, verse_text)
2982
2983=== modified file 'tests/functional/openlp_plugins/bibles/test_swordimport.py'
2984--- tests/functional/openlp_plugins/bibles/test_swordimport.py 2016-08-09 20:45:25 +0000
2985+++ tests/functional/openlp_plugins/bibles/test_swordimport.py 2016-09-09 21:59:44 +0000
2986@@ -46,7 +46,7 @@
2987 """
2988
2989 def setUp(self):
2990- self.registry_patcher = patch('openlp.plugins.bibles.lib.db.Registry')
2991+ self.registry_patcher = patch('openlp.plugins.bibles.lib.bibleimport.Registry')
2992 self.registry_patcher.start()
2993 self.manager_patcher = patch('openlp.plugins.bibles.lib.db.Manager')
2994 self.manager_patcher.start()
2995
2996=== modified file 'tests/functional/openlp_plugins/bibles/test_zefaniaimport.py'
2997--- tests/functional/openlp_plugins/bibles/test_zefaniaimport.py 2016-08-09 20:45:25 +0000
2998+++ tests/functional/openlp_plugins/bibles/test_zefaniaimport.py 2016-09-09 21:59:44 +0000
2999@@ -41,15 +41,13 @@
3000 """
3001
3002 def setUp(self):
3003- self.registry_patcher = patch('openlp.plugins.bibles.lib.db.Registry')
3004+ self.registry_patcher = patch('openlp.plugins.bibles.lib.bibleimport.Registry')
3005+ self.addCleanup(self.registry_patcher.stop)
3006 self.registry_patcher.start()
3007 self.manager_patcher = patch('openlp.plugins.bibles.lib.db.Manager')
3008+ self.addCleanup(self.manager_patcher.stop)
3009 self.manager_patcher.start()
3010
3011- def tearDown(self):
3012- self.registry_patcher.stop()
3013- self.manager_patcher.stop()
3014-
3015 def test_create_importer(self):
3016 """
3017 Test creating an instance of the Zefania file importer
3018@@ -90,7 +88,7 @@
3019 # THEN: The create_verse() method should have been called with each verse in the file.
3020 self.assertTrue(importer.create_verse.called)
3021 for verse_tag, verse_text in test_data['verses']:
3022- importer.create_verse.assert_any_call(importer.create_book().id, '1', verse_tag, verse_text)
3023+ importer.create_verse.assert_any_call(importer.create_book().id, 1, verse_tag, verse_text)
3024 importer.create_book.assert_any_call('Genesis', 1, 1)
3025
3026 def test_file_import_no_book_name(self):
3027@@ -120,5 +118,5 @@
3028 # THEN: The create_verse() method should have been called with each verse in the file.
3029 self.assertTrue(importer.create_verse.called)
3030 for verse_tag, verse_text in test_data['verses']:
3031- importer.create_verse.assert_any_call(importer.create_book().id, '1', verse_tag, verse_text)
3032+ importer.create_verse.assert_any_call(importer.create_book().id, 1, verse_tag, verse_text)
3033 importer.create_book.assert_any_call('Exodus', 2, 1)
3034
3035=== modified file 'tests/resources/bibles/dk1933.json'
3036--- tests/resources/bibles/dk1933.json 2014-08-24 14:40:45 +0000
3037+++ tests/resources/bibles/dk1933.json 2016-09-09 21:59:44 +0000
3038@@ -2,15 +2,15 @@
3039 "book": "Genesis",
3040 "chapter": 1,
3041 "verses": [
3042- [ "1", "I Begyndelsen skabte Gud Himmelen og Jorden."],
3043- [ "2", "Og Jorden var øde og tom, og der var Mørke over Verdensdybet. Men Guds Ånd svævede over Vandene." ],
3044- [ "3", "Og Gud sagde: \"Der blive Lys!\" Og der blev Lys." ],
3045- [ "4", "Og Gud så, at Lyset var godt, og Gud satte Skel mellem Lyset og Mørket," ],
3046- [ "5", "og Gud kaldte Lyset Dag, og Mørket kaldte han Nat. Og det blev Aften, og det blev Morgen, første Dag." ],
3047- [ "6", "Derpå sagde Gud: \"Der blive en Hvælving midt i Vandene til at skille Vandene ad!\"" ],
3048- [ "7", "Og således skete det: Gud gjorde Hvælvingen og skilte Vandet under Hvælvingen fra Vandet over Hvælvingen;" ],
3049- [ "8", "og Gud kaldte Hvælvingen Himmel. Og det blev Aften, og det blev Morgen, anden Dag." ],
3050- [ "9", "Derpå sagde Gud: \"Vandet under Himmelen samle sig på eet Sted, så det faste Land kommer til Syne!\" Og således skete det;" ],
3051- [ "10", "og Gud kaldte det faste Land Jord, og Stedet, hvor Vandet samlede sig, kaldte han Hav. Og Gud så, at det var godt." ]
3052+ [ 1, "I Begyndelsen skabte Gud Himmelen og Jorden."],
3053+ [ 2, "Og Jorden var øde og tom, og der var Mørke over Verdensdybet. Men Guds Ånd svævede over Vandene." ],
3054+ [ 3, "Og Gud sagde: \"Der blive Lys!\" Og der blev Lys." ],
3055+ [ 4, "Og Gud så, at Lyset var godt, og Gud satte Skel mellem Lyset og Mørket," ],
3056+ [ 5, "og Gud kaldte Lyset Dag, og Mørket kaldte han Nat. Og det blev Aften, og det blev Morgen, første Dag." ],
3057+ [ 6, "Derpå sagde Gud: \"Der blive en Hvælving midt i Vandene til at skille Vandene ad!\"" ],
3058+ [ 7, "Og således skete det: Gud gjorde Hvælvingen og skilte Vandet under Hvælvingen fra Vandet over Hvælvingen;" ],
3059+ [ 8, "og Gud kaldte Hvælvingen Himmel. Og det blev Aften, og det blev Morgen, anden Dag." ],
3060+ [ 9, "Derpå sagde Gud: \"Vandet under Himmelen samle sig på eet Sted, så det faste Land kommer til Syne!\" Og således skete det;" ],
3061+ [ 10, "og Gud kaldte det faste Land Jord, og Stedet, hvor Vandet samlede sig, kaldte han Hav. Og Gud så, at det var godt." ]
3062 ]
3063 }
3064\ No newline at end of file
3065
3066=== modified file 'tests/resources/bibles/kjv.json'
3067--- tests/resources/bibles/kjv.json 2014-08-24 14:40:45 +0000
3068+++ tests/resources/bibles/kjv.json 2016-09-09 21:59:44 +0000
3069@@ -2,15 +2,15 @@
3070 "book": "Genesis",
3071 "chapter": 1,
3072 "verses": [
3073- [ "1", "In the beginning God created the heaven and the earth."],
3074- [ "2", "And the earth was without form, and void; and darkness was upon the face of the deep. And the Spirit of God moved upon the face of the waters." ],
3075- [ "3", "And God said, Let there be light: and there was light." ],
3076- [ "4", "And God saw the light, that it was good: and God divided the light from the darkness." ],
3077- [ "5", "And God called the light Day, and the darkness he called Night. And the evening and the morning were the first day." ],
3078- [ "6", "And God said, Let there be a firmament in the midst of the waters, and let it divide the waters from the waters." ],
3079- [ "7", "And God made the firmament, and divided the waters which were under the firmament from the waters which were above the firmament: and it was so." ],
3080- [ "8", "And God called the firmament Heaven. And the evening and the morning were the second day." ],
3081- [ "9", "And God said, Let the waters under the heaven be gathered together unto one place, and let the dry land appear: and it was so." ],
3082- [ "10", "And God called the dry land Earth; and the gathering together of the waters called he Seas: and God saw that it was good." ]
3083+ [ 1, "In the beginning God created the heaven and the earth."],
3084+ [ 2, "And the earth was without form, and void; and darkness was upon the face of the deep. And the Spirit of God moved upon the face of the waters." ],
3085+ [ 3, "And God said, Let there be light: and there was light." ],
3086+ [ 4, "And God saw the light, that it was good: and God divided the light from the darkness." ],
3087+ [ 5, "And God called the light Day, and the darkness he called Night. And the evening and the morning were the first day." ],
3088+ [ 6, "And God said, Let there be a firmament in the midst of the waters, and let it divide the waters from the waters." ],
3089+ [ 7, "And God made the firmament, and divided the waters which were under the firmament from the waters which were above the firmament: and it was so." ],
3090+ [ 8, "And God called the firmament Heaven. And the evening and the morning were the second day." ],
3091+ [ 9, "And God said, Let the waters under the heaven be gathered together unto one place, and let the dry land appear: and it was so." ],
3092+ [ 10, "And God called the dry land Earth; and the gathering together of the waters called he Seas: and God saw that it was good." ]
3093 ]
3094 }
3095
3096=== modified file 'tests/resources/bibles/rst.json'
3097--- tests/resources/bibles/rst.json 2015-02-02 20:40:31 +0000
3098+++ tests/resources/bibles/rst.json 2016-09-09 21:59:44 +0000
3099@@ -2,15 +2,15 @@
3100 "book": "Exodus",
3101 "chapter": 1,
3102 "verses": [
3103- [ "1", "Вот имена сынов Израилевых, которые вошли в Египет с Иаковом, вошли каждый с домом своим:" ],
3104- [ "2", "Рувим, Симеон, Левий и Иуда," ],
3105- [ "3", "Иссахар, Завулон и Вениамин," ],
3106- [ "4", "Дан и Неффалим, Гад и Асир." ],
3107- [ "5", "Всех же душ, происшедших от чресл Иакова, было семьдесят, а Иосиф был [уже] в Египте." ],
3108- [ "6", "И умер Иосиф и все братья его и весь род их;" ],
3109- [ "7", "а сыны Израилевы расплодились и размножились, и возросли и усилились чрезвычайно, и наполнилась ими земля та." ],
3110- [ "8", "И восстал в Египте новый царь, который не знал Иосифа," ],
3111- [ "9", "и сказал народу своему: вот, народ сынов Израилевых многочислен и сильнее нас;" ],
3112- [ "10", "перехитрим же его, чтобы он не размножался; иначе, когда случится война, соединится и он с нашими неприятелями, и вооружится против нас, и выйдет из земли [нашей]." ]
3113+ [ 1, "Вот имена сынов Израилевых, которые вошли в Египет с Иаковом, вошли каждый с домом своим:" ],
3114+ [ 2, "Рувим, Симеон, Левий и Иуда," ],
3115+ [ 3, "Иссахар, Завулон и Вениамин," ],
3116+ [ 4, "Дан и Неффалим, Гад и Асир." ],
3117+ [ 5, "Всех же душ, происшедших от чресл Иакова, было семьдесят, а Иосиф был [уже] в Египте." ],
3118+ [ 6, "И умер Иосиф и все братья его и весь род их;" ],
3119+ [ 7, "а сыны Израилевы расплодились и размножились, и возросли и усилились чрезвычайно, и наполнилась ими земля та." ],
3120+ [ 8, "И восстал в Египте новый царь, который не знал Иосифа," ],
3121+ [ 9, "и сказал народу своему: вот, народ сынов Израилевых многочислен и сильнее нас;" ],
3122+ [ 10, "перехитрим же его, чтобы он не размножался; иначе, когда случится война, соединится и он с нашими неприятелями, и вооружится против нас, и выйдет из земли [нашей]." ]
3123 ]
3124 }
3125
3126=== modified file 'tests/resources/bibles/web.json'
3127--- tests/resources/bibles/web.json 2014-08-24 14:40:45 +0000
3128+++ tests/resources/bibles/web.json 2016-09-09 21:59:44 +0000
3129@@ -2,15 +2,15 @@
3130 "book": "Genesis",
3131 "chapter": "1",
3132 "verses": [
3133- [ "1", "In the beginning God created the heavens and the earth."],
3134- [ "2", "Now the earth was formless and empty. Darkness was on the surface of the deep. God’s Spirit was hovering over the surface of the waters." ],
3135- [ "3", "God said, “Let there be light,” and there was light." ],
3136- [ "4", "God saw the light, and saw that it was good. God divided the light from the darkness." ],
3137- [ "5", "God called the light “day,” and the darkness he called “night.” There was evening and there was morning, one day." ],
3138- [ "6", "God said, “Let there be an expanse in the middle of the waters, and let it divide the waters from the waters.”" ],
3139- [ "7", "God made the expanse, and divided the waters which were under the expanse from the waters which were above the expanse; and it was so." ],
3140- [ "8", "God called the expanse “sky.” There was evening and there was morning, a second day." ],
3141- [ "9", "God said, “Let the waters under the sky be gathered together to one place, and let the dry land appear;” and it was so." ],
3142- [ "10", "God called the dry land “earth,” and the gathering together of the waters he called “seas.” God saw that it was good." ]
3143+ [ 1, "In the beginning God created the heavens and the earth."],
3144+ [ 2, "Now the earth was formless and empty. Darkness was on the surface of the deep. God’s Spirit was hovering over the surface of the waters." ],
3145+ [ 3, "God said, “Let there be light,” and there was light." ],
3146+ [ 4, "God saw the light, and saw that it was good. God divided the light from the darkness." ],
3147+ [ 5, "God called the light “day,” and the darkness he called “night.” There was evening and there was morning, one day." ],
3148+ [ 6, "God said, “Let there be an expanse in the middle of the waters, and let it divide the waters from the waters.”" ],
3149+ [ 7, "God made the expanse, and divided the waters which were under the expanse from the waters which were above the expanse; and it was so." ],
3150+ [ 8, "God called the expanse “sky.” There was evening and there was morning, a second day." ],
3151+ [ 9, "God said, “Let the waters under the sky be gathered together to one place, and let the dry land appear;” and it was so." ],
3152+ [ 10, "God called the dry land “earth,” and the gathering together of the waters he called “seas.” God saw that it was good." ]
3153 ]
3154 }