forked from openlp/openlp
Fixes a few bugs, and some path lib refactors
bzr-revno: 2850
This commit is contained in:
commit
0669a6c5d4
@ -39,8 +39,8 @@ def deploy_zipfile(app_root_path, zip_name):
|
|||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
zip_path = app_root_path / zip_name
|
zip_path = app_root_path / zip_name
|
||||||
web_zip = ZipFile(str(zip_path))
|
web_zip = ZipFile(zip_path)
|
||||||
web_zip.extractall(str(app_root_path))
|
web_zip.extractall(app_root_path)
|
||||||
|
|
||||||
|
|
||||||
def download_sha256():
|
def download_sha256():
|
||||||
|
@ -317,8 +317,7 @@ def set_up_logging(log_path):
|
|||||||
"""
|
"""
|
||||||
create_paths(log_path, do_not_log=True)
|
create_paths(log_path, do_not_log=True)
|
||||||
file_path = log_path / 'openlp.log'
|
file_path = log_path / 'openlp.log'
|
||||||
# TODO: FileHandler accepts a Path object in Py3.6
|
logfile = logging.FileHandler(file_path, 'w', encoding='UTF-8')
|
||||||
logfile = logging.FileHandler(str(file_path), 'w', encoding='UTF-8')
|
|
||||||
logfile.setFormatter(logging.Formatter('%(asctime)s %(threadName)s %(name)-55s %(levelname)-8s %(message)s'))
|
logfile.setFormatter(logging.Formatter('%(asctime)s %(threadName)s %(name)-55s %(levelname)-8s %(message)s'))
|
||||||
log.addHandler(logfile)
|
log.addHandler(logfile)
|
||||||
if log.isEnabledFor(logging.DEBUG):
|
if log.isEnabledFor(logging.DEBUG):
|
||||||
@ -364,7 +363,7 @@ def main(args=None):
|
|||||||
portable_settings_path = data_path / 'OpenLP.ini'
|
portable_settings_path = data_path / 'OpenLP.ini'
|
||||||
# Make this our settings file
|
# Make this our settings file
|
||||||
log.info('INI file: {name}'.format(name=portable_settings_path))
|
log.info('INI file: {name}'.format(name=portable_settings_path))
|
||||||
Settings.set_filename(str(portable_settings_path))
|
Settings.set_filename(portable_settings_path)
|
||||||
portable_settings = Settings()
|
portable_settings = Settings()
|
||||||
# Set our data path
|
# Set our data path
|
||||||
log.info('Data path: {name}'.format(name=data_path))
|
log.info('Data path: {name}'.format(name=data_path))
|
||||||
@ -405,7 +404,12 @@ def main(args=None):
|
|||||||
None, translate('OpenLP', 'Settings Upgrade'),
|
None, translate('OpenLP', 'Settings Upgrade'),
|
||||||
translate('OpenLP', 'Your settings are about to be upgraded. A backup will be created at '
|
translate('OpenLP', 'Your settings are about to be upgraded. A backup will be created at '
|
||||||
'{back_up_path}').format(back_up_path=back_up_path))
|
'{back_up_path}').format(back_up_path=back_up_path))
|
||||||
|
try:
|
||||||
settings.export(back_up_path)
|
settings.export(back_up_path)
|
||||||
|
except OSError:
|
||||||
|
QtWidgets.QMessageBox.warning(
|
||||||
|
None, translate('OpenLP', 'Settings Upgrade'),
|
||||||
|
translate('OpenLP', 'Settings back up failed.\n\nContinuining to upgrade.'))
|
||||||
settings.upgrade_settings()
|
settings.upgrade_settings()
|
||||||
# First time checks in settings
|
# First time checks in settings
|
||||||
if not Settings().value('core/has run wizard'):
|
if not Settings().value('core/has run wizard'):
|
||||||
|
@ -474,10 +474,10 @@ def get_file_encoding(file_path):
|
|||||||
if not chunk:
|
if not chunk:
|
||||||
break
|
break
|
||||||
detector.feed(chunk)
|
detector.feed(chunk)
|
||||||
detector.close()
|
|
||||||
return detector.result
|
|
||||||
except OSError:
|
except OSError:
|
||||||
log.exception('Error detecting file encoding')
|
log.exception('Error detecting file encoding')
|
||||||
|
finally:
|
||||||
|
return detector.close()
|
||||||
|
|
||||||
|
|
||||||
def normalize_str(irregular_string):
|
def normalize_str(irregular_string):
|
||||||
|
@ -78,7 +78,7 @@ class Path(PathVariant):
|
|||||||
:param onerror: Handler function to handle any errors
|
:param onerror: Handler function to handle any errors
|
||||||
:rtype: None
|
:rtype: None
|
||||||
"""
|
"""
|
||||||
shutil.rmtree(str(self), ignore_errors, onerror)
|
shutil.rmtree(self, ignore_errors, onerror)
|
||||||
|
|
||||||
|
|
||||||
def replace_params(args, kwargs, params):
|
def replace_params(args, kwargs, params):
|
||||||
|
@ -540,7 +540,7 @@ class Settings(QtCore.QSettings):
|
|||||||
old_values = [self._convert_value(old_value, default_value)
|
old_values = [self._convert_value(old_value, default_value)
|
||||||
for old_value, default_value in zip(old_values, default_values)]
|
for old_value, default_value in zip(old_values, default_values)]
|
||||||
# Iterate over our rules and check what the old_value should be "converted" to.
|
# Iterate over our rules and check what the old_value should be "converted" to.
|
||||||
new_value = None
|
new_value = old_values[0]
|
||||||
for new_rule, old_rule in rules:
|
for new_rule, old_rule in rules:
|
||||||
# If the value matches with the condition (rule), then use the provided value. This is used to
|
# If the value matches with the condition (rule), then use the provided value. This is used to
|
||||||
# convert values. E. g. an old value 1 results in True, and 0 in False.
|
# convert values. E. g. an old value 1 results in True, and 0 in False.
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
body {
|
body {
|
||||||
background: transparent !important;
|
background: transparent !important;
|
||||||
color: #fff !important;
|
color: rgb(255, 255, 255) !important;
|
||||||
}
|
}
|
||||||
sup {
|
sup {
|
||||||
vertical-align: super !important;
|
vertical-align: super !important;
|
||||||
|
@ -315,7 +315,7 @@ var Display = {
|
|||||||
section.setAttribute("style", "height: 100%; width: 100%; position: relative;");
|
section.setAttribute("style", "height: 100%; width: 100%; position: relative;");
|
||||||
var img = document.createElement('img');
|
var img = document.createElement('img');
|
||||||
img.src = image;
|
img.src = image;
|
||||||
img.setAttribute("style", "position: absolute; top: 0; bottom: 0; left: 0; right: 0; margin: auto;");
|
img.setAttribute("style", "position: absolute; top: 0; bottom: 0; left: 0; right: 0; margin: auto; max-height: 100%; max-width: 100%");
|
||||||
section.appendChild(img);
|
section.appendChild(img);
|
||||||
slidesDiv.appendChild(section);
|
slidesDiv.appendChild(section);
|
||||||
Display._slides['0'] = 0;
|
Display._slides['0'] = 0;
|
||||||
|
@ -242,16 +242,16 @@ def image_to_byte(image, base_64=True):
|
|||||||
"""
|
"""
|
||||||
Resize an image to fit on the current screen for the web and returns it as a byte stream.
|
Resize an image to fit on the current screen for the web and returns it as a byte stream.
|
||||||
|
|
||||||
:param image: The image to converted.
|
:param image: The image to be converted.
|
||||||
:param base_64: If True returns the image as Base64 bytes, otherwise the image is returned as a byte array.
|
:param base_64: If True returns the image as Base64 bytes, otherwise the image is returned as a byte array.
|
||||||
To preserve original intention, this defaults to True
|
To preserve original intention, this defaults to True
|
||||||
"""
|
"""
|
||||||
log.debug('image_to_byte - start')
|
log.debug('image_to_byte - start')
|
||||||
byte_array = QtCore.QByteArray()
|
byte_array = QtCore.QByteArray()
|
||||||
# use buffer to store pixmap into byteArray
|
# use buffer to store pixmap into byteArray
|
||||||
buffie = QtCore.QBuffer(byte_array)
|
buffer = QtCore.QBuffer(byte_array)
|
||||||
buffie.open(QtCore.QIODevice.WriteOnly)
|
buffer.open(QtCore.QIODevice.WriteOnly)
|
||||||
image.save(buffie, "PNG")
|
image.save(buffer, "PNG")
|
||||||
log.debug('image_to_byte - end')
|
log.debug('image_to_byte - end')
|
||||||
if not base_64:
|
if not base_64:
|
||||||
return byte_array
|
return byte_array
|
||||||
@ -263,15 +263,13 @@ def create_thumb(image_path, thumb_path, return_icon=True, size=None):
|
|||||||
"""
|
"""
|
||||||
Create a thumbnail from the given image path and depending on ``return_icon`` it returns an icon from this thumb.
|
Create a thumbnail from the given image path and depending on ``return_icon`` it returns an icon from this thumb.
|
||||||
|
|
||||||
:param image_path: The image file to create the icon from.
|
:param openlp.core.common.path.Path image_path: The image file to create the icon from.
|
||||||
:param thumb_path: The filename to save the thumbnail to.
|
:param openlp.core.common.path.Path thumb_path: The filename to save the thumbnail to.
|
||||||
:param return_icon: States if an icon should be build and returned from the thumb. Defaults to ``True``.
|
:param return_icon: States if an icon should be build and returned from the thumb. Defaults to ``True``.
|
||||||
:param size: Allows to state a own size (QtCore.QSize) to use. Defaults to ``None``, which means that a default
|
:param size: Allows to state a own size (QtCore.QSize) to use. Defaults to ``None``, which means that a default
|
||||||
height of 88 is used.
|
height of 88 is used.
|
||||||
:return: The final icon.
|
:return: The final icon.
|
||||||
"""
|
"""
|
||||||
# TODO: To path object
|
|
||||||
thumb_path = Path(thumb_path)
|
|
||||||
reader = QtGui.QImageReader(str(image_path))
|
reader = QtGui.QImageReader(str(image_path))
|
||||||
if size is None:
|
if size is None:
|
||||||
# No size given; use default height of 88
|
# No size given; use default height of 88
|
||||||
|
@ -132,8 +132,9 @@ def get_db_path(plugin_name, db_file_name=None):
|
|||||||
Create a path to a database from the plugin name and database name
|
Create a path to a database from the plugin name and database name
|
||||||
|
|
||||||
:param plugin_name: Name of plugin
|
:param plugin_name: Name of plugin
|
||||||
:param db_file_name: File name of database
|
:param openlp.core.common.path.Path | str | None db_file_name: File name of database
|
||||||
:return: The path to the database as type str
|
:return: The path to the database
|
||||||
|
:rtype: str
|
||||||
"""
|
"""
|
||||||
if db_file_name is None:
|
if db_file_name is None:
|
||||||
return 'sqlite:///{path}/{plugin}.sqlite'.format(path=AppLocation.get_section_data_path(plugin_name),
|
return 'sqlite:///{path}/{plugin}.sqlite'.format(path=AppLocation.get_section_data_path(plugin_name),
|
||||||
@ -144,15 +145,15 @@ def get_db_path(plugin_name, db_file_name=None):
|
|||||||
return 'sqlite:///{path}/{name}'.format(path=AppLocation.get_section_data_path(plugin_name), name=db_file_name)
|
return 'sqlite:///{path}/{name}'.format(path=AppLocation.get_section_data_path(plugin_name), name=db_file_name)
|
||||||
|
|
||||||
|
|
||||||
def handle_db_error(plugin_name, db_file_name):
|
def handle_db_error(plugin_name, db_file_path):
|
||||||
"""
|
"""
|
||||||
Log and report to the user that a database cannot be loaded
|
Log and report to the user that a database cannot be loaded
|
||||||
|
|
||||||
:param plugin_name: Name of plugin
|
:param plugin_name: Name of plugin
|
||||||
:param db_file_name: File name of database
|
:param openlp.core.common.path.Path db_file_path: File name of database
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
db_path = get_db_path(plugin_name, db_file_name)
|
db_path = get_db_path(plugin_name, db_file_path)
|
||||||
log.exception('Error loading database: {db}'.format(db=db_path))
|
log.exception('Error loading database: {db}'.format(db=db_path))
|
||||||
critical_error_message_box(translate('OpenLP.Manager', 'Database Error'),
|
critical_error_message_box(translate('OpenLP.Manager', 'Database Error'),
|
||||||
translate('OpenLP.Manager',
|
translate('OpenLP.Manager',
|
||||||
@ -161,10 +162,13 @@ def handle_db_error(plugin_name, db_file_name):
|
|||||||
|
|
||||||
def init_url(plugin_name, db_file_name=None):
|
def init_url(plugin_name, db_file_name=None):
|
||||||
"""
|
"""
|
||||||
Return the database URL.
|
Construct the connection string for a database.
|
||||||
|
|
||||||
:param plugin_name: The name of the plugin for the database creation.
|
:param plugin_name: The name of the plugin for the database creation.
|
||||||
:param db_file_name: The database file name. Defaults to None resulting in the plugin_name being used.
|
:param openlp.core.common.path.Path | str | None db_file_name: The database file name. Defaults to None resulting
|
||||||
|
in the plugin_name being used.
|
||||||
|
:return: The database URL
|
||||||
|
:rtype: str
|
||||||
"""
|
"""
|
||||||
settings = Settings()
|
settings = Settings()
|
||||||
settings.beginGroup(plugin_name)
|
settings.beginGroup(plugin_name)
|
||||||
@ -357,14 +361,14 @@ class Manager(object):
|
|||||||
self.db_url = None
|
self.db_url = None
|
||||||
if db_file_path:
|
if db_file_path:
|
||||||
log.debug('Manager: Creating new DB url')
|
log.debug('Manager: Creating new DB url')
|
||||||
self.db_url = init_url(plugin_name, str(db_file_path))
|
self.db_url = init_url(plugin_name, str(db_file_path)) # TOdO :PATHLIB
|
||||||
else:
|
else:
|
||||||
self.db_url = init_url(plugin_name)
|
self.db_url = init_url(plugin_name)
|
||||||
if upgrade_mod:
|
if upgrade_mod:
|
||||||
try:
|
try:
|
||||||
db_ver, up_ver = upgrade_db(self.db_url, upgrade_mod)
|
db_ver, up_ver = upgrade_db(self.db_url, upgrade_mod)
|
||||||
except (SQLAlchemyError, DBAPIError):
|
except (SQLAlchemyError, DBAPIError):
|
||||||
handle_db_error(plugin_name, str(db_file_path))
|
handle_db_error(plugin_name, db_file_path)
|
||||||
return
|
return
|
||||||
if db_ver > up_ver:
|
if db_ver > up_ver:
|
||||||
critical_error_message_box(
|
critical_error_message_box(
|
||||||
@ -379,7 +383,7 @@ class Manager(object):
|
|||||||
try:
|
try:
|
||||||
self.session = init_schema(self.db_url)
|
self.session = init_schema(self.db_url)
|
||||||
except (SQLAlchemyError, DBAPIError):
|
except (SQLAlchemyError, DBAPIError):
|
||||||
handle_db_error(plugin_name, str(db_file_path))
|
handle_db_error(plugin_name, db_file_path)
|
||||||
else:
|
else:
|
||||||
self.session = session
|
self.session = session
|
||||||
|
|
||||||
|
@ -454,15 +454,16 @@ class MediaManagerItem(QtWidgets.QWidget, RegistryProperties):
|
|||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def generate_slide_data(self, service_item, item=None, xml_version=False, remote=False,
|
def generate_slide_data(self, service_item, *, item=None, remote=False, context=ServiceItemContext.Live,
|
||||||
context=ServiceItemContext.Live):
|
file_path=None):
|
||||||
"""
|
"""
|
||||||
Generate the slide data. Needs to be implemented by the plugin.
|
Generate the slide data. Needs to be implemented by the plugin.
|
||||||
|
|
||||||
:param service_item: The service Item to be processed
|
:param service_item: The service Item to be processed
|
||||||
:param item: The database item to be used to build the service item
|
:param item: The database item to be used to build the service item
|
||||||
:param xml_version:
|
|
||||||
:param remote: Was this remote triggered (False)
|
:param remote: Was this remote triggered (False)
|
||||||
:param context: The service context
|
:param context: The service context
|
||||||
|
:param openlp.core.common.path.Path file_path:
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError('MediaManagerItem.generate_slide_data needs to be defined by the plugin')
|
raise NotImplementedError('MediaManagerItem.generate_slide_data needs to be defined by the plugin')
|
||||||
|
|
||||||
@ -624,17 +625,16 @@ class MediaManagerItem(QtWidgets.QWidget, RegistryProperties):
|
|||||||
'You must select a {title} '
|
'You must select a {title} '
|
||||||
'service item.').format(title=self.title))
|
'service item.').format(title=self.title))
|
||||||
|
|
||||||
def build_service_item(self, item=None, xml_version=False, remote=False, context=ServiceItemContext.Live):
|
def build_service_item(self, item=None, remote=False, context=ServiceItemContext.Live):
|
||||||
"""
|
"""
|
||||||
Common method for generating a service item
|
Common method for generating a service item
|
||||||
:param item: Service Item to be built.
|
:param item: Service Item to be built.
|
||||||
:param xml_version: version of XML (False)
|
|
||||||
:param remote: Remote triggered (False)
|
:param remote: Remote triggered (False)
|
||||||
:param context: The context on which this is called
|
:param context: The context on which this is called
|
||||||
"""
|
"""
|
||||||
service_item = ServiceItem(self.plugin)
|
service_item = ServiceItem(self.plugin)
|
||||||
service_item.add_icon()
|
service_item.add_icon()
|
||||||
if self.generate_slide_data(service_item, item, xml_version, remote, context):
|
if self.generate_slide_data(service_item, item=item, remote=remote, context=context):
|
||||||
return service_item
|
return service_item
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
@ -263,7 +263,7 @@ class ServiceItem(RegistryProperties):
|
|||||||
file_location = os.path.join(path, file_name)
|
file_location = os.path.join(path, file_name)
|
||||||
file_location_hash = md5_hash(file_location.encode('utf-8'))
|
file_location_hash = md5_hash(file_location.encode('utf-8'))
|
||||||
image = os.path.join(str(AppLocation.get_section_data_path(self.name)), 'thumbnails',
|
image = os.path.join(str(AppLocation.get_section_data_path(self.name)), 'thumbnails',
|
||||||
file_location_hash, ntpath.basename(image))
|
file_location_hash, ntpath.basename(image)) # TODO: Pathlib
|
||||||
self.slides.append({'title': file_name, 'image': image, 'path': path, 'display_title': display_title,
|
self.slides.append({'title': file_name, 'image': image, 'path': path, 'display_title': display_title,
|
||||||
'notes': notes, 'thumbnail': image})
|
'notes': notes, 'thumbnail': image})
|
||||||
# if self.is_capable(ItemCapabilities.HasThumbnails):
|
# if self.is_capable(ItemCapabilities.HasThumbnails):
|
||||||
@ -361,7 +361,7 @@ class ServiceItem(RegistryProperties):
|
|||||||
if isinstance(file_path, str):
|
if isinstance(file_path, str):
|
||||||
# Handle service files prior to OpenLP 3.0
|
# Handle service files prior to OpenLP 3.0
|
||||||
# Windows can handle both forward and backward slashes, so we use ntpath to get the basename
|
# Windows can handle both forward and backward slashes, so we use ntpath to get the basename
|
||||||
file_path = Path(path, ntpath.basename(file_path))
|
file_path = path / ntpath.basename(file_path)
|
||||||
self.background_audio.append(file_path)
|
self.background_audio.append(file_path)
|
||||||
self.theme_overwritten = header.get('theme_overwritten', False)
|
self.theme_overwritten = header.get('theme_overwritten', False)
|
||||||
if self.service_item_type == ServiceItemType.Text:
|
if self.service_item_type == ServiceItemType.Text:
|
||||||
@ -374,7 +374,7 @@ class ServiceItem(RegistryProperties):
|
|||||||
if path:
|
if path:
|
||||||
self.has_original_files = False
|
self.has_original_files = False
|
||||||
for text_image in service_item['serviceitem']['data']:
|
for text_image in service_item['serviceitem']['data']:
|
||||||
file_path = os.path.join(path, text_image)
|
file_path = path / text_image
|
||||||
self.add_from_image(file_path, text_image, background)
|
self.add_from_image(file_path, text_image, background)
|
||||||
else:
|
else:
|
||||||
for text_image in service_item['serviceitem']['data']:
|
for text_image in service_item['serviceitem']['data']:
|
||||||
|
@ -478,7 +478,7 @@ class AdvancedTab(SettingsTab):
|
|||||||
minute=self.service_name_time.time().minute()
|
minute=self.service_name_time.time().minute()
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
service_name_example = format_time(str(self.service_name_edit.text()), local_time)
|
service_name_example = format_time(self.service_name_edit.text(), local_time)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
preset_is_valid = False
|
preset_is_valid = False
|
||||||
service_name_example = translate('OpenLP.AdvancedTab', 'Syntax error.')
|
service_name_example = translate('OpenLP.AdvancedTab', 'Syntax error.')
|
||||||
|
@ -77,11 +77,11 @@ class Ui_ExceptionDialog(object):
|
|||||||
self.save_report_button = create_button(exception_dialog, 'save_report_button',
|
self.save_report_button = create_button(exception_dialog, 'save_report_button',
|
||||||
icon=UiIcons().save,
|
icon=UiIcons().save,
|
||||||
click=self.on_save_report_button_clicked)
|
click=self.on_save_report_button_clicked)
|
||||||
self.attach_tile_button = create_button(exception_dialog, 'attach_tile_button',
|
self.attach_file_button = create_button(exception_dialog, 'attach_file_button',
|
||||||
icon=UiIcons().open,
|
icon=UiIcons().open,
|
||||||
click=self.on_attach_file_button_clicked)
|
click=self.on_attach_file_button_clicked)
|
||||||
self.button_box = create_button_box(exception_dialog, 'button_box', ['close'],
|
self.button_box = create_button_box(exception_dialog, 'button_box', ['close'],
|
||||||
[self.send_report_button, self.save_report_button, self.attach_tile_button])
|
[self.send_report_button, self.save_report_button, self.attach_file_button])
|
||||||
self.exception_layout.addWidget(self.button_box)
|
self.exception_layout.addWidget(self.button_box)
|
||||||
|
|
||||||
self.retranslate_ui(exception_dialog)
|
self.retranslate_ui(exception_dialog)
|
||||||
@ -112,4 +112,4 @@ class Ui_ExceptionDialog(object):
|
|||||||
).format(first_part=exception_part1))
|
).format(first_part=exception_part1))
|
||||||
self.send_report_button.setText(translate('OpenLP.ExceptionDialog', 'Send E-Mail'))
|
self.send_report_button.setText(translate('OpenLP.ExceptionDialog', 'Send E-Mail'))
|
||||||
self.save_report_button.setText(translate('OpenLP.ExceptionDialog', 'Save to File'))
|
self.save_report_button.setText(translate('OpenLP.ExceptionDialog', 'Save to File'))
|
||||||
self.attach_tile_button.setText(translate('OpenLP.ExceptionDialog', 'Attach File'))
|
self.attach_file_button.setText(translate('OpenLP.ExceptionDialog', 'Attach File'))
|
||||||
|
@ -95,12 +95,14 @@ class ExceptionForm(QtWidgets.QDialog, Ui_ExceptionDialog, RegistryProperties):
|
|||||||
"""
|
"""
|
||||||
Saving exception log and system information to a file.
|
Saving exception log and system information to a file.
|
||||||
"""
|
"""
|
||||||
|
while True:
|
||||||
file_path, filter_used = FileDialog.getSaveFileName(
|
file_path, filter_used = FileDialog.getSaveFileName(
|
||||||
self,
|
self,
|
||||||
translate('OpenLP.ExceptionForm', 'Save Crash Report'),
|
translate('OpenLP.ExceptionForm', 'Save Crash Report'),
|
||||||
Settings().value(self.settings_section + '/last directory'),
|
Settings().value(self.settings_section + '/last directory'),
|
||||||
translate('OpenLP.ExceptionForm', 'Text files (*.txt *.log *.text)'))
|
translate('OpenLP.ExceptionForm', 'Text files (*.txt *.log *.text)'))
|
||||||
if file_path:
|
if file_path is None:
|
||||||
|
break
|
||||||
Settings().setValue(self.settings_section + '/last directory', file_path.parent)
|
Settings().setValue(self.settings_section + '/last directory', file_path.parent)
|
||||||
opts = self._create_report()
|
opts = self._create_report()
|
||||||
report_text = self.report_text.format(version=opts['version'], description=opts['description'],
|
report_text = self.report_text.format(version=opts['version'], description=opts['description'],
|
||||||
@ -108,8 +110,13 @@ class ExceptionForm(QtWidgets.QDialog, Ui_ExceptionDialog, RegistryProperties):
|
|||||||
try:
|
try:
|
||||||
with file_path.open('w') as report_file:
|
with file_path.open('w') as report_file:
|
||||||
report_file.write(report_text)
|
report_file.write(report_text)
|
||||||
except OSError:
|
break
|
||||||
|
except OSError as e:
|
||||||
log.exception('Failed to write crash report')
|
log.exception('Failed to write crash report')
|
||||||
|
QtWidgets.QMessageBox.warning(
|
||||||
|
self, translate('OpenLP.ExceptionDialog', 'Failed to Save Report'),
|
||||||
|
translate('OpenLP.ExceptionDialog', 'The following error occured when saving the report.\n\n'
|
||||||
|
'{exception}').format(file_name=file_path, exception=e))
|
||||||
|
|
||||||
def on_send_report_button_clicked(self):
|
def on_send_report_button_clicked(self):
|
||||||
"""
|
"""
|
||||||
|
@ -47,7 +47,6 @@ class GeneralTab(SettingsTab):
|
|||||||
"""
|
"""
|
||||||
Initialise the general settings tab
|
Initialise the general settings tab
|
||||||
"""
|
"""
|
||||||
self.logo_file = ':/graphics/openlp-splash-screen.png'
|
|
||||||
self.logo_background_color = '#ffffff'
|
self.logo_background_color = '#ffffff'
|
||||||
self.screens = ScreenList()
|
self.screens = ScreenList()
|
||||||
self.icon_path = ':/icon/openlp-logo.svg'
|
self.icon_path = ':/icon/openlp-logo.svg'
|
||||||
|
@ -1334,7 +1334,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, LogMixin, RegistryPropert
|
|||||||
self.show_status_message(
|
self.show_status_message(
|
||||||
translate('OpenLP.MainWindow', 'Copying OpenLP data to new data directory location - {path} '
|
translate('OpenLP.MainWindow', 'Copying OpenLP data to new data directory location - {path} '
|
||||||
'- Please wait for copy to finish').format(path=self.new_data_path))
|
'- Please wait for copy to finish').format(path=self.new_data_path))
|
||||||
dir_util.copy_tree(str(old_data_path), str(self.new_data_path))
|
dir_util.copy_tree(old_data_path, self.new_data_path)
|
||||||
self.log_info('Copy successful')
|
self.log_info('Copy successful')
|
||||||
except (OSError, DistutilsFileError) as why:
|
except (OSError, DistutilsFileError) as why:
|
||||||
self.application.set_normal_cursor()
|
self.application.set_normal_cursor()
|
||||||
|
@ -25,7 +25,6 @@ The service manager sets up, loads, saves and manages services.
|
|||||||
import html
|
import html
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import shutil
|
|
||||||
import zipfile
|
import zipfile
|
||||||
from contextlib import suppress
|
from contextlib import suppress
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
@ -234,7 +233,7 @@ class Ui_ServiceManager(object):
|
|||||||
self.service_manager_list.itemExpanded.connect(self.expanded)
|
self.service_manager_list.itemExpanded.connect(self.expanded)
|
||||||
# Last little bits of setting up
|
# Last little bits of setting up
|
||||||
self.service_theme = Settings().value(self.main_window.service_manager_settings_section + '/service theme')
|
self.service_theme = Settings().value(self.main_window.service_manager_settings_section + '/service theme')
|
||||||
self.service_path = str(AppLocation.get_section_data_path('servicemanager'))
|
self.service_path = AppLocation.get_section_data_path('servicemanager')
|
||||||
# build the drag and drop context menu
|
# build the drag and drop context menu
|
||||||
self.dnd_menu = QtWidgets.QMenu()
|
self.dnd_menu = QtWidgets.QMenu()
|
||||||
self.new_action = self.dnd_menu.addAction(translate('OpenLP.ServiceManager', '&Add New Item'))
|
self.new_action = self.dnd_menu.addAction(translate('OpenLP.ServiceManager', '&Add New Item'))
|
||||||
@ -590,11 +589,11 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
|
|||||||
self.main_window.increment_progress_bar(service_content_size)
|
self.main_window.increment_progress_bar(service_content_size)
|
||||||
# Finally add all the listed media files.
|
# Finally add all the listed media files.
|
||||||
for write_path in write_list:
|
for write_path in write_list:
|
||||||
zip_file.write(str(write_path), str(write_path))
|
zip_file.write(write_path, write_path)
|
||||||
self.main_window.increment_progress_bar(write_path.stat().st_size)
|
self.main_window.increment_progress_bar(write_path.stat().st_size)
|
||||||
with suppress(FileNotFoundError):
|
with suppress(FileNotFoundError):
|
||||||
file_path.unlink()
|
file_path.unlink()
|
||||||
os.link(temp_file.name, str(file_path))
|
os.link(temp_file.name, file_path)
|
||||||
Settings().setValue(self.main_window.service_manager_settings_section + '/last directory', file_path.parent)
|
Settings().setValue(self.main_window.service_manager_settings_section + '/last directory', file_path.parent)
|
||||||
except (PermissionError, OSError) as error:
|
except (PermissionError, OSError) as error:
|
||||||
self.log_exception('Failed to save service to disk: {name}'.format(name=file_path))
|
self.log_exception('Failed to save service to disk: {name}'.format(name=file_path))
|
||||||
@ -679,7 +678,7 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
|
|||||||
service_data = None
|
service_data = None
|
||||||
self.application.set_busy_cursor()
|
self.application.set_busy_cursor()
|
||||||
try:
|
try:
|
||||||
with zipfile.ZipFile(str(file_path)) as zip_file:
|
with zipfile.ZipFile(file_path) as zip_file:
|
||||||
compressed_size = 0
|
compressed_size = 0
|
||||||
for zip_info in zip_file.infolist():
|
for zip_info in zip_file.infolist():
|
||||||
compressed_size += zip_info.compress_size
|
compressed_size += zip_info.compress_size
|
||||||
@ -692,7 +691,7 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
|
|||||||
service_data = json_file.read()
|
service_data = json_file.read()
|
||||||
else:
|
else:
|
||||||
zip_info.filename = os.path.basename(zip_info.filename)
|
zip_info.filename = os.path.basename(zip_info.filename)
|
||||||
zip_file.extract(zip_info, str(self.service_path))
|
zip_file.extract(zip_info, self.service_path)
|
||||||
self.main_window.increment_progress_bar(zip_info.compress_size)
|
self.main_window.increment_progress_bar(zip_info.compress_size)
|
||||||
if service_data:
|
if service_data:
|
||||||
items = json.loads(service_data, cls=OpenLPJsonDecoder)
|
items = json.loads(service_data, cls=OpenLPJsonDecoder)
|
||||||
@ -705,11 +704,13 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
|
|||||||
else:
|
else:
|
||||||
raise ValidationError(msg='No service data found')
|
raise ValidationError(msg='No service data found')
|
||||||
except (NameError, OSError, ValidationError, zipfile.BadZipFile):
|
except (NameError, OSError, ValidationError, zipfile.BadZipFile):
|
||||||
|
self.application.set_normal_cursor()
|
||||||
self.log_exception('Problem loading service file {name}'.format(name=file_path))
|
self.log_exception('Problem loading service file {name}'.format(name=file_path))
|
||||||
critical_error_message_box(
|
critical_error_message_box(
|
||||||
message=translate('OpenLP.ServiceManager',
|
message=translate('OpenLP.ServiceManager',
|
||||||
'The service file {file_path} could not be loaded because it is either corrupt, or '
|
'The service file {file_path} could not be loaded because it is either corrupt, '
|
||||||
'not a valid OpenLP 2 or OpenLP 3 service file.'.format(file_path=file_path)))
|
'inaccessible, or not a valid OpenLP 2 or OpenLP 3 service file.'
|
||||||
|
).format(file_path=file_path))
|
||||||
self.main_window.finished_progress_bar()
|
self.main_window.finished_progress_bar()
|
||||||
self.application.set_normal_cursor()
|
self.application.set_normal_cursor()
|
||||||
self.repaint_service_list(-1, -1)
|
self.repaint_service_list(-1, -1)
|
||||||
@ -1237,11 +1238,11 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
|
|||||||
"""
|
"""
|
||||||
Empties the service_path of temporary files on system exit.
|
Empties the service_path of temporary files on system exit.
|
||||||
"""
|
"""
|
||||||
for file_name in os.listdir(self.service_path):
|
for file_path in self.service_path.iterdir():
|
||||||
file_path = Path(self.service_path, file_name)
|
|
||||||
delete_file(file_path)
|
delete_file(file_path)
|
||||||
if os.path.exists(os.path.join(self.service_path, 'audio')):
|
audio_path = self.service_path / 'audio'
|
||||||
shutil.rmtree(os.path.join(self.service_path, 'audio'), True)
|
if audio_path.exists():
|
||||||
|
audio_path.rmtree(True)
|
||||||
|
|
||||||
def on_theme_combo_box_selected(self, current_index):
|
def on_theme_combo_box_selected(self, current_index):
|
||||||
"""
|
"""
|
||||||
|
@ -150,7 +150,7 @@ class ThemeManager(QtWidgets.QWidget, RegistryBase, Ui_ThemeManager, LogMixin, R
|
|||||||
self.global_theme = Settings().value(self.settings_section + '/global theme')
|
self.global_theme = Settings().value(self.settings_section + '/global theme')
|
||||||
self.build_theme_path()
|
self.build_theme_path()
|
||||||
self.load_first_time_themes()
|
self.load_first_time_themes()
|
||||||
self.upgrade_themes()
|
self.upgrade_themes() # TODO: Can be removed when upgrade path from OpenLP 2.4 no longer needed
|
||||||
|
|
||||||
def bootstrap_post_set_up(self):
|
def bootstrap_post_set_up(self):
|
||||||
"""
|
"""
|
||||||
@ -422,10 +422,10 @@ class ThemeManager(QtWidgets.QWidget, RegistryBase, Ui_ThemeManager, LogMixin, R
|
|||||||
:rtype: bool
|
:rtype: bool
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
with zipfile.ZipFile(str(theme_path), 'w') as theme_zip:
|
with zipfile.ZipFile(theme_path, 'w') as theme_zip:
|
||||||
source_path = self.theme_path / theme_name
|
source_path = self.theme_path / theme_name
|
||||||
for file_path in source_path.iterdir():
|
for file_path in source_path.iterdir():
|
||||||
theme_zip.write(str(file_path), os.path.join(theme_name, file_path.name))
|
theme_zip.write(file_path, Path(theme_name, file_path.name))
|
||||||
return True
|
return True
|
||||||
except OSError as ose:
|
except OSError as ose:
|
||||||
self.log_exception('Export Theme Failed')
|
self.log_exception('Export Theme Failed')
|
||||||
@ -567,10 +567,10 @@ class ThemeManager(QtWidgets.QWidget, RegistryBase, Ui_ThemeManager, LogMixin, R
|
|||||||
json_theme = False
|
json_theme = False
|
||||||
theme_name = ""
|
theme_name = ""
|
||||||
try:
|
try:
|
||||||
with zipfile.ZipFile(str(file_path)) as theme_zip:
|
with zipfile.ZipFile(file_path) as theme_zip:
|
||||||
json_file = [name for name in theme_zip.namelist() if os.path.splitext(name)[1].lower() == '.json']
|
json_file = [name for name in theme_zip.namelist() if os.path.splitext(name)[1].lower() == '.json']
|
||||||
if len(json_file) != 1:
|
if len(json_file) != 1:
|
||||||
# TODO: remove XML handling after the 2.6 release.
|
# TODO: remove XML handling after once the upgrade path from 2.4 is no longer required
|
||||||
xml_file = [name for name in theme_zip.namelist() if os.path.splitext(name)[1].lower() == '.xml']
|
xml_file = [name for name in theme_zip.namelist() if os.path.splitext(name)[1].lower() == '.xml']
|
||||||
if len(xml_file) != 1:
|
if len(xml_file) != 1:
|
||||||
self.log_error('Theme contains "{val:d}" theme files'.format(val=len(xml_file)))
|
self.log_error('Theme contains "{val:d}" theme files'.format(val=len(xml_file)))
|
||||||
@ -607,12 +607,12 @@ class ThemeManager(QtWidgets.QWidget, RegistryBase, Ui_ThemeManager, LogMixin, R
|
|||||||
else:
|
else:
|
||||||
with full_name.open('wb') as out_file:
|
with full_name.open('wb') as out_file:
|
||||||
out_file.write(theme_zip.read(zipped_file))
|
out_file.write(theme_zip.read(zipped_file))
|
||||||
except (OSError, zipfile.BadZipFile):
|
except (OSError, ValidationError, zipfile.BadZipFile):
|
||||||
self.log_exception('Importing theme from zip failed {name}'.format(name=file_path))
|
self.log_exception('Importing theme from zip failed {name}'.format(name=file_path))
|
||||||
raise ValidationError
|
critical_error_message_box(
|
||||||
except ValidationError:
|
translate('OpenLP.ThemeManager', 'Import Error'),
|
||||||
critical_error_message_box(translate('OpenLP.ThemeManager', 'Validation Error'),
|
translate('OpenLP.ThemeManager', 'There was a problem imoorting {file_name}.\n\nIt is corrupt,'
|
||||||
translate('OpenLP.ThemeManager', 'File is not a valid theme.'))
|
'inaccessible or not a valid theme.').format(file_name=file_path))
|
||||||
finally:
|
finally:
|
||||||
if not abort_import:
|
if not abort_import:
|
||||||
# As all files are closed, we can create the Theme.
|
# As all files are closed, we can create the Theme.
|
||||||
|
@ -200,7 +200,7 @@ def get_library_versions():
|
|||||||
"""
|
"""
|
||||||
library_versions = OrderedDict([(library, _get_lib_version(*args)) for library, args in LIBRARIES.items()])
|
library_versions = OrderedDict([(library, _get_lib_version(*args)) for library, args in LIBRARIES.items()])
|
||||||
# Just update the dict for display purposes, changing the None to a '-'
|
# Just update the dict for display purposes, changing the None to a '-'
|
||||||
for library, version in library_versions:
|
for library, version in library_versions.items():
|
||||||
if version is None:
|
if version is None:
|
||||||
library_versions[library] = '-'
|
library_versions[library] = '-'
|
||||||
return library_versions
|
return library_versions
|
||||||
|
@ -352,7 +352,7 @@ class PathEdit(QtWidgets.QWidget):
|
|||||||
:rtype: None
|
:rtype: None
|
||||||
"""
|
"""
|
||||||
if self._path != path:
|
if self._path != path:
|
||||||
self._path = path
|
self.path = path
|
||||||
self.pathChanged.emit(path)
|
self.pathChanged.emit(path)
|
||||||
|
|
||||||
|
|
||||||
|
@ -27,13 +27,11 @@ import logging
|
|||||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
|
|
||||||
from openlp.core.common import is_macosx
|
from openlp.core.common import is_macosx
|
||||||
from openlp.core.common.i18n import UiStrings, translate
|
from openlp.core.common.i18n import translate
|
||||||
from openlp.core.common.mixins import RegistryProperties
|
from openlp.core.common.mixins import RegistryProperties
|
||||||
from openlp.core.common.registry import Registry
|
from openlp.core.common.registry import Registry
|
||||||
from openlp.core.common.settings import Settings
|
|
||||||
from openlp.core.lib.ui import add_welcome_page
|
from openlp.core.lib.ui import add_welcome_page
|
||||||
from openlp.core.ui.icons import UiIcons
|
from openlp.core.ui.icons import UiIcons
|
||||||
from openlp.core.widgets.dialogs import FileDialog
|
|
||||||
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
@ -280,41 +278,3 @@ class OpenLPWizard(QtWidgets.QWizard, RegistryProperties):
|
|||||||
self.finish_button.setVisible(True)
|
self.finish_button.setVisible(True)
|
||||||
self.cancel_button.setVisible(False)
|
self.cancel_button.setVisible(False)
|
||||||
self.application.process_events()
|
self.application.process_events()
|
||||||
|
|
||||||
def get_file_name(self, title, editbox, setting_name, filters=''):
|
|
||||||
"""
|
|
||||||
Opens a FileDialog and saves the filename to the given editbox.
|
|
||||||
|
|
||||||
:param str title: The title of the dialog.
|
|
||||||
:param QtWidgets.QLineEdit editbox: An QLineEdit.
|
|
||||||
:param str setting_name: The place where to save the last opened directory.
|
|
||||||
:param str filters: The file extension filters. It should contain the file description
|
|
||||||
as well as the file extension. For example::
|
|
||||||
|
|
||||||
'OpenLP 2 Databases (*.sqlite)'
|
|
||||||
:rtype: None
|
|
||||||
"""
|
|
||||||
if filters:
|
|
||||||
filters += ';;'
|
|
||||||
filters += '%s (*)' % UiStrings().AllFiles
|
|
||||||
file_path, filter_used = FileDialog.getOpenFileName(
|
|
||||||
self, title, Settings().value(self.plugin.settings_section + '/' + setting_name), filters)
|
|
||||||
if file_path:
|
|
||||||
editbox.setText(str(file_path))
|
|
||||||
Settings().setValue(self.plugin.settings_section + '/' + setting_name, file_path.parent)
|
|
||||||
|
|
||||||
def get_folder(self, title, editbox, setting_name):
|
|
||||||
"""
|
|
||||||
Opens a FileDialog and saves the selected folder to the given editbox.
|
|
||||||
|
|
||||||
:param str title: The title of the dialog.
|
|
||||||
:param QtWidgets.QLineEdit editbox: An QLineEditbox.
|
|
||||||
:param str setting_name: The place where to save the last opened directory.
|
|
||||||
:rtype: None
|
|
||||||
"""
|
|
||||||
folder_path = FileDialog.getExistingDirectory(
|
|
||||||
self, title, Settings().value(self.plugin.settings_section + '/' + setting_name),
|
|
||||||
FileDialog.ShowDirsOnly)
|
|
||||||
if folder_path:
|
|
||||||
editbox.setText(str(folder_path))
|
|
||||||
Settings().setValue(self.plugin.settings_section + '/' + setting_name, folder_path)
|
|
||||||
|
@ -463,14 +463,14 @@ class BibleImportForm(OpenLPWizard):
|
|||||||
UiStrings().NFSs, translate('BiblesPlugin.ImportWizardForm',
|
UiStrings().NFSs, translate('BiblesPlugin.ImportWizardForm',
|
||||||
'You need to specify a file with books of the Bible to use in the '
|
'You need to specify a file with books of the Bible to use in the '
|
||||||
'import.'))
|
'import.'))
|
||||||
self.csv_books_edit.setFocus()
|
self.csv_books_path_edit.setFocus()
|
||||||
return False
|
return False
|
||||||
elif not self.field('csv_versefile'):
|
elif not self.field('csv_versefile'):
|
||||||
critical_error_message_box(
|
critical_error_message_box(
|
||||||
UiStrings().NFSs,
|
UiStrings().NFSs,
|
||||||
translate('BiblesPlugin.ImportWizardForm', 'You need to specify a file of Bible verses to '
|
translate('BiblesPlugin.ImportWizardForm', 'You need to specify a file of Bible verses to '
|
||||||
'import.'))
|
'import.'))
|
||||||
self.csv_verses_edit.setFocus()
|
self.csv_verses_pathedit.setFocus()
|
||||||
return False
|
return False
|
||||||
elif self.field('source_format') == BibleFormat.OpenSong:
|
elif self.field('source_format') == BibleFormat.OpenSong:
|
||||||
if not self.field('opensong_file'):
|
if not self.field('opensong_file'):
|
||||||
|
@ -48,9 +48,9 @@ class BibleImport(BibleDB, LogMixin, RegistryProperties):
|
|||||||
"""
|
"""
|
||||||
Check if the supplied file is compressed
|
Check if the supplied file is compressed
|
||||||
|
|
||||||
:param file_path: A path to the file to check
|
:param openlp.core.common.path.Path file_path: A path to the file to check
|
||||||
"""
|
"""
|
||||||
if is_zipfile(str(file_path)):
|
if is_zipfile(file_path):
|
||||||
critical_error_message_box(
|
critical_error_message_box(
|
||||||
message=translate('BiblesPlugin.BibleImport',
|
message=translate('BiblesPlugin.BibleImport',
|
||||||
'The file "{file}" you supplied is compressed. You must decompress it before import.'
|
'The file "{file}" you supplied is compressed. You must decompress it before import.'
|
||||||
|
@ -158,11 +158,10 @@ class BibleDB(Manager):
|
|||||||
self.name = kwargs['name']
|
self.name = kwargs['name']
|
||||||
if not isinstance(self.name, str):
|
if not isinstance(self.name, str):
|
||||||
self.name = str(self.name, 'utf-8')
|
self.name = str(self.name, 'utf-8')
|
||||||
# TODO: To path object
|
self.file_path = Path(clean_filename(self.name) + '.sqlite')
|
||||||
file_path = Path(clean_filename(self.name) + '.sqlite')
|
|
||||||
if 'file' in kwargs:
|
if 'file' in kwargs:
|
||||||
file_path = kwargs['file']
|
self.file_path = kwargs['file']
|
||||||
Manager.__init__(self, 'bibles', init_schema, file_path, upgrade)
|
Manager.__init__(self, 'bibles', init_schema, self.file_path, upgrade)
|
||||||
if self.session and 'file' in kwargs:
|
if self.session and 'file' in kwargs:
|
||||||
self.get_name()
|
self.get_name()
|
||||||
self._is_web_bible = None
|
self._is_web_bible = None
|
||||||
@ -750,7 +749,7 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class AlternativeBookNamesDB(QtCore.QObject, Manager):
|
class AlternativeBookNamesDB(Manager):
|
||||||
"""
|
"""
|
||||||
This class represents a database-bound alternative book names system.
|
This class represents a database-bound alternative book names system.
|
||||||
"""
|
"""
|
||||||
@ -765,8 +764,9 @@ class AlternativeBookNamesDB(QtCore.QObject, Manager):
|
|||||||
"""
|
"""
|
||||||
if AlternativeBookNamesDB.cursor is None:
|
if AlternativeBookNamesDB.cursor is None:
|
||||||
file_path = AppLocation.get_directory(AppLocation.DataDir) / 'bibles' / 'alternative_book_names.sqlite'
|
file_path = AppLocation.get_directory(AppLocation.DataDir) / 'bibles' / 'alternative_book_names.sqlite'
|
||||||
|
exists = file_path.exists()
|
||||||
AlternativeBookNamesDB.conn = sqlite3.connect(str(file_path))
|
AlternativeBookNamesDB.conn = sqlite3.connect(str(file_path))
|
||||||
if not file_path.exists():
|
if not exists:
|
||||||
# create new DB, create table alternative_book_names
|
# create new DB, create table alternative_book_names
|
||||||
AlternativeBookNamesDB.conn.execute(
|
AlternativeBookNamesDB.conn.execute(
|
||||||
'CREATE TABLE alternative_book_names(id INTEGER NOT NULL, '
|
'CREATE TABLE alternative_book_names(id INTEGER NOT NULL, '
|
||||||
|
@ -51,7 +51,7 @@ class WordProjectBible(BibleImport):
|
|||||||
Unzip the file to a temporary directory
|
Unzip the file to a temporary directory
|
||||||
"""
|
"""
|
||||||
self.tmp = TemporaryDirectory()
|
self.tmp = TemporaryDirectory()
|
||||||
with ZipFile(str(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)
|
||||||
self.base_path = Path(self.tmp.name, self.file_path.stem)
|
self.base_path = Path(self.tmp.name, self.file_path.stem)
|
||||||
|
|
||||||
|
@ -187,7 +187,7 @@ class BibleManager(LogMixin, RegistryProperties):
|
|||||||
bible = self.db_cache[name]
|
bible = self.db_cache[name]
|
||||||
bible.session.close_all()
|
bible.session.close_all()
|
||||||
bible.session = None
|
bible.session = None
|
||||||
return delete_file(Path(bible.path, bible.file))
|
return delete_file(bible.path, bible.file_path)
|
||||||
|
|
||||||
def get_bibles(self):
|
def get_bibles(self):
|
||||||
"""
|
"""
|
||||||
|
@ -911,16 +911,16 @@ class BibleMediaItem(MediaManagerItem):
|
|||||||
list_widget_items.append(bible_verse)
|
list_widget_items.append(bible_verse)
|
||||||
return list_widget_items
|
return list_widget_items
|
||||||
|
|
||||||
def generate_slide_data(self, service_item, item=None, xml_version=False, remote=False,
|
def generate_slide_data(self, service_item, *, item=None, remote=False, context=ServiceItemContext.Service,
|
||||||
context=ServiceItemContext.Service):
|
**kwargs):
|
||||||
"""
|
"""
|
||||||
Generate the slide data. Needs to be implemented by the plugin.
|
Generate the slide data. Needs to be implemented by the plugin.
|
||||||
|
|
||||||
:param service_item: The service item to be built on
|
:param service_item: The service item to be built on
|
||||||
:param item: The Song item to be used
|
:param item: The Song item to be used
|
||||||
:param xml_version: The xml version (not used)
|
|
||||||
:param remote: Triggered from remote
|
:param remote: Triggered from remote
|
||||||
:param context: Why is it being generated
|
:param context: Why is it being generated
|
||||||
|
:param kwargs: Consume other unused args specified by the base implementation, but not use by this one.
|
||||||
"""
|
"""
|
||||||
log.debug('generating slide data')
|
log.debug('generating slide data')
|
||||||
if item:
|
if item:
|
||||||
|
@ -28,7 +28,7 @@ from sqlalchemy.sql import and_, func, or_
|
|||||||
from openlp.core.common.i18n import UiStrings, translate
|
from openlp.core.common.i18n import UiStrings, translate
|
||||||
from openlp.core.common.registry import Registry
|
from openlp.core.common.registry import Registry
|
||||||
from openlp.core.common.settings import Settings
|
from openlp.core.common.settings import Settings
|
||||||
from openlp.core.lib import ServiceItemContext, check_item_selected
|
from openlp.core.lib import check_item_selected
|
||||||
from openlp.core.lib.mediamanageritem import MediaManagerItem
|
from openlp.core.lib.mediamanageritem import MediaManagerItem
|
||||||
from openlp.core.lib.plugin import PluginStatus
|
from openlp.core.lib.plugin import PluginStatus
|
||||||
from openlp.core.lib.serviceitem import ItemCapabilities
|
from openlp.core.lib.serviceitem import ItemCapabilities
|
||||||
@ -219,15 +219,12 @@ class CustomMediaItem(MediaManagerItem):
|
|||||||
self.search_text_edit.setFocus()
|
self.search_text_edit.setFocus()
|
||||||
self.search_text_edit.selectAll()
|
self.search_text_edit.selectAll()
|
||||||
|
|
||||||
def generate_slide_data(self, service_item, item=None, xml_version=False,
|
def generate_slide_data(self, service_item, *, item=None, **kwargs):
|
||||||
remote=False, context=ServiceItemContext.Service):
|
|
||||||
"""
|
"""
|
||||||
Generate the slide data. Needs to be implemented by the plugin.
|
Generate the slide data. Needs to be implemented by the plugin.
|
||||||
:param service_item: To be updated
|
:param service_item: To be updated
|
||||||
:param item: The custom database item to be used
|
:param item: The custom database item to be used
|
||||||
:param xml_version: No used
|
:param kwargs: Consume other unused args specified by the base implementation, but not use by this one.
|
||||||
:param remote: Is this triggered by the Preview Controller or Service Manager.
|
|
||||||
:param context: Why is this item required to be build (Default Service).
|
|
||||||
"""
|
"""
|
||||||
item_id = self._get_id_of_item_to_generate(item, self.remote_custom)
|
item_id = self._get_id_of_item_to_generate(item, self.remote_custom)
|
||||||
service_item.add_capability(ItemCapabilities.CanEdit)
|
service_item.add_capability(ItemCapabilities.CanEdit)
|
||||||
|
@ -542,16 +542,16 @@ class ImageMediaItem(MediaManagerItem):
|
|||||||
image_items.sort(key=lambda item: get_natural_key(item.text(0)))
|
image_items.sort(key=lambda item: get_natural_key(item.text(0)))
|
||||||
target_group.addChildren(image_items)
|
target_group.addChildren(image_items)
|
||||||
|
|
||||||
def generate_slide_data(self, service_item, item=None, xml_version=False, remote=False,
|
def generate_slide_data(self, service_item, *, item=None, remote=False, context=ServiceItemContext.Service,
|
||||||
context=ServiceItemContext.Service):
|
**kwargs):
|
||||||
"""
|
"""
|
||||||
Generate the slide data. Needs to be implemented by the plugin.
|
Generate the slide data. Needs to be implemented by the plugin.
|
||||||
|
|
||||||
:param service_item: The service item to be built on
|
:param service_item: The service item to be built on
|
||||||
:param item: The Song item to be used
|
:param item: The Song item to be used
|
||||||
:param xml_version: The xml version (not used)
|
|
||||||
:param remote: Triggered from remote
|
:param remote: Triggered from remote
|
||||||
:param context: Why is it being generated
|
:param context: Why is it being generated
|
||||||
|
:param kwargs: Consume other unused args specified by the base implementation, but not use by this one.
|
||||||
"""
|
"""
|
||||||
background = QtGui.QColor(Settings().value(self.settings_section + '/background color'))
|
background = QtGui.QColor(Settings().value(self.settings_section + '/background color'))
|
||||||
if item:
|
if item:
|
||||||
|
@ -29,7 +29,7 @@ from openlp.core.state import State
|
|||||||
from openlp.core.common.applocation import AppLocation
|
from openlp.core.common.applocation import AppLocation
|
||||||
from openlp.core.common.i18n import UiStrings, get_natural_key, translate
|
from openlp.core.common.i18n import UiStrings, get_natural_key, translate
|
||||||
from openlp.core.common.mixins import RegistryProperties
|
from openlp.core.common.mixins import RegistryProperties
|
||||||
from openlp.core.common.path import Path, create_paths, path_to_str
|
from openlp.core.common.path import create_paths, path_to_str
|
||||||
from openlp.core.common.registry import Registry
|
from openlp.core.common.registry import Registry
|
||||||
from openlp.core.common.settings import Settings
|
from openlp.core.common.settings import Settings
|
||||||
from openlp.core.lib import MediaType, ServiceItemContext, check_item_selected
|
from openlp.core.lib import MediaType, ServiceItemContext, check_item_selected
|
||||||
@ -166,16 +166,16 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
|
|||||||
# self.display_type_combo_box.currentIndexChanged.connect(self.override_player_changed)
|
# self.display_type_combo_box.currentIndexChanged.connect(self.override_player_changed)
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def generate_slide_data(self, service_item, item=None, xml_version=False, remote=False,
|
def generate_slide_data(self, service_item, *, item=None, remote=False, context=ServiceItemContext.Service,
|
||||||
context=ServiceItemContext.Service):
|
**kwargs):
|
||||||
"""
|
"""
|
||||||
Generate the slide data. Needs to be implemented by the plugin.
|
Generate the slide data. Needs to be implemented by the plugin.
|
||||||
|
|
||||||
:param service_item: The service item to be built on
|
:param service_item: The service item to be built on
|
||||||
:param item: The Song item to be used
|
:param item: The Song item to be used
|
||||||
:param xml_version: The xml version (not used)
|
|
||||||
:param remote: Triggered from remote
|
:param remote: Triggered from remote
|
||||||
:param context: Why is it being generated
|
:param context: Why is it being generated
|
||||||
|
:param kwargs: Consume other unused args specified by the base implementation, but not use by this one.
|
||||||
"""
|
"""
|
||||||
if item is None:
|
if item is None:
|
||||||
item = self.list_view.currentItem()
|
item = self.list_view.currentItem()
|
||||||
@ -229,8 +229,8 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
|
|||||||
Initialize media item.
|
Initialize media item.
|
||||||
"""
|
"""
|
||||||
self.list_view.clear()
|
self.list_view.clear()
|
||||||
self.service_path = str(AppLocation.get_section_data_path(self.settings_section) / 'thumbnails')
|
self.service_path = AppLocation.get_section_data_path(self.settings_section) / 'thumbnails'
|
||||||
create_paths(Path(self.service_path))
|
create_paths(self.service_path)
|
||||||
self.load_list([path_to_str(file) for file in Settings().value(self.settings_section + '/media files')])
|
self.load_list([path_to_str(file) for file in Settings().value(self.settings_section + '/media files')])
|
||||||
self.rebuild_players()
|
self.rebuild_players()
|
||||||
|
|
||||||
@ -264,7 +264,7 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
|
|||||||
:param media: The media
|
:param media: The media
|
||||||
:param target_group:
|
:param target_group:
|
||||||
"""
|
"""
|
||||||
media.sort(key=lambda file_name: get_natural_key(os.path.split(str(file_name))[1]))
|
media.sort(key=lambda file_path: get_natural_key(file_path.name))
|
||||||
for track in media:
|
for track in media:
|
||||||
track_info = QtCore.QFileInfo(track)
|
track_info = QtCore.QFileInfo(track)
|
||||||
item_name = None
|
item_name = None
|
||||||
|
@ -260,16 +260,16 @@ class PresentationMediaItem(MediaManagerItem):
|
|||||||
doc.presentation_deleted()
|
doc.presentation_deleted()
|
||||||
doc.close_presentation()
|
doc.close_presentation()
|
||||||
|
|
||||||
def generate_slide_data(self, service_item, item=None, xml_version=False, remote=False,
|
def generate_slide_data(self, service_item, *, item=None, remote=False, context=ServiceItemContext.Service,
|
||||||
context=ServiceItemContext.Service, file_path=None):
|
file_path=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
Generate the slide data. Needs to be implemented by the plugin.
|
Generate the slide data. Needs to be implemented by the plugin.
|
||||||
|
|
||||||
:param service_item: The service item to be built on
|
:param service_item: The service item to be built on
|
||||||
:param item: The Song item to be used
|
:param item: The Song item to be used
|
||||||
:param xml_version: The xml version (not used)
|
|
||||||
:param remote: Triggered from remote
|
:param remote: Triggered from remote
|
||||||
:param context: Why is it being generated
|
:param context: Why is it being generated
|
||||||
|
:param kwargs: Consume other unused args specified by the base implementation, but not use by this one.
|
||||||
"""
|
"""
|
||||||
if item:
|
if item:
|
||||||
items = [item]
|
items = [item]
|
||||||
|
@ -337,14 +337,8 @@ class MessageListener(object):
|
|||||||
# Create a copy of the original item, and then clear the original item so it can be filled with images
|
# Create a copy of the original item, and then clear the original item so it can be filled with images
|
||||||
item_cpy = copy.copy(item)
|
item_cpy = copy.copy(item)
|
||||||
item.__init__(None)
|
item.__init__(None)
|
||||||
if is_live:
|
context = ServiceItemContext.Live if is_live else ServiceItemContext.Preview
|
||||||
# TODO: To Path object
|
self.media_item.generate_slide_data(item, item=item_cpy, context=context, file_path=file_path)
|
||||||
self.media_item.generate_slide_data(item, item_cpy, False, False, ServiceItemContext.Live,
|
|
||||||
str(file_path))
|
|
||||||
else:
|
|
||||||
# TODO: To Path object
|
|
||||||
self.media_item.generate_slide_data(item, item_cpy, False, False, ServiceItemContext.Preview,
|
|
||||||
str(file_path))
|
|
||||||
# Some of the original serviceitem attributes is needed in the new serviceitem
|
# Some of the original serviceitem attributes is needed in the new serviceitem
|
||||||
item.footer = item_cpy.footer
|
item.footer = item_cpy.footer
|
||||||
item.from_service = item_cpy.from_service
|
item.from_service = item_cpy.from_service
|
||||||
|
@ -329,8 +329,13 @@ class SongImportForm(OpenLPWizard, RegistryProperties):
|
|||||||
importer = self.plugin.import_songs(
|
importer = self.plugin.import_songs(
|
||||||
source_format,
|
source_format,
|
||||||
file_paths=self.get_list_of_paths(self.format_widgets[source_format]['file_list_widget']))
|
file_paths=self.get_list_of_paths(self.format_widgets[source_format]['file_list_widget']))
|
||||||
|
try:
|
||||||
importer.do_import()
|
importer.do_import()
|
||||||
self.progress_label.setText(WizardStrings.FinishedImport)
|
self.progress_label.setText(WizardStrings.FinishedImport)
|
||||||
|
except OSError as e:
|
||||||
|
log.exception('Importing songs failed')
|
||||||
|
self.progress_label.setText(translate('SongsPlugin.ImportWizardForm',
|
||||||
|
'Your Song import failed. {error}').format(error=e))
|
||||||
|
|
||||||
def on_error_copy_to_button_clicked(self):
|
def on_error_copy_to_button_clicked(self):
|
||||||
"""
|
"""
|
||||||
|
@ -67,7 +67,7 @@ class CCLIFileImport(SongImport):
|
|||||||
details = {'confidence': 1, 'encoding': 'utf-8'}
|
details = {'confidence': 1, 'encoding': 'utf-8'}
|
||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError:
|
||||||
details = chardet.detect(detect_content)
|
details = chardet.detect(detect_content)
|
||||||
in_file = codecs.open(str(file_path), 'r', details['encoding'])
|
in_file = codecs.open(file_path, 'r', details['encoding'])
|
||||||
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)
|
||||||
@ -251,10 +251,10 @@ class CCLIFileImport(SongImport):
|
|||||||
line_number = 0
|
line_number = 0
|
||||||
check_first_verse_line = False
|
check_first_verse_line = False
|
||||||
verse_text = ''
|
verse_text = ''
|
||||||
|
verse_type = VerseType.tags[VerseType.Verse]
|
||||||
song_author = ''
|
song_author = ''
|
||||||
verse_start = False
|
verse_start = False
|
||||||
for line in text_list:
|
for line in text_list:
|
||||||
verse_type = 'v'
|
|
||||||
clean_line = line.strip()
|
clean_line = line.strip()
|
||||||
if not clean_line:
|
if not clean_line:
|
||||||
if line_number == 0:
|
if line_number == 0:
|
||||||
@ -263,6 +263,7 @@ class CCLIFileImport(SongImport):
|
|||||||
if verse_text:
|
if verse_text:
|
||||||
self.add_verse(verse_text, verse_type)
|
self.add_verse(verse_text, verse_type)
|
||||||
verse_text = ''
|
verse_text = ''
|
||||||
|
verse_type = VerseType.tags[VerseType.Verse]
|
||||||
verse_start = False
|
verse_start = False
|
||||||
else:
|
else:
|
||||||
# line_number=0, song title
|
# line_number=0, song title
|
||||||
@ -279,7 +280,7 @@ class CCLIFileImport(SongImport):
|
|||||||
elif not verse_start:
|
elif not verse_start:
|
||||||
# We have the verse descriptor
|
# We have the verse descriptor
|
||||||
verse_desc_parts = clean_line.split(' ')
|
verse_desc_parts = clean_line.split(' ')
|
||||||
if len(verse_desc_parts) == 2:
|
if len(verse_desc_parts):
|
||||||
if verse_desc_parts[0].startswith('Ver'):
|
if verse_desc_parts[0].startswith('Ver'):
|
||||||
verse_type = VerseType.tags[VerseType.Verse]
|
verse_type = VerseType.tags[VerseType.Verse]
|
||||||
elif verse_desc_parts[0].startswith('Ch'):
|
elif verse_desc_parts[0].startswith('Ch'):
|
||||||
|
@ -557,16 +557,14 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
self.plugin.manager.save_object(new_song)
|
self.plugin.manager.save_object(new_song)
|
||||||
self.on_song_list_load()
|
self.on_song_list_load()
|
||||||
|
|
||||||
def generate_slide_data(self, service_item, item=None, xml_version=False, remote=False,
|
def generate_slide_data(self, service_item, *, item=None, context=ServiceItemContext.Service, **kwargs):
|
||||||
context=ServiceItemContext.Service):
|
|
||||||
"""
|
"""
|
||||||
Generate the slide data. Needs to be implemented by the plugin.
|
Generate the slide data. Needs to be implemented by the plugin.
|
||||||
|
|
||||||
:param service_item: The service item to be built on
|
:param service_item: The service item to be built on
|
||||||
:param item: The Song item to be used
|
:param item: The Song item to be used
|
||||||
:param xml_version: The xml version (not used)
|
|
||||||
:param remote: Triggered from remote
|
|
||||||
:param context: Why is it being generated
|
:param context: Why is it being generated
|
||||||
|
:param kwargs: Consume other unused args specified by the base implementation, but not use by this one.
|
||||||
"""
|
"""
|
||||||
log.debug('generate_slide_data: {service}, {item}, {remote}'.format(service=service_item, item=item,
|
log.debug('generate_slide_data: {service}, {item}, {remote}'.format(service=service_item, item=item,
|
||||||
remote=self.remote_song))
|
remote=self.remote_song))
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
The entrypoint for OpenLP
|
The entrypoint for OpenLP
|
||||||
"""
|
"""
|
||||||
import faulthandler
|
import faulthandler
|
||||||
|
import logging
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
@ -34,14 +35,19 @@ from openlp.core.common import is_macosx, is_win
|
|||||||
from openlp.core.common.applocation import AppLocation
|
from openlp.core.common.applocation import AppLocation
|
||||||
from openlp.core.common.path import create_paths
|
from openlp.core.common.path import create_paths
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def set_up_fault_handling():
|
def set_up_fault_handling():
|
||||||
"""
|
"""
|
||||||
Set up the Python fault handler
|
Set up the Python fault handler
|
||||||
"""
|
"""
|
||||||
# Create the cache directory if it doesn't exist, and enable the fault handler to log to an error log file
|
# Create the cache directory if it doesn't exist, and enable the fault handler to log to an error log file
|
||||||
|
try:
|
||||||
create_paths(AppLocation.get_directory(AppLocation.CacheDir))
|
create_paths(AppLocation.get_directory(AppLocation.CacheDir))
|
||||||
faulthandler.enable((AppLocation.get_directory(AppLocation.CacheDir) / 'error.log').open('wb'))
|
faulthandler.enable((AppLocation.get_directory(AppLocation.CacheDir) / 'error.log').open('wb'))
|
||||||
|
except OSError:
|
||||||
|
log.exception('An exception occurred when enabling the fault handler')
|
||||||
|
|
||||||
|
|
||||||
def start():
|
def start():
|
||||||
|
@ -63,8 +63,8 @@ class TestRemoteDeploy(TestCase):
|
|||||||
deploy_zipfile(root_path, 'site.zip')
|
deploy_zipfile(root_path, 'site.zip')
|
||||||
|
|
||||||
# THEN: the zip file should have been extracted to the right location
|
# THEN: the zip file should have been extracted to the right location
|
||||||
MockZipFile.assert_called_once_with(root_path_str + os.sep + 'site.zip')
|
MockZipFile.assert_called_once_with(Path('/tmp/remotes/site.zip'))
|
||||||
mocked_zipfile.extractall.assert_called_once_with(root_path_str)
|
mocked_zipfile.extractall.assert_called_once_with(Path('/tmp/remotes'))
|
||||||
|
|
||||||
@patch('openlp.core.api.deploy.Registry')
|
@patch('openlp.core.api.deploy.Registry')
|
||||||
@patch('openlp.core.api.deploy.get_web_page')
|
@patch('openlp.core.api.deploy.get_web_page')
|
||||||
|
@ -309,9 +309,9 @@ class TestInit(TestCase, TestMixin):
|
|||||||
"""
|
"""
|
||||||
# GIVEN: A mocked UniversalDetector instance with done attribute set to True after first iteration
|
# GIVEN: A mocked UniversalDetector instance with done attribute set to True after first iteration
|
||||||
with patch('openlp.core.common.UniversalDetector') as mocked_universal_detector, \
|
with patch('openlp.core.common.UniversalDetector') as mocked_universal_detector, \
|
||||||
patch.object(Path, 'open', return_value=BytesIO(b"data" * 260)) as mocked_open:
|
patch.object(Path, 'open', return_value=BytesIO(b'data' * 260)) as mocked_open:
|
||||||
encoding_result = {'encoding': 'UTF-8', 'confidence': 0.99}
|
encoding_result = {'encoding': 'UTF-8', 'confidence': 0.99}
|
||||||
mocked_universal_detector_inst = MagicMock(result=encoding_result)
|
mocked_universal_detector_inst = MagicMock(**{'close.return_value': encoding_result})
|
||||||
type(mocked_universal_detector_inst).done = PropertyMock(side_effect=[False, True])
|
type(mocked_universal_detector_inst).done = PropertyMock(side_effect=[False, True])
|
||||||
mocked_universal_detector.return_value = mocked_universal_detector_inst
|
mocked_universal_detector.return_value = mocked_universal_detector_inst
|
||||||
|
|
||||||
@ -320,7 +320,7 @@ class TestInit(TestCase, TestMixin):
|
|||||||
|
|
||||||
# THEN: The feed method of UniversalDetector should only br called once before returning a result
|
# THEN: The feed method of UniversalDetector should only br called once before returning a result
|
||||||
mocked_open.assert_called_once_with('rb')
|
mocked_open.assert_called_once_with('rb')
|
||||||
assert mocked_universal_detector_inst.feed.mock_calls == [call(b"data" * 256)]
|
assert mocked_universal_detector_inst.feed.mock_calls == [call(b'data' * 256)]
|
||||||
mocked_universal_detector_inst.close.assert_called_once_with()
|
mocked_universal_detector_inst.close.assert_called_once_with()
|
||||||
assert result == encoding_result
|
assert result == encoding_result
|
||||||
|
|
||||||
@ -331,10 +331,10 @@ class TestInit(TestCase, TestMixin):
|
|||||||
# GIVEN: A mocked UniversalDetector instance which isn't set to done and a mocked open, with 1040 bytes of test
|
# GIVEN: A mocked UniversalDetector instance which isn't set to done and a mocked open, with 1040 bytes of test
|
||||||
# data (enough to run the iterator twice)
|
# data (enough to run the iterator twice)
|
||||||
with patch('openlp.core.common.UniversalDetector') as mocked_universal_detector, \
|
with patch('openlp.core.common.UniversalDetector') as mocked_universal_detector, \
|
||||||
patch.object(Path, 'open', return_value=BytesIO(b"data" * 260)) as mocked_open:
|
patch.object(Path, 'open', return_value=BytesIO(b'data' * 260)) as mocked_open:
|
||||||
encoding_result = {'encoding': 'UTF-8', 'confidence': 0.99}
|
encoding_result = {'encoding': 'UTF-8', 'confidence': 0.99}
|
||||||
mocked_universal_detector_inst = MagicMock(mock=mocked_universal_detector,
|
mocked_universal_detector_inst = MagicMock(mock=mocked_universal_detector,
|
||||||
**{'done': False, 'result': encoding_result})
|
**{'done': False, 'close.return_value': encoding_result})
|
||||||
mocked_universal_detector.return_value = mocked_universal_detector_inst
|
mocked_universal_detector.return_value = mocked_universal_detector_inst
|
||||||
|
|
||||||
# WHEN: Calling get_file_encoding
|
# WHEN: Calling get_file_encoding
|
||||||
@ -342,7 +342,7 @@ class TestInit(TestCase, TestMixin):
|
|||||||
|
|
||||||
# THEN: The feed method of UniversalDetector should have been called twice before returning a result
|
# THEN: The feed method of UniversalDetector should have been called twice before returning a result
|
||||||
mocked_open.assert_called_once_with('rb')
|
mocked_open.assert_called_once_with('rb')
|
||||||
assert mocked_universal_detector_inst.feed.mock_calls == [call(b"data" * 256), call(b"data" * 4)]
|
assert mocked_universal_detector_inst.feed.mock_calls == [call(b'data' * 256), call(b'data' * 4)]
|
||||||
mocked_universal_detector_inst.close.assert_called_once_with()
|
mocked_universal_detector_inst.close.assert_called_once_with()
|
||||||
assert result == encoding_result
|
assert result == encoding_result
|
||||||
|
|
||||||
@ -352,13 +352,19 @@ class TestInit(TestCase, TestMixin):
|
|||||||
"""
|
"""
|
||||||
# GIVEN: A mocked UniversalDetector instance which isn't set to done and a mocked open, with 1040 bytes of test
|
# GIVEN: A mocked UniversalDetector instance which isn't set to done and a mocked open, with 1040 bytes of test
|
||||||
# data (enough to run the iterator twice)
|
# data (enough to run the iterator twice)
|
||||||
with patch('openlp.core.common.UniversalDetector'), \
|
with patch('openlp.core.common.UniversalDetector') as mocked_universal_detector, \
|
||||||
patch('builtins.open', side_effect=OSError), \
|
patch('builtins.open', side_effect=OSError), \
|
||||||
patch('openlp.core.common.log') as mocked_log:
|
patch('openlp.core.common.log') as mocked_log:
|
||||||
|
encoding_result = {'encoding': 'UTF-8', 'confidence': 0.99}
|
||||||
|
mocked_universal_detector_inst = MagicMock(mock=mocked_universal_detector,
|
||||||
|
**{'done': False, 'close.return_value': encoding_result})
|
||||||
|
mocked_universal_detector.return_value = mocked_universal_detector_inst
|
||||||
|
|
||||||
# WHEN: Calling get_file_encoding
|
# WHEN: Calling get_file_encoding
|
||||||
result = get_file_encoding(Path('file name'))
|
result = get_file_encoding(Path('file name'))
|
||||||
|
|
||||||
# THEN: log.exception should be called and get_file_encoding should return None
|
# THEN: log.exception should be called and get_file_encoding should return None
|
||||||
mocked_log.exception.assert_called_once_with('Error detecting file encoding')
|
mocked_log.exception.assert_called_once_with('Error detecting file encoding')
|
||||||
assert result is None
|
mocked_universal_detector_inst.feed.assert_not_called()
|
||||||
|
mocked_universal_detector_inst.close.assert_called_once_with()
|
||||||
|
assert result == encoding_result
|
||||||
|
@ -179,9 +179,8 @@ class TestShutil(TestCase):
|
|||||||
# WHEN: Calling :func:`openlp.core.common.path.rmtree` with the path parameter as Path object type
|
# WHEN: Calling :func:`openlp.core.common.path.rmtree` with the path parameter as Path object type
|
||||||
path.rmtree()
|
path.rmtree()
|
||||||
|
|
||||||
# THEN: :func:`shutil.rmtree` should have been called with the str equivalents of the Path object.
|
# THEN: :func:`shutil.rmtree` should have been called with the the Path object.
|
||||||
mocked_shutil_rmtree.assert_called_once_with(
|
mocked_shutil_rmtree.assert_called_once_with(Path('test', 'path'), False, None)
|
||||||
os.path.join('test', 'path'), False, None)
|
|
||||||
|
|
||||||
def test_rmtree_optional_params(self):
|
def test_rmtree_optional_params(self):
|
||||||
"""
|
"""
|
||||||
@ -198,8 +197,7 @@ class TestShutil(TestCase):
|
|||||||
|
|
||||||
# THEN: :func:`shutil.rmtree` should have been called with the optional parameters, with out any of the
|
# THEN: :func:`shutil.rmtree` should have been called with the optional parameters, with out any of the
|
||||||
# values being modified
|
# values being modified
|
||||||
mocked_shutil_rmtree.assert_called_once_with(
|
mocked_shutil_rmtree.assert_called_once_with(path, True, mocked_on_error)
|
||||||
os.path.join('test', 'path'), True, mocked_on_error)
|
|
||||||
|
|
||||||
def test_which_no_command(self):
|
def test_which_no_command(self):
|
||||||
"""
|
"""
|
||||||
|
@ -141,7 +141,7 @@ class TestServiceItem(TestCase, TestMixin):
|
|||||||
"""
|
"""
|
||||||
# GIVEN: A new service item and a mocked add icon function
|
# GIVEN: A new service item and a mocked add icon function
|
||||||
image_name = 'image_1.jpg'
|
image_name = 'image_1.jpg'
|
||||||
test_file = os.path.join(str(TEST_PATH), image_name)
|
test_file = TEST_PATH / image_name
|
||||||
frame_array = {'path': test_file, 'title': image_name}
|
frame_array = {'path': test_file, 'title': image_name}
|
||||||
|
|
||||||
service_item = ServiceItem(None)
|
service_item = ServiceItem(None)
|
||||||
@ -154,13 +154,13 @@ class TestServiceItem(TestCase, TestMixin):
|
|||||||
mocked_get_section_data_path:
|
mocked_get_section_data_path:
|
||||||
mocked_exists.return_value = True
|
mocked_exists.return_value = True
|
||||||
mocked_get_section_data_path.return_value = Path('/path/')
|
mocked_get_section_data_path.return_value = Path('/path/')
|
||||||
service_item.set_from_service(line, str(TEST_PATH))
|
service_item.set_from_service(line, TEST_PATH)
|
||||||
|
|
||||||
# THEN: We should get back a valid service item
|
# THEN: We should get back a valid service item
|
||||||
assert service_item.is_valid is True, 'The new service item should be valid'
|
assert service_item.is_valid is True, 'The new service item should be valid'
|
||||||
assert test_file == service_item.get_rendered_frame(0), 'The first frame should match the path to the image'
|
assert test_file == service_item.get_rendered_frame(0), 'The first frame should match the path to the image'
|
||||||
assert frame_array == service_item.get_frames()[0], 'The return should match frame array1'
|
assert frame_array == service_item.get_frames()[0], 'The return should match frame array1'
|
||||||
assert test_file == str(service_item.get_frame_path(0)), \
|
assert test_file == service_item.get_frame_path(0), \
|
||||||
'The frame path should match the full path to the image'
|
'The frame path should match the full path to the image'
|
||||||
assert image_name == service_item.get_frame_title(0), 'The frame title should match the image name'
|
assert image_name == service_item.get_frame_title(0), 'The frame title should match the image name'
|
||||||
assert image_name == service_item.get_display_title(), 'The display title should match the first image name'
|
assert image_name == service_item.get_display_title(), 'The display title should match the first image name'
|
||||||
@ -328,7 +328,7 @@ class TestServiceItem(TestCase, TestMixin):
|
|||||||
|
|
||||||
# WHEN: We add a custom from a saved service
|
# WHEN: We add a custom from a saved service
|
||||||
line = convert_file_service_item(TEST_PATH, 'serviceitem-song-linked-audio.osj')
|
line = convert_file_service_item(TEST_PATH, 'serviceitem-song-linked-audio.osj')
|
||||||
service_item.set_from_service(line, '/test/')
|
service_item.set_from_service(line, Path('/test/'))
|
||||||
|
|
||||||
# THEN: We should get back a valid service item
|
# THEN: We should get back a valid service item
|
||||||
assert service_item.is_valid is True, 'The new service item should be valid'
|
assert service_item.is_valid is True, 'The new service item should be valid'
|
||||||
|
@ -66,9 +66,9 @@ class TestThemeManager(TestCase):
|
|||||||
theme_manager._export_theme(Path('some', 'path', 'Default.otz'), 'Default')
|
theme_manager._export_theme(Path('some', 'path', 'Default.otz'), 'Default')
|
||||||
|
|
||||||
# THEN: The zipfile should be created at the given path
|
# THEN: The zipfile should be created at the given path
|
||||||
mocked_zipfile_init.assert_called_with(os.path.join('some', 'path', 'Default.otz'), 'w')
|
mocked_zipfile_init.assert_called_with(Path('some', 'path', 'Default.otz'), 'w')
|
||||||
mocked_zipfile_write.assert_called_with(str(RESOURCE_PATH / 'themes' / 'Default' / 'Default.xml'),
|
mocked_zipfile_write.assert_called_with(RESOURCE_PATH / 'themes' / 'Default' / 'Default.xml',
|
||||||
os.path.join('Default', 'Default.xml'))
|
Path('Default', 'Default.xml'))
|
||||||
|
|
||||||
def test_initial_theme_manager(self):
|
def test_initial_theme_manager(self):
|
||||||
"""
|
"""
|
||||||
|
@ -55,7 +55,8 @@ class TestManager(TestCase):
|
|||||||
instance = BibleManager(MagicMock())
|
instance = BibleManager(MagicMock())
|
||||||
# We need to keep a reference to the mock for close_all as it gets set to None later on!
|
# We need to keep a reference to the mock for close_all as it gets set to None later on!
|
||||||
mocked_close_all = MagicMock()
|
mocked_close_all = MagicMock()
|
||||||
mocked_bible = MagicMock(file='KJV.sqlite', path='bibles', **{'session.close_all': mocked_close_all})
|
mocked_bible = MagicMock(file_path='KJV.sqlite', path=Path('bibles'),
|
||||||
|
**{'session.close_all': mocked_close_all})
|
||||||
instance.db_cache = {'KJV': mocked_bible}
|
instance.db_cache = {'KJV': mocked_bible}
|
||||||
|
|
||||||
# WHEN: Calling delete_bible with 'KJV'
|
# WHEN: Calling delete_bible with 'KJV'
|
||||||
@ -66,4 +67,4 @@ class TestManager(TestCase):
|
|||||||
assert result is True
|
assert result is True
|
||||||
mocked_close_all.assert_called_once_with()
|
mocked_close_all.assert_called_once_with()
|
||||||
assert mocked_bible.session is None
|
assert mocked_bible.session is None
|
||||||
mocked_delete_file.assert_called_once_with(Path('bibles', 'KJV.sqlite'))
|
mocked_delete_file.assert_called_once_with(Path('bibles'), 'KJV.sqlite')
|
||||||
|
@ -236,8 +236,8 @@ class TestSongMaintenanceForm(TestCase, TestMixin):
|
|||||||
assert MockedQListWidgetItem.call_args_list == expected_widget_item_calls, MockedQListWidgetItem.call_args_list
|
assert MockedQListWidgetItem.call_args_list == expected_widget_item_calls, MockedQListWidgetItem.call_args_list
|
||||||
mocked_author_item1.setData.assert_called_once_with(QtCore.Qt.UserRole, 2)
|
mocked_author_item1.setData.assert_called_once_with(QtCore.Qt.UserRole, 2)
|
||||||
mocked_author_item2.setData.assert_called_once_with(QtCore.Qt.UserRole, 1)
|
mocked_author_item2.setData.assert_called_once_with(QtCore.Qt.UserRole, 1)
|
||||||
mocked_authors_list_widget.addItem.call_args_list == [
|
mocked_authors_list_widget.addItem.assert_has_calls([
|
||||||
call(mocked_author_item1), call(mocked_author_item2)]
|
call(mocked_author_item1), call(mocked_author_item2)])
|
||||||
|
|
||||||
@patch('openlp.plugins.songs.forms.songmaintenanceform.QtWidgets.QListWidgetItem')
|
@patch('openlp.plugins.songs.forms.songmaintenanceform.QtWidgets.QListWidgetItem')
|
||||||
@patch('openlp.plugins.songs.forms.songmaintenanceform.Topic')
|
@patch('openlp.plugins.songs.forms.songmaintenanceform.Topic')
|
||||||
|
@ -70,7 +70,7 @@ class FakeIP4InterfaceEntry(QObject):
|
|||||||
"""
|
"""
|
||||||
Return a QFlags enum with IsUp and IsRunning
|
Return a QFlags enum with IsUp and IsRunning
|
||||||
"""
|
"""
|
||||||
return (QNetworkInterface.IsUp | QNetworkInterface.IsRunning)
|
return QNetworkInterface.IsUp | QNetworkInterface.IsRunning
|
||||||
|
|
||||||
def name(self):
|
def name(self):
|
||||||
return self.my_name
|
return self.my_name
|
||||||
|
@ -605,9 +605,9 @@ class TestPJLinkCommands(TestCase):
|
|||||||
|
|
||||||
# THEN: Power should be set to ON
|
# THEN: Power should be set to ON
|
||||||
assert pjlink.power == S_STANDBY, 'Power should not have changed'
|
assert pjlink.power == S_STANDBY, 'Power should not have changed'
|
||||||
assert mock_UpdateIcons.emit.called is False, 'projectorUpdateIcons() should not have been called'
|
mock_UpdateIcons.emit.assert_not_called()
|
||||||
mock_change_status.called is False, 'change_status() should not have been called'
|
mock_change_status.assert_not_called()
|
||||||
mock_send_command.called is False, 'send_command() should not have been called'
|
mock_send_command.assert_not_called()
|
||||||
mock_log.warning.assert_has_calls(log_warn_calls)
|
mock_log.warning.assert_has_calls(log_warn_calls)
|
||||||
|
|
||||||
def test_projector_process_powr_off(self):
|
def test_projector_process_powr_off(self):
|
||||||
@ -627,9 +627,9 @@ class TestPJLinkCommands(TestCase):
|
|||||||
|
|
||||||
# THEN: Power should be set to ON
|
# THEN: Power should be set to ON
|
||||||
assert pjlink.power == S_STANDBY, 'Power should have changed to S_STANDBY'
|
assert pjlink.power == S_STANDBY, 'Power should have changed to S_STANDBY'
|
||||||
assert mock_UpdateIcons.emit.called is True, 'projectorUpdateIcons should have been called'
|
mock_UpdateIcons.emit.assert_called_with()
|
||||||
mock_change_status.called is True, 'change_status should have been called'
|
mock_change_status.assert_called_with(313)
|
||||||
mock_send_command.called is False, 'send_command should not have been called'
|
mock_send_command.assert_not_called()
|
||||||
|
|
||||||
def test_projector_process_rfil_save(self):
|
def test_projector_process_rfil_save(self):
|
||||||
"""
|
"""
|
||||||
|
@ -83,8 +83,8 @@ class ProjectorSourceFormTest(TestCase, TestMixin):
|
|||||||
Delete all C++ objects at end so we don't segfault.
|
Delete all C++ objects at end so we don't segfault.
|
||||||
"""
|
"""
|
||||||
self.projectordb.session.close()
|
self.projectordb.session.close()
|
||||||
del(self.projectordb)
|
del self.projectordb
|
||||||
del(self.projector)
|
del self.projector
|
||||||
retries = 0
|
retries = 0
|
||||||
while retries < 5:
|
while retries < 5:
|
||||||
try:
|
try:
|
||||||
|
Loading…
Reference in New Issue
Block a user