This commit is contained in:
Raoul Snyman 2018-01-30 21:10:09 -07:00
commit 97172efba5
11 changed files with 317 additions and 293 deletions

View File

@ -36,6 +36,7 @@ from openlp.core.common import md5_hash
from openlp.core.common.applocation import AppLocation from openlp.core.common.applocation import AppLocation
from openlp.core.common.i18n import 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.path import Path
from openlp.core.common.settings import Settings from openlp.core.common.settings import Settings
from openlp.core.display.render import remove_tags, render_tags, render_chords from openlp.core.display.render import remove_tags, render_tags, render_chords
from openlp.core.lib import ImageSource, build_icon from openlp.core.lib import ImageSource, build_icon
@ -428,13 +429,13 @@ class ServiceItem(RegistryProperties):
self.has_original_files = True self.has_original_files = True
if 'background_audio' in header: if 'background_audio' in header:
self.background_audio = [] self.background_audio = []
for filename in header['background_audio']: for file_path in header['background_audio']:
# Give them real file paths. # In OpenLP 3.0 we switched to storing Path objects in JSON files
filepath = str(filename) if isinstance(file_path, str):
if path: # 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
filepath = os.path.join(path, ntpath.basename(str(filename))) file_path = Path(path, ntpath.basename(file_path))
self.background_audio.append(filepath) 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:
for slide in service_item['serviceitem']['data']: for slide in service_item['serviceitem']['data']:
@ -445,8 +446,8 @@ 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']:
filename = os.path.join(path, text_image) file_path = os.path.join(path, text_image)
self.add_from_image(filename, 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']:
self.add_from_image(text_image['path'], text_image['title'], background) self.add_from_image(text_image['path'], text_image['title'], background)

View File

@ -400,9 +400,9 @@ class FirstTimeForm(QtWidgets.QWizard, UiFirstTimeWizard, RegistryProperties):
if item: if item:
item.setIcon(build_icon(Path(gettempdir(), 'openlp', screenshot))) item.setIcon(build_icon(Path(gettempdir(), 'openlp', screenshot)))
def _download_progress(self, count, block_size): def update_progress(self, count, block_size):
""" """
Calculate and display the download progress. Calculate and display the download progress. This method is called by download_file().
""" """
increment = (count * block_size) - self.previous_size increment = (count * block_size) - self.previous_size
self._increment_progress_bar(None, increment) self._increment_progress_bar(None, increment)

View File

@ -63,7 +63,7 @@ class UiFirstTimeWizard(object):
first_time_wizard.setOptions(QtWidgets.QWizard.IndependentPages | QtWidgets.QWizard.NoBackButtonOnStartPage | first_time_wizard.setOptions(QtWidgets.QWizard.IndependentPages | QtWidgets.QWizard.NoBackButtonOnStartPage |
QtWidgets.QWizard.NoBackButtonOnLastPage | QtWidgets.QWizard.HaveCustomButton1 | QtWidgets.QWizard.NoBackButtonOnLastPage | QtWidgets.QWizard.HaveCustomButton1 |
QtWidgets.QWizard.HaveCustomButton2) QtWidgets.QWizard.HaveCustomButton2)
if is_macosx(): if is_macosx(): # pragma: nocover
first_time_wizard.setPixmap(QtWidgets.QWizard.BackgroundPixmap, first_time_wizard.setPixmap(QtWidgets.QWizard.BackgroundPixmap,
QtGui.QPixmap(':/wizards/openlp-osx-wizard.png')) QtGui.QPixmap(':/wizards/openlp-osx-wizard.png'))
first_time_wizard.resize(634, 386) first_time_wizard.resize(634, 386)

View File

@ -1313,11 +1313,13 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, RegistryProperties):
self.load_progress_bar.setValue(0) self.load_progress_bar.setValue(0)
self.application.process_events() self.application.process_events()
def increment_progress_bar(self): def increment_progress_bar(self, increment=1):
""" """
Increase the Progress Bar value by 1 Increase the Progress Bar by the value in increment.
:param int increment: The value you to increase the progress bar by.
""" """
self.load_progress_bar.setValue(self.load_progress_bar.value() + 1) self.load_progress_bar.setValue(self.load_progress_bar.value() + increment)
self.application.process_events() self.application.process_events()
def finished_progress_bar(self): def finished_progress_bar(self):
@ -1385,4 +1387,4 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, RegistryProperties):
if not isinstance(filename, str): if not isinstance(filename, str):
filename = str(filename, sys.getfilesystemencoding()) filename = str(filename, sys.getfilesystemencoding())
if filename.endswith(('.osz', '.oszl')): if filename.endswith(('.osz', '.oszl')):
self.service_manager_contents.load_file(filename) self.service_manager_contents.load_file(Path(filename))

View File

@ -27,8 +27,9 @@ import json
import os import os
import shutil import shutil
import zipfile import zipfile
from contextlib import suppress
from datetime import datetime, timedelta from datetime import datetime, timedelta
from tempfile import mkstemp from tempfile import NamedTemporaryFile
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5 import QtCore, QtGui, QtWidgets
@ -36,11 +37,13 @@ from openlp.core.common import ThemeLevel, delete_file
from openlp.core.common.actions import ActionList, CategoryOrder from openlp.core.common.actions import ActionList, CategoryOrder
from openlp.core.common.applocation import AppLocation from openlp.core.common.applocation import AppLocation
from openlp.core.common.i18n import UiStrings, format_time, translate from openlp.core.common.i18n import UiStrings, format_time, translate
from openlp.core.common.json import OpenLPJsonDecoder, OpenLPJsonEncoder
from openlp.core.common.mixins import LogMixin, RegistryProperties from openlp.core.common.mixins import LogMixin, RegistryProperties
from openlp.core.common.path import Path, create_paths, str_to_path from openlp.core.common.path import Path, str_to_path
from openlp.core.common.registry import Registry, RegistryBase from openlp.core.common.registry import Registry, RegistryBase
from openlp.core.common.settings import Settings from openlp.core.common.settings import Settings
from openlp.core.lib import ServiceItem, ItemCapabilities, PluginStatus, build_icon from openlp.core.lib import ServiceItem, ItemCapabilities, PluginStatus, build_icon
from openlp.core.lib.exceptions import ValidationError
from openlp.core.lib.ui import critical_error_message_box, create_widget_action, find_and_set_in_combo_box from openlp.core.lib.ui import critical_error_message_box, create_widget_action, find_and_set_in_combo_box
from openlp.core.ui import ServiceNoteForm, ServiceItemEditForm, StartTimeForm from openlp.core.ui import ServiceNoteForm, ServiceItemEditForm, StartTimeForm
from openlp.core.widgets.dialogs import FileDialog from openlp.core.widgets.dialogs import FileDialog
@ -449,7 +452,7 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
else: else:
file_path = str_to_path(load_file) file_path = str_to_path(load_file)
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)
self.load_file(str(file_path)) self.load_file(file_path)
def save_modified_service(self): def save_modified_service(self):
""" """
@ -475,7 +478,7 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
elif result == QtWidgets.QMessageBox.Save: elif result == QtWidgets.QMessageBox.Save:
self.decide_save_method() self.decide_save_method()
sender = self.sender() sender = self.sender()
self.load_file(sender.data()) self.load_file(Path(sender.data()))
def new_file(self): def new_file(self):
""" """
@ -503,7 +506,32 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
service.append({'openlp_core': core}) service.append({'openlp_core': core})
return service return service
def save_file(self, field=None): def get_write_file_list(self):
"""
Get a list of files used in the service and files that are missing.
:return: A list of files used in the service that exist, and a list of files that don't.
:rtype: (list[openlp.core.common.path.Path], list[openlp.core.common.path.Path])
"""
write_list = []
missing_list = []
for item in self.service_items:
if item['service_item'].uses_file():
for frame in item['service_item'].get_frames():
path_from = item['service_item'].get_frame_path(frame=frame)
if path_from in write_list or path_from in missing_list:
continue
if not os.path.exists(path_from):
missing_list.append(Path(path_from))
else:
write_list.append(Path(path_from))
for audio_path in item['service_item'].background_audio:
if audio_path in write_list:
continue
write_list.append(audio_path)
return write_list, missing_list
def save_file(self):
""" """
Save the current service file. Save the current service file.
@ -511,178 +539,74 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
there be an error when saving. Audio files are also copied into the service manager directory, and then packaged there be an error when saving. Audio files are also copied into the service manager directory, and then packaged
into the zip file. into the zip file.
""" """
if not self.file_name(): file_path = self.file_name()
return self.save_file_as() self.log_debug('ServiceManager.save_file - {name}'.format(name=file_path))
temp_file, temp_file_name = mkstemp('.osz', 'openlp_') self.application.set_busy_cursor()
# We don't need the file handle.
os.close(temp_file)
self.log_debug(temp_file_name)
path_file_name = str(self.file_name())
path, file_name = os.path.split(path_file_name)
base_name = os.path.splitext(file_name)[0]
service_file_name = '{name}.osj'.format(name=base_name)
self.log_debug('ServiceManager.save_file - {name}'.format(name=path_file_name))
Settings().setValue(self.main_window.service_manager_settings_section + '/last directory', Path(path))
service = self.create_basic_service() service = self.create_basic_service()
write_list = [] write_list = []
missing_list = [] missing_list = []
audio_files = []
total_size = 0 if not self._save_lite:
self.application.set_busy_cursor() write_list, missing_list = self.get_write_file_list()
# Number of items + 1 to zip it
self.main_window.display_progress_bar(len(self.service_items) + 1) if missing_list:
# Get list of missing files, and list of files to write self.application.set_normal_cursor()
for item in self.service_items: title = translate('OpenLP.ServiceManager', 'Service File(s) Missing')
if not item['service_item'].uses_file(): message = translate('OpenLP.ServiceManager',
continue 'The following file(s) in the service are missing: {name}\n\n'
for frame in item['service_item'].get_frames(): 'These files will be removed if you continue to save.'
path_from = item['service_item'].get_frame_path(frame=frame) ).format(name='\n\t'.join(missing_list))
if path_from in write_list or path_from in missing_list: answer = QtWidgets.QMessageBox.critical(self, title, message,
continue QtWidgets.QMessageBox.StandardButtons(
if not os.path.exists(path_from): QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel))
missing_list.append(path_from) if answer == QtWidgets.QMessageBox.Cancel:
else: return False
write_list.append(path_from)
if missing_list:
self.application.set_normal_cursor()
title = translate('OpenLP.ServiceManager', 'Service File(s) Missing')
message = translate('OpenLP.ServiceManager',
'The following file(s) in the service are missing: {name}\n\n'
'These files will be removed if you continue to save.'
).format(name="\n\t".join(missing_list))
answer = QtWidgets.QMessageBox.critical(self, title, message,
QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Ok |
QtWidgets.QMessageBox.Cancel))
if answer == QtWidgets.QMessageBox.Cancel:
self.main_window.finished_progress_bar()
return False
# Check if item contains a missing file. # Check if item contains a missing file.
for item in list(self.service_items): for item in list(self.service_items):
self.main_window.increment_progress_bar() if not self._save_lite:
item['service_item'].remove_invalid_frames(missing_list) item['service_item'].remove_invalid_frames(missing_list)
if item['service_item'].missing_frames(): if item['service_item'].missing_frames():
self.service_items.remove(item) self.service_items.remove(item)
else: continue
service_item = item['service_item'].get_service_repr(self._save_lite)
if service_item['header']['background_audio']:
for i, file_name in enumerate(service_item['header']['background_audio']):
new_file = os.path.join('audio', item['service_item'].unique_identifier, str(file_name))
audio_files.append((file_name, new_file))
service_item['header']['background_audio'][i] = new_file
# Add the service item to the service.
service.append({'serviceitem': service_item})
self.repaint_service_list(-1, -1)
for file_item in write_list:
file_size = os.path.getsize(file_item)
total_size += file_size
self.log_debug('ServiceManager.save_file - ZIP contents size is %i bytes' % total_size)
service_content = json.dumps(service)
# Usual Zip file cannot exceed 2GiB, file with Zip64 cannot be extracted using unzip in UNIX.
allow_zip_64 = (total_size > 2147483648 + len(service_content))
self.log_debug('ServiceManager.save_file - allowZip64 is {text}'.format(text=allow_zip_64))
zip_file = None
success = True
self.main_window.increment_progress_bar()
try:
zip_file = zipfile.ZipFile(temp_file_name, 'w', zipfile.ZIP_STORED, allow_zip_64)
# First we add service contents..
zip_file.writestr(service_file_name, service_content)
# Finally add all the listed media files.
for write_from in write_list:
zip_file.write(write_from, write_from)
for audio_from, audio_to in audio_files:
audio_from = str(audio_from)
audio_to = str(audio_to)
if audio_from.startswith('audio'):
# When items are saved, they get new unique_identifier. Let's copy the file to the new location.
# Unused files can be ignored, OpenLP automatically cleans up the service manager dir on exit.
audio_from = os.path.join(self.service_path, audio_from)
save_file = os.path.join(self.service_path, audio_to)
save_path = os.path.split(save_file)[0]
create_paths(Path(save_path))
if not os.path.exists(save_file):
shutil.copy(audio_from, save_file)
zip_file.write(audio_from, audio_to)
except OSError:
self.log_exception('Failed to save service to disk: {name}'.format(name=temp_file_name))
self.main_window.error_message(translate('OpenLP.ServiceManager', 'Error Saving File'),
translate('OpenLP.ServiceManager', 'There was an error saving your file.'))
success = False
finally:
if zip_file:
zip_file.close()
self.main_window.finished_progress_bar()
self.application.set_normal_cursor()
if success:
try:
shutil.copy(temp_file_name, path_file_name)
except (shutil.Error, PermissionError):
return self.save_file_as()
except OSError as ose:
QtWidgets.QMessageBox.critical(self, translate('OpenLP.ServiceManager', 'Error Saving File'),
translate('OpenLP.ServiceManager', 'An error occurred while writing the '
'service file: {error}').format(error=ose.strerror),
QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Ok))
success = False
self.main_window.add_recent_file(path_file_name)
self.set_modified(False)
delete_file(Path(temp_file_name))
return success
def save_local_file(self):
"""
Save the current service file but leave all the file references alone to point to the current machine.
This format is not transportable as it will not contain any files.
"""
if not self.file_name():
return self.save_file_as()
temp_file, temp_file_name = mkstemp('.oszl', 'openlp_')
# We don't need the file handle.
os.close(temp_file)
self.log_debug(temp_file_name)
path_file_name = str(self.file_name())
path, file_name = os.path.split(path_file_name)
base_name = os.path.splitext(file_name)[0]
service_file_name = '{name}.osj'.format(name=base_name)
self.log_debug('ServiceManager.save_file - {name}'.format(name=path_file_name))
Settings().setValue(self.main_window.service_manager_settings_section + '/last directory', Path(path))
service = self.create_basic_service()
self.application.set_busy_cursor()
# Number of items + 1 to zip it
self.main_window.display_progress_bar(len(self.service_items) + 1)
for item in self.service_items:
self.main_window.increment_progress_bar()
service_item = item['service_item'].get_service_repr(self._save_lite) service_item = item['service_item'].get_service_repr(self._save_lite)
# TODO: check for file item on save. # Add the service item to the service.
service.append({'serviceitem': service_item}) service.append({'serviceitem': service_item})
self.main_window.increment_progress_bar() self.repaint_service_list(-1, -1)
service_content = json.dumps(service) service_content = json.dumps(service, cls=OpenLPJsonEncoder)
zip_file = None service_content_size = len(bytes(service_content, encoding='utf-8'))
success = True total_size = service_content_size
self.main_window.increment_progress_bar() for file_item in write_list:
total_size += file_item.stat().st_size
self.log_debug('ServiceManager.save_file - ZIP contents size is %i bytes' % total_size)
self.main_window.display_progress_bar(total_size)
try: try:
zip_file = zipfile.ZipFile(temp_file_name, 'w', zipfile.ZIP_STORED, True) with NamedTemporaryFile(dir=str(file_path.parent), prefix='.') as temp_file, \
# First we add service contents. zipfile.ZipFile(temp_file, 'w') as zip_file:
zip_file.writestr(service_file_name, service_content) # First we add service contents..
except OSError: zip_file.writestr('service_data.osj', service_content)
self.log_exception('Failed to save service to disk: {name}'.format(name=temp_file_name)) self.main_window.increment_progress_bar(service_content_size)
self.main_window.error_message(translate('OpenLP.ServiceManager', 'Error Saving File'), # Finally add all the listed media files.
translate('OpenLP.ServiceManager', 'There was an error saving your file.')) for write_path in write_list:
success = False zip_file.write(str(write_path), str(write_path))
finally: self.main_window.increment_progress_bar(write_path.stat().st_size)
if zip_file: with suppress(FileNotFoundError):
zip_file.close() file_path.unlink()
os.link(temp_file.name, str(file_path))
Settings().setValue(self.main_window.service_manager_settings_section + '/last directory', file_path.parent)
except (PermissionError, OSError) as error:
self.log_exception('Failed to save service to disk: {name}'.format(name=file_path))
self.main_window.error_message(
translate('OpenLP.ServiceManager', 'Error Saving File'),
translate('OpenLP.ServiceManager',
'There was an error saving your file.\n\n{error}').format(error=error))
return self.save_file_as()
self.main_window.finished_progress_bar() self.main_window.finished_progress_bar()
self.application.set_normal_cursor() self.application.set_normal_cursor()
if success: self.main_window.add_recent_file(file_path)
try: self.set_modified(False)
shutil.copy(temp_file_name, path_file_name) return True
except (shutil.Error, PermissionError):
return self.save_file_as()
self.main_window.add_recent_file(path_file_name)
self.set_modified(False)
delete_file(Path(temp_file_name))
return success
def save_file_as(self, field=None): def save_file_as(self, field=None):
""" """
@ -743,87 +667,49 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
""" """
if not self.file_name(): if not self.file_name():
return self.save_file_as() return self.save_file_as()
if self._save_lite: return self.save_file()
return self.save_local_file()
else:
return self.save_file()
def load_file(self, file_name): def load_file(self, file_path):
""" """
Load an existing service file Load an existing service file
:param file_name: :param file_path:
""" """
if not file_name: if not file_path.exists():
return False return False
file_name = str(file_name) service_data = None
if not os.path.exists(file_name):
return False
zip_file = None
file_to = None
self.application.set_busy_cursor() self.application.set_busy_cursor()
try: try:
zip_file = zipfile.ZipFile(file_name) with zipfile.ZipFile(str(file_path)) as zip_file:
for zip_info in zip_file.infolist(): compressed_size = 0
try: for zip_info in zip_file.infolist():
ucs_file = zip_info.filename compressed_size += zip_info.compress_size
except UnicodeDecodeError: self.main_window.display_progress_bar(compressed_size)
self.log_exception('file_name "{name}" is not valid UTF-8'.format(name=zip_info.file_name)) for zip_info in zip_file.infolist():
critical_error_message_box(message=translate('OpenLP.ServiceManager', self.log_debug('Extract file: {name}'.format(name=zip_info.filename))
'File is not a valid service.\n The content encoding is not UTF-8.')) # The json file has been called 'service_data.osj' since OpenLP 3.0
continue if zip_info.filename == 'service_data.osj' or zip_info.filename.endswith('osj'):
os_file = ucs_file.replace('/', os.path.sep) with zip_file.open(zip_info, 'r') as json_file:
os_file = os.path.basename(os_file) service_data = json_file.read()
self.log_debug('Extract file: {name}'.format(name=os_file)) else:
zip_info.filename = os_file zip_info.filename = os.path.basename(zip_info.filename)
zip_file.extract(zip_info, self.service_path) zip_file.extract(zip_info, str(self.service_path))
if os_file.endswith('osj') or os_file.endswith('osd'): self.main_window.increment_progress_bar(zip_info.compress_size)
p_file = os.path.join(self.service_path, os_file) if service_data:
if 'p_file' in locals(): items = json.loads(service_data, cls=OpenLPJsonDecoder)
file_to = open(p_file, 'r')
if p_file.endswith('osj'):
items = json.load(file_to)
else:
critical_error_message_box(message=translate('OpenLP.ServiceManager',
'The service file you are trying to open is in an old '
'format.\n Please save it using OpenLP 2.0.2 or '
'greater.'))
return
file_to.close()
self.new_file() self.new_file()
self.set_file_name(str_to_path(file_name))
self.main_window.display_progress_bar(len(items))
self.process_service_items(items) self.process_service_items(items)
delete_file(Path(p_file)) self.set_file_name(file_path)
self.main_window.add_recent_file(file_name) self.main_window.add_recent_file(file_path)
self.set_modified(False) self.set_modified(False)
Settings().setValue('servicemanager/last file', Path(file_name)) Settings().setValue('servicemanager/last file', file_path)
else: else:
critical_error_message_box(message=translate('OpenLP.ServiceManager', 'File is not a valid service.')) raise ValidationError(msg='No service data found')
self.log_error('File contains no service data') except (NameError, OSError, ValidationError, zipfile.BadZipFile) as e:
except (OSError, NameError): self.log_exception('Problem loading service file {name}'.format(name=file_path))
self.log_exception('Problem loading service file {name}'.format(name=file_name)) critical_error_message_box(
critical_error_message_box(message=translate('OpenLP.ServiceManager', message=translate('OpenLP.ServiceManager',
'File could not be opened because it is corrupt.')) 'The service file {file_path} could not be loaded because it is either corrupt, or '
except zipfile.BadZipFile: 'not a valid OpenLP 2 or OpenLP 3 service file.'.format(file_path=file_path)))
if os.path.getsize(file_name) == 0:
self.log_exception('Service file is zero sized: {name}'.format(name=file_name))
QtWidgets.QMessageBox.information(self, translate('OpenLP.ServiceManager', 'Empty File'),
translate('OpenLP.ServiceManager',
'This service file does not contain '
'any data.'))
else:
self.log_exception('Service file is cannot be extracted as zip: {name}'.format(name=file_name))
QtWidgets.QMessageBox.information(self, translate('OpenLP.ServiceManager', 'Corrupt File'),
translate('OpenLP.ServiceManager',
'This file is either corrupt or it is not an OpenLP 2 '
'service file.'))
self.application.set_normal_cursor()
return
finally:
if file_to:
file_to.close()
if zip_file:
zip_file.close()
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)
@ -838,7 +724,8 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
self.main_window.increment_progress_bar() self.main_window.increment_progress_bar()
service_item = ServiceItem() service_item = ServiceItem()
if 'openlp_core' in item: if 'openlp_core' in item:
item = item.get('openlp_core') item = item['openlp_core']
self._save_lite = item.get('lite-service', False)
theme = item.get('service-theme', None) theme = item.get('service-theme', None)
if theme: if theme:
find_and_set_in_combo_box(self.theme_combo_box, theme, set_missing=False) find_and_set_in_combo_box(self.theme_combo_box, theme, set_missing=False)
@ -863,9 +750,9 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
Load the last service item from the service manager when the service was last closed. Can be blank if there was Load the last service item from the service manager when the service was last closed. Can be blank if there was
no service present. no service present.
""" """
file_name = str_to_path(Settings().value('servicemanager/last file')) file_path = Settings().value('servicemanager/last file')
if file_name: if file_path:
self.load_file(file_name) self.load_file(file_path)
def context_menu(self, point): def context_menu(self, point):
""" """

View File

@ -0,0 +1,44 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2018 OpenLP Developers #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
Functional tests to test the remote index
"""
from unittest.mock import MagicMock, patch
from openlp.core.api.endpoint.remote import index
from openlp.core.api.endpoint.core import TRANSLATED_STRINGS
@patch('openlp.core.api.endpoint.remote.remote_endpoint')
def test_index(mocked_endpoint):
"""
Test the index method of the remote
"""
# GIVEN: A mocked Endpoint
mocked_endpoint.render_template.return_value = 'test template'
# WHEN: index is called
result = index(MagicMock(), 'index')
# THEN: The result should be "test template" and the right methods should have been called
mocked_endpoint.render_template.assert_called_once_with('index.mako', **TRANSLATED_STRINGS)
assert result == 'test template'

View File

@ -23,7 +23,7 @@
Package to test the openlp.core.lib.pluginmanager package. Package to test the openlp.core.lib.pluginmanager package.
""" """
from unittest import TestCase from unittest import TestCase
from unittest.mock import MagicMock from unittest.mock import MagicMock, patch
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
@ -50,6 +50,32 @@ class TestPluginManager(TestCase):
Registry().register('main_window', self.mocked_main_window) Registry().register('main_window', self.mocked_main_window)
Registry().register('settings_form', self.mocked_settings_form) Registry().register('settings_form', self.mocked_settings_form)
def test_bootstrap_initialise(self):
"""
Test the PluginManager.bootstrap_initialise() method
"""
# GIVEN: A plugin manager with some mocked out methods
manager = PluginManager()
with patch.object(manager, 'find_plugins') as mocked_find_plugins, \
patch.object(manager, 'hook_settings_tabs') as mocked_hook_settings_tabs, \
patch.object(manager, 'hook_media_manager') as mocked_hook_media_manager, \
patch.object(manager, 'hook_import_menu') as mocked_hook_import_menu, \
patch.object(manager, 'hook_export_menu') as mocked_hook_export_menu, \
patch.object(manager, 'hook_tools_menu') as mocked_hook_tools_menu, \
patch.object(manager, 'initialise_plugins') as mocked_initialise_plugins:
# WHEN: bootstrap_initialise() is called
manager.bootstrap_initialise()
# THEN: The hook methods should have been called
mocked_find_plugins.assert_called_with()
mocked_hook_settings_tabs.assert_called_with()
mocked_hook_media_manager.assert_called_with()
mocked_hook_import_menu.assert_called_with()
mocked_hook_export_menu.assert_called_with()
mocked_hook_tools_menu.assert_called_with()
mocked_initialise_plugins.assert_called_with()
def test_hook_media_manager_with_disabled_plugin(self): def test_hook_media_manager_with_disabled_plugin(self):
""" """
Test running the hook_media_manager() method with a disabled plugin Test running the hook_media_manager() method with a disabled plugin

View File

@ -27,6 +27,7 @@ from unittest import TestCase
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
from openlp.core.common import md5_hash from openlp.core.common import md5_hash
from openlp.core.common.path import Path
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 ItemCapabilities, ServiceItem, ServiceItemType, FormattingTags from openlp.core.lib import ItemCapabilities, ServiceItem, ServiceItemType, FormattingTags
@ -351,5 +352,5 @@ class TestServiceItem(TestCase, TestMixin):
'"Amazing Grace! how sweet the s" has been returned as the title' '"Amazing Grace! how sweet the s" has been returned as the title'
assert 'Twas grace that taught my hea' == service_item.get_frame_title(1), \ assert 'Twas grace that taught my hea' == service_item.get_frame_title(1), \
'"Twas grace that taught my hea" has been returned as the title' '"Twas grace that taught my hea" has been returned as the title'
assert '/test/amazing_grace.mp3' == service_item.background_audio[0], \ assert Path('/test/amazing_grace.mp3') == service_item.background_audio[0], \
'"/test/amazing_grace.mp3" should be in the background_audio list' '"/test/amazing_grace.mp3" should be in the background_audio list'

View File

@ -23,6 +23,7 @@
Package to test openlp.core.ui.mainwindow package. Package to test openlp.core.ui.mainwindow package.
""" """
import os import os
from pathlib import Path
from unittest import TestCase from unittest import TestCase
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
@ -87,14 +88,13 @@ class TestMainWindow(TestCase, TestMixin):
""" """
# GIVEN a service as an argument to openlp # GIVEN a service as an argument to openlp
service = os.path.join(TEST_RESOURCES_PATH, 'service', 'test.osz') service = os.path.join(TEST_RESOURCES_PATH, 'service', 'test.osz')
self.main_window.arguments = [service]
# WHEN the argument is processed # WHEN the argument is processed
with patch.object(self.main_window.service_manager, 'load_file') as mocked_load_file: with patch.object(self.main_window.service_manager, 'load_file') as mocked_load_file:
self.main_window.open_cmd_line_files(service) self.main_window.open_cmd_line_files(service)
# THEN the service from the arguments is loaded # THEN the service from the arguments is loaded
mocked_load_file.assert_called_with(service) mocked_load_file.assert_called_with(Path(service))
@patch('openlp.core.ui.servicemanager.ServiceManager.load_file') @patch('openlp.core.ui.servicemanager.ServiceManager.load_file')
def test_cmd_line_arg(self, mocked_load_file): def test_cmd_line_arg(self, mocked_load_file):
@ -253,3 +253,30 @@ class TestMainWindow(TestCase, TestMixin):
# THEN: projector_manager_dock.setVisible should had been called once # THEN: projector_manager_dock.setVisible should had been called once
mocked_dock.setVisible.assert_called_once_with(False) mocked_dock.setVisible.assert_called_once_with(False)
def test_increment_progress_bar_default_increment(self):
"""
Test that increment_progress_bar increments the progress bar by 1 when called without the `increment` arg.
"""
# GIVEN: A mocked progress bar
with patch.object(self.main_window, 'load_progress_bar', **{'value.return_value': 0}) as mocked_progress_bar:
# WHEN: Calling increment_progress_bar without the `increment` arg
self.main_window.increment_progress_bar()
# THEN: The progress bar value should have been incremented by 1
mocked_progress_bar.setValue.assert_called_once_with(1)
def test_increment_progress_bar_custom_increment(self):
"""
Test that increment_progress_bar increments the progress bar by the `increment` arg when called with the
`increment` arg with a set value.
"""
# GIVEN: A mocked progress bar
with patch.object(self.main_window, 'load_progress_bar', **{'value.return_value': 0}) as mocked_progress_bar:
# WHEN: Calling increment_progress_bar with `increment` set to 10
self.main_window.increment_progress_bar(increment=10)
# THEN: The progress bar value should have been incremented by 10
mocked_progress_bar.setValue.assert_called_once_with(10)

View File

@ -623,10 +623,10 @@ class TestServiceManager(TestCase):
# THEN: make_preview() should not have been called # THEN: make_preview() should not have been called
assert mocked_make_preview.call_count == 0, 'ServiceManager.make_preview() should not be called' assert mocked_make_preview.call_count == 0, 'ServiceManager.make_preview() should not be called'
@patch('openlp.core.ui.servicemanager.shutil.copy')
@patch('openlp.core.ui.servicemanager.zipfile') @patch('openlp.core.ui.servicemanager.zipfile')
@patch('openlp.core.ui.servicemanager.ServiceManager.save_file_as') @patch('openlp.core.ui.servicemanager.ServiceManager.save_file_as')
def test_save_file_raises_permission_error(self, mocked_save_file_as, mocked_zipfile, mocked_shutil_copy): @patch('openlp.core.ui.servicemanager.os')
def test_save_file_raises_permission_error(self, mocked_os, mocked_save_file_as, mocked_zipfile):
""" """
Test that when a PermissionError is raised when trying to save a file, it is handled correctly Test that when a PermissionError is raised when trying to save a file, it is handled correctly
""" """
@ -636,50 +636,22 @@ class TestServiceManager(TestCase):
Registry().register('main_window', mocked_main_window) Registry().register('main_window', mocked_main_window)
Registry().register('application', MagicMock()) Registry().register('application', MagicMock())
service_manager = ServiceManager(None) service_manager = ServiceManager(None)
service_manager._service_path = os.path.join('temp', 'filename.osz') service_manager._service_path = MagicMock()
service_manager._save_lite = False service_manager._save_lite = False
service_manager.service_items = [] service_manager.service_items = []
service_manager.service_theme = 'Default' service_manager.service_theme = 'Default'
service_manager.service_manager_list = MagicMock() service_manager.service_manager_list = MagicMock()
mocked_save_file_as.return_value = True mocked_save_file_as.return_value = True
mocked_zipfile.ZipFile.return_value = MagicMock() mocked_zipfile.ZipFile.return_value = MagicMock()
mocked_shutil_copy.side_effect = PermissionError mocked_os.link.side_effect = PermissionError
# WHEN: The service is saved and a PermissionError is thrown # WHEN: The service is saved and a PermissionError is raised
result = service_manager.save_file() result = service_manager.save_file()
# THEN: The "save_as" method is called to save the service # THEN: The "save_as" method is called to save the service
assert result is True assert result is True
mocked_save_file_as.assert_called_with() mocked_save_file_as.assert_called_with()
@patch('openlp.core.ui.servicemanager.shutil.copy')
@patch('openlp.core.ui.servicemanager.zipfile')
@patch('openlp.core.ui.servicemanager.ServiceManager.save_file_as')
def test_save_local_file_raises_permission_error(self, mocked_save_file_as, mocked_zipfile, mocked_shutil_copy):
"""
Test that when a PermissionError is raised when trying to save a local file, it is handled correctly
"""
# GIVEN: A service manager, a service to save
mocked_main_window = MagicMock()
mocked_main_window.service_manager_settings_section = 'servicemanager'
Registry().register('main_window', mocked_main_window)
Registry().register('application', MagicMock())
service_manager = ServiceManager(None)
service_manager._service_path = os.path.join('temp', 'filename.osz')
service_manager._save_lite = False
service_manager.service_items = []
service_manager.service_theme = 'Default'
mocked_save_file_as.return_value = True
mocked_zipfile.ZipFile.return_value = MagicMock()
mocked_shutil_copy.side_effect = PermissionError
# WHEN: The service is saved and a PermissionError is thrown
result = service_manager.save_local_file()
# THEN: The "save_as" method is called to save the service
assert result is True
mocked_save_file_as.assert_called_with()
@patch('openlp.core.ui.servicemanager.ServiceManager.regenerate_service_items') @patch('openlp.core.ui.servicemanager.ServiceManager.regenerate_service_items')
def test_theme_change_global(self, mocked_regenerate_service_items): def test_theme_change_global(self, mocked_regenerate_service_items):
""" """

View File

@ -0,0 +1,64 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2018 OpenLP Developers #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
Test the openlp.core.ui package.
"""
from unittest.mock import MagicMock, patch
from openlp.core.ui import SingleColumnTableWidget
def test_single_col_widget_create():
"""
Test creating the SingleColumnTableWidget object
"""
# GIVEN: the SingleColumnTableWidget class
# WHEN: An object is created
widget = SingleColumnTableWidget(None)
# THEN: The object should have 1 column and no visible header
assert widget.columnCount() == 1, 'There should be only 1 column'
assert widget.horizontalHeader().isVisible() is False, 'The horizontal header should not be visible'
@patch('openlp.core.ui.QtWidgets.QTableWidget')
def test_single_col_widget_resize_event(MockQTableWidget):
"""
Test that the resizeEvent method does the right thing
"""
# GIVEN: An instance of a SingleColumnTableWidget and a mocked event
widget = SingleColumnTableWidget(None)
mocked_event = MagicMock()
mocked_event.size.return_value.width.return_value = 10
# WHEN: resizeEvent() is called
with patch.object(widget, 'columnCount') as mocked_column_count, \
patch.object(widget, 'setColumnWidth') as mocked_set_column_width, \
patch.object(widget, 'resizeRowsToContents') as mocked_resize_rows_to_contents:
mocked_column_count.return_value = 1
widget.resizeEvent(mocked_event)
# THEN: The correct calls should have been made
MockQTableWidget.resizeEvent.assert_called_once_with(widget, mocked_event)
mocked_column_count.assert_called_once_with()
mocked_set_column_width.assert_called_once_with(0, 10)
mocked_resize_rows_to_contents.assert_called_once_with()