Moved some import related stuff out of the db module

This commit is contained in:
Philip Ridout 2016-09-04 22:16:43 +01:00
parent 80cdc35433
commit 223b198dc8
8 changed files with 171 additions and 149 deletions

View File

@ -23,23 +23,21 @@
from lxml import etree, objectify from lxml import etree, objectify
from zipfile import is_zipfile from zipfile import is_zipfile
from openlp.core.common import OpenLPMixin, languages, translate from openlp.core.common import OpenLPMixin, Registry, RegistryProperties, languages, translate
from openlp.core.lib import ValidationError from openlp.core.lib import ValidationError
from openlp.core.lib.ui import critical_error_message_box from openlp.core.lib.ui import critical_error_message_box
from openlp.plugins.bibles.lib.db import BibleDB, BiblesResourcesDB from openlp.plugins.bibles.lib.db import AlternativeBookNamesDB, BibleDB, BiblesResourcesDB
class BibleImport(OpenLPMixin, BibleDB): class BibleImport(OpenLPMixin, RegistryProperties, BibleDB):
""" """
Helper class to import bibles from a third party source into OpenLP Helper class to import bibles from a third party source into OpenLP
""" """
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.filename = kwargs['filename'] if 'filename' in kwargs else None self.filename = kwargs['filename'] if 'filename' in kwargs else None
self.wizard = None
def set_current_chapter(self, book_name, chapter_name): Registry().register_function('openlp_stop_wizard', self.stop_import)
self.wizard.increment_progress_bar(translate('BiblesPlugin.OsisImport', 'Importing {book} {chapter}...')
.format(book=book_name, chapter=chapter_name))
@staticmethod @staticmethod
def is_compressed(file): def is_compressed(file):
@ -56,6 +54,45 @@ class BibleImport(OpenLPMixin, BibleDB):
return True return True
return False return False
def get_book_ref_id_by_name(self, book, maxbooks, language_id=None):
self.log_debug('BibleDB.get_book_ref_id_by_name:("{book}", "{lang}")'.format(book=book, lang=language_id))
book_id = None
if BiblesResourcesDB.get_book(book, True):
book_temp = BiblesResourcesDB.get_book(book, True)
book_id = book_temp['id']
elif BiblesResourcesDB.get_alternative_book_name(book):
book_id = BiblesResourcesDB.get_alternative_book_name(book)
elif AlternativeBookNamesDB.get_book_reference_id(book):
book_id = AlternativeBookNamesDB.get_book_reference_id(book)
else:
from openlp.plugins.bibles.forms import BookNameForm
book_name = BookNameForm(self.wizard)
if book_name.exec(book, self.get_books(), maxbooks):
book_id = book_name.book_id
if book_id:
AlternativeBookNamesDB.create_alternative_book_name(
book, book_id, language_id)
return book_id
def get_language(self, bible_name=None):
"""
If no language is given it calls a dialog window where the user could select the bible language.
Return the language id of a bible.
:param bible_name: The language the bible is.
"""
self.log_debug('BibleImpoer.get_language()')
from openlp.plugins.bibles.forms import LanguageForm
language_id = None
language_form = LanguageForm(self.wizard)
if language_form.exec(bible_name):
combo_box = language_form.language_combo_box
language_id = combo_box.itemData(combo_box.currentIndex())
if not language_id:
return None
self.save_meta('language_id', language_id)
return language_id
def get_language_id(self, file_language=None, bible_name=None): def get_language_id(self, file_language=None, bible_name=None):
""" """
Get the language_id for the language of the bible. Fallback to user input if we cannot do this pragmatically. Get the language_id for the language of the bible. Fallback to user input if we cannot do this pragmatically.
@ -138,6 +175,28 @@ class BibleImport(OpenLPMixin, BibleDB):
.format(file_name=e.filename, error=e.strerror)) .format(file_name=e.filename, error=e.strerror))
return None return None
def register(self, wizard):
"""
This method basically just initialises the database. It is called from the Bible Manager when a Bible is
imported. Descendant classes may want to override this method to supply their own custom
initialisation as well.
:param wizard: The actual Qt wizard form.
"""
self.wizard = wizard
return self.name
def set_current_chapter(self, book_name, chapter_name):
self.wizard.increment_progress_bar(translate('BiblesPlugin.OsisImport', 'Importing {book} {chapter}...')
.format(book=book_name, chapter=chapter_name))
def stop_import(self):
"""
Stops the import of the Bible.
"""
self.log_debug('Stopping import')
self.stop_import_flag = True
def validate_xml_file(self, filename, tag): def validate_xml_file(self, filename, tag):
""" """
Validate the supplied file Validate the supplied file

View File

@ -33,7 +33,7 @@ from sqlalchemy.exc import OperationalError
from sqlalchemy.orm import class_mapper, mapper, relation from sqlalchemy.orm import class_mapper, mapper, relation
from sqlalchemy.orm.exc import UnmappedClassError from sqlalchemy.orm.exc import UnmappedClassError
from openlp.core.common import Registry, RegistryProperties, AppLocation, translate, clean_filename from openlp.core.common import AppLocation, translate, clean_filename
from openlp.core.lib.db import BaseModel, init_db, Manager from openlp.core.lib.db import BaseModel, init_db, Manager
from openlp.core.lib.ui import critical_error_message_box from openlp.core.lib.ui import critical_error_message_box
from openlp.plugins.bibles.lib import upgrade from openlp.plugins.bibles.lib import upgrade
@ -106,7 +106,7 @@ def init_schema(url):
return session return session
class BibleDB(Manager, RegistryProperties): class BibleDB(Manager):
""" """
This class represents a database-bound Bible. It is used as a base class for all the custom importers, so that This class represents a database-bound Bible. It is used as a base class for all the custom importers, so that
the can implement their own import methods, but benefit from the database methods in here via inheritance, the can implement their own import methods, but benefit from the database methods in here via inheritance,
@ -153,15 +153,6 @@ class BibleDB(Manager, RegistryProperties):
self.get_name() self.get_name()
if 'path' in kwargs: if 'path' in kwargs:
self.path = kwargs['path'] self.path = kwargs['path']
self.wizard = None
Registry().register_function('openlp_stop_wizard', self.stop_import)
def stop_import(self):
"""
Stops the import of the Bible.
"""
log.debug('Stopping import')
self.stop_import_flag = True
def get_name(self): def get_name(self):
""" """
@ -171,17 +162,6 @@ class BibleDB(Manager, RegistryProperties):
self.name = version_name.value if version_name else None self.name = version_name.value if version_name else None
return self.name return self.name
def register(self, wizard):
"""
This method basically just initialises the database. It is called from the Bible Manager when a Bible is
imported. Descendant classes may want to override this method to supply their own custom
initialisation as well.
:param wizard: The actual Qt wizard form.
"""
self.wizard = wizard
return self.name
def create_book(self, name, bk_ref_id, testament=1): def create_book(self, name, bk_ref_id, testament=1):
""" """
Add a book to the database. Add a book to the database.
@ -306,26 +286,6 @@ class BibleDB(Manager, RegistryProperties):
log.debug('BibleDB.get_book_by_book_ref_id("{ref}")'.format(ref=ref_id)) log.debug('BibleDB.get_book_by_book_ref_id("{ref}")'.format(ref=ref_id))
return self.get_object_filtered(Book, Book.book_reference_id.like(ref_id)) return self.get_object_filtered(Book, Book.book_reference_id.like(ref_id))
def get_book_ref_id_by_name(self, book, maxbooks, language_id=None):
log.debug('BibleDB.get_book_ref_id_by_name:("{book}", "{lang}")'.format(book=book, lang=language_id))
book_id = None
if BiblesResourcesDB.get_book(book, True):
book_temp = BiblesResourcesDB.get_book(book, True)
book_id = book_temp['id']
elif BiblesResourcesDB.get_alternative_book_name(book):
book_id = BiblesResourcesDB.get_alternative_book_name(book)
elif AlternativeBookNamesDB.get_book_reference_id(book):
book_id = AlternativeBookNamesDB.get_book_reference_id(book)
else:
from openlp.plugins.bibles.forms import BookNameForm
book_name = BookNameForm(self.wizard)
if book_name.exec(book, self.get_books(), maxbooks):
book_id = book_name.book_id
if book_id:
AlternativeBookNamesDB.create_alternative_book_name(
book, book_id, language_id)
return book_id
def get_book_ref_id_by_localised_name(self, book, language_selection): def get_book_ref_id_by_localised_name(self, book, language_selection):
""" """
Return the id of a named book. Return the id of a named book.
@ -462,25 +422,6 @@ class BibleDB(Manager, RegistryProperties):
return 0 return 0
return count return count
def get_language(self, bible_name=None):
"""
If no language is given it calls a dialog window where the user could select the bible language.
Return the language id of a bible.
:param bible_name: The language the bible is.
"""
log.debug('BibleDB.get_language()')
from openlp.plugins.bibles.forms import LanguageForm
language_id = None
language_form = LanguageForm(self.wizard)
if language_form.exec(bible_name):
combo_box = language_form.language_combo_box
language_id = combo_box.itemData(combo_box.currentIndex())
if not language_id:
return None
self.save_meta('language_id', language_id)
return language_id
def dump_bible(self): def dump_bible(self):
""" """
Utility debugging method to dump the contents of a bible. Utility debugging method to dump the contents of a bible.

View File

@ -62,6 +62,9 @@ class TestBibleImport(TestCase):
side_effect=lambda module, string_to_translate, *args: string_to_translate) side_effect=lambda module, string_to_translate, *args: string_to_translate)
self.addCleanup(self.translate_patcher.stop) self.addCleanup(self.translate_patcher.stop)
self.mocked_translate = self.translate_patcher.start() self.mocked_translate = self.translate_patcher.start()
self.registry_patcher = patch('openlp.plugins.bibles.lib.bibleimport.Registry')
self.addCleanup(self.registry_patcher.stop)
self.registry_patcher.start()
def init_kwargs_none_test(self): def init_kwargs_none_test(self):
""" """
@ -88,6 +91,54 @@ class TestBibleImport(TestCase):
self.assertEqual(instance.filename, 'bible.xml') self.assertEqual(instance.filename, 'bible.xml')
self.assertIsInstance(instance, BibleDB) self.assertIsInstance(instance, BibleDB)
def get_language_canceled_test(self):
"""
Test the BibleImport.get_language method when the user rejects the dialog box
"""
# GIVEN: A mocked LanguageForm with an exec method which returns QtDialog.Rejected and an instance of BibleDB
with patch.object(BibleDB, '_setup'), patch('openlp.plugins.bibles.forms.LanguageForm') as mocked_language_form:
# The integer value of QtDialog.Rejected is 0. Using the enumeration causes a seg fault for some reason
mocked_language_form_instance = MagicMock(**{'exec.return_value': 0})
mocked_language_form.return_value = mocked_language_form_instance
instance = BibleImport(MagicMock())
mocked_wizard = MagicMock()
instance.wizard = mocked_wizard
# WHEN: Calling get_language()
result = instance.get_language()
# THEN: get_language() should return False
mocked_language_form.assert_called_once_with(mocked_wizard)
mocked_language_form_instance.exec.assert_called_once_with(None)
self.assertFalse(result, 'get_language() should return False if the user rejects the dialog box')
def get_language_accepted_test(self):
"""
Test the BibleImport.get_language method when the user accepts the dialog box
"""
# GIVEN: A mocked LanguageForm with an exec method which returns QtDialog.Accepted an instance of BibleDB and
# a combobox with the selected item data as 10
with patch.object(BibleDB, 'save_meta'), patch.object(BibleDB, '_setup'), \
patch('openlp.plugins.bibles.forms.LanguageForm') as mocked_language_form:
# The integer value of QtDialog.Accepted is 1. Using the enumeration causes a seg fault for some reason
mocked_language_form_instance = MagicMock(**{'exec.return_value': 1,
'language_combo_box.itemData.return_value': 10})
mocked_language_form.return_value = mocked_language_form_instance
instance = BibleImport(MagicMock())
mocked_wizard = MagicMock()
instance.wizard = mocked_wizard
# WHEN: Calling get_language()
result = instance.get_language('Bible Name')
# THEN: get_language() should return the id of the selected language in the combo box
mocked_language_form.assert_called_once_with(mocked_wizard)
mocked_language_form_instance.exec.assert_called_once_with('Bible Name')
self.assertEqual(result, 10, 'get_language() should return the id of the language the user has chosen when '
'they accept the dialog box')
def get_language_id_language_found_test(self): def get_language_id_language_found_test(self):
""" """
Test get_language_id() when called with a name found in the languages list Test get_language_id() when called with a name found in the languages list
@ -172,6 +223,38 @@ class TestBibleImport(TestCase):
self.assertFalse(instance.save_meta.called) self.assertFalse(instance.save_meta.called)
self.assertIsNone(result) self.assertIsNone(result)
def is_compressed_compressed_test(self):
"""
Test is_compressed when the 'file' being tested is compressed
"""
# GIVEN: An instance of BibleImport and a mocked is_zipfile which returns True
with patch('openlp.plugins.bibles.lib.bibleimport.is_zipfile', return_value=True):
instance = BibleImport(MagicMock())
# WHEN: Calling is_compressed
result = instance.is_compressed('file.ext')
# THEN: Then critical_error_message_box should be called informing the user that the file is compressed and
# True should be returned
self.mocked_critical_error_message_box.assert_called_once_with(
message='The file "file.ext" you supplied is compressed. You must decompress it before import.')
self.assertTrue(result)
def is_compressed_not_compressed_test(self):
"""
Test is_compressed when the 'file' being tested is compressed
"""
# GIVEN: An instance of BibleImport and a mocked is_zipfile which returns True
with patch('openlp.plugins.bibles.lib.bibleimport.is_zipfile', return_value=False):
instance = BibleImport(MagicMock())
# WHEN: Calling is_compressed
result = instance.is_compressed('file.ext')
# THEN: False should be returned and critical_error_message_box should not have been called
self.assertFalse(result)
self.assertFalse(self.mocked_critical_error_message_box.called)
def parse_xml_etree_test(self): def parse_xml_etree_test(self):
""" """
Test BibleImport.parse_xml() when called with the use_objectify default value Test BibleImport.parse_xml() when called with the use_objectify default value
@ -259,27 +342,6 @@ class TestBibleImport(TestCase):
# THEN: The result returned should contain the correct data # THEN: The result returned should contain the correct data
self.assertEqual(etree.tostring(result), b'<root>\n <data>Testdatatokeep</data>\n <data/>\n</root>') self.assertEqual(etree.tostring(result), b'<root>\n <data>Testdatatokeep</data>\n <data/>\n</root>')
def parse_xml_file_file_not_found_exception_test(self):
"""
Test that validate_xml_file raises a ValidationError with an OpenSong root tag
"""
# GIVEN: A mocked open which raises a FileNotFoundError and an instance of BibleImporter
exception = FileNotFoundError()
exception.filename = 'file.tst'
exception.strerror = 'No such file or directory'
self.mocked_open.side_effect = exception
importer = BibleImport(MagicMock(), path='.', name='.', filename='')
# WHEN: Calling parse_xml
result = importer.parse_xml('file.tst')
# THEN: parse_xml should have caught the error, informed the user and returned None
self.mocked_log.exception.assert_called_once_with('Opening file.tst failed.')
self.mocked_critical_error_message_box.assert_called_once_with(
title='An Error Occured When Opening A File',
message='The following error occurred when trying to open\nfile.tst:\n\nNo such file or directory')
self.assertIsNone(result)
def parse_xml_file_file_not_found_exception_test(self): def parse_xml_file_file_not_found_exception_test(self):
""" """
Test that parse_xml handles a FileNotFoundError exception correctly Test that parse_xml handles a FileNotFoundError exception correctly
@ -324,6 +386,20 @@ class TestBibleImport(TestCase):
message='The following error occurred when trying to open\nfile.tst:\n\nPermission denied') message='The following error occurred when trying to open\nfile.tst:\n\nPermission denied')
self.assertIsNone(result) self.assertIsNone(result)
def set_current_chapter_test(self):
"""
Test set_current_chapter
"""
# GIVEN: An instance of BibleImport and a mocked wizard
importer = BibleImport(MagicMock(), path='.', name='.', filename='')
importer.wizard = MagicMock()
# WHEN: Calling set_current_chapter
importer.set_current_chapter('Book_Name', 'Chapter')
# THEN: Increment_progress_bar should have been called with a text string
importer.wizard.increment_progress_bar.assert_called_once_with('Importing Book_Name Chapter...')
def validate_xml_file_compressed_file_test(self): def validate_xml_file_compressed_file_test(self):
""" """
Test that validate_xml_file raises a ValidationError when is_compressed returns True Test that validate_xml_file raises a ValidationError when is_compressed returns True

View File

@ -48,7 +48,7 @@ class TestCSVImport(TestCase):
self.manager_patcher = patch('openlp.plugins.bibles.lib.db.Manager') self.manager_patcher = patch('openlp.plugins.bibles.lib.db.Manager')
self.addCleanup(self.manager_patcher.stop) self.addCleanup(self.manager_patcher.stop)
self.manager_patcher.start() self.manager_patcher.start()
self.registry_patcher = patch('openlp.plugins.bibles.lib.db.Registry') self.registry_patcher = patch('openlp.plugins.bibles.lib.bibleimport.Registry')
self.addCleanup(self.registry_patcher.stop) self.addCleanup(self.registry_patcher.stop)
self.registry_patcher.start() self.registry_patcher.start()

View File

@ -25,63 +25,9 @@ This module contains tests for the db submodule of the Bibles plugin.
from unittest import TestCase from unittest import TestCase
from openlp.plugins.bibles.lib.db import BibleDB
from tests.functional import MagicMock, patch
class TestBibleDB(TestCase): class TestBibleDB(TestCase):
""" """
Test the functions in the BibleDB class. Test the functions in the BibleDB class.
""" """
pass
def test_get_language_canceled(self):
"""
Test the BibleDB.get_language method when the user rejects the dialog box
"""
# GIVEN: A mocked LanguageForm with an exec method which returns QtDialog.Rejected and an instance of BibleDB
with patch('openlp.plugins.bibles.lib.db.BibleDB._setup'),\
patch('openlp.plugins.bibles.forms.LanguageForm') as mocked_language_form:
# The integer value of QtDialog.Rejected is 0. Using the enumeration causes a seg fault for some reason
mocked_language_form_instance = MagicMock(**{'exec.return_value': 0})
mocked_language_form.return_value = mocked_language_form_instance
mocked_parent = MagicMock()
instance = BibleDB(mocked_parent)
mocked_wizard = MagicMock()
instance.wizard = mocked_wizard
# WHEN: Calling get_language()
result = instance.get_language()
# THEN: get_language() should return False
mocked_language_form.assert_called_once_with(mocked_wizard)
mocked_language_form_instance.exec.assert_called_once_with(None)
self.assertFalse(result, 'get_language() should return False if the user rejects the dialog box')
def test_get_language_accepted(self):
"""
Test the BibleDB.get_language method when the user accepts the dialog box
"""
# GIVEN: A mocked LanguageForm with an exec method which returns QtDialog.Accepted an instance of BibleDB and
# a combobox with the selected item data as 10
with patch('openlp.plugins.bibles.lib.db.BibleDB._setup'), \
patch('openlp.plugins.bibles.lib.db.BibleDB.save_meta'), \
patch('openlp.plugins.bibles.forms.LanguageForm') as mocked_language_form:
# The integer value of QtDialog.Accepted is 1. Using the enumeration causes a seg fault for some reason
mocked_language_form_instance = MagicMock(**{'exec.return_value': 1,
'language_combo_box.itemData.return_value': 10})
mocked_language_form.return_value = mocked_language_form_instance
mocked_parent = MagicMock()
instance = BibleDB(mocked_parent)
mocked_wizard = MagicMock()
instance.wizard = mocked_wizard
# WHEN: Calling get_language()
result = instance.get_language('Bible Name')
# THEN: get_language() should return the id of the selected language in the combo box
mocked_language_form.assert_called_once_with(mocked_wizard)
mocked_language_form_instance.exec.assert_called_once_with('Bible Name')
self.assertEqual(result, 10, 'get_language() should return the id of the language the user has chosen when '
'they accept the dialog box')

View File

@ -49,7 +49,7 @@ class TestOsisImport(TestCase):
self.find_and_create_book_patch = patch.object(BibleImport, 'find_and_create_book') self.find_and_create_book_patch = patch.object(BibleImport, 'find_and_create_book')
self.addCleanup(self.find_and_create_book_patch.stop) self.addCleanup(self.find_and_create_book_patch.stop)
self.mocked_find_and_create_book = self.find_and_create_book_patch.start() self.mocked_find_and_create_book = self.find_and_create_book_patch.start()
self.registry_patcher = patch('openlp.plugins.bibles.lib.db.Registry') self.registry_patcher = patch('openlp.plugins.bibles.lib.bibleimport.Registry')
self.addCleanup(self.registry_patcher.stop) self.addCleanup(self.registry_patcher.stop)
self.registry_patcher.start() self.registry_patcher.start()
self.manager_patcher = patch('openlp.plugins.bibles.lib.db.Manager') self.manager_patcher = patch('openlp.plugins.bibles.lib.db.Manager')
@ -409,7 +409,7 @@ class TestOsisImportFileImports(TestCase):
Test the functions in the :mod:`osisimport` module. Test the functions in the :mod:`osisimport` module.
""" """
def setUp(self): def setUp(self):
self.registry_patcher = patch('openlp.plugins.bibles.lib.db.Registry') self.registry_patcher = patch('openlp.plugins.bibles.lib.bibleimport.Registry')
self.addCleanup(self.registry_patcher.stop) self.addCleanup(self.registry_patcher.stop)
self.registry_patcher.start() self.registry_patcher.start()
self.manager_patcher = patch('openlp.plugins.bibles.lib.db.Manager') self.manager_patcher = patch('openlp.plugins.bibles.lib.db.Manager')

View File

@ -46,7 +46,7 @@ class TestSwordImport(TestCase):
""" """
def setUp(self): def setUp(self):
self.registry_patcher = patch('openlp.plugins.bibles.lib.db.Registry') self.registry_patcher = patch('openlp.plugins.bibles.lib.bibleimport.Registry')
self.registry_patcher.start() self.registry_patcher.start()
self.manager_patcher = patch('openlp.plugins.bibles.lib.db.Manager') self.manager_patcher = patch('openlp.plugins.bibles.lib.db.Manager')
self.manager_patcher.start() self.manager_patcher.start()

View File

@ -41,7 +41,7 @@ class TestZefaniaImport(TestCase):
""" """
def setUp(self): def setUp(self):
self.registry_patcher = patch('openlp.plugins.bibles.lib.db.Registry') self.registry_patcher = patch('openlp.plugins.bibles.lib.bibleimport.Registry')
self.addCleanup(self.registry_patcher.stop) self.addCleanup(self.registry_patcher.stop)
self.registry_patcher.start() self.registry_patcher.start()
self.manager_patcher = patch('openlp.plugins.bibles.lib.db.Manager') self.manager_patcher = patch('openlp.plugins.bibles.lib.db.Manager')