Merge lp:~phill-ridout/openlp/fixes-I into lp:openlp

Proposed by Phill
Status: Merged
Merged at revision: 2850
Proposed branch: lp:~phill-ridout/openlp/fixes-I
Merge into: lp:openlp
Diff against target: 1333 lines (+187/-208)
47 files modified
openlp/core/api/deploy.py (+2/-2)
openlp/core/app.py (+8/-4)
openlp/core/common/__init__.py (+2/-2)
openlp/core/common/path.py (+1/-1)
openlp/core/common/settings.py (+1/-1)
openlp/core/display/html/display.html (+1/-1)
openlp/core/display/html/display.js (+1/-1)
openlp/core/lib/__init__.py (+6/-8)
openlp/core/lib/db.py (+15/-11)
openlp/core/lib/mediamanageritem.py (+6/-6)
openlp/core/lib/serviceitem.py (+3/-3)
openlp/core/ui/advancedtab.py (+1/-1)
openlp/core/ui/exceptiondialog.py (+3/-3)
openlp/core/ui/exceptionform.py (+14/-7)
openlp/core/ui/generaltab.py (+0/-1)
openlp/core/ui/mainwindow.py (+1/-1)
openlp/core/ui/servicemanager.py (+13/-12)
openlp/core/ui/thememanager.py (+10/-10)
openlp/core/version.py (+1/-1)
openlp/core/widgets/edits.py (+1/-1)
openlp/core/widgets/wizard.py (+1/-41)
openlp/plugins/bibles/forms/bibleimportform.py (+2/-2)
openlp/plugins/bibles/lib/bibleimport.py (+2/-2)
openlp/plugins/bibles/lib/db.py (+6/-6)
openlp/plugins/bibles/lib/importers/wordproject.py (+1/-1)
openlp/plugins/bibles/lib/manager.py (+1/-1)
openlp/plugins/bibles/lib/mediaitem.py (+3/-3)
openlp/plugins/custom/lib/mediaitem.py (+3/-6)
openlp/plugins/images/lib/mediaitem.py (+3/-3)
openlp/plugins/media/lib/mediaitem.py (+7/-7)
openlp/plugins/presentations/lib/mediaitem.py (+3/-3)
openlp/plugins/presentations/lib/messagelistener.py (+2/-8)
openlp/plugins/songs/forms/songimportform.py (+7/-2)
openlp/plugins/songs/lib/importers/cclifile.py (+4/-3)
openlp/plugins/songs/lib/mediaitem.py (+2/-4)
openlp/plugins/songusage/forms/songusagedetailform.py (+2/-2)
run_openlp.py (+8/-2)
tests/functional/openlp_core/api/test_deploy.py (+2/-2)
tests/functional/openlp_core/common/test_init.py (+14/-8)
tests/functional/openlp_core/common/test_path.py (+3/-5)
tests/functional/openlp_core/lib/test_serviceitem.py (+4/-4)
tests/functional/openlp_core/ui/test_thememanager.py (+3/-3)
tests/functional/openlp_plugins/bibles/test_manager.py (+3/-2)
tests/interfaces/openlp_plugins/songs/forms/test_songmaintenanceform.py (+2/-2)
tests/openlp_core/common/test_network_interfaces.py (+1/-1)
tests/openlp_core/projectors/test_projector_pjlink_commands_01.py (+6/-6)
tests/openlp_core/projectors/test_projector_sourceform.py (+2/-2)
To merge this branch: bzr merge lp:~phill-ridout/openlp/fixes-I
Reviewer Review Type Date Requested Status
Tim Bentley Pending
Review via email: mp+364652@code.launchpad.net

This proposal supersedes a proposal from 2019-03-17.

Commit message

Fixes a few bugs, and some path lib refactors

To post a comment you must log in.
Revision history for this message
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal

Linux tests passed!

Revision history for this message
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal

Linting failed, please see https://ci.openlp.io/job/MP-03-Linting/55/ for more details

Revision history for this message
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal

Linux tests passed!

Revision history for this message
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal

Linting failed, please see https://ci.openlp.io/job/MP-03-Linting/56/ for more details

Revision history for this message
Tim Bentley (trb143) wrote : Posted in a previous version of this proposal

Looks ok.

Please do not try and change vlcplayer as you will be on a mission of despair!

review: Approve
Revision history for this message
Raoul Snyman (raoul-snyman) wrote :

Linux tests passed!

Revision history for this message
Raoul Snyman (raoul-snyman) wrote :

Linting passed!

Revision history for this message
Raoul Snyman (raoul-snyman) wrote :

macOS tests failed, please see https://ci.openlp.io/job/MP-04-macOS-Tests/48/ for more details

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'openlp/core/api/deploy.py'
--- openlp/core/api/deploy.py 2019-02-14 15:09:09 +0000
+++ openlp/core/api/deploy.py 2019-03-17 21:06:27 +0000
@@ -39,8 +39,8 @@
39 :return: None39 :return: None
40 """40 """
41 zip_path = app_root_path / zip_name41 zip_path = app_root_path / zip_name
42 web_zip = ZipFile(str(zip_path))42 web_zip = ZipFile(zip_path)
43 web_zip.extractall(str(app_root_path))43 web_zip.extractall(app_root_path)
4444
4545
46def download_sha256():46def download_sha256():
4747
=== modified file 'openlp/core/app.py'
--- openlp/core/app.py 2019-02-14 15:09:09 +0000
+++ openlp/core/app.py 2019-03-17 21:06:27 +0000
@@ -317,8 +317,7 @@
317 """317 """
318 create_paths(log_path, do_not_log=True)318 create_paths(log_path, do_not_log=True)
319 file_path = log_path / 'openlp.log'319 file_path = log_path / 'openlp.log'
320 # TODO: FileHandler accepts a Path object in Py3.6320 logfile = logging.FileHandler(file_path, 'w', encoding='UTF-8')
321 logfile = logging.FileHandler(str(file_path), 'w', encoding='UTF-8')
322 logfile.setFormatter(logging.Formatter('%(asctime)s %(threadName)s %(name)-55s %(levelname)-8s %(message)s'))321 logfile.setFormatter(logging.Formatter('%(asctime)s %(threadName)s %(name)-55s %(levelname)-8s %(message)s'))
323 log.addHandler(logfile)322 log.addHandler(logfile)
324 if log.isEnabledFor(logging.DEBUG):323 if log.isEnabledFor(logging.DEBUG):
@@ -364,7 +363,7 @@
364 portable_settings_path = data_path / 'OpenLP.ini'363 portable_settings_path = data_path / 'OpenLP.ini'
365 # Make this our settings file364 # Make this our settings file
366 log.info('INI file: {name}'.format(name=portable_settings_path))365 log.info('INI file: {name}'.format(name=portable_settings_path))
367 Settings.set_filename(str(portable_settings_path))366 Settings.set_filename(portable_settings_path)
368 portable_settings = Settings()367 portable_settings = Settings()
369 # Set our data path368 # Set our data path
370 log.info('Data path: {name}'.format(name=data_path))369 log.info('Data path: {name}'.format(name=data_path))
@@ -405,7 +404,12 @@
405 None, translate('OpenLP', 'Settings Upgrade'),404 None, translate('OpenLP', 'Settings Upgrade'),
406 translate('OpenLP', 'Your settings are about to be upgraded. A backup will be created at '405 translate('OpenLP', 'Your settings are about to be upgraded. A backup will be created at '
407 '{back_up_path}').format(back_up_path=back_up_path))406 '{back_up_path}').format(back_up_path=back_up_path))
408 settings.export(back_up_path)407 try:
408 settings.export(back_up_path)
409 except OSError:
410 QtWidgets.QMessageBox.warning(
411 None, translate('OpenLP', 'Settings Upgrade'),
412 translate('OpenLP', 'Settings back up failed.\n\nContinuining to upgrade.'))
409 settings.upgrade_settings()413 settings.upgrade_settings()
410 # First time checks in settings414 # First time checks in settings
411 if not Settings().value('core/has run wizard'):415 if not Settings().value('core/has run wizard'):
412416
=== modified file 'openlp/core/common/__init__.py'
--- openlp/core/common/__init__.py 2019-02-14 15:09:09 +0000
+++ openlp/core/common/__init__.py 2019-03-17 21:06:27 +0000
@@ -474,10 +474,10 @@
474 if not chunk:474 if not chunk:
475 break475 break
476 detector.feed(chunk)476 detector.feed(chunk)
477 detector.close()
478 return detector.result
479 except OSError:477 except OSError:
480 log.exception('Error detecting file encoding')478 log.exception('Error detecting file encoding')
479 finally:
480 return detector.close()
481481
482482
483def normalize_str(irregular_string):483def normalize_str(irregular_string):
484484
=== modified file 'openlp/core/common/path.py'
--- openlp/core/common/path.py 2019-02-14 15:09:09 +0000
+++ openlp/core/common/path.py 2019-03-17 21:06:27 +0000
@@ -78,7 +78,7 @@
78 :param onerror: Handler function to handle any errors78 :param onerror: Handler function to handle any errors
79 :rtype: None79 :rtype: None
80 """80 """
81 shutil.rmtree(str(self), ignore_errors, onerror)81 shutil.rmtree(self, ignore_errors, onerror)
8282
8383
84def replace_params(args, kwargs, params):84def replace_params(args, kwargs, params):
8585
=== modified file 'openlp/core/common/settings.py'
--- openlp/core/common/settings.py 2019-02-14 15:09:09 +0000
+++ openlp/core/common/settings.py 2019-03-17 21:06:27 +0000
@@ -540,7 +540,7 @@
540 old_values = [self._convert_value(old_value, default_value)540 old_values = [self._convert_value(old_value, default_value)
541 for old_value, default_value in zip(old_values, default_values)]541 for old_value, default_value in zip(old_values, default_values)]
542 # Iterate over our rules and check what the old_value should be "converted" to.542 # Iterate over our rules and check what the old_value should be "converted" to.
543 new_value = None543 new_value = old_values[0]
544 for new_rule, old_rule in rules:544 for new_rule, old_rule in rules:
545 # If the value matches with the condition (rule), then use the provided value. This is used to545 # If the value matches with the condition (rule), then use the provided value. This is used to
546 # convert values. E. g. an old value 1 results in True, and 0 in False.546 # convert values. E. g. an old value 1 results in True, and 0 in False.
547547
=== modified file 'openlp/core/display/html/display.html'
--- openlp/core/display/html/display.html 2018-10-12 19:51:51 +0000
+++ openlp/core/display/html/display.html 2019-03-17 21:06:27 +0000
@@ -6,7 +6,7 @@
6 <style type="text/css">6 <style type="text/css">
7 body {7 body {
8 background: transparent !important;8 background: transparent !important;
9 color: #fff !important;9 color: rgb(255, 255, 255) !important;
10 }10 }
11 sup {11 sup {
12 vertical-align: super !important;12 vertical-align: super !important;
1313
=== modified file 'openlp/core/display/html/display.js'
--- openlp/core/display/html/display.js 2019-02-05 21:26:30 +0000
+++ openlp/core/display/html/display.js 2019-03-17 21:06:27 +0000
@@ -315,7 +315,7 @@
315 section.setAttribute("style", "height: 100%; width: 100%; position: relative;");315 section.setAttribute("style", "height: 100%; width: 100%; position: relative;");
316 var img = document.createElement('img');316 var img = document.createElement('img');
317 img.src = image;317 img.src = image;
318 img.setAttribute("style", "position: absolute; top: 0; bottom: 0; left: 0; right: 0; margin: auto;");318 img.setAttribute("style", "position: absolute; top: 0; bottom: 0; left: 0; right: 0; margin: auto; max-height: 100%; max-width: 100%");
319 section.appendChild(img);319 section.appendChild(img);
320 slidesDiv.appendChild(section);320 slidesDiv.appendChild(section);
321 Display._slides['0'] = 0;321 Display._slides['0'] = 0;
322322
=== modified file 'openlp/core/lib/__init__.py'
--- openlp/core/lib/__init__.py 2019-02-14 15:09:09 +0000
+++ openlp/core/lib/__init__.py 2019-03-17 21:06:27 +0000
@@ -242,16 +242,16 @@
242 """242 """
243 Resize an image to fit on the current screen for the web and returns it as a byte stream.243 Resize an image to fit on the current screen for the web and returns it as a byte stream.
244244
245 :param image: The image to converted.245 :param image: The image to be converted.
246 :param base_64: If True returns the image as Base64 bytes, otherwise the image is returned as a byte array.246 :param base_64: If True returns the image as Base64 bytes, otherwise the image is returned as a byte array.
247 To preserve original intention, this defaults to True247 To preserve original intention, this defaults to True
248 """248 """
249 log.debug('image_to_byte - start')249 log.debug('image_to_byte - start')
250 byte_array = QtCore.QByteArray()250 byte_array = QtCore.QByteArray()
251 # use buffer to store pixmap into byteArray251 # use buffer to store pixmap into byteArray
252 buffie = QtCore.QBuffer(byte_array)252 buffer = QtCore.QBuffer(byte_array)
253 buffie.open(QtCore.QIODevice.WriteOnly)253 buffer.open(QtCore.QIODevice.WriteOnly)
254 image.save(buffie, "PNG")254 image.save(buffer, "PNG")
255 log.debug('image_to_byte - end')255 log.debug('image_to_byte - end')
256 if not base_64:256 if not base_64:
257 return byte_array257 return byte_array
@@ -263,15 +263,13 @@
263 """263 """
264 Create a thumbnail from the given image path and depending on ``return_icon`` it returns an icon from this thumb.264 Create a thumbnail from the given image path and depending on ``return_icon`` it returns an icon from this thumb.
265265
266 :param image_path: The image file to create the icon from.266 :param openlp.core.common.path.Path image_path: The image file to create the icon from.
267 :param thumb_path: The filename to save the thumbnail to.267 :param openlp.core.common.path.Path thumb_path: The filename to save the thumbnail to.
268 :param return_icon: States if an icon should be build and returned from the thumb. Defaults to ``True``.268 :param return_icon: States if an icon should be build and returned from the thumb. Defaults to ``True``.
269 :param size: Allows to state a own size (QtCore.QSize) to use. Defaults to ``None``, which means that a default269 :param size: Allows to state a own size (QtCore.QSize) to use. Defaults to ``None``, which means that a default
270 height of 88 is used.270 height of 88 is used.
271 :return: The final icon.271 :return: The final icon.
272 """272 """
273 # TODO: To path object
274 thumb_path = Path(thumb_path)
275 reader = QtGui.QImageReader(str(image_path))273 reader = QtGui.QImageReader(str(image_path))
276 if size is None:274 if size is None:
277 # No size given; use default height of 88275 # No size given; use default height of 88
278276
=== modified file 'openlp/core/lib/db.py'
--- openlp/core/lib/db.py 2019-02-14 15:09:09 +0000
+++ openlp/core/lib/db.py 2019-03-17 21:06:27 +0000
@@ -132,8 +132,9 @@
132 Create a path to a database from the plugin name and database name132 Create a path to a database from the plugin name and database name
133133
134 :param plugin_name: Name of plugin134 :param plugin_name: Name of plugin
135 :param db_file_name: File name of database135 :param openlp.core.common.path.Path | str | None db_file_name: File name of database
136 :return: The path to the database as type str136 :return: The path to the database
137 :rtype: str
137 """138 """
138 if db_file_name is None:139 if db_file_name is None:
139 return 'sqlite:///{path}/{plugin}.sqlite'.format(path=AppLocation.get_section_data_path(plugin_name),140 return 'sqlite:///{path}/{plugin}.sqlite'.format(path=AppLocation.get_section_data_path(plugin_name),
@@ -144,15 +145,15 @@
144 return 'sqlite:///{path}/{name}'.format(path=AppLocation.get_section_data_path(plugin_name), name=db_file_name)145 return 'sqlite:///{path}/{name}'.format(path=AppLocation.get_section_data_path(plugin_name), name=db_file_name)
145146
146147
147def handle_db_error(plugin_name, db_file_name):148def handle_db_error(plugin_name, db_file_path):
148 """149 """
149 Log and report to the user that a database cannot be loaded150 Log and report to the user that a database cannot be loaded
150151
151 :param plugin_name: Name of plugin152 :param plugin_name: Name of plugin
152 :param db_file_name: File name of database153 :param openlp.core.common.path.Path db_file_path: File name of database
153 :return: None154 :return: None
154 """155 """
155 db_path = get_db_path(plugin_name, db_file_name)156 db_path = get_db_path(plugin_name, db_file_path)
156 log.exception('Error loading database: {db}'.format(db=db_path))157 log.exception('Error loading database: {db}'.format(db=db_path))
157 critical_error_message_box(translate('OpenLP.Manager', 'Database Error'),158 critical_error_message_box(translate('OpenLP.Manager', 'Database Error'),
158 translate('OpenLP.Manager',159 translate('OpenLP.Manager',
@@ -161,10 +162,13 @@
161162
162def init_url(plugin_name, db_file_name=None):163def init_url(plugin_name, db_file_name=None):
163 """164 """
164 Return the database URL.165 Construct the connection string for a database.
165166
166 :param plugin_name: The name of the plugin for the database creation.167 :param plugin_name: The name of the plugin for the database creation.
167 :param db_file_name: The database file name. Defaults to None resulting in the plugin_name being used.168 :param openlp.core.common.path.Path | str | None db_file_name: The database file name. Defaults to None resulting
169 in the plugin_name being used.
170 :return: The database URL
171 :rtype: str
168 """172 """
169 settings = Settings()173 settings = Settings()
170 settings.beginGroup(plugin_name)174 settings.beginGroup(plugin_name)
@@ -345,7 +349,7 @@
345 Runs the initialisation process that includes creating the connection to the database and the tables if they do349 Runs the initialisation process that includes creating the connection to the database and the tables if they do
346 not exist.350 not exist.
347351
348 :param plugin_name: The name to setup paths and settings section names352 :param plugin_name: The name to setup paths and settings section names
349 :param init_schema: The init_schema function for this database353 :param init_schema: The init_schema function for this database
350 :param openlp.core.common.path.Path db_file_path: The file name to use for this database. Defaults to None354 :param openlp.core.common.path.Path db_file_path: The file name to use for this database. Defaults to None
351 resulting in the plugin_name being used.355 resulting in the plugin_name being used.
@@ -357,14 +361,14 @@
357 self.db_url = None361 self.db_url = None
358 if db_file_path:362 if db_file_path:
359 log.debug('Manager: Creating new DB url')363 log.debug('Manager: Creating new DB url')
360 self.db_url = init_url(plugin_name, str(db_file_path))364 self.db_url = init_url(plugin_name, str(db_file_path)) # TOdO :PATHLIB
361 else:365 else:
362 self.db_url = init_url(plugin_name)366 self.db_url = init_url(plugin_name)
363 if upgrade_mod:367 if upgrade_mod:
364 try:368 try:
365 db_ver, up_ver = upgrade_db(self.db_url, upgrade_mod)369 db_ver, up_ver = upgrade_db(self.db_url, upgrade_mod)
366 except (SQLAlchemyError, DBAPIError):370 except (SQLAlchemyError, DBAPIError):
367 handle_db_error(plugin_name, str(db_file_path))371 handle_db_error(plugin_name, db_file_path)
368 return372 return
369 if db_ver > up_ver:373 if db_ver > up_ver:
370 critical_error_message_box(374 critical_error_message_box(
@@ -379,7 +383,7 @@
379 try:383 try:
380 self.session = init_schema(self.db_url)384 self.session = init_schema(self.db_url)
381 except (SQLAlchemyError, DBAPIError):385 except (SQLAlchemyError, DBAPIError):
382 handle_db_error(plugin_name, str(db_file_path))386 handle_db_error(plugin_name, db_file_path)
383 else:387 else:
384 self.session = session388 self.session = session
385389
386390
=== modified file 'openlp/core/lib/mediamanageritem.py'
--- openlp/core/lib/mediamanageritem.py 2019-02-14 15:09:09 +0000
+++ openlp/core/lib/mediamanageritem.py 2019-03-17 21:06:27 +0000
@@ -454,15 +454,16 @@
454 """454 """
455 pass455 pass
456456
457 def generate_slide_data(self, service_item, item=None, xml_version=False, remote=False,457 def generate_slide_data(self, service_item, *, item=None, remote=False, context=ServiceItemContext.Live,
458 context=ServiceItemContext.Live):458 file_path=None):
459 """459 """
460 Generate the slide data. Needs to be implemented by the plugin.460 Generate the slide data. Needs to be implemented by the plugin.
461
461 :param service_item: The service Item to be processed462 :param service_item: The service Item to be processed
462 :param item: The database item to be used to build the service item463 :param item: The database item to be used to build the service item
463 :param xml_version:
464 :param remote: Was this remote triggered (False)464 :param remote: Was this remote triggered (False)
465 :param context: The service context465 :param context: The service context
466 :param openlp.core.common.path.Path file_path:
466 """467 """
467 raise NotImplementedError('MediaManagerItem.generate_slide_data needs to be defined by the plugin')468 raise NotImplementedError('MediaManagerItem.generate_slide_data needs to be defined by the plugin')
468469
@@ -624,17 +625,16 @@
624 'You must select a {title} '625 'You must select a {title} '
625 'service item.').format(title=self.title))626 'service item.').format(title=self.title))
626627
627 def build_service_item(self, item=None, xml_version=False, remote=False, context=ServiceItemContext.Live):628 def build_service_item(self, item=None, remote=False, context=ServiceItemContext.Live):
628 """629 """
629 Common method for generating a service item630 Common method for generating a service item
630 :param item: Service Item to be built.631 :param item: Service Item to be built.
631 :param xml_version: version of XML (False)
632 :param remote: Remote triggered (False)632 :param remote: Remote triggered (False)
633 :param context: The context on which this is called633 :param context: The context on which this is called
634 """634 """
635 service_item = ServiceItem(self.plugin)635 service_item = ServiceItem(self.plugin)
636 service_item.add_icon()636 service_item.add_icon()
637 if self.generate_slide_data(service_item, item, xml_version, remote, context):637 if self.generate_slide_data(service_item, item=item, remote=remote, context=context):
638 return service_item638 return service_item
639 else:639 else:
640 return None640 return None
641641
=== modified file 'openlp/core/lib/serviceitem.py'
--- openlp/core/lib/serviceitem.py 2019-02-14 15:09:09 +0000
+++ openlp/core/lib/serviceitem.py 2019-03-17 21:06:27 +0000
@@ -263,7 +263,7 @@
263 file_location = os.path.join(path, file_name)263 file_location = os.path.join(path, file_name)
264 file_location_hash = md5_hash(file_location.encode('utf-8'))264 file_location_hash = md5_hash(file_location.encode('utf-8'))
265 image = os.path.join(str(AppLocation.get_section_data_path(self.name)), 'thumbnails',265 image = os.path.join(str(AppLocation.get_section_data_path(self.name)), 'thumbnails',
266 file_location_hash, ntpath.basename(image))266 file_location_hash, ntpath.basename(image)) # TODO: Pathlib
267 self.slides.append({'title': file_name, 'image': image, 'path': path, 'display_title': display_title,267 self.slides.append({'title': file_name, 'image': image, 'path': path, 'display_title': display_title,
268 'notes': notes, 'thumbnail': image})268 'notes': notes, 'thumbnail': image})
269 # if self.is_capable(ItemCapabilities.HasThumbnails):269 # if self.is_capable(ItemCapabilities.HasThumbnails):
@@ -361,7 +361,7 @@
361 if isinstance(file_path, str):361 if isinstance(file_path, str):
362 # Handle service files prior to OpenLP 3.0362 # Handle service files prior to OpenLP 3.0
363 # Windows can handle both forward and backward slashes, so we use ntpath to get the basename363 # Windows can handle both forward and backward slashes, so we use ntpath to get the basename
364 file_path = Path(path, ntpath.basename(file_path))364 file_path = path / ntpath.basename(file_path)
365 self.background_audio.append(file_path)365 self.background_audio.append(file_path)
366 self.theme_overwritten = header.get('theme_overwritten', False)366 self.theme_overwritten = header.get('theme_overwritten', False)
367 if self.service_item_type == ServiceItemType.Text:367 if self.service_item_type == ServiceItemType.Text:
@@ -374,7 +374,7 @@
374 if path:374 if path:
375 self.has_original_files = False375 self.has_original_files = False
376 for text_image in service_item['serviceitem']['data']:376 for text_image in service_item['serviceitem']['data']:
377 file_path = os.path.join(path, text_image)377 file_path = path / text_image
378 self.add_from_image(file_path, text_image, background)378 self.add_from_image(file_path, text_image, background)
379 else:379 else:
380 for text_image in service_item['serviceitem']['data']:380 for text_image in service_item['serviceitem']['data']:
381381
=== modified file 'openlp/core/ui/advancedtab.py'
--- openlp/core/ui/advancedtab.py 2019-03-09 03:53:20 +0000
+++ openlp/core/ui/advancedtab.py 2019-03-17 21:06:27 +0000
@@ -478,7 +478,7 @@
478 minute=self.service_name_time.time().minute()478 minute=self.service_name_time.time().minute()
479 )479 )
480 try:480 try:
481 service_name_example = format_time(str(self.service_name_edit.text()), local_time)481 service_name_example = format_time(self.service_name_edit.text(), local_time)
482 except ValueError:482 except ValueError:
483 preset_is_valid = False483 preset_is_valid = False
484 service_name_example = translate('OpenLP.AdvancedTab', 'Syntax error.')484 service_name_example = translate('OpenLP.AdvancedTab', 'Syntax error.')
485485
=== modified file 'openlp/core/ui/exceptiondialog.py'
--- openlp/core/ui/exceptiondialog.py 2019-02-14 15:09:09 +0000
+++ openlp/core/ui/exceptiondialog.py 2019-03-17 21:06:27 +0000
@@ -77,11 +77,11 @@
77 self.save_report_button = create_button(exception_dialog, 'save_report_button',77 self.save_report_button = create_button(exception_dialog, 'save_report_button',
78 icon=UiIcons().save,78 icon=UiIcons().save,
79 click=self.on_save_report_button_clicked)79 click=self.on_save_report_button_clicked)
80 self.attach_tile_button = create_button(exception_dialog, 'attach_tile_button',80 self.attach_file_button = create_button(exception_dialog, 'attach_file_button',
81 icon=UiIcons().open,81 icon=UiIcons().open,
82 click=self.on_attach_file_button_clicked)82 click=self.on_attach_file_button_clicked)
83 self.button_box = create_button_box(exception_dialog, 'button_box', ['close'],83 self.button_box = create_button_box(exception_dialog, 'button_box', ['close'],
84 [self.send_report_button, self.save_report_button, self.attach_tile_button])84 [self.send_report_button, self.save_report_button, self.attach_file_button])
85 self.exception_layout.addWidget(self.button_box)85 self.exception_layout.addWidget(self.button_box)
8686
87 self.retranslate_ui(exception_dialog)87 self.retranslate_ui(exception_dialog)
@@ -112,4 +112,4 @@
112 ).format(first_part=exception_part1))112 ).format(first_part=exception_part1))
113 self.send_report_button.setText(translate('OpenLP.ExceptionDialog', 'Send E-Mail'))113 self.send_report_button.setText(translate('OpenLP.ExceptionDialog', 'Send E-Mail'))
114 self.save_report_button.setText(translate('OpenLP.ExceptionDialog', 'Save to File'))114 self.save_report_button.setText(translate('OpenLP.ExceptionDialog', 'Save to File'))
115 self.attach_tile_button.setText(translate('OpenLP.ExceptionDialog', 'Attach File'))115 self.attach_file_button.setText(translate('OpenLP.ExceptionDialog', 'Attach File'))
116116
=== modified file 'openlp/core/ui/exceptionform.py'
--- openlp/core/ui/exceptionform.py 2019-02-14 15:09:09 +0000
+++ openlp/core/ui/exceptionform.py 2019-03-17 21:06:27 +0000
@@ -95,12 +95,14 @@
95 """95 """
96 Saving exception log and system information to a file.96 Saving exception log and system information to a file.
97 """97 """
98 file_path, filter_used = FileDialog.getSaveFileName(98 while True:
99 self,99 file_path, filter_used = FileDialog.getSaveFileName(
100 translate('OpenLP.ExceptionForm', 'Save Crash Report'),100 self,
101 Settings().value(self.settings_section + '/last directory'),101 translate('OpenLP.ExceptionForm', 'Save Crash Report'),
102 translate('OpenLP.ExceptionForm', 'Text files (*.txt *.log *.text)'))102 Settings().value(self.settings_section + '/last directory'),
103 if file_path:103 translate('OpenLP.ExceptionForm', 'Text files (*.txt *.log *.text)'))
104 if file_path is None:
105 break
104 Settings().setValue(self.settings_section + '/last directory', file_path.parent)106 Settings().setValue(self.settings_section + '/last directory', file_path.parent)
105 opts = self._create_report()107 opts = self._create_report()
106 report_text = self.report_text.format(version=opts['version'], description=opts['description'],108 report_text = self.report_text.format(version=opts['version'], description=opts['description'],
@@ -108,8 +110,13 @@
108 try:110 try:
109 with file_path.open('w') as report_file:111 with file_path.open('w') as report_file:
110 report_file.write(report_text)112 report_file.write(report_text)
111 except OSError:113 break
114 except OSError as e:
112 log.exception('Failed to write crash report')115 log.exception('Failed to write crash report')
116 QtWidgets.QMessageBox.warning(
117 self, translate('OpenLP.ExceptionDialog', 'Failed to Save Report'),
118 translate('OpenLP.ExceptionDialog', 'The following error occured when saving the report.\n\n'
119 '{exception}').format(file_name=file_path, exception=e))
113120
114 def on_send_report_button_clicked(self):121 def on_send_report_button_clicked(self):
115 """122 """
116123
=== modified file 'openlp/core/ui/generaltab.py'
--- openlp/core/ui/generaltab.py 2019-02-14 15:09:09 +0000
+++ openlp/core/ui/generaltab.py 2019-03-17 21:06:27 +0000
@@ -47,7 +47,6 @@
47 """47 """
48 Initialise the general settings tab48 Initialise the general settings tab
49 """49 """
50 self.logo_file = ':/graphics/openlp-splash-screen.png'
51 self.logo_background_color = '#ffffff'50 self.logo_background_color = '#ffffff'
52 self.screens = ScreenList()51 self.screens = ScreenList()
53 self.icon_path = ':/icon/openlp-logo.svg'52 self.icon_path = ':/icon/openlp-logo.svg'
5453
=== modified file 'openlp/core/ui/mainwindow.py'
--- openlp/core/ui/mainwindow.py 2019-02-14 15:09:09 +0000
+++ openlp/core/ui/mainwindow.py 2019-03-17 21:06:27 +0000
@@ -1334,7 +1334,7 @@
1334 self.show_status_message(1334 self.show_status_message(
1335 translate('OpenLP.MainWindow', 'Copying OpenLP data to new data directory location - {path} '1335 translate('OpenLP.MainWindow', 'Copying OpenLP data to new data directory location - {path} '
1336 '- Please wait for copy to finish').format(path=self.new_data_path))1336 '- Please wait for copy to finish').format(path=self.new_data_path))
1337 dir_util.copy_tree(str(old_data_path), str(self.new_data_path))1337 dir_util.copy_tree(old_data_path, self.new_data_path)
1338 self.log_info('Copy successful')1338 self.log_info('Copy successful')
1339 except (OSError, DistutilsFileError) as why:1339 except (OSError, DistutilsFileError) as why:
1340 self.application.set_normal_cursor()1340 self.application.set_normal_cursor()
13411341
=== modified file 'openlp/core/ui/servicemanager.py'
--- openlp/core/ui/servicemanager.py 2019-02-14 15:09:09 +0000
+++ openlp/core/ui/servicemanager.py 2019-03-17 21:06:27 +0000
@@ -25,7 +25,6 @@
25import html25import html
26import json26import json
27import os27import os
28import shutil
29import zipfile28import zipfile
30from contextlib import suppress29from contextlib import suppress
31from datetime import datetime, timedelta30from datetime import datetime, timedelta
@@ -234,7 +233,7 @@
234 self.service_manager_list.itemExpanded.connect(self.expanded)233 self.service_manager_list.itemExpanded.connect(self.expanded)
235 # Last little bits of setting up234 # Last little bits of setting up
236 self.service_theme = Settings().value(self.main_window.service_manager_settings_section + '/service theme')235 self.service_theme = Settings().value(self.main_window.service_manager_settings_section + '/service theme')
237 self.service_path = str(AppLocation.get_section_data_path('servicemanager'))236 self.service_path = AppLocation.get_section_data_path('servicemanager')
238 # build the drag and drop context menu237 # build the drag and drop context menu
239 self.dnd_menu = QtWidgets.QMenu()238 self.dnd_menu = QtWidgets.QMenu()
240 self.new_action = self.dnd_menu.addAction(translate('OpenLP.ServiceManager', '&Add New Item'))239 self.new_action = self.dnd_menu.addAction(translate('OpenLP.ServiceManager', '&Add New Item'))
@@ -590,11 +589,11 @@
590 self.main_window.increment_progress_bar(service_content_size)589 self.main_window.increment_progress_bar(service_content_size)
591 # Finally add all the listed media files.590 # Finally add all the listed media files.
592 for write_path in write_list:591 for write_path in write_list:
593 zip_file.write(str(write_path), str(write_path))592 zip_file.write(write_path, write_path)
594 self.main_window.increment_progress_bar(write_path.stat().st_size)593 self.main_window.increment_progress_bar(write_path.stat().st_size)
595 with suppress(FileNotFoundError):594 with suppress(FileNotFoundError):
596 file_path.unlink()595 file_path.unlink()
597 os.link(temp_file.name, str(file_path))596 os.link(temp_file.name, file_path)
598 Settings().setValue(self.main_window.service_manager_settings_section + '/last directory', file_path.parent)597 Settings().setValue(self.main_window.service_manager_settings_section + '/last directory', file_path.parent)
599 except (PermissionError, OSError) as error:598 except (PermissionError, OSError) as error:
600 self.log_exception('Failed to save service to disk: {name}'.format(name=file_path))599 self.log_exception('Failed to save service to disk: {name}'.format(name=file_path))
@@ -679,7 +678,7 @@
679 service_data = None678 service_data = None
680 self.application.set_busy_cursor()679 self.application.set_busy_cursor()
681 try:680 try:
682 with zipfile.ZipFile(str(file_path)) as zip_file:681 with zipfile.ZipFile(file_path) as zip_file:
683 compressed_size = 0682 compressed_size = 0
684 for zip_info in zip_file.infolist():683 for zip_info in zip_file.infolist():
685 compressed_size += zip_info.compress_size684 compressed_size += zip_info.compress_size
@@ -692,7 +691,7 @@
692 service_data = json_file.read()691 service_data = json_file.read()
693 else:692 else:
694 zip_info.filename = os.path.basename(zip_info.filename)693 zip_info.filename = os.path.basename(zip_info.filename)
695 zip_file.extract(zip_info, str(self.service_path))694 zip_file.extract(zip_info, self.service_path)
696 self.main_window.increment_progress_bar(zip_info.compress_size)695 self.main_window.increment_progress_bar(zip_info.compress_size)
697 if service_data:696 if service_data:
698 items = json.loads(service_data, cls=OpenLPJsonDecoder)697 items = json.loads(service_data, cls=OpenLPJsonDecoder)
@@ -705,11 +704,13 @@
705 else:704 else:
706 raise ValidationError(msg='No service data found')705 raise ValidationError(msg='No service data found')
707 except (NameError, OSError, ValidationError, zipfile.BadZipFile):706 except (NameError, OSError, ValidationError, zipfile.BadZipFile):
707 self.application.set_normal_cursor()
708 self.log_exception('Problem loading service file {name}'.format(name=file_path))708 self.log_exception('Problem loading service file {name}'.format(name=file_path))
709 critical_error_message_box(709 critical_error_message_box(
710 message=translate('OpenLP.ServiceManager',710 message=translate('OpenLP.ServiceManager',
711 'The service file {file_path} could not be loaded because it is either corrupt, or '711 'The service file {file_path} could not be loaded because it is either corrupt, '
712 'not a valid OpenLP 2 or OpenLP 3 service file.'.format(file_path=file_path)))712 'inaccessible, or not a valid OpenLP 2 or OpenLP 3 service file.'
713 ).format(file_path=file_path))
713 self.main_window.finished_progress_bar()714 self.main_window.finished_progress_bar()
714 self.application.set_normal_cursor()715 self.application.set_normal_cursor()
715 self.repaint_service_list(-1, -1)716 self.repaint_service_list(-1, -1)
@@ -1237,11 +1238,11 @@
1237 """1238 """
1238 Empties the service_path of temporary files on system exit.1239 Empties the service_path of temporary files on system exit.
1239 """1240 """
1240 for file_name in os.listdir(self.service_path):1241 for file_path in self.service_path.iterdir():
1241 file_path = Path(self.service_path, file_name)
1242 delete_file(file_path)1242 delete_file(file_path)
1243 if os.path.exists(os.path.join(self.service_path, 'audio')):1243 audio_path = self.service_path / 'audio'
1244 shutil.rmtree(os.path.join(self.service_path, 'audio'), True)1244 if audio_path.exists():
1245 audio_path.rmtree(True)
12451246
1246 def on_theme_combo_box_selected(self, current_index):1247 def on_theme_combo_box_selected(self, current_index):
1247 """1248 """
12481249
=== modified file 'openlp/core/ui/thememanager.py'
--- openlp/core/ui/thememanager.py 2019-02-14 15:09:09 +0000
+++ openlp/core/ui/thememanager.py 2019-03-17 21:06:27 +0000
@@ -150,7 +150,7 @@
150 self.global_theme = Settings().value(self.settings_section + '/global theme')150 self.global_theme = Settings().value(self.settings_section + '/global theme')
151 self.build_theme_path()151 self.build_theme_path()
152 self.load_first_time_themes()152 self.load_first_time_themes()
153 self.upgrade_themes()153 self.upgrade_themes() # TODO: Can be removed when upgrade path from OpenLP 2.4 no longer needed
154154
155 def bootstrap_post_set_up(self):155 def bootstrap_post_set_up(self):
156 """156 """
@@ -422,10 +422,10 @@
422 :rtype: bool422 :rtype: bool
423 """423 """
424 try:424 try:
425 with zipfile.ZipFile(str(theme_path), 'w') as theme_zip:425 with zipfile.ZipFile(theme_path, 'w') as theme_zip:
426 source_path = self.theme_path / theme_name426 source_path = self.theme_path / theme_name
427 for file_path in source_path.iterdir():427 for file_path in source_path.iterdir():
428 theme_zip.write(str(file_path), os.path.join(theme_name, file_path.name))428 theme_zip.write(file_path, Path(theme_name, file_path.name))
429 return True429 return True
430 except OSError as ose:430 except OSError as ose:
431 self.log_exception('Export Theme Failed')431 self.log_exception('Export Theme Failed')
@@ -567,10 +567,10 @@
567 json_theme = False567 json_theme = False
568 theme_name = ""568 theme_name = ""
569 try:569 try:
570 with zipfile.ZipFile(str(file_path)) as theme_zip:570 with zipfile.ZipFile(file_path) as theme_zip:
571 json_file = [name for name in theme_zip.namelist() if os.path.splitext(name)[1].lower() == '.json']571 json_file = [name for name in theme_zip.namelist() if os.path.splitext(name)[1].lower() == '.json']
572 if len(json_file) != 1:572 if len(json_file) != 1:
573 # TODO: remove XML handling after the 2.6 release.573 # TODO: remove XML handling after once the upgrade path from 2.4 is no longer required
574 xml_file = [name for name in theme_zip.namelist() if os.path.splitext(name)[1].lower() == '.xml']574 xml_file = [name for name in theme_zip.namelist() if os.path.splitext(name)[1].lower() == '.xml']
575 if len(xml_file) != 1:575 if len(xml_file) != 1:
576 self.log_error('Theme contains "{val:d}" theme files'.format(val=len(xml_file)))576 self.log_error('Theme contains "{val:d}" theme files'.format(val=len(xml_file)))
@@ -607,12 +607,12 @@
607 else:607 else:
608 with full_name.open('wb') as out_file:608 with full_name.open('wb') as out_file:
609 out_file.write(theme_zip.read(zipped_file))609 out_file.write(theme_zip.read(zipped_file))
610 except (OSError, zipfile.BadZipFile):610 except (OSError, ValidationError, zipfile.BadZipFile):
611 self.log_exception('Importing theme from zip failed {name}'.format(name=file_path))611 self.log_exception('Importing theme from zip failed {name}'.format(name=file_path))
612 raise ValidationError612 critical_error_message_box(
613 except ValidationError:613 translate('OpenLP.ThemeManager', 'Import Error'),
614 critical_error_message_box(translate('OpenLP.ThemeManager', 'Validation Error'),614 translate('OpenLP.ThemeManager', 'There was a problem imoorting {file_name}.\n\nIt is corrupt,'
615 translate('OpenLP.ThemeManager', 'File is not a valid theme.'))615 'inaccessible or not a valid theme.').format(file_name=file_path))
616 finally:616 finally:
617 if not abort_import:617 if not abort_import:
618 # As all files are closed, we can create the Theme.618 # As all files are closed, we can create the Theme.
619619
=== modified file 'openlp/core/version.py'
--- openlp/core/version.py 2019-02-14 15:09:09 +0000
+++ openlp/core/version.py 2019-03-17 21:06:27 +0000
@@ -200,7 +200,7 @@
200 """200 """
201 library_versions = OrderedDict([(library, _get_lib_version(*args)) for library, args in LIBRARIES.items()])201 library_versions = OrderedDict([(library, _get_lib_version(*args)) for library, args in LIBRARIES.items()])
202 # Just update the dict for display purposes, changing the None to a '-'202 # Just update the dict for display purposes, changing the None to a '-'
203 for library, version in library_versions:203 for library, version in library_versions.items():
204 if version is None:204 if version is None:
205 library_versions[library] = '-'205 library_versions[library] = '-'
206 return library_versions206 return library_versions
207207
=== modified file 'openlp/core/widgets/edits.py'
--- openlp/core/widgets/edits.py 2019-02-14 15:09:09 +0000
+++ openlp/core/widgets/edits.py 2019-03-17 21:06:27 +0000
@@ -352,7 +352,7 @@
352 :rtype: None352 :rtype: None
353 """353 """
354 if self._path != path:354 if self._path != path:
355 self._path = path355 self.path = path
356 self.pathChanged.emit(path)356 self.pathChanged.emit(path)
357357
358358
359359
=== modified file 'openlp/core/widgets/wizard.py'
--- openlp/core/widgets/wizard.py 2019-02-14 15:09:09 +0000
+++ openlp/core/widgets/wizard.py 2019-03-17 21:06:27 +0000
@@ -27,13 +27,11 @@
27from PyQt5 import QtCore, QtGui, QtWidgets27from PyQt5 import QtCore, QtGui, QtWidgets
2828
29from openlp.core.common import is_macosx29from openlp.core.common import is_macosx
30from openlp.core.common.i18n import UiStrings, translate30from openlp.core.common.i18n import translate
31from openlp.core.common.mixins import RegistryProperties31from openlp.core.common.mixins import RegistryProperties
32from openlp.core.common.registry import Registry32from openlp.core.common.registry import Registry
33from openlp.core.common.settings import Settings
34from openlp.core.lib.ui import add_welcome_page33from openlp.core.lib.ui import add_welcome_page
35from openlp.core.ui.icons import UiIcons34from openlp.core.ui.icons import UiIcons
36from openlp.core.widgets.dialogs import FileDialog
3735
3836
39log = logging.getLogger(__name__)37log = logging.getLogger(__name__)
@@ -280,41 +278,3 @@
280 self.finish_button.setVisible(True)278 self.finish_button.setVisible(True)
281 self.cancel_button.setVisible(False)279 self.cancel_button.setVisible(False)
282 self.application.process_events()280 self.application.process_events()
283
284 def get_file_name(self, title, editbox, setting_name, filters=''):
285 """
286 Opens a FileDialog and saves the filename to the given editbox.
287
288 :param str title: The title of the dialog.
289 :param QtWidgets.QLineEdit editbox: An QLineEdit.
290 :param str setting_name: The place where to save the last opened directory.
291 :param str filters: The file extension filters. It should contain the file description
292 as well as the file extension. For example::
293
294 'OpenLP 2 Databases (*.sqlite)'
295 :rtype: None
296 """
297 if filters:
298 filters += ';;'
299 filters += '%s (*)' % UiStrings().AllFiles
300 file_path, filter_used = FileDialog.getOpenFileName(
301 self, title, Settings().value(self.plugin.settings_section + '/' + setting_name), filters)
302 if file_path:
303 editbox.setText(str(file_path))
304 Settings().setValue(self.plugin.settings_section + '/' + setting_name, file_path.parent)
305
306 def get_folder(self, title, editbox, setting_name):
307 """
308 Opens a FileDialog and saves the selected folder to the given editbox.
309
310 :param str title: The title of the dialog.
311 :param QtWidgets.QLineEdit editbox: An QLineEditbox.
312 :param str setting_name: The place where to save the last opened directory.
313 :rtype: None
314 """
315 folder_path = FileDialog.getExistingDirectory(
316 self, title, Settings().value(self.plugin.settings_section + '/' + setting_name),
317 FileDialog.ShowDirsOnly)
318 if folder_path:
319 editbox.setText(str(folder_path))
320 Settings().setValue(self.plugin.settings_section + '/' + setting_name, folder_path)
321281
=== modified file 'openlp/plugins/bibles/forms/bibleimportform.py'
--- openlp/plugins/bibles/forms/bibleimportform.py 2019-02-14 15:09:09 +0000
+++ openlp/plugins/bibles/forms/bibleimportform.py 2019-03-17 21:06:27 +0000
@@ -463,14 +463,14 @@
463 UiStrings().NFSs, translate('BiblesPlugin.ImportWizardForm',463 UiStrings().NFSs, translate('BiblesPlugin.ImportWizardForm',
464 'You need to specify a file with books of the Bible to use in the '464 'You need to specify a file with books of the Bible to use in the '
465 'import.'))465 'import.'))
466 self.csv_books_edit.setFocus()466 self.csv_books_path_edit.setFocus()
467 return False467 return False
468 elif not self.field('csv_versefile'):468 elif not self.field('csv_versefile'):
469 critical_error_message_box(469 critical_error_message_box(
470 UiStrings().NFSs,470 UiStrings().NFSs,
471 translate('BiblesPlugin.ImportWizardForm', 'You need to specify a file of Bible verses to '471 translate('BiblesPlugin.ImportWizardForm', 'You need to specify a file of Bible verses to '
472 'import.'))472 'import.'))
473 self.csv_verses_edit.setFocus()473 self.csv_verses_pathedit.setFocus()
474 return False474 return False
475 elif self.field('source_format') == BibleFormat.OpenSong:475 elif self.field('source_format') == BibleFormat.OpenSong:
476 if not self.field('opensong_file'):476 if not self.field('opensong_file'):
477477
=== modified file 'openlp/plugins/bibles/lib/bibleimport.py'
--- openlp/plugins/bibles/lib/bibleimport.py 2019-02-14 15:09:09 +0000
+++ openlp/plugins/bibles/lib/bibleimport.py 2019-03-17 21:06:27 +0000
@@ -48,9 +48,9 @@
48 """48 """
49 Check if the supplied file is compressed49 Check if the supplied file is compressed
5050
51 :param file_path: A path to the file to check51 :param openlp.core.common.path.Path file_path: A path to the file to check
52 """52 """
53 if is_zipfile(str(file_path)):53 if is_zipfile(file_path):
54 critical_error_message_box(54 critical_error_message_box(
55 message=translate('BiblesPlugin.BibleImport',55 message=translate('BiblesPlugin.BibleImport',
56 'The file "{file}" you supplied is compressed. You must decompress it before import.'56 'The file "{file}" you supplied is compressed. You must decompress it before import.'
5757
=== modified file 'openlp/plugins/bibles/lib/db.py'
--- openlp/plugins/bibles/lib/db.py 2019-02-14 15:09:09 +0000
+++ openlp/plugins/bibles/lib/db.py 2019-03-17 21:06:27 +0000
@@ -158,11 +158,10 @@
158 self.name = kwargs['name']158 self.name = kwargs['name']
159 if not isinstance(self.name, str):159 if not isinstance(self.name, str):
160 self.name = str(self.name, 'utf-8')160 self.name = str(self.name, 'utf-8')
161 # TODO: To path object161 self.file_path = Path(clean_filename(self.name) + '.sqlite')
162 file_path = Path(clean_filename(self.name) + '.sqlite')
163 if 'file' in kwargs:162 if 'file' in kwargs:
164 file_path = kwargs['file']163 self.file_path = kwargs['file']
165 Manager.__init__(self, 'bibles', init_schema, file_path, upgrade)164 Manager.__init__(self, 'bibles', init_schema, self.file_path, upgrade)
166 if self.session and 'file' in kwargs:165 if self.session and 'file' in kwargs:
167 self.get_name()166 self.get_name()
168 self._is_web_bible = None167 self._is_web_bible = None
@@ -750,7 +749,7 @@
750 ]749 ]
751750
752751
753class AlternativeBookNamesDB(QtCore.QObject, Manager):752class AlternativeBookNamesDB(Manager):
754 """753 """
755 This class represents a database-bound alternative book names system.754 This class represents a database-bound alternative book names system.
756 """755 """
@@ -765,8 +764,9 @@
765 """764 """
766 if AlternativeBookNamesDB.cursor is None:765 if AlternativeBookNamesDB.cursor is None:
767 file_path = AppLocation.get_directory(AppLocation.DataDir) / 'bibles' / 'alternative_book_names.sqlite'766 file_path = AppLocation.get_directory(AppLocation.DataDir) / 'bibles' / 'alternative_book_names.sqlite'
767 exists = file_path.exists()
768 AlternativeBookNamesDB.conn = sqlite3.connect(str(file_path))768 AlternativeBookNamesDB.conn = sqlite3.connect(str(file_path))
769 if not file_path.exists():769 if not exists:
770 # create new DB, create table alternative_book_names770 # create new DB, create table alternative_book_names
771 AlternativeBookNamesDB.conn.execute(771 AlternativeBookNamesDB.conn.execute(
772 'CREATE TABLE alternative_book_names(id INTEGER NOT NULL, '772 'CREATE TABLE alternative_book_names(id INTEGER NOT NULL, '
773773
=== modified file 'openlp/plugins/bibles/lib/importers/wordproject.py'
--- openlp/plugins/bibles/lib/importers/wordproject.py 2019-02-14 15:09:09 +0000
+++ openlp/plugins/bibles/lib/importers/wordproject.py 2019-03-17 21:06:27 +0000
@@ -51,7 +51,7 @@
51 Unzip the file to a temporary directory51 Unzip the file to a temporary directory
52 """52 """
53 self.tmp = TemporaryDirectory()53 self.tmp = TemporaryDirectory()
54 with ZipFile(str(self.file_path)) as zip_file:54 with ZipFile(self.file_path) as zip_file:
55 zip_file.extractall(self.tmp.name)55 zip_file.extractall(self.tmp.name)
56 self.base_path = Path(self.tmp.name, self.file_path.stem)56 self.base_path = Path(self.tmp.name, self.file_path.stem)
5757
5858
=== modified file 'openlp/plugins/bibles/lib/manager.py'
--- openlp/plugins/bibles/lib/manager.py 2019-02-14 15:09:09 +0000
+++ openlp/plugins/bibles/lib/manager.py 2019-03-17 21:06:27 +0000
@@ -187,7 +187,7 @@
187 bible = self.db_cache[name]187 bible = self.db_cache[name]
188 bible.session.close_all()188 bible.session.close_all()
189 bible.session = None189 bible.session = None
190 return delete_file(Path(bible.path, bible.file))190 return delete_file(bible.path, bible.file_path)
191191
192 def get_bibles(self):192 def get_bibles(self):
193 """193 """
194194
=== modified file 'openlp/plugins/bibles/lib/mediaitem.py'
--- openlp/plugins/bibles/lib/mediaitem.py 2019-02-14 15:09:09 +0000
+++ openlp/plugins/bibles/lib/mediaitem.py 2019-03-17 21:06:27 +0000
@@ -911,16 +911,16 @@
911 list_widget_items.append(bible_verse)911 list_widget_items.append(bible_verse)
912 return list_widget_items912 return list_widget_items
913913
914 def generate_slide_data(self, service_item, item=None, xml_version=False, remote=False,914 def generate_slide_data(self, service_item, *, item=None, remote=False, context=ServiceItemContext.Service,
915 context=ServiceItemContext.Service):915 **kwargs):
916 """916 """
917 Generate the slide data. Needs to be implemented by the plugin.917 Generate the slide data. Needs to be implemented by the plugin.
918918
919 :param service_item: The service item to be built on919 :param service_item: The service item to be built on
920 :param item: The Song item to be used920 :param item: The Song item to be used
921 :param xml_version: The xml version (not used)
922 :param remote: Triggered from remote921 :param remote: Triggered from remote
923 :param context: Why is it being generated922 :param context: Why is it being generated
923 :param kwargs: Consume other unused args specified by the base implementation, but not use by this one.
924 """924 """
925 log.debug('generating slide data')925 log.debug('generating slide data')
926 if item:926 if item:
927927
=== modified file 'openlp/plugins/custom/lib/mediaitem.py'
--- openlp/plugins/custom/lib/mediaitem.py 2019-02-14 15:09:09 +0000
+++ openlp/plugins/custom/lib/mediaitem.py 2019-03-17 21:06:27 +0000
@@ -28,7 +28,7 @@
28from openlp.core.common.i18n import UiStrings, translate28from openlp.core.common.i18n import UiStrings, translate
29from openlp.core.common.registry import Registry29from openlp.core.common.registry import Registry
30from openlp.core.common.settings import Settings30from openlp.core.common.settings import Settings
31from openlp.core.lib import ServiceItemContext, check_item_selected31from openlp.core.lib import check_item_selected
32from openlp.core.lib.mediamanageritem import MediaManagerItem32from openlp.core.lib.mediamanageritem import MediaManagerItem
33from openlp.core.lib.plugin import PluginStatus33from openlp.core.lib.plugin import PluginStatus
34from openlp.core.lib.serviceitem import ItemCapabilities34from openlp.core.lib.serviceitem import ItemCapabilities
@@ -219,15 +219,12 @@
219 self.search_text_edit.setFocus()219 self.search_text_edit.setFocus()
220 self.search_text_edit.selectAll()220 self.search_text_edit.selectAll()
221221
222 def generate_slide_data(self, service_item, item=None, xml_version=False,222 def generate_slide_data(self, service_item, *, item=None, **kwargs):
223 remote=False, context=ServiceItemContext.Service):
224 """223 """
225 Generate the slide data. Needs to be implemented by the plugin.224 Generate the slide data. Needs to be implemented by the plugin.
226 :param service_item: To be updated225 :param service_item: To be updated
227 :param item: The custom database item to be used226 :param item: The custom database item to be used
228 :param xml_version: No used227 :param kwargs: Consume other unused args specified by the base implementation, but not use by this one.
229 :param remote: Is this triggered by the Preview Controller or Service Manager.
230 :param context: Why is this item required to be build (Default Service).
231 """228 """
232 item_id = self._get_id_of_item_to_generate(item, self.remote_custom)229 item_id = self._get_id_of_item_to_generate(item, self.remote_custom)
233 service_item.add_capability(ItemCapabilities.CanEdit)230 service_item.add_capability(ItemCapabilities.CanEdit)
234231
=== modified file 'openlp/plugins/images/lib/mediaitem.py'
--- openlp/plugins/images/lib/mediaitem.py 2019-02-14 15:09:09 +0000
+++ openlp/plugins/images/lib/mediaitem.py 2019-03-17 21:06:27 +0000
@@ -542,16 +542,16 @@
542 image_items.sort(key=lambda item: get_natural_key(item.text(0)))542 image_items.sort(key=lambda item: get_natural_key(item.text(0)))
543 target_group.addChildren(image_items)543 target_group.addChildren(image_items)
544544
545 def generate_slide_data(self, service_item, item=None, xml_version=False, remote=False,545 def generate_slide_data(self, service_item, *, item=None, remote=False, context=ServiceItemContext.Service,
546 context=ServiceItemContext.Service):546 **kwargs):
547 """547 """
548 Generate the slide data. Needs to be implemented by the plugin.548 Generate the slide data. Needs to be implemented by the plugin.
549549
550 :param service_item: The service item to be built on550 :param service_item: The service item to be built on
551 :param item: The Song item to be used551 :param item: The Song item to be used
552 :param xml_version: The xml version (not used)
553 :param remote: Triggered from remote552 :param remote: Triggered from remote
554 :param context: Why is it being generated553 :param context: Why is it being generated
554 :param kwargs: Consume other unused args specified by the base implementation, but not use by this one.
555 """555 """
556 background = QtGui.QColor(Settings().value(self.settings_section + '/background color'))556 background = QtGui.QColor(Settings().value(self.settings_section + '/background color'))
557 if item:557 if item:
558558
=== modified file 'openlp/plugins/media/lib/mediaitem.py'
--- openlp/plugins/media/lib/mediaitem.py 2019-02-14 15:09:09 +0000
+++ openlp/plugins/media/lib/mediaitem.py 2019-03-17 21:06:27 +0000
@@ -29,7 +29,7 @@
29from openlp.core.common.applocation import AppLocation29from openlp.core.common.applocation import AppLocation
30from openlp.core.common.i18n import UiStrings, get_natural_key, translate30from openlp.core.common.i18n import UiStrings, get_natural_key, translate
31from openlp.core.common.mixins import RegistryProperties31from openlp.core.common.mixins import RegistryProperties
32from openlp.core.common.path import Path, create_paths, path_to_str32from openlp.core.common.path import create_paths, path_to_str
33from openlp.core.common.registry import Registry33from openlp.core.common.registry import Registry
34from openlp.core.common.settings import Settings34from openlp.core.common.settings import Settings
35from openlp.core.lib import MediaType, ServiceItemContext, check_item_selected35from openlp.core.lib import MediaType, ServiceItemContext, check_item_selected
@@ -166,16 +166,16 @@
166 # self.display_type_combo_box.currentIndexChanged.connect(self.override_player_changed)166 # self.display_type_combo_box.currentIndexChanged.connect(self.override_player_changed)
167 pass167 pass
168168
169 def generate_slide_data(self, service_item, item=None, xml_version=False, remote=False,169 def generate_slide_data(self, service_item, *, item=None, remote=False, context=ServiceItemContext.Service,
170 context=ServiceItemContext.Service):170 **kwargs):
171 """171 """
172 Generate the slide data. Needs to be implemented by the plugin.172 Generate the slide data. Needs to be implemented by the plugin.
173173
174 :param service_item: The service item to be built on174 :param service_item: The service item to be built on
175 :param item: The Song item to be used175 :param item: The Song item to be used
176 :param xml_version: The xml version (not used)
177 :param remote: Triggered from remote176 :param remote: Triggered from remote
178 :param context: Why is it being generated177 :param context: Why is it being generated
178 :param kwargs: Consume other unused args specified by the base implementation, but not use by this one.
179 """179 """
180 if item is None:180 if item is None:
181 item = self.list_view.currentItem()181 item = self.list_view.currentItem()
@@ -229,8 +229,8 @@
229 Initialize media item.229 Initialize media item.
230 """230 """
231 self.list_view.clear()231 self.list_view.clear()
232 self.service_path = str(AppLocation.get_section_data_path(self.settings_section) / 'thumbnails')232 self.service_path = AppLocation.get_section_data_path(self.settings_section) / 'thumbnails'
233 create_paths(Path(self.service_path))233 create_paths(self.service_path)
234 self.load_list([path_to_str(file) for file in Settings().value(self.settings_section + '/media files')])234 self.load_list([path_to_str(file) for file in Settings().value(self.settings_section + '/media files')])
235 self.rebuild_players()235 self.rebuild_players()
236236
@@ -264,7 +264,7 @@
264 :param media: The media264 :param media: The media
265 :param target_group:265 :param target_group:
266 """266 """
267 media.sort(key=lambda file_name: get_natural_key(os.path.split(str(file_name))[1]))267 media.sort(key=lambda file_path: get_natural_key(file_path.name))
268 for track in media:268 for track in media:
269 track_info = QtCore.QFileInfo(track)269 track_info = QtCore.QFileInfo(track)
270 item_name = None270 item_name = None
271271
=== modified file 'openlp/plugins/presentations/lib/mediaitem.py'
--- openlp/plugins/presentations/lib/mediaitem.py 2019-02-14 15:09:09 +0000
+++ openlp/plugins/presentations/lib/mediaitem.py 2019-03-17 21:06:27 +0000
@@ -260,16 +260,16 @@
260 doc.presentation_deleted()260 doc.presentation_deleted()
261 doc.close_presentation()261 doc.close_presentation()
262262
263 def generate_slide_data(self, service_item, item=None, xml_version=False, remote=False,263 def generate_slide_data(self, service_item, *, item=None, remote=False, context=ServiceItemContext.Service,
264 context=ServiceItemContext.Service, file_path=None):264 file_path=None, **kwargs):
265 """265 """
266 Generate the slide data. Needs to be implemented by the plugin.266 Generate the slide data. Needs to be implemented by the plugin.
267267
268 :param service_item: The service item to be built on268 :param service_item: The service item to be built on
269 :param item: The Song item to be used269 :param item: The Song item to be used
270 :param xml_version: The xml version (not used)
271 :param remote: Triggered from remote270 :param remote: Triggered from remote
272 :param context: Why is it being generated271 :param context: Why is it being generated
272 :param kwargs: Consume other unused args specified by the base implementation, but not use by this one.
273 """273 """
274 if item:274 if item:
275 items = [item]275 items = [item]
276276
=== modified file 'openlp/plugins/presentations/lib/messagelistener.py'
--- openlp/plugins/presentations/lib/messagelistener.py 2019-02-14 15:09:09 +0000
+++ openlp/plugins/presentations/lib/messagelistener.py 2019-03-17 21:06:27 +0000
@@ -337,14 +337,8 @@
337 # Create a copy of the original item, and then clear the original item so it can be filled with images337 # Create a copy of the original item, and then clear the original item so it can be filled with images
338 item_cpy = copy.copy(item)338 item_cpy = copy.copy(item)
339 item.__init__(None)339 item.__init__(None)
340 if is_live:340 context = ServiceItemContext.Live if is_live else ServiceItemContext.Preview
341 # TODO: To Path object341 self.media_item.generate_slide_data(item, item=item_cpy, context=context, file_path=file_path)
342 self.media_item.generate_slide_data(item, item_cpy, False, False, ServiceItemContext.Live,
343 str(file_path))
344 else:
345 # TODO: To Path object
346 self.media_item.generate_slide_data(item, item_cpy, False, False, ServiceItemContext.Preview,
347 str(file_path))
348 # Some of the original serviceitem attributes is needed in the new serviceitem342 # Some of the original serviceitem attributes is needed in the new serviceitem
349 item.footer = item_cpy.footer343 item.footer = item_cpy.footer
350 item.from_service = item_cpy.from_service344 item.from_service = item_cpy.from_service
351345
=== modified file 'openlp/plugins/songs/forms/songimportform.py'
--- openlp/plugins/songs/forms/songimportform.py 2019-02-14 15:09:09 +0000
+++ openlp/plugins/songs/forms/songimportform.py 2019-03-17 21:06:27 +0000
@@ -329,8 +329,13 @@
329 importer = self.plugin.import_songs(329 importer = self.plugin.import_songs(
330 source_format,330 source_format,
331 file_paths=self.get_list_of_paths(self.format_widgets[source_format]['file_list_widget']))331 file_paths=self.get_list_of_paths(self.format_widgets[source_format]['file_list_widget']))
332 importer.do_import()332 try:
333 self.progress_label.setText(WizardStrings.FinishedImport)333 importer.do_import()
334 self.progress_label.setText(WizardStrings.FinishedImport)
335 except OSError as e:
336 log.exception('Importing songs failed')
337 self.progress_label.setText(translate('SongsPlugin.ImportWizardForm',
338 'Your Song import failed. {error}').format(error=e))
334339
335 def on_error_copy_to_button_clicked(self):340 def on_error_copy_to_button_clicked(self):
336 """341 """
337342
=== modified file 'openlp/plugins/songs/lib/importers/cclifile.py'
--- openlp/plugins/songs/lib/importers/cclifile.py 2019-02-14 15:09:09 +0000
+++ openlp/plugins/songs/lib/importers/cclifile.py 2019-03-17 21:06:27 +0000
@@ -67,7 +67,7 @@
67 details = {'confidence': 1, 'encoding': 'utf-8'}67 details = {'confidence': 1, 'encoding': 'utf-8'}
68 except UnicodeDecodeError:68 except UnicodeDecodeError:
69 details = chardet.detect(detect_content)69 details = chardet.detect(detect_content)
70 in_file = codecs.open(str(file_path), 'r', details['encoding'])70 in_file = codecs.open(file_path, 'r', details['encoding'])
71 if not in_file.read(1) == '\ufeff':71 if not in_file.read(1) == '\ufeff':
72 # not UTF or no BOM was found72 # not UTF or no BOM was found
73 in_file.seek(0)73 in_file.seek(0)
@@ -251,10 +251,10 @@
251 line_number = 0251 line_number = 0
252 check_first_verse_line = False252 check_first_verse_line = False
253 verse_text = ''253 verse_text = ''
254 verse_type = VerseType.tags[VerseType.Verse]
254 song_author = ''255 song_author = ''
255 verse_start = False256 verse_start = False
256 for line in text_list:257 for line in text_list:
257 verse_type = 'v'
258 clean_line = line.strip()258 clean_line = line.strip()
259 if not clean_line:259 if not clean_line:
260 if line_number == 0:260 if line_number == 0:
@@ -263,6 +263,7 @@
263 if verse_text:263 if verse_text:
264 self.add_verse(verse_text, verse_type)264 self.add_verse(verse_text, verse_type)
265 verse_text = ''265 verse_text = ''
266 verse_type = VerseType.tags[VerseType.Verse]
266 verse_start = False267 verse_start = False
267 else:268 else:
268 # line_number=0, song title269 # line_number=0, song title
@@ -279,7 +280,7 @@
279 elif not verse_start:280 elif not verse_start:
280 # We have the verse descriptor281 # We have the verse descriptor
281 verse_desc_parts = clean_line.split(' ')282 verse_desc_parts = clean_line.split(' ')
282 if len(verse_desc_parts) == 2:283 if len(verse_desc_parts):
283 if verse_desc_parts[0].startswith('Ver'):284 if verse_desc_parts[0].startswith('Ver'):
284 verse_type = VerseType.tags[VerseType.Verse]285 verse_type = VerseType.tags[VerseType.Verse]
285 elif verse_desc_parts[0].startswith('Ch'):286 elif verse_desc_parts[0].startswith('Ch'):
286287
=== modified file 'openlp/plugins/songs/lib/mediaitem.py'
--- openlp/plugins/songs/lib/mediaitem.py 2019-02-14 15:09:09 +0000
+++ openlp/plugins/songs/lib/mediaitem.py 2019-03-17 21:06:27 +0000
@@ -557,16 +557,14 @@
557 self.plugin.manager.save_object(new_song)557 self.plugin.manager.save_object(new_song)
558 self.on_song_list_load()558 self.on_song_list_load()
559559
560 def generate_slide_data(self, service_item, item=None, xml_version=False, remote=False,560 def generate_slide_data(self, service_item, *, item=None, context=ServiceItemContext.Service, **kwargs):
561 context=ServiceItemContext.Service):
562 """561 """
563 Generate the slide data. Needs to be implemented by the plugin.562 Generate the slide data. Needs to be implemented by the plugin.
564563
565 :param service_item: The service item to be built on564 :param service_item: The service item to be built on
566 :param item: The Song item to be used565 :param item: The Song item to be used
567 :param xml_version: The xml version (not used)
568 :param remote: Triggered from remote
569 :param context: Why is it being generated566 :param context: Why is it being generated
567 :param kwargs: Consume other unused args specified by the base implementation, but not use by this one.
570 """568 """
571 log.debug('generate_slide_data: {service}, {item}, {remote}'.format(service=service_item, item=item,569 log.debug('generate_slide_data: {service}, {item}, {remote}'.format(service=service_item, item=item,
572 remote=self.remote_song))570 remote=self.remote_song))
573571
=== modified file 'openlp/plugins/songusage/forms/songusagedetailform.py'
--- openlp/plugins/songusage/forms/songusagedetailform.py 2019-02-14 15:09:09 +0000
+++ openlp/plugins/songusage/forms/songusagedetailform.py 2019-03-17 21:06:27 +0000
@@ -85,7 +85,7 @@
85 self.main_window.error_message(85 self.main_window.error_message(
86 translate('SongUsagePlugin.SongUsageDetailForm', 'Output Path Not Selected'),86 translate('SongUsagePlugin.SongUsageDetailForm', 'Output Path Not Selected'),
87 translate('SongUsagePlugin.SongUsageDetailForm', 'You have not set a valid output location for your'87 translate('SongUsagePlugin.SongUsageDetailForm', 'You have not set a valid output location for your'
88 ' song usage report. \nPlease select an existing path on your computer.')88 ' song usage report.\nPlease select an existing path on your computer.')
89 )89 )
90 return90 return
91 create_paths(path)91 create_paths(path)
@@ -112,7 +112,7 @@
112 self.main_window.information_message(112 self.main_window.information_message(
113 translate('SongUsagePlugin.SongUsageDetailForm', 'Report Creation'),113 translate('SongUsagePlugin.SongUsageDetailForm', 'Report Creation'),
114 translate('SongUsagePlugin.SongUsageDetailForm',114 translate('SongUsagePlugin.SongUsageDetailForm',
115 'Report \n{name} \nhas been successfully created. ').format(name=report_file_name)115 'Report\n{name}\nhas been successfully created.').format(name=report_file_name)
116 )116 )
117 except OSError as ose:117 except OSError as ose:
118 log.exception('Failed to write out song usage records')118 log.exception('Failed to write out song usage records')
119119
=== modified file 'run_openlp.py'
--- run_openlp.py 2019-02-14 15:09:09 +0000
+++ run_openlp.py 2019-03-17 21:06:27 +0000
@@ -24,6 +24,7 @@
24The entrypoint for OpenLP24The entrypoint for OpenLP
25"""25"""
26import faulthandler26import faulthandler
27import logging
27import multiprocessing28import multiprocessing
28import sys29import sys
2930
@@ -34,14 +35,19 @@
34from openlp.core.common.applocation import AppLocation35from openlp.core.common.applocation import AppLocation
35from openlp.core.common.path import create_paths36from openlp.core.common.path import create_paths
3637
38log = logging.getLogger(__name__)
39
3740
38def set_up_fault_handling():41def set_up_fault_handling():
39 """42 """
40 Set up the Python fault handler43 Set up the Python fault handler
41 """44 """
42 # Create the cache directory if it doesn't exist, and enable the fault handler to log to an error log file45 # Create the cache directory if it doesn't exist, and enable the fault handler to log to an error log file
43 create_paths(AppLocation.get_directory(AppLocation.CacheDir))46 try:
44 faulthandler.enable((AppLocation.get_directory(AppLocation.CacheDir) / 'error.log').open('wb'))47 create_paths(AppLocation.get_directory(AppLocation.CacheDir))
48 faulthandler.enable((AppLocation.get_directory(AppLocation.CacheDir) / 'error.log').open('wb'))
49 except OSError:
50 log.exception('An exception occurred when enabling the fault handler')
4551
4652
47def start():53def start():
4854
=== modified file 'tests/functional/openlp_core/api/test_deploy.py'
--- tests/functional/openlp_core/api/test_deploy.py 2019-02-14 21:19:26 +0000
+++ tests/functional/openlp_core/api/test_deploy.py 2019-03-17 21:06:27 +0000
@@ -63,8 +63,8 @@
63 deploy_zipfile(root_path, 'site.zip')63 deploy_zipfile(root_path, 'site.zip')
6464
65 # THEN: the zip file should have been extracted to the right location65 # THEN: the zip file should have been extracted to the right location
66 MockZipFile.assert_called_once_with(root_path_str + os.sep + 'site.zip')66 MockZipFile.assert_called_once_with(Path('/tmp/remotes/site.zip'))
67 mocked_zipfile.extractall.assert_called_once_with(root_path_str)67 mocked_zipfile.extractall.assert_called_once_with(Path('/tmp/remotes'))
6868
69 @patch('openlp.core.api.deploy.Registry')69 @patch('openlp.core.api.deploy.Registry')
70 @patch('openlp.core.api.deploy.get_web_page')70 @patch('openlp.core.api.deploy.get_web_page')
7171
=== modified file 'tests/functional/openlp_core/common/test_init.py'
--- tests/functional/openlp_core/common/test_init.py 2019-02-14 15:09:09 +0000
+++ tests/functional/openlp_core/common/test_init.py 2019-03-17 21:06:27 +0000
@@ -309,9 +309,9 @@
309 """309 """
310 # GIVEN: A mocked UniversalDetector instance with done attribute set to True after first iteration310 # GIVEN: A mocked UniversalDetector instance with done attribute set to True after first iteration
311 with patch('openlp.core.common.UniversalDetector') as mocked_universal_detector, \311 with patch('openlp.core.common.UniversalDetector') as mocked_universal_detector, \
312 patch.object(Path, 'open', return_value=BytesIO(b"data" * 260)) as mocked_open:312 patch.object(Path, 'open', return_value=BytesIO(b'data' * 260)) as mocked_open:
313 encoding_result = {'encoding': 'UTF-8', 'confidence': 0.99}313 encoding_result = {'encoding': 'UTF-8', 'confidence': 0.99}
314 mocked_universal_detector_inst = MagicMock(result=encoding_result)314 mocked_universal_detector_inst = MagicMock(**{'close.return_value': encoding_result})
315 type(mocked_universal_detector_inst).done = PropertyMock(side_effect=[False, True])315 type(mocked_universal_detector_inst).done = PropertyMock(side_effect=[False, True])
316 mocked_universal_detector.return_value = mocked_universal_detector_inst316 mocked_universal_detector.return_value = mocked_universal_detector_inst
317317
@@ -320,7 +320,7 @@
320320
321 # THEN: The feed method of UniversalDetector should only br called once before returning a result321 # THEN: The feed method of UniversalDetector should only br called once before returning a result
322 mocked_open.assert_called_once_with('rb')322 mocked_open.assert_called_once_with('rb')
323 assert mocked_universal_detector_inst.feed.mock_calls == [call(b"data" * 256)]323 assert mocked_universal_detector_inst.feed.mock_calls == [call(b'data' * 256)]
324 mocked_universal_detector_inst.close.assert_called_once_with()324 mocked_universal_detector_inst.close.assert_called_once_with()
325 assert result == encoding_result325 assert result == encoding_result
326326
@@ -331,10 +331,10 @@
331 # GIVEN: A mocked UniversalDetector instance which isn't set to done and a mocked open, with 1040 bytes of test331 # GIVEN: A mocked UniversalDetector instance which isn't set to done and a mocked open, with 1040 bytes of test
332 # data (enough to run the iterator twice)332 # data (enough to run the iterator twice)
333 with patch('openlp.core.common.UniversalDetector') as mocked_universal_detector, \333 with patch('openlp.core.common.UniversalDetector') as mocked_universal_detector, \
334 patch.object(Path, 'open', return_value=BytesIO(b"data" * 260)) as mocked_open:334 patch.object(Path, 'open', return_value=BytesIO(b'data' * 260)) as mocked_open:
335 encoding_result = {'encoding': 'UTF-8', 'confidence': 0.99}335 encoding_result = {'encoding': 'UTF-8', 'confidence': 0.99}
336 mocked_universal_detector_inst = MagicMock(mock=mocked_universal_detector,336 mocked_universal_detector_inst = MagicMock(mock=mocked_universal_detector,
337 **{'done': False, 'result': encoding_result})337 **{'done': False, 'close.return_value': encoding_result})
338 mocked_universal_detector.return_value = mocked_universal_detector_inst338 mocked_universal_detector.return_value = mocked_universal_detector_inst
339339
340 # WHEN: Calling get_file_encoding340 # WHEN: Calling get_file_encoding
@@ -342,7 +342,7 @@
342342
343 # THEN: The feed method of UniversalDetector should have been called twice before returning a result343 # THEN: The feed method of UniversalDetector should have been called twice before returning a result
344 mocked_open.assert_called_once_with('rb')344 mocked_open.assert_called_once_with('rb')
345 assert mocked_universal_detector_inst.feed.mock_calls == [call(b"data" * 256), call(b"data" * 4)]345 assert mocked_universal_detector_inst.feed.mock_calls == [call(b'data' * 256), call(b'data' * 4)]
346 mocked_universal_detector_inst.close.assert_called_once_with()346 mocked_universal_detector_inst.close.assert_called_once_with()
347 assert result == encoding_result347 assert result == encoding_result
348348
@@ -352,13 +352,19 @@
352 """352 """
353 # GIVEN: A mocked UniversalDetector instance which isn't set to done and a mocked open, with 1040 bytes of test353 # GIVEN: A mocked UniversalDetector instance which isn't set to done and a mocked open, with 1040 bytes of test
354 # data (enough to run the iterator twice)354 # data (enough to run the iterator twice)
355 with patch('openlp.core.common.UniversalDetector'), \355 with patch('openlp.core.common.UniversalDetector') as mocked_universal_detector, \
356 patch('builtins.open', side_effect=OSError), \356 patch('builtins.open', side_effect=OSError), \
357 patch('openlp.core.common.log') as mocked_log:357 patch('openlp.core.common.log') as mocked_log:
358 encoding_result = {'encoding': 'UTF-8', 'confidence': 0.99}
359 mocked_universal_detector_inst = MagicMock(mock=mocked_universal_detector,
360 **{'done': False, 'close.return_value': encoding_result})
361 mocked_universal_detector.return_value = mocked_universal_detector_inst
358362
359 # WHEN: Calling get_file_encoding363 # WHEN: Calling get_file_encoding
360 result = get_file_encoding(Path('file name'))364 result = get_file_encoding(Path('file name'))
361365
362 # THEN: log.exception should be called and get_file_encoding should return None366 # THEN: log.exception should be called and get_file_encoding should return None
363 mocked_log.exception.assert_called_once_with('Error detecting file encoding')367 mocked_log.exception.assert_called_once_with('Error detecting file encoding')
364 assert result is None368 mocked_universal_detector_inst.feed.assert_not_called()
369 mocked_universal_detector_inst.close.assert_called_once_with()
370 assert result == encoding_result
365371
=== modified file 'tests/functional/openlp_core/common/test_path.py'
--- tests/functional/openlp_core/common/test_path.py 2019-02-14 21:19:26 +0000
+++ tests/functional/openlp_core/common/test_path.py 2019-03-17 21:06:27 +0000
@@ -179,9 +179,8 @@
179 # WHEN: Calling :func:`openlp.core.common.path.rmtree` with the path parameter as Path object type179 # WHEN: Calling :func:`openlp.core.common.path.rmtree` with the path parameter as Path object type
180 path.rmtree()180 path.rmtree()
181181
182 # THEN: :func:`shutil.rmtree` should have been called with the str equivalents of the Path object.182 # THEN: :func:`shutil.rmtree` should have been called with the the Path object.
183 mocked_shutil_rmtree.assert_called_once_with(183 mocked_shutil_rmtree.assert_called_once_with(Path('test', 'path'), False, None)
184 os.path.join('test', 'path'), False, None)
185184
186 def test_rmtree_optional_params(self):185 def test_rmtree_optional_params(self):
187 """186 """
@@ -198,8 +197,7 @@
198197
199 # THEN: :func:`shutil.rmtree` should have been called with the optional parameters, with out any of the198 # THEN: :func:`shutil.rmtree` should have been called with the optional parameters, with out any of the
200 # values being modified199 # values being modified
201 mocked_shutil_rmtree.assert_called_once_with(200 mocked_shutil_rmtree.assert_called_once_with(path, True, mocked_on_error)
202 os.path.join('test', 'path'), True, mocked_on_error)
203201
204 def test_which_no_command(self):202 def test_which_no_command(self):
205 """203 """
206204
=== modified file 'tests/functional/openlp_core/lib/test_serviceitem.py'
--- tests/functional/openlp_core/lib/test_serviceitem.py 2019-02-14 15:09:09 +0000
+++ tests/functional/openlp_core/lib/test_serviceitem.py 2019-03-17 21:06:27 +0000
@@ -141,7 +141,7 @@
141 """141 """
142 # GIVEN: A new service item and a mocked add icon function142 # GIVEN: A new service item and a mocked add icon function
143 image_name = 'image_1.jpg'143 image_name = 'image_1.jpg'
144 test_file = os.path.join(str(TEST_PATH), image_name)144 test_file = TEST_PATH / image_name
145 frame_array = {'path': test_file, 'title': image_name}145 frame_array = {'path': test_file, 'title': image_name}
146146
147 service_item = ServiceItem(None)147 service_item = ServiceItem(None)
@@ -154,13 +154,13 @@
154 mocked_get_section_data_path:154 mocked_get_section_data_path:
155 mocked_exists.return_value = True155 mocked_exists.return_value = True
156 mocked_get_section_data_path.return_value = Path('/path/')156 mocked_get_section_data_path.return_value = Path('/path/')
157 service_item.set_from_service(line, str(TEST_PATH))157 service_item.set_from_service(line, TEST_PATH)
158158
159 # THEN: We should get back a valid service item159 # THEN: We should get back a valid service item
160 assert service_item.is_valid is True, 'The new service item should be valid'160 assert service_item.is_valid is True, 'The new service item should be valid'
161 assert test_file == service_item.get_rendered_frame(0), 'The first frame should match the path to the image'161 assert test_file == service_item.get_rendered_frame(0), 'The first frame should match the path to the image'
162 assert frame_array == service_item.get_frames()[0], 'The return should match frame array1'162 assert frame_array == service_item.get_frames()[0], 'The return should match frame array1'
163 assert test_file == str(service_item.get_frame_path(0)), \163 assert test_file == service_item.get_frame_path(0), \
164 'The frame path should match the full path to the image'164 'The frame path should match the full path to the image'
165 assert image_name == service_item.get_frame_title(0), 'The frame title should match the image name'165 assert image_name == service_item.get_frame_title(0), 'The frame title should match the image name'
166 assert image_name == service_item.get_display_title(), 'The display title should match the first image name'166 assert image_name == service_item.get_display_title(), 'The display title should match the first image name'
@@ -328,7 +328,7 @@
328328
329 # WHEN: We add a custom from a saved service329 # WHEN: We add a custom from a saved service
330 line = convert_file_service_item(TEST_PATH, 'serviceitem-song-linked-audio.osj')330 line = convert_file_service_item(TEST_PATH, 'serviceitem-song-linked-audio.osj')
331 service_item.set_from_service(line, '/test/')331 service_item.set_from_service(line, Path('/test/'))
332332
333 # THEN: We should get back a valid service item333 # THEN: We should get back a valid service item
334 assert service_item.is_valid is True, 'The new service item should be valid'334 assert service_item.is_valid is True, 'The new service item should be valid'
335335
=== modified file 'tests/functional/openlp_core/ui/test_thememanager.py'
--- tests/functional/openlp_core/ui/test_thememanager.py 2019-02-14 15:09:09 +0000
+++ tests/functional/openlp_core/ui/test_thememanager.py 2019-03-17 21:06:27 +0000
@@ -66,9 +66,9 @@
66 theme_manager._export_theme(Path('some', 'path', 'Default.otz'), 'Default')66 theme_manager._export_theme(Path('some', 'path', 'Default.otz'), 'Default')
6767
68 # THEN: The zipfile should be created at the given path68 # THEN: The zipfile should be created at the given path
69 mocked_zipfile_init.assert_called_with(os.path.join('some', 'path', 'Default.otz'), 'w')69 mocked_zipfile_init.assert_called_with(Path('some', 'path', 'Default.otz'), 'w')
70 mocked_zipfile_write.assert_called_with(str(RESOURCE_PATH / 'themes' / 'Default' / 'Default.xml'),70 mocked_zipfile_write.assert_called_with(RESOURCE_PATH / 'themes' / 'Default' / 'Default.xml',
71 os.path.join('Default', 'Default.xml'))71 Path('Default', 'Default.xml'))
7272
73 def test_initial_theme_manager(self):73 def test_initial_theme_manager(self):
74 """74 """
7575
=== modified file 'tests/functional/openlp_plugins/bibles/test_manager.py'
--- tests/functional/openlp_plugins/bibles/test_manager.py 2019-02-14 15:09:09 +0000
+++ tests/functional/openlp_plugins/bibles/test_manager.py 2019-03-17 21:06:27 +0000
@@ -55,7 +55,8 @@
55 instance = BibleManager(MagicMock())55 instance = BibleManager(MagicMock())
56 # We need to keep a reference to the mock for close_all as it gets set to None later on!56 # We need to keep a reference to the mock for close_all as it gets set to None later on!
57 mocked_close_all = MagicMock()57 mocked_close_all = MagicMock()
58 mocked_bible = MagicMock(file='KJV.sqlite', path='bibles', **{'session.close_all': mocked_close_all})58 mocked_bible = MagicMock(file_path='KJV.sqlite', path=Path('bibles'),
59 **{'session.close_all': mocked_close_all})
59 instance.db_cache = {'KJV': mocked_bible}60 instance.db_cache = {'KJV': mocked_bible}
6061
61 # WHEN: Calling delete_bible with 'KJV'62 # WHEN: Calling delete_bible with 'KJV'
@@ -66,4 +67,4 @@
66 assert result is True67 assert result is True
67 mocked_close_all.assert_called_once_with()68 mocked_close_all.assert_called_once_with()
68 assert mocked_bible.session is None69 assert mocked_bible.session is None
69 mocked_delete_file.assert_called_once_with(Path('bibles', 'KJV.sqlite'))70 mocked_delete_file.assert_called_once_with(Path('bibles'), 'KJV.sqlite')
7071
=== modified file 'tests/interfaces/openlp_plugins/songs/forms/test_songmaintenanceform.py'
--- tests/interfaces/openlp_plugins/songs/forms/test_songmaintenanceform.py 2019-02-14 15:09:09 +0000
+++ tests/interfaces/openlp_plugins/songs/forms/test_songmaintenanceform.py 2019-03-17 21:06:27 +0000
@@ -236,8 +236,8 @@
236 assert MockedQListWidgetItem.call_args_list == expected_widget_item_calls, MockedQListWidgetItem.call_args_list236 assert MockedQListWidgetItem.call_args_list == expected_widget_item_calls, MockedQListWidgetItem.call_args_list
237 mocked_author_item1.setData.assert_called_once_with(QtCore.Qt.UserRole, 2)237 mocked_author_item1.setData.assert_called_once_with(QtCore.Qt.UserRole, 2)
238 mocked_author_item2.setData.assert_called_once_with(QtCore.Qt.UserRole, 1)238 mocked_author_item2.setData.assert_called_once_with(QtCore.Qt.UserRole, 1)
239 mocked_authors_list_widget.addItem.call_args_list == [239 mocked_authors_list_widget.addItem.assert_has_calls([
240 call(mocked_author_item1), call(mocked_author_item2)]240 call(mocked_author_item1), call(mocked_author_item2)])
241241
242 @patch('openlp.plugins.songs.forms.songmaintenanceform.QtWidgets.QListWidgetItem')242 @patch('openlp.plugins.songs.forms.songmaintenanceform.QtWidgets.QListWidgetItem')
243 @patch('openlp.plugins.songs.forms.songmaintenanceform.Topic')243 @patch('openlp.plugins.songs.forms.songmaintenanceform.Topic')
244244
=== modified file 'tests/openlp_core/common/test_network_interfaces.py'
--- tests/openlp_core/common/test_network_interfaces.py 2019-02-14 15:09:09 +0000
+++ tests/openlp_core/common/test_network_interfaces.py 2019-03-17 21:06:27 +0000
@@ -70,7 +70,7 @@
70 """70 """
71 Return a QFlags enum with IsUp and IsRunning71 Return a QFlags enum with IsUp and IsRunning
72 """72 """
73 return (QNetworkInterface.IsUp | QNetworkInterface.IsRunning)73 return QNetworkInterface.IsUp | QNetworkInterface.IsRunning
7474
75 def name(self):75 def name(self):
76 return self.my_name76 return self.my_name
7777
=== modified file 'tests/openlp_core/projectors/test_projector_pjlink_commands_01.py'
--- tests/openlp_core/projectors/test_projector_pjlink_commands_01.py 2019-03-08 15:19:57 +0000
+++ tests/openlp_core/projectors/test_projector_pjlink_commands_01.py 2019-03-17 21:06:27 +0000
@@ -605,9 +605,9 @@
605605
606 # THEN: Power should be set to ON606 # THEN: Power should be set to ON
607 assert pjlink.power == S_STANDBY, 'Power should not have changed'607 assert pjlink.power == S_STANDBY, 'Power should not have changed'
608 assert mock_UpdateIcons.emit.called is False, 'projectorUpdateIcons() should not have been called'608 mock_UpdateIcons.emit.assert_not_called()
609 mock_change_status.called is False, 'change_status() should not have been called'609 mock_change_status.assert_not_called()
610 mock_send_command.called is False, 'send_command() should not have been called'610 mock_send_command.assert_not_called()
611 mock_log.warning.assert_has_calls(log_warn_calls)611 mock_log.warning.assert_has_calls(log_warn_calls)
612612
613 def test_projector_process_powr_off(self):613 def test_projector_process_powr_off(self):
@@ -627,9 +627,9 @@
627627
628 # THEN: Power should be set to ON628 # THEN: Power should be set to ON
629 assert pjlink.power == S_STANDBY, 'Power should have changed to S_STANDBY'629 assert pjlink.power == S_STANDBY, 'Power should have changed to S_STANDBY'
630 assert mock_UpdateIcons.emit.called is True, 'projectorUpdateIcons should have been called'630 mock_UpdateIcons.emit.assert_called_with()
631 mock_change_status.called is True, 'change_status should have been called'631 mock_change_status.assert_called_with(313)
632 mock_send_command.called is False, 'send_command should not have been called'632 mock_send_command.assert_not_called()
633633
634 def test_projector_process_rfil_save(self):634 def test_projector_process_rfil_save(self):
635 """635 """
636636
=== modified file 'tests/openlp_core/projectors/test_projector_sourceform.py'
--- tests/openlp_core/projectors/test_projector_sourceform.py 2019-02-14 15:09:09 +0000
+++ tests/openlp_core/projectors/test_projector_sourceform.py 2019-03-17 21:06:27 +0000
@@ -83,8 +83,8 @@
83 Delete all C++ objects at end so we don't segfault.83 Delete all C++ objects at end so we don't segfault.
84 """84 """
85 self.projectordb.session.close()85 self.projectordb.session.close()
86 del(self.projectordb)86 del self.projectordb
87 del(self.projector)87 del self.projector
88 retries = 088 retries = 0
89 while retries < 5:89 while retries < 5:
90 try:90 try: