forked from openlp/openlp
Made the bibel and song import more robust.
Tested the importers by loading a text file, a png file and an xml file.
This commit is contained in:
parent
9dd8f51773
commit
1ebc6737d7
@ -29,7 +29,7 @@ test_script:
|
|||||||
after_test:
|
after_test:
|
||||||
# This is where we create a package using PyInstaller
|
# This is where we create a package using PyInstaller
|
||||||
# Install PyInstaller
|
# Install PyInstaller
|
||||||
- "%PYTHON%\\python.exe -m pip install pyinstaller"
|
- "%PYTHON%\\python.exe -m pip install pyinstaller==3.5"
|
||||||
# Disabled portable installers - can't figure out how to make them silent
|
# Disabled portable installers - can't figure out how to make them silent
|
||||||
# - curl -L -O http://downloads.sourceforge.net/project/portableapps/PortableApps.com%20Installer/PortableApps.comInstaller_3.4.4.paf.exe
|
# - curl -L -O http://downloads.sourceforge.net/project/portableapps/PortableApps.com%20Installer/PortableApps.comInstaller_3.4.4.paf.exe
|
||||||
# - PortableApps.comInstaller_3.4.4.paf.exe /S
|
# - PortableApps.comInstaller_3.4.4.paf.exe /S
|
||||||
|
@ -306,7 +306,7 @@ class Settings(QtCore.QSettings):
|
|||||||
'songs/db hostname': '',
|
'songs/db hostname': '',
|
||||||
'songs/db database': '',
|
'songs/db database': '',
|
||||||
'songs/last used search type': SongSearch.Entire,
|
'songs/last used search type': SongSearch.Entire,
|
||||||
'songs/last import type': None,
|
'songs/last import type': 0,
|
||||||
'songs/update service on edit': False,
|
'songs/update service on edit': False,
|
||||||
'songs/add song from service': True,
|
'songs/add song from service': True,
|
||||||
'songs/add songbook slide': False,
|
'songs/add songbook slide': False,
|
||||||
|
@ -21,10 +21,9 @@
|
|||||||
"""
|
"""
|
||||||
The :mod:`~openlp.core.ui.dark` module looks for and loads a dark theme
|
The :mod:`~openlp.core.ui.dark` module looks for and loads a dark theme
|
||||||
"""
|
"""
|
||||||
from PyQt5 import QtGui
|
from PyQt5 import QtGui, QtWidgets
|
||||||
|
|
||||||
from openlp.core.common import is_win
|
from openlp.core.common import is_win
|
||||||
from openlp.core.common.registry import Registry
|
|
||||||
from openlp.core.common.settings import Settings
|
from openlp.core.common.settings import Settings
|
||||||
|
|
||||||
|
|
||||||
@ -88,7 +87,7 @@ def get_application_stylesheet():
|
|||||||
stylesheet = qdarkstyle.load_stylesheet_pyqt5()
|
stylesheet = qdarkstyle.load_stylesheet_pyqt5()
|
||||||
else:
|
else:
|
||||||
if not Settings().value('advanced/alternate rows'):
|
if not Settings().value('advanced/alternate rows'):
|
||||||
base_color = Registry().get('application').palette().color(QtGui.QPalette.Active, QtGui.QPalette.Base)
|
base_color = QtWidgets.QApplication.palette().color(QtGui.QPalette.Active, QtGui.QPalette.Base)
|
||||||
alternate_rows_repair_stylesheet = \
|
alternate_rows_repair_stylesheet = \
|
||||||
'QTableWidget, QListWidget, QTreeWidget {alternate-background-color: ' + base_color.name() + ';}\n'
|
'QTableWidget, QListWidget, QTreeWidget {alternate-background-color: ' + base_color.name() + ';}\n'
|
||||||
stylesheet += alternate_rows_repair_stylesheet
|
stylesheet += alternate_rows_repair_stylesheet
|
||||||
|
@ -40,6 +40,7 @@ from openlp.core.common.settings import Settings
|
|||||||
from openlp.core.lib.db import delete_database
|
from openlp.core.lib.db import delete_database
|
||||||
from openlp.core.lib.exceptions import ValidationError
|
from openlp.core.lib.exceptions import ValidationError
|
||||||
from openlp.core.lib.ui import critical_error_message_box
|
from openlp.core.lib.ui import critical_error_message_box
|
||||||
|
from openlp.core.widgets.enums import PathEditType
|
||||||
from openlp.core.widgets.edits import PathEdit
|
from openlp.core.widgets.edits import PathEdit
|
||||||
from openlp.core.widgets.wizard import OpenLPWizard, WizardStrings
|
from openlp.core.widgets.wizard import OpenLPWizard, WizardStrings
|
||||||
from openlp.plugins.bibles.lib.db import clean_filename
|
from openlp.plugins.bibles.lib.db import clean_filename
|
||||||
@ -276,6 +277,7 @@ class BibleImportForm(OpenLPWizard):
|
|||||||
self.sword_folder_label.setObjectName('SwordFolderLabel')
|
self.sword_folder_label.setObjectName('SwordFolderLabel')
|
||||||
self.sword_folder_path_edit = PathEdit(
|
self.sword_folder_path_edit = PathEdit(
|
||||||
self.sword_folder_tab,
|
self.sword_folder_tab,
|
||||||
|
path_type=PathEditType.Directories,
|
||||||
default_path=Settings().value('bibles/last directory import'),
|
default_path=Settings().value('bibles/last directory import'),
|
||||||
dialog_caption=WizardStrings.OpenTypeFile.format(file_type=WizardStrings.SWORD),
|
dialog_caption=WizardStrings.OpenTypeFile.format(file_type=WizardStrings.SWORD),
|
||||||
show_revert=False,
|
show_revert=False,
|
||||||
@ -502,6 +504,11 @@ class BibleImportForm(OpenLPWizard):
|
|||||||
self.sword_folder_path_edit.setFocus()
|
self.sword_folder_path_edit.setFocus()
|
||||||
return False
|
return False
|
||||||
key = self.sword_bible_combo_box.itemData(self.sword_bible_combo_box.currentIndex())
|
key = self.sword_bible_combo_box.itemData(self.sword_bible_combo_box.currentIndex())
|
||||||
|
if not key:
|
||||||
|
critical_error_message_box(UiStrings().NFSs,
|
||||||
|
WizardStrings.YouSpecifyFolder % WizardStrings.SWORD)
|
||||||
|
self.sword_folder_path_edit.setFocus()
|
||||||
|
return False
|
||||||
if 'description' in self.pysword_folder_modules_json[key]:
|
if 'description' in self.pysword_folder_modules_json[key]:
|
||||||
self.version_name_edit.setText(self.pysword_folder_modules_json[key]['description'])
|
self.version_name_edit.setText(self.pysword_folder_modules_json[key]['description'])
|
||||||
if 'distributionlicense' in self.pysword_folder_modules_json[key]:
|
if 'distributionlicense' in self.pysword_folder_modules_json[key]:
|
||||||
|
@ -49,6 +49,7 @@ There are two acceptable formats of the verses file. They are:
|
|||||||
All CSV files are expected to use a comma (',') as the delimiter and double quotes ('"') as the quote symbol.
|
All CSV files are expected to use a comma (',') as the delimiter and double quotes ('"') as the quote symbol.
|
||||||
"""
|
"""
|
||||||
import csv
|
import csv
|
||||||
|
import logging
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
|
|
||||||
from openlp.core.common import get_file_encoding
|
from openlp.core.common import get_file_encoding
|
||||||
@ -56,6 +57,7 @@ from openlp.core.common.i18n import translate
|
|||||||
from openlp.core.lib.exceptions import ValidationError
|
from openlp.core.lib.exceptions import ValidationError
|
||||||
from openlp.plugins.bibles.lib.bibleimport import BibleImport
|
from openlp.plugins.bibles.lib.bibleimport import BibleImport
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
Book = namedtuple('Book', 'id, testament_id, name, abbreviation')
|
Book = namedtuple('Book', 'id, testament_id, name, abbreviation')
|
||||||
Verse = namedtuple('Verse', 'book_id_name, chapter_number, number, text')
|
Verse = namedtuple('Verse', 'book_id_name, chapter_number, number, text')
|
||||||
@ -105,7 +107,8 @@ class CSVBible(BibleImport):
|
|||||||
with file_path.open('r', encoding=encoding, newline='') as csv_file:
|
with file_path.open('r', encoding=encoding, newline='') as csv_file:
|
||||||
csv_reader = csv.reader(csv_file, delimiter=',', quotechar='"')
|
csv_reader = csv.reader(csv_file, delimiter=',', quotechar='"')
|
||||||
return [results_tuple(*line) for line in csv_reader]
|
return [results_tuple(*line) for line in csv_reader]
|
||||||
except (OSError, csv.Error):
|
except (OSError, csv.Error, TypeError, UnicodeDecodeError):
|
||||||
|
log.exception('Parsing {file} failed.'.format(file=file_path))
|
||||||
raise ValidationError(msg='Parsing "{file}" failed'.format(file=file_path))
|
raise ValidationError(msg='Parsing "{file}" failed'.format(file=file_path))
|
||||||
|
|
||||||
def process_books(self, books):
|
def process_books(self, books):
|
||||||
|
@ -22,10 +22,12 @@ import logging
|
|||||||
import re
|
import re
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from tempfile import TemporaryDirectory
|
from tempfile import TemporaryDirectory
|
||||||
from zipfile import ZipFile
|
from zipfile import ZipFile, BadZipFile
|
||||||
|
|
||||||
from bs4 import BeautifulSoup, NavigableString, Tag
|
from bs4 import BeautifulSoup, NavigableString, Tag
|
||||||
|
|
||||||
|
from openlp.core.common.i18n import translate
|
||||||
|
from openlp.core.lib.ui import critical_error_message_box
|
||||||
from openlp.plugins.bibles.lib.bibleimport import BibleImport
|
from openlp.plugins.bibles.lib.bibleimport import BibleImport
|
||||||
|
|
||||||
|
|
||||||
@ -50,18 +52,30 @@ class WordProjectBible(BibleImport):
|
|||||||
Unzip the file to a temporary directory
|
Unzip the file to a temporary directory
|
||||||
"""
|
"""
|
||||||
self.tmp = TemporaryDirectory()
|
self.tmp = TemporaryDirectory()
|
||||||
|
try:
|
||||||
with ZipFile(self.file_path) as zip_file:
|
with ZipFile(self.file_path) as zip_file:
|
||||||
zip_file.extractall(self.tmp.name)
|
zip_file.extractall(self.tmp.name)
|
||||||
|
except BadZipFile:
|
||||||
|
self.log_exception('Extracting {file} failed.'.format(file=self.file_path))
|
||||||
|
critical_error_message_box(message=translate('BiblesPlugin.WordProjectBible',
|
||||||
|
'Incorrect Bible file type, not a Zip file.'))
|
||||||
|
return False
|
||||||
self.base_path = Path(self.tmp.name, self.file_path.stem)
|
self.base_path = Path(self.tmp.name, self.file_path.stem)
|
||||||
|
return True
|
||||||
|
|
||||||
def process_books(self):
|
def process_books(self):
|
||||||
"""
|
"""
|
||||||
Extract and create the bible books from the parsed html
|
Extract and create the bible books from the parsed html
|
||||||
|
|
||||||
:param bible_data: parsed xml
|
:param bible_data: parsed xml
|
||||||
:return: None
|
:return: True if books was parsed, otherwise False
|
||||||
"""
|
"""
|
||||||
page = (self.base_path / 'index.htm').read_text(encoding='utf-8', errors='ignore')
|
idx_file = (self.base_path / 'index.htm')
|
||||||
|
if not idx_file.exists():
|
||||||
|
critical_error_message_box(message=translate('BiblesPlugin.WordProjectBible',
|
||||||
|
'Incorrect Bible file type, files are missing.'))
|
||||||
|
return False
|
||||||
|
page = idx_file.read_text(encoding='utf-8', errors='ignore')
|
||||||
soup = BeautifulSoup(page, 'lxml')
|
soup = BeautifulSoup(page, 'lxml')
|
||||||
bible_books = soup.find('div', 'textOptions').find_all('li')
|
bible_books = soup.find('div', 'textOptions').find_all('li')
|
||||||
book_count = len(bible_books)
|
book_count = len(bible_books)
|
||||||
@ -81,6 +95,7 @@ class WordProjectBible(BibleImport):
|
|||||||
db_book = self.find_and_create_book(book_name, book_count, self.language_id, book_id)
|
db_book = self.find_and_create_book(book_name, book_count, self.language_id, book_id)
|
||||||
self.process_chapters(db_book, book_id, book_link)
|
self.process_chapters(db_book, book_id, book_link)
|
||||||
self.session.commit()
|
self.session.commit()
|
||||||
|
return True
|
||||||
|
|
||||||
def process_chapters(self, db_book, book_id, book_link):
|
def process_chapters(self, db_book, book_id, book_link):
|
||||||
"""
|
"""
|
||||||
@ -154,11 +169,11 @@ class WordProjectBible(BibleImport):
|
|||||||
Loads a Bible from file.
|
Loads a Bible from file.
|
||||||
"""
|
"""
|
||||||
self.log_debug('Starting WordProject import from "{name}"'.format(name=self.file_path))
|
self.log_debug('Starting WordProject import from "{name}"'.format(name=self.file_path))
|
||||||
self._unzip_file()
|
if not self._unzip_file():
|
||||||
|
return False
|
||||||
self.language_id = self.get_language_id(None, bible_name=str(self.file_path))
|
self.language_id = self.get_language_id(None, bible_name=str(self.file_path))
|
||||||
result = False
|
result = False
|
||||||
if self.language_id:
|
if self.language_id:
|
||||||
self.process_books()
|
result = self.process_books()
|
||||||
result = True
|
|
||||||
self._cleanup()
|
self._cleanup()
|
||||||
return result
|
return result
|
||||||
|
@ -54,7 +54,12 @@ class ZefaniaBible(BibleImport):
|
|||||||
if not language_id:
|
if not language_id:
|
||||||
return False
|
return False
|
||||||
no_of_books = int(xmlbible.xpath('count(//BIBLEBOOK)'))
|
no_of_books = int(xmlbible.xpath('count(//BIBLEBOOK)'))
|
||||||
self.wizard.progress_bar.setMaximum(int(xmlbible.xpath('count(//CHAPTER)')))
|
no_of_chap = int(xmlbible.xpath('count(//CHAPTER)'))
|
||||||
|
if not no_of_books or not no_of_chap:
|
||||||
|
critical_error_message_box(message=translate('BiblesPlugin.ZefaniaImport',
|
||||||
|
'Incorrect Bible file type. Expected data is missing.'))
|
||||||
|
return False
|
||||||
|
self.wizard.progress_bar.setMaximum(no_of_chap)
|
||||||
for BIBLEBOOK in xmlbible:
|
for BIBLEBOOK in xmlbible:
|
||||||
if self.stop_import_flag:
|
if self.stop_import_flag:
|
||||||
break
|
break
|
||||||
|
@ -396,7 +396,10 @@ class SongImportForm(OpenLPWizard, RegistryProperties):
|
|||||||
dialog_caption = WizardStrings.OpenTypeFolder.format(folder_name=format_name)
|
dialog_caption = WizardStrings.OpenTypeFolder.format(folder_name=format_name)
|
||||||
path_edit = PathEdit(
|
path_edit = PathEdit(
|
||||||
parent=import_widget, path_type=path_type, dialog_caption=dialog_caption, show_revert=False)
|
parent=import_widget, path_type=path_type, dialog_caption=dialog_caption, show_revert=False)
|
||||||
path_edit.filters = path_edit.filters + filters
|
if path_edit.filters:
|
||||||
|
path_edit.filters = filters + ';;' + path_edit.filters
|
||||||
|
else:
|
||||||
|
path_edit.filters = filters
|
||||||
path_edit.path = Settings().value(self.plugin.settings_section + '/last directory import')
|
path_edit.path = Settings().value(self.plugin.settings_section + '/last directory import')
|
||||||
file_path_layout.addWidget(path_edit)
|
file_path_layout.addWidget(path_edit)
|
||||||
import_layout.addLayout(file_path_layout)
|
import_layout.addLayout(file_path_layout)
|
||||||
|
@ -66,11 +66,16 @@ class CCLIFileImport(SongImport):
|
|||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError:
|
||||||
details = chardet.detect(detect_content)
|
details = chardet.detect(detect_content)
|
||||||
in_file = codecs.open(file_path, 'r', details['encoding'])
|
in_file = codecs.open(file_path, 'r', details['encoding'])
|
||||||
|
try:
|
||||||
if not in_file.read(1) == '\ufeff':
|
if not in_file.read(1) == '\ufeff':
|
||||||
# not UTF or no BOM was found
|
# not UTF or no BOM was found
|
||||||
in_file.seek(0)
|
in_file.seek(0)
|
||||||
lines = in_file.readlines()
|
lines = in_file.readlines()
|
||||||
in_file.close()
|
in_file.close()
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
self.log_error(file_path, translate('SongsPlugin.CCLIFileImport',
|
||||||
|
'The file contains unreadable characters.'))
|
||||||
|
continue
|
||||||
ext = file_path.suffix.lower()
|
ext = file_path.suffix.lower()
|
||||||
if ext == '.usr' or ext == '.bin':
|
if ext == '.usr' or ext == '.bin':
|
||||||
log.info('SongSelect USR format file found: {name}'.format(name=file_path))
|
log.info('SongSelect USR format file found: {name}'.format(name=file_path))
|
||||||
|
@ -25,6 +25,7 @@ ChordPro files into the current database.
|
|||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
from openlp.core.common.i18n import translate
|
||||||
from openlp.core.common.settings import Settings
|
from openlp.core.common.settings import Settings
|
||||||
from openlp.plugins.songs.lib.importers.songimport import SongImport
|
from openlp.plugins.songs.lib.importers.songimport import SongImport
|
||||||
from openlp.plugins.songs.lib.db import AuthorType
|
from openlp.plugins.songs.lib.db import AuthorType
|
||||||
@ -60,7 +61,12 @@ class ChordProImport(SongImport):
|
|||||||
"""
|
"""
|
||||||
self.set_defaults()
|
self.set_defaults()
|
||||||
# Loop over the lines of the file
|
# Loop over the lines of the file
|
||||||
|
try:
|
||||||
file_content = song_file.read()
|
file_content = song_file.read()
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
self.log_error(song_file.name, translate('SongsPlugin.CCLIFileImport',
|
||||||
|
'The file contains unreadable characters.'))
|
||||||
|
return
|
||||||
current_verse = ''
|
current_verse = ''
|
||||||
current_verse_type = 'v'
|
current_verse_type = 'v'
|
||||||
skip_block = False
|
skip_block = False
|
||||||
@ -181,7 +187,7 @@ class ChordProImport(SongImport):
|
|||||||
current_verse = re.sub(r'\[.*?\]', '', current_verse)
|
current_verse = re.sub(r'\[.*?\]', '', current_verse)
|
||||||
self.add_verse(current_verse.rstrip(), current_verse_type)
|
self.add_verse(current_verse.rstrip(), current_verse_type)
|
||||||
# if no title was in directives, get it from the first line
|
# if no title was in directives, get it from the first line
|
||||||
if not self.title:
|
if not self.title and self.verses:
|
||||||
(verse_def, verse_text, lang) = self.verses[0]
|
(verse_def, verse_text, lang) = self.verses[0]
|
||||||
# strip any chords from the title
|
# strip any chords from the title
|
||||||
self.title = re.sub(r'\[.*?\]', '', verse_text.split('\n')[0])
|
self.title = re.sub(r'\[.*?\]', '', verse_text.split('\n')[0])
|
||||||
|
@ -87,21 +87,30 @@ class DreamBeamImport(SongImport):
|
|||||||
return
|
return
|
||||||
self.set_defaults()
|
self.set_defaults()
|
||||||
author_copyright = ''
|
author_copyright = ''
|
||||||
parser = etree.XMLParser(remove_blank_text=True)
|
parser = etree.XMLParser(remove_blank_text=True, recover=True)
|
||||||
try:
|
try:
|
||||||
with file_path.open('r') as xml_file:
|
with file_path.open('r') as xml_file:
|
||||||
parsed_file = etree.parse(xml_file, parser)
|
parsed_file = etree.parse(xml_file, parser)
|
||||||
except etree.XMLSyntaxError:
|
except etree.XMLSyntaxError:
|
||||||
log.exception('XML syntax error in file_path {name}'.format(name=file_path))
|
log.exception('XML syntax error in file {name}'.format(name=file_path))
|
||||||
self.log_error(file_path, SongStrings.XMLSyntaxError)
|
self.log_error(file_path, SongStrings.XMLSyntaxError)
|
||||||
continue
|
continue
|
||||||
xml = etree.tostring(parsed_file).decode()
|
except UnicodeDecodeError:
|
||||||
|
log.exception('Unreadable characters in {name}'.format(name=file_path))
|
||||||
|
self.log_error(file_path, SongStrings.XMLSyntaxError)
|
||||||
|
continue
|
||||||
|
file_str = etree.tostring(parsed_file)
|
||||||
|
if not file_str:
|
||||||
|
log.exception('Could not find XML in file {name}'.format(name=file_path))
|
||||||
|
self.log_error(file_path, SongStrings.XMLSyntaxError)
|
||||||
|
continue
|
||||||
|
xml = file_str.decode()
|
||||||
song_xml = objectify.fromstring(xml)
|
song_xml = objectify.fromstring(xml)
|
||||||
if song_xml.tag != 'DreamSong':
|
if song_xml.tag != 'DreamSong':
|
||||||
self.log_error(
|
self.log_error(
|
||||||
file_path,
|
file_path,
|
||||||
translate('SongsPlugin.DreamBeamImport',
|
translate('SongsPlugin.DreamBeamImport',
|
||||||
'Invalid DreamBeam song file_path. Missing DreamSong tag.'))
|
'Invalid DreamBeam song file. Missing DreamSong tag.'))
|
||||||
continue
|
continue
|
||||||
if hasattr(song_xml, 'Version'):
|
if hasattr(song_xml, 'Version'):
|
||||||
self.version = float(song_xml.Version.text)
|
self.version = float(song_xml.Version.text)
|
||||||
|
@ -25,9 +25,10 @@ import re
|
|||||||
from lxml import etree, objectify
|
from lxml import etree, objectify
|
||||||
|
|
||||||
from openlp.core.common import normalize_str
|
from openlp.core.common import normalize_str
|
||||||
|
from openlp.core.common.i18n import translate
|
||||||
from openlp.plugins.songs.lib import VerseType
|
from openlp.plugins.songs.lib import VerseType
|
||||||
from openlp.plugins.songs.lib.importers.songimport import SongImport
|
from openlp.plugins.songs.lib.importers.songimport import SongImport
|
||||||
|
from openlp.plugins.songs.lib.ui import SongStrings
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -48,10 +49,32 @@ class EasySlidesImport(SongImport):
|
|||||||
def do_import(self):
|
def do_import(self):
|
||||||
log.info('Importing EasySlides XML file {source}'.format(source=self.import_source))
|
log.info('Importing EasySlides XML file {source}'.format(source=self.import_source))
|
||||||
parser = etree.XMLParser(remove_blank_text=True, recover=True)
|
parser = etree.XMLParser(remove_blank_text=True, recover=True)
|
||||||
|
try:
|
||||||
|
with self.import_source.open('r') as xml_file:
|
||||||
parsed_file = etree.parse(str(self.import_source), parser)
|
parsed_file = etree.parse(str(self.import_source), parser)
|
||||||
xml = etree.tostring(parsed_file).decode()
|
except etree.XMLSyntaxError:
|
||||||
|
log.exception('XML syntax error in file {name}'.format(name=xml_file))
|
||||||
|
self.log_error(self.import_source, SongStrings.XMLSyntaxError)
|
||||||
|
return
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
log.exception('Unreadable characters in {name}'.format(name=xml_file))
|
||||||
|
self.log_error(self.import_source, SongStrings.XMLSyntaxError)
|
||||||
|
return
|
||||||
|
file_str = etree.tostring(parsed_file)
|
||||||
|
if not file_str:
|
||||||
|
log.exception('Could not find XML in file {name}'.format(name=xml_file))
|
||||||
|
self.log_error(self.import_source, SongStrings.XMLSyntaxError)
|
||||||
|
return
|
||||||
|
xml = file_str.decode()
|
||||||
song_xml = objectify.fromstring(xml)
|
song_xml = objectify.fromstring(xml)
|
||||||
self.import_wizard.progress_bar.setMaximum(len(song_xml.Item))
|
try:
|
||||||
|
item_count = len(song_xml.Item)
|
||||||
|
except AttributeError:
|
||||||
|
log.exception('No root attribute "Item"')
|
||||||
|
self.log_error(self.import_source, translate('SongsPlugin.EasySlidesImport',
|
||||||
|
'Invalid EasySlides song file. Missing Item tag.'))
|
||||||
|
return
|
||||||
|
self.import_wizard.progress_bar.setMaximum(item_count)
|
||||||
for song in song_xml.Item:
|
for song in song_xml.Item:
|
||||||
if self.stop_import_flag:
|
if self.stop_import_flag:
|
||||||
return
|
return
|
||||||
|
@ -78,12 +78,17 @@ class EasyWorshipSongImport(SongImport):
|
|||||||
"""
|
"""
|
||||||
self.import_source = Path(self.import_source)
|
self.import_source = Path(self.import_source)
|
||||||
ext = self.import_source.suffix.lower()
|
ext = self.import_source.suffix.lower()
|
||||||
|
try:
|
||||||
if ext == '.ews':
|
if ext == '.ews':
|
||||||
self.import_ews()
|
self.import_ews()
|
||||||
elif ext == '.db':
|
elif ext == '.db':
|
||||||
self.import_db()
|
self.import_db()
|
||||||
else:
|
else:
|
||||||
self.import_sqlite_db()
|
self.import_sqlite_db()
|
||||||
|
except Exception:
|
||||||
|
log.exception('Unexpected data in file {name}'.format(name=self.import_source))
|
||||||
|
self.log_error(self.import_source,
|
||||||
|
'{name} contains unexpected data and can not be imported'.format(name=self.import_source))
|
||||||
|
|
||||||
def import_ews(self):
|
def import_ews(self):
|
||||||
"""
|
"""
|
||||||
|
@ -127,6 +127,10 @@ class FoilPresenterImport(SongImport):
|
|||||||
except etree.XMLSyntaxError:
|
except etree.XMLSyntaxError:
|
||||||
self.log_error(file_path, SongStrings.XMLSyntaxError)
|
self.log_error(file_path, SongStrings.XMLSyntaxError)
|
||||||
log.exception('XML syntax error in file {path}'.format(path=file_path))
|
log.exception('XML syntax error in file {path}'.format(path=file_path))
|
||||||
|
except AttributeError:
|
||||||
|
self.log_error(file_path, translate('SongsPlugin.FoilPresenterSongImport',
|
||||||
|
'Invalid Foilpresenter song file. Missing expected tags'))
|
||||||
|
log.exception('Missing content in file {path}'.format(path=file_path))
|
||||||
|
|
||||||
|
|
||||||
class FoilPresenter(object):
|
class FoilPresenter(object):
|
||||||
|
@ -130,6 +130,7 @@ class OpenLPSongImport(SongImport):
|
|||||||
else:
|
else:
|
||||||
has_authors_songs = False
|
has_authors_songs = False
|
||||||
# Load up the tabls and map them out
|
# Load up the tabls and map them out
|
||||||
|
try:
|
||||||
source_authors_table = source_meta.tables['authors']
|
source_authors_table = source_meta.tables['authors']
|
||||||
source_song_books_table = source_meta.tables['song_books']
|
source_song_books_table = source_meta.tables['song_books']
|
||||||
source_songs_table = source_meta.tables['songs']
|
source_songs_table = source_meta.tables['songs']
|
||||||
@ -137,6 +138,10 @@ class OpenLPSongImport(SongImport):
|
|||||||
source_authors_songs_table = source_meta.tables['authors_songs']
|
source_authors_songs_table = source_meta.tables['authors_songs']
|
||||||
source_songs_topics_table = source_meta.tables['songs_topics']
|
source_songs_topics_table = source_meta.tables['songs_topics']
|
||||||
source_media_files_songs_table = None
|
source_media_files_songs_table = None
|
||||||
|
except KeyError:
|
||||||
|
self.log_error(self.import_source, translate('SongsPlugin.OpenLPSongImport',
|
||||||
|
'Not a valid OpenLP 2 song database.'))
|
||||||
|
return
|
||||||
# Set up media_files relations
|
# Set up media_files relations
|
||||||
if has_media_files:
|
if has_media_files:
|
||||||
source_media_files_table = source_meta.tables['media_files']
|
source_media_files_table = source_meta.tables['media_files']
|
||||||
|
@ -58,7 +58,7 @@ class OPSProImport(SongImport):
|
|||||||
try:
|
try:
|
||||||
conn = pyodbc.connect('DRIVER={{Microsoft Access Driver (*.mdb)}};DBQ={source};'
|
conn = pyodbc.connect('DRIVER={{Microsoft Access Driver (*.mdb)}};DBQ={source};'
|
||||||
'PWD={password}'.format(source=self.import_source, password=password))
|
'PWD={password}'.format(source=self.import_source, password=password))
|
||||||
except (pyodbc.DatabaseError, pyodbc.IntegrityError, pyodbc.InternalError, pyodbc.OperationalError) as e:
|
except Exception as e:
|
||||||
log.warning('Unable to connect the OPS Pro database {source}. {error}'.format(source=self.import_source,
|
log.warning('Unable to connect the OPS Pro database {source}. {error}'.format(source=self.import_source,
|
||||||
error=str(e)))
|
error=str(e)))
|
||||||
# Unfortunately no specific exception type
|
# Unfortunately no specific exception type
|
||||||
|
@ -22,10 +22,16 @@
|
|||||||
The :mod:`powerpraiseimport` module provides the functionality for importing
|
The :mod:`powerpraiseimport` module provides the functionality for importing
|
||||||
Powerpraise song files into the current database.
|
Powerpraise song files into the current database.
|
||||||
"""
|
"""
|
||||||
from lxml import objectify
|
import logging
|
||||||
|
|
||||||
|
from lxml import objectify, etree
|
||||||
|
|
||||||
|
from openlp.core.common.i18n import translate
|
||||||
from openlp.core.widgets.wizard import WizardStrings
|
from openlp.core.widgets.wizard import WizardStrings
|
||||||
from openlp.plugins.songs.lib.importers.songimport import SongImport
|
from openlp.plugins.songs.lib.importers.songimport import SongImport
|
||||||
|
from openlp.plugins.songs.lib.ui import SongStrings
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class PowerPraiseImport(SongImport):
|
class PowerPraiseImport(SongImport):
|
||||||
@ -40,11 +46,21 @@ class PowerPraiseImport(SongImport):
|
|||||||
return
|
return
|
||||||
self.import_wizard.increment_progress_bar(WizardStrings.ImportingType.format(source=file_path.name))
|
self.import_wizard.increment_progress_bar(WizardStrings.ImportingType.format(source=file_path.name))
|
||||||
with file_path.open('rb') as xml_file:
|
with file_path.open('rb') as xml_file:
|
||||||
|
try:
|
||||||
root = objectify.parse(xml_file).getroot()
|
root = objectify.parse(xml_file).getroot()
|
||||||
self.process_song(root)
|
except etree.XMLSyntaxError:
|
||||||
|
log.exception('XML syntax error in file {name}'.format(name=file_path))
|
||||||
|
self.log_error(file_path, SongStrings.XMLSyntaxError)
|
||||||
|
continue
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
log.exception('Unreadable characters in {name}'.format(name=file_path))
|
||||||
|
self.log_error(file_path, SongStrings.XMLSyntaxError)
|
||||||
|
continue
|
||||||
|
self.process_song(root, file_path)
|
||||||
|
|
||||||
def process_song(self, root):
|
def process_song(self, root, file_path):
|
||||||
self.set_defaults()
|
self.set_defaults()
|
||||||
|
try:
|
||||||
self.title = str(root.general.title)
|
self.title = str(root.general.title)
|
||||||
verse_order_list = []
|
verse_order_list = []
|
||||||
verse_count = {}
|
verse_count = {}
|
||||||
@ -79,4 +95,7 @@ class PowerPraiseImport(SongImport):
|
|||||||
|
|
||||||
self.verse_order_list = verse_order_list
|
self.verse_order_list = verse_order_list
|
||||||
if not self.finish():
|
if not self.finish():
|
||||||
self.log_error(self.import_source)
|
self.log_error(file_path)
|
||||||
|
except AttributeError:
|
||||||
|
self.log_error(file_path, translate('SongsPlugin.PowerPraiseImport',
|
||||||
|
'Invalid PowerPraise song file. Missing needed tag.'))
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
The :mod:`presentationmanager` module provides the functionality for importing
|
The :mod:`presentationmanager` module provides the functionality for importing
|
||||||
Presentationmanager song files into the current database.
|
Presentationmanager song files into the current database.
|
||||||
"""
|
"""
|
||||||
|
import logging
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from lxml import etree, objectify
|
from lxml import etree, objectify
|
||||||
@ -31,6 +32,8 @@ from openlp.core.common.i18n import translate
|
|||||||
from openlp.core.widgets.wizard import WizardStrings
|
from openlp.core.widgets.wizard import WizardStrings
|
||||||
from openlp.plugins.songs.lib.importers.songimport import SongImport
|
from openlp.plugins.songs.lib.importers.songimport import SongImport
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class PresentationManagerImport(SongImport):
|
class PresentationManagerImport(SongImport):
|
||||||
"""
|
"""
|
||||||
@ -54,12 +57,24 @@ class PresentationManagerImport(SongImport):
|
|||||||
try:
|
try:
|
||||||
tree = etree.fromstring(text, parser=etree.XMLParser(recover=True))
|
tree = etree.fromstring(text, parser=etree.XMLParser(recover=True))
|
||||||
except ValueError:
|
except ValueError:
|
||||||
|
log.exception('XML syntax error in file {name}'.format(name=file_path))
|
||||||
self.log_error(file_path,
|
self.log_error(file_path,
|
||||||
translate('SongsPlugin.PresentationManagerImport',
|
translate('SongsPlugin.PresentationManagerImport',
|
||||||
'File is not in XML-format, which is the only format supported.'))
|
'File is not in XML-format, which is the only format supported.'))
|
||||||
continue
|
continue
|
||||||
root = objectify.fromstring(etree.tostring(tree))
|
file_str = etree.tostring(tree)
|
||||||
|
if not file_str:
|
||||||
|
log.exception('Could not find XML in file {name}'.format(name=file_path))
|
||||||
|
self.log_error(file_path, translate('SongsPlugin.PresentationManagerImport',
|
||||||
|
'File is not in XML-format, which is the only format supported.'))
|
||||||
|
continue
|
||||||
|
root = objectify.fromstring(file_str)
|
||||||
|
try:
|
||||||
self.process_song(root, file_path)
|
self.process_song(root, file_path)
|
||||||
|
except AttributeError:
|
||||||
|
log.exception('XML syntax error in file {name}'.format(name=file_path))
|
||||||
|
self.log_error(file_path, translate('SongsPlugin.PresentationManagerImport',
|
||||||
|
'File is not a valid PresentationManager XMl file.'))
|
||||||
|
|
||||||
def _get_attr(self, elem, name):
|
def _get_attr(self, elem, name):
|
||||||
"""
|
"""
|
||||||
|
@ -25,11 +25,13 @@ ProPresenter song files into the current installation database.
|
|||||||
import base64
|
import base64
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from lxml import objectify
|
from lxml import objectify, etree
|
||||||
|
|
||||||
|
from openlp.core.common.i18n import translate
|
||||||
from openlp.core.widgets.wizard import WizardStrings
|
from openlp.core.widgets.wizard import WizardStrings
|
||||||
from openlp.plugins.songs.lib import strip_rtf
|
from openlp.plugins.songs.lib import strip_rtf
|
||||||
from openlp.plugins.songs.lib.importers.songimport import SongImport
|
from openlp.plugins.songs.lib.importers.songimport import SongImport
|
||||||
|
from openlp.plugins.songs.lib.ui import SongStrings
|
||||||
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
@ -48,8 +50,23 @@ class ProPresenterImport(SongImport):
|
|||||||
self.import_wizard.increment_progress_bar(
|
self.import_wizard.increment_progress_bar(
|
||||||
WizardStrings.ImportingType.format(source=file_path.name))
|
WizardStrings.ImportingType.format(source=file_path.name))
|
||||||
with file_path.open('rb') as xml_file:
|
with file_path.open('rb') as xml_file:
|
||||||
|
try:
|
||||||
root = objectify.parse(xml_file).getroot()
|
root = objectify.parse(xml_file).getroot()
|
||||||
|
except etree.XMLSyntaxError:
|
||||||
|
log.exception('XML syntax error in file {name}'.format(name=file_path))
|
||||||
|
self.log_error(file_path, SongStrings.XMLSyntaxError)
|
||||||
|
continue
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
log.exception('Unreadable characters in {name}'.format(name=file_path))
|
||||||
|
self.log_error(file_path, SongStrings.XMLSyntaxError)
|
||||||
|
continue
|
||||||
|
try:
|
||||||
self.process_song(root, file_path)
|
self.process_song(root, file_path)
|
||||||
|
except AttributeError:
|
||||||
|
log.exception('XML syntax error in file {name}'.format(name=file_path))
|
||||||
|
self.log_error(file_path, translate('SongsPlugin.ProPresenterImport',
|
||||||
|
'File is not a valid ProPresenter XMl file.'))
|
||||||
|
continue
|
||||||
|
|
||||||
def process_song(self, root, file_path):
|
def process_song(self, root, file_path):
|
||||||
"""
|
"""
|
||||||
@ -62,7 +79,7 @@ class ProPresenterImport(SongImport):
|
|||||||
# Extract ProPresenter versionNumber
|
# Extract ProPresenter versionNumber
|
||||||
try:
|
try:
|
||||||
self.version = int(root.get('versionNumber'))
|
self.version = int(root.get('versionNumber'))
|
||||||
except ValueError:
|
except (ValueError, TypeError):
|
||||||
log.debug('ProPresenter versionNumber invalid or missing')
|
log.debug('ProPresenter versionNumber invalid or missing')
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -29,6 +29,7 @@ import re
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from openlp.core.common import get_file_encoding, is_macosx, is_win
|
from openlp.core.common import get_file_encoding, is_macosx, is_win
|
||||||
|
from openlp.core.common.i18n import translate
|
||||||
from openlp.core.common.settings import Settings
|
from openlp.core.common.settings import Settings
|
||||||
from openlp.plugins.songs.lib import VerseType
|
from openlp.plugins.songs.lib import VerseType
|
||||||
from openlp.plugins.songs.lib.importers.songimport import SongImport
|
from openlp.plugins.songs.lib.importers.songimport import SongImport
|
||||||
@ -130,7 +131,13 @@ class SongBeamerImport(SongImport):
|
|||||||
if self.input_file_encoding and not self.input_file_encoding.lower().startswith('u'):
|
if self.input_file_encoding and not self.input_file_encoding.lower().startswith('u'):
|
||||||
self.input_file_encoding = 'cp1252'
|
self.input_file_encoding = 'cp1252'
|
||||||
with file_path.open(encoding=self.input_file_encoding) as song_file:
|
with file_path.open(encoding=self.input_file_encoding) as song_file:
|
||||||
|
try:
|
||||||
song_data = song_file.readlines()
|
song_data = song_file.readlines()
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
log.exception('Unreadable characters in {name}'.format(name=file_path))
|
||||||
|
self.log_error(file_path, translate('SongsPlugin.SongBeamerImport',
|
||||||
|
'File is not a valid SongBeamer file.'))
|
||||||
|
continue
|
||||||
else:
|
else:
|
||||||
continue
|
continue
|
||||||
self.title = file_path.stem
|
self.title = file_path.stem
|
||||||
|
@ -22,12 +22,16 @@
|
|||||||
The :mod:`songpro` module provides the functionality for importing SongPro
|
The :mod:`songpro` module provides the functionality for importing SongPro
|
||||||
songs into the OpenLP database.
|
songs into the OpenLP database.
|
||||||
"""
|
"""
|
||||||
|
import logging
|
||||||
import re
|
import re
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
from openlp.core.common.i18n import translate
|
||||||
from openlp.plugins.songs.lib import strip_rtf
|
from openlp.plugins.songs.lib import strip_rtf
|
||||||
from openlp.plugins.songs.lib.importers.songimport import SongImport
|
from openlp.plugins.songs.lib.importers.songimport import SongImport
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class SongProImport(SongImport):
|
class SongProImport(SongImport):
|
||||||
"""
|
"""
|
||||||
@ -82,7 +86,13 @@ class SongProImport(SongImport):
|
|||||||
break
|
break
|
||||||
file_text = file_line.rstrip()
|
file_text = file_line.rstrip()
|
||||||
if file_text and file_text[0] == '#':
|
if file_text and file_text[0] == '#':
|
||||||
|
try:
|
||||||
self.process_section(tag, text.rstrip())
|
self.process_section(tag, text.rstrip())
|
||||||
|
except ValueError:
|
||||||
|
log.exception('Missing data in {name}'.format(name=self.import_source))
|
||||||
|
self.log_error(self.import_source, translate('SongsPlugin.SongProImport',
|
||||||
|
'File is not a valid SongPro file.'))
|
||||||
|
return
|
||||||
tag = file_text[1:]
|
tag = file_text[1:]
|
||||||
text = ''
|
text = ''
|
||||||
else:
|
else:
|
||||||
|
@ -26,6 +26,7 @@ import logging
|
|||||||
import re
|
import re
|
||||||
import struct
|
import struct
|
||||||
|
|
||||||
|
from openlp.core.common.i18n import translate
|
||||||
from openlp.core.widgets.wizard import WizardStrings
|
from openlp.core.widgets.wizard import WizardStrings
|
||||||
from openlp.plugins.songs.lib import VerseType, retrieve_windows_encoding
|
from openlp.plugins.songs.lib import VerseType, retrieve_windows_encoding
|
||||||
from openlp.plugins.songs.lib.importers.songimport import SongImport
|
from openlp.plugins.songs.lib.importers.songimport import SongImport
|
||||||
@ -100,6 +101,7 @@ class SongShowPlusImport(SongImport):
|
|||||||
self.other_list = {}
|
self.other_list = {}
|
||||||
self.import_wizard.increment_progress_bar(WizardStrings.ImportingType.format(source=file_path.name), 0)
|
self.import_wizard.increment_progress_bar(WizardStrings.ImportingType.format(source=file_path.name), 0)
|
||||||
with file_path.open('rb') as song_file:
|
with file_path.open('rb') as song_file:
|
||||||
|
try:
|
||||||
while True:
|
while True:
|
||||||
block_key, = struct.unpack("I", song_file.read(4))
|
block_key, = struct.unpack("I", song_file.read(4))
|
||||||
log.debug('block_key: %d' % block_key)
|
log.debug('block_key: %d' % block_key)
|
||||||
@ -150,16 +152,17 @@ class SongShowPlusImport(SongImport):
|
|||||||
if match:
|
if match:
|
||||||
self.ccli_number = int(match.group())
|
self.ccli_number = int(match.group())
|
||||||
else:
|
else:
|
||||||
log.warning("Can't parse CCLI Number from string: {text}".format(text=self.decode(data)))
|
log.warning("Can't parse CCLI Number from string: {text}".format(
|
||||||
|
text=self.decode(data)))
|
||||||
elif block_key == VERSE:
|
elif block_key == VERSE:
|
||||||
self.add_verse(self.decode(data), "{tag}{number}".format(tag=VerseType.tags[VerseType.Verse],
|
self.add_verse(self.decode(data), "{tag}{number}".format(
|
||||||
number=verse_no))
|
tag=VerseType.tags[VerseType.Verse], number=verse_no))
|
||||||
elif block_key == CHORUS:
|
elif block_key == CHORUS:
|
||||||
self.add_verse(self.decode(data), "{tag}{number}".format(tag=VerseType.tags[VerseType.Chorus],
|
self.add_verse(self.decode(data), "{tag}{number}".format(
|
||||||
number=verse_no))
|
tag=VerseType.tags[VerseType.Chorus], number=verse_no))
|
||||||
elif block_key == BRIDGE:
|
elif block_key == BRIDGE:
|
||||||
self.add_verse(self.decode(data), "{tag}{number}".format(tag=VerseType.tags[VerseType.Bridge],
|
self.add_verse(self.decode(data), "{tag}{number}".format(
|
||||||
number=verse_no))
|
tag=VerseType.tags[VerseType.Bridge], number=verse_no))
|
||||||
elif block_key == TOPIC:
|
elif block_key == TOPIC:
|
||||||
self.topics.append(self.decode(data))
|
self.topics.append(self.decode(data))
|
||||||
elif block_key == COMMENTS:
|
elif block_key == COMMENTS:
|
||||||
@ -178,6 +181,11 @@ class SongShowPlusImport(SongImport):
|
|||||||
else:
|
else:
|
||||||
log.debug("Unrecognised blockKey: {key}, data: {data}".format(key=block_key, data=data))
|
log.debug("Unrecognised blockKey: {key}, data: {data}".format(key=block_key, data=data))
|
||||||
song_file.seek(next_block_starts)
|
song_file.seek(next_block_starts)
|
||||||
|
except struct.error:
|
||||||
|
log.exception('Unexpected data in {name}'.format(name=file_path))
|
||||||
|
self.log_error(file_path, translate('SongsPlugin.SongShowPlusImport',
|
||||||
|
'File is not a valid SongShowPlus file.'))
|
||||||
|
continue
|
||||||
self.verse_order_list = self.ssp_verse_order_list
|
self.verse_order_list = self.ssp_verse_order_list
|
||||||
if not self.finish():
|
if not self.finish():
|
||||||
self.log_error(file_path)
|
self.log_error(file_path)
|
||||||
|
@ -91,6 +91,10 @@ class WorshipAssistantImport(SongImport):
|
|||||||
translate('SongsPlugin.WorshipAssistantImport',
|
translate('SongsPlugin.WorshipAssistantImport',
|
||||||
'Line {number:d}: {error}').format(number=songs_reader.line_num, error=e))
|
'Line {number:d}: {error}').format(number=songs_reader.line_num, error=e))
|
||||||
return
|
return
|
||||||
|
except UnicodeDecodeError as e:
|
||||||
|
self.log_error(translate('SongsPlugin.WorshipAssistantImport',
|
||||||
|
'Decoding error: {error}').format(error=e))
|
||||||
|
return
|
||||||
num_records = len(records)
|
num_records = len(records)
|
||||||
log.info('{count} records found in CSV file'.format(count=num_records))
|
log.info('{count} records found in CSV file'.format(count=num_records))
|
||||||
self.import_wizard.progress_bar.setMaximum(num_records)
|
self.import_wizard.progress_bar.setMaximum(num_records)
|
||||||
@ -103,11 +107,11 @@ class WorshipAssistantImport(SongImport):
|
|||||||
record = dict((field.upper(), value) for field, value in record.items())
|
record = dict((field.upper(), value) for field, value in record.items())
|
||||||
# The CSV file has a line in the middle of the file where the headers are repeated.
|
# The CSV file has a line in the middle of the file where the headers are repeated.
|
||||||
# We need to skip this line.
|
# We need to skip this line.
|
||||||
|
try:
|
||||||
if record['TITLE'] == "TITLE" and record['AUTHOR'] == 'AUTHOR' and record['LYRICS2'] == 'LYRICS2':
|
if record['TITLE'] == "TITLE" and record['AUTHOR'] == 'AUTHOR' and record['LYRICS2'] == 'LYRICS2':
|
||||||
continue
|
continue
|
||||||
self.set_defaults()
|
self.set_defaults()
|
||||||
verse_order_list = []
|
verse_order_list = []
|
||||||
try:
|
|
||||||
self.title = record['TITLE']
|
self.title = record['TITLE']
|
||||||
if record['AUTHOR'] != EMPTY_STR:
|
if record['AUTHOR'] != EMPTY_STR:
|
||||||
self.parse_author(record['AUTHOR'])
|
self.parse_author(record['AUTHOR'])
|
||||||
@ -128,6 +132,11 @@ class WorshipAssistantImport(SongImport):
|
|||||||
'File not valid WorshipAssistant CSV format.'),
|
'File not valid WorshipAssistant CSV format.'),
|
||||||
'TypeError: {error}'.format(error=e))
|
'TypeError: {error}'.format(error=e))
|
||||||
return
|
return
|
||||||
|
except KeyError as e:
|
||||||
|
self.log_error(translate('SongsPlugin.WorshipAssistantImport',
|
||||||
|
'File not valid WorshipAssistant CSV format.'),
|
||||||
|
'KeyError: {error}'.format(error=e))
|
||||||
|
return
|
||||||
verse = ''
|
verse = ''
|
||||||
used_verses = []
|
used_verses = []
|
||||||
verse_id = VerseType.tags[VerseType.Verse] + '1'
|
verse_id = VerseType.tags[VerseType.Verse] + '1'
|
||||||
|
@ -52,7 +52,7 @@ class WorshipCenterProImport(SongImport):
|
|||||||
try:
|
try:
|
||||||
conn = pyodbc.connect('DRIVER={{Microsoft Access Driver (*.mdb)}};'
|
conn = pyodbc.connect('DRIVER={{Microsoft Access Driver (*.mdb)}};'
|
||||||
'DBQ={source}'.format(source=self.import_source))
|
'DBQ={source}'.format(source=self.import_source))
|
||||||
except (pyodbc.DatabaseError, pyodbc.IntegrityError, pyodbc.InternalError, pyodbc.OperationalError) as e:
|
except Exception as e:
|
||||||
log.warning('Unable to connect the WorshipCenter Pro '
|
log.warning('Unable to connect the WorshipCenter Pro '
|
||||||
'database {source}. {error}'.format(source=self.import_source, error=str(e)))
|
'database {source}. {error}'.format(source=self.import_source, error=str(e)))
|
||||||
# Unfortunately no specific exception type
|
# Unfortunately no specific exception type
|
||||||
|
@ -87,6 +87,7 @@ class ZionWorxImport(SongImport):
|
|||||||
num_records = len(records)
|
num_records = len(records)
|
||||||
log.info('{count} records found in CSV file'.format(count=num_records))
|
log.info('{count} records found in CSV file'.format(count=num_records))
|
||||||
self.import_wizard.progress_bar.setMaximum(num_records)
|
self.import_wizard.progress_bar.setMaximum(num_records)
|
||||||
|
try:
|
||||||
for index, record in enumerate(records, 1):
|
for index, record in enumerate(records, 1):
|
||||||
if self.stop_import_flag:
|
if self.stop_import_flag:
|
||||||
return
|
return
|
||||||
@ -100,7 +101,8 @@ class ZionWorxImport(SongImport):
|
|||||||
lyrics = record['Lyrics']
|
lyrics = record['Lyrics']
|
||||||
except UnicodeDecodeError as e:
|
except UnicodeDecodeError as e:
|
||||||
self.log_error(translate('SongsPlugin.ZionWorxImport', 'Record {index}').format(index=index),
|
self.log_error(translate('SongsPlugin.ZionWorxImport', 'Record {index}').format(index=index),
|
||||||
translate('SongsPlugin.ZionWorxImport', 'Decoding error: {error}').format(error=e))
|
translate('SongsPlugin.ZionWorxImport',
|
||||||
|
'Decoding error: {error}').format(error=e))
|
||||||
continue
|
continue
|
||||||
except TypeError as e:
|
except TypeError as e:
|
||||||
self.log_error(translate('SongsPlugin.ZionWorxImport', 'File not valid ZionWorx CSV format.'),
|
self.log_error(translate('SongsPlugin.ZionWorxImport', 'File not valid ZionWorx CSV format.'),
|
||||||
@ -119,3 +121,5 @@ class ZionWorxImport(SongImport):
|
|||||||
if not self.finish():
|
if not self.finish():
|
||||||
self.log_error(translate('SongsPlugin.ZionWorxImport', 'Record %d') % index +
|
self.log_error(translate('SongsPlugin.ZionWorxImport', 'Record %d') % index +
|
||||||
(': "' + title + '"' if title else ''))
|
(': "' + title + '"' if title else ''))
|
||||||
|
except AttributeError:
|
||||||
|
self.log_error(translate('SongsPlugin.ZionWorxImport', 'Error reading CSV file.'))
|
||||||
|
@ -51,13 +51,13 @@ def test_get_application_stylesheet_dark(mocked_qdarkstyle, MockSettings):
|
|||||||
@patch('openlp.core.ui.style.HAS_DARK_STYLE', False)
|
@patch('openlp.core.ui.style.HAS_DARK_STYLE', False)
|
||||||
@patch('openlp.core.ui.style.is_win')
|
@patch('openlp.core.ui.style.is_win')
|
||||||
@patch('openlp.core.ui.style.Settings')
|
@patch('openlp.core.ui.style.Settings')
|
||||||
@patch('openlp.core.ui.style.Registry')
|
@patch('openlp.core.app.QtWidgets.QApplication.palette')
|
||||||
def test_get_application_stylesheet_not_alternate_rows(MockRegistry, MockSettings, mocked_is_win):
|
def test_get_application_stylesheet_not_alternate_rows(mocked_palette, MockSettings, mocked_is_win):
|
||||||
"""Test that the alternate rows stylesheet is returned when enabled in settings"""
|
"""Test that the alternate rows stylesheet is returned when enabled in settings"""
|
||||||
# GIVEN: We're on Windows and no dark style is set
|
# GIVEN: We're on Windows and no dark style is set
|
||||||
mocked_is_win.return_value = False
|
mocked_is_win.return_value = False
|
||||||
MockSettings.return_value.value.return_value = False
|
MockSettings.return_value.value.return_value = False
|
||||||
MockRegistry.return_value.get.return_value.palette.return_value.color.return_value.name.return_value = 'color'
|
mocked_palette.return_value.color.return_value.name.return_value = 'color'
|
||||||
|
|
||||||
# WHEN: can_show_icon() is called
|
# WHEN: can_show_icon() is called
|
||||||
result = get_application_stylesheet()
|
result = get_application_stylesheet()
|
||||||
|
@ -48,7 +48,8 @@ class TestWordProjectImport(TestCase):
|
|||||||
self.manager_patcher.start()
|
self.manager_patcher.start()
|
||||||
|
|
||||||
@patch.object(Path, 'read_text')
|
@patch.object(Path, 'read_text')
|
||||||
def test_process_books(self, mocked_read_text):
|
@patch.object(Path, 'exists')
|
||||||
|
def test_process_books(self, mocked_exists, mocked_read_text):
|
||||||
"""
|
"""
|
||||||
Test the process_books() method
|
Test the process_books() method
|
||||||
"""
|
"""
|
||||||
@ -58,11 +59,14 @@ class TestWordProjectImport(TestCase):
|
|||||||
importer.stop_import_flag = False
|
importer.stop_import_flag = False
|
||||||
importer.language_id = 'en'
|
importer.language_id = 'en'
|
||||||
mocked_read_text.return_value = INDEX_PAGE
|
mocked_read_text.return_value = INDEX_PAGE
|
||||||
|
mocked_exists.return_value = True
|
||||||
|
|
||||||
# WHEN: process_books() is called
|
# WHEN: process_books() is called
|
||||||
with patch.object(importer, 'find_and_create_book') as mocked_find_and_create_book, \
|
with patch.object(importer, '_unzip_file') as mocked_unzip_file, \
|
||||||
|
patch.object(importer, 'find_and_create_book') as mocked_find_and_create_book, \
|
||||||
patch.object(importer, 'process_chapters') as mocked_process_chapters, \
|
patch.object(importer, 'process_chapters') as mocked_process_chapters, \
|
||||||
patch.object(importer, 'session') as mocked_session:
|
patch.object(importer, 'session') as mocked_session:
|
||||||
|
mocked_unzip_file.return_value = True
|
||||||
importer.process_books()
|
importer.process_books()
|
||||||
|
|
||||||
# THEN: The right methods should have been called
|
# THEN: The right methods should have been called
|
||||||
@ -173,6 +177,8 @@ class TestWordProjectImport(TestCase):
|
|||||||
patch.object(importer, 'get_language_id') as mocked_get_language_id, \
|
patch.object(importer, 'get_language_id') as mocked_get_language_id, \
|
||||||
patch.object(importer, 'process_books') as mocked_process_books, \
|
patch.object(importer, 'process_books') as mocked_process_books, \
|
||||||
patch.object(importer, '_cleanup') as mocked_cleanup:
|
patch.object(importer, '_cleanup') as mocked_cleanup:
|
||||||
|
mocked_unzip_file.return_value = True
|
||||||
|
mocked_process_books.return_value = True
|
||||||
mocked_get_language_id.return_value = 1
|
mocked_get_language_id.return_value = 1
|
||||||
result = importer.do_import()
|
result = importer.do_import()
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user