Merge lp:~phill-ridout/openlp/pathlib12 into lp:openlp

Proposed by Phill
Status: Superseded
Proposed branch: lp:~phill-ridout/openlp/pathlib12
Merge into: lp:openlp
Diff against target: 682 lines (+181/-291)
6 files modified
openlp/core/lib/serviceitem.py (+9/-8)
openlp/core/ui/mainwindow.py (+8/-6)
openlp/core/ui/servicemanager.py (+128/-241)
tests/functional/openlp_core/lib/test_serviceitem.py (+2/-1)
tests/functional/openlp_core/ui/test_mainwindow.py (+29/-2)
tests/functional/openlp_core/ui/test_servicemanager.py (+5/-33)
To merge this branch: bzr merge lp:~phill-ridout/openlp/pathlib12
Reviewer Review Type Date Requested Status
Tim Bentley Needs Fixing
Review via email: mp+336398@code.launchpad.net

This proposal has been superseded by a proposal from 2018-01-24.

Description of the change

Started work on storing path objects in service file.
Refactored save code and reduced duplication.
Fixed + improved the loading / saving progress bars
improved performance

loading powerpoint from a service still does work

lp:~phill-ridout/openlp/pathlib12 (revision 2815)
https://ci.openlp.io/job/Branch-01-Pull/2425/ [WAITING]
[RUNNING]
[SUCCESS]
https://ci.openlp.io/job/Branch-02a-Linux-Tests/2326/ [WAITING]
[RUNNING]
[SUCCESS]
https://ci.openlp.io/job/Branch-02b-macOS-Tests/120/ [WAITING]
[SUCCESS]
https://ci.openlp.io/job/Branch-03a-Build-Source/43/ [WAITING]
[SUCCESS]
https://ci.openlp.io/job/Branch-03b-Build-macOS/41/ [WAITING]
[RUNNING]
[SUCCESS]
https://ci.openlp.io/job/Branch-04a-Code-Analysis/1505/ [WAITING]
[SUCCESS]
https://ci.openlp.io/job/Branch-04b-Test-Coverage/1318/ [WAITING]
[SUCCESS]
https://ci.openlp.io/job/Branch-05-AppVeyor-Tests/266/ [WAITING]
[RUNNING]
[FAILURE]
Stopping after failure

Failed builds:
 - Branch-05-AppVeyor-Tests #266: https://ci.openlp.io/job/Branch-05-AppVeyor-Tests/266/console

To post a comment you must log in.
Revision history for this message
Tim Bentley (trb143) wrote :

Tried to save a file and got this error

There was an error saving your file.

[Errno 18] Invalid cross-device link: '/tmp/tmpz25kj0a1' -> '/home/tim/Projects/OpenLP/Service 2018-01-21 17-30.osz'

review: Needs Fixing
lp:~phill-ridout/openlp/pathlib12 updated
2816. By Phill

Save the temp file in a different dir

2817. By Phill

Small fix

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'openlp/core/lib/serviceitem.py'
--- openlp/core/lib/serviceitem.py 2017-12-29 09:15:48 +0000
+++ openlp/core/lib/serviceitem.py 2018-01-22 21:37:22 +0000
@@ -36,6 +36,7 @@
36from openlp.core.common.applocation import AppLocation36from openlp.core.common.applocation import AppLocation
37from openlp.core.common.i18n import translate37from openlp.core.common.i18n import translate
38from openlp.core.common.mixins import RegistryProperties38from openlp.core.common.mixins import RegistryProperties
39from openlp.core.common.path import Path
39from openlp.core.common.settings import Settings40from openlp.core.common.settings import Settings
40from openlp.core.lib import ImageSource, build_icon, clean_tags, expand_tags, expand_chords41from openlp.core.lib import ImageSource, build_icon, clean_tags, expand_tags, expand_chords
4142
@@ -427,13 +428,13 @@
427 self.has_original_files = True428 self.has_original_files = True
428 if 'background_audio' in header:429 if 'background_audio' in header:
429 self.background_audio = []430 self.background_audio = []
430 for filename in header['background_audio']:431 for file_path in header['background_audio']:
431 # Give them real file paths.432 # In OpenLP 3.0 we switched to storing Path objects in JSON files
432 filepath = str(filename)433 if isinstance(file_path, str):
433 if path:434 # Handle service files prior to OpenLP 3.0
434 # Windows can handle both forward and backward slashes, so we use ntpath to get the basename435 # Windows can handle both forward and backward slashes, so we use ntpath to get the basename
435 filepath = os.path.join(path, ntpath.basename(str(filename)))436 file_path = Path(path, ntpath.basename(file_path))
436 self.background_audio.append(filepath)437 self.background_audio.append(file_path)
437 self.theme_overwritten = header.get('theme_overwritten', False)438 self.theme_overwritten = header.get('theme_overwritten', False)
438 if self.service_item_type == ServiceItemType.Text:439 if self.service_item_type == ServiceItemType.Text:
439 for slide in service_item['serviceitem']['data']:440 for slide in service_item['serviceitem']['data']:
@@ -444,8 +445,8 @@
444 if path:445 if path:
445 self.has_original_files = False446 self.has_original_files = False
446 for text_image in service_item['serviceitem']['data']:447 for text_image in service_item['serviceitem']['data']:
447 filename = os.path.join(path, text_image)448 file_path = os.path.join(path, text_image)
448 self.add_from_image(filename, text_image, background)449 self.add_from_image(file_path, text_image, background)
449 else:450 else:
450 for text_image in service_item['serviceitem']['data']:451 for text_image in service_item['serviceitem']['data']:
451 self.add_from_image(text_image['path'], text_image['title'], background)452 self.add_from_image(text_image['path'], text_image['title'], background)
452453
=== modified file 'openlp/core/ui/mainwindow.py'
--- openlp/core/ui/mainwindow.py 2018-01-12 18:29:32 +0000
+++ openlp/core/ui/mainwindow.py 2018-01-22 21:37:22 +0000
@@ -1314,11 +1314,13 @@
1314 self.load_progress_bar.setValue(0)1314 self.load_progress_bar.setValue(0)
1315 self.application.process_events()1315 self.application.process_events()
13161316
1317 def increment_progress_bar(self):1317 def increment_progress_bar(self, increment=1):
1318 """1318 """
1319 Increase the Progress Bar value by 11319 Increase the Progress Bar by the value in increment.
1320 """1320
1321 self.load_progress_bar.setValue(self.load_progress_bar.value() + 1)1321 :param int increment: The value you to increase the progress bar by.
1322 """
1323 self.load_progress_bar.setValue(self.load_progress_bar.value() + increment)
1322 self.application.process_events()1324 self.application.process_events()
13231325
1324 def finished_progress_bar(self):1326 def finished_progress_bar(self):
@@ -1386,4 +1388,4 @@
1386 if not isinstance(filename, str):1388 if not isinstance(filename, str):
1387 filename = str(filename, sys.getfilesystemencoding())1389 filename = str(filename, sys.getfilesystemencoding())
1388 if filename.endswith(('.osz', '.oszl')):1390 if filename.endswith(('.osz', '.oszl')):
1389 self.service_manager_contents.load_file(filename)1391 self.service_manager_contents.load_file(Path(filename))
13901392
=== modified file 'openlp/core/ui/servicemanager.py'
--- openlp/core/ui/servicemanager.py 2017-12-29 09:15:48 +0000
+++ openlp/core/ui/servicemanager.py 2018-01-22 21:37:22 +0000
@@ -27,8 +27,9 @@
27import os27import os
28import shutil28import shutil
29import zipfile29import zipfile
30from contextlib import suppress
30from datetime import datetime, timedelta31from datetime import datetime, timedelta
31from tempfile import mkstemp32from tempfile import NamedTemporaryFile
3233
33from PyQt5 import QtCore, QtGui, QtWidgets34from PyQt5 import QtCore, QtGui, QtWidgets
3435
@@ -36,11 +37,13 @@
36from openlp.core.common.actions import ActionList, CategoryOrder37from openlp.core.common.actions import ActionList, CategoryOrder
37from openlp.core.common.applocation import AppLocation38from openlp.core.common.applocation import AppLocation
38from openlp.core.common.i18n import UiStrings, format_time, translate39from openlp.core.common.i18n import UiStrings, format_time, translate
40from openlp.core.common.json import OpenLPJsonDecoder, OpenLPJsonEncoder
39from openlp.core.common.mixins import LogMixin, RegistryProperties41from openlp.core.common.mixins import LogMixin, RegistryProperties
40from openlp.core.common.path import Path, create_paths, str_to_path42from openlp.core.common.path import Path, str_to_path
41from openlp.core.common.registry import Registry, RegistryBase43from openlp.core.common.registry import Registry, RegistryBase
42from openlp.core.common.settings import Settings44from openlp.core.common.settings import Settings
43from openlp.core.lib import ServiceItem, ItemCapabilities, PluginStatus, build_icon45from openlp.core.lib import ServiceItem, ItemCapabilities, PluginStatus, build_icon
46from openlp.core.lib.exceptions import ValidationError
44from openlp.core.lib.ui import critical_error_message_box, create_widget_action, find_and_set_in_combo_box47from openlp.core.lib.ui import critical_error_message_box, create_widget_action, find_and_set_in_combo_box
45from openlp.core.ui import ServiceNoteForm, ServiceItemEditForm, StartTimeForm48from openlp.core.ui import ServiceNoteForm, ServiceItemEditForm, StartTimeForm
46from openlp.core.widgets.dialogs import FileDialog49from openlp.core.widgets.dialogs import FileDialog
@@ -449,7 +452,7 @@
449 else:452 else:
450 file_path = str_to_path(load_file)453 file_path = str_to_path(load_file)
451 Settings().setValue(self.main_window.service_manager_settings_section + '/last directory', file_path.parent)454 Settings().setValue(self.main_window.service_manager_settings_section + '/last directory', file_path.parent)
452 self.load_file(str(file_path))455 self.load_file(file_path)
453456
454 def save_modified_service(self):457 def save_modified_service(self):
455 """458 """
@@ -475,7 +478,7 @@
475 elif result == QtWidgets.QMessageBox.Save:478 elif result == QtWidgets.QMessageBox.Save:
476 self.decide_save_method()479 self.decide_save_method()
477 sender = self.sender()480 sender = self.sender()
478 self.load_file(sender.data())481 self.load_file(Path(sender.data()))
479482
480 def new_file(self):483 def new_file(self):
481 """484 """
@@ -503,7 +506,32 @@
503 service.append({'openlp_core': core})506 service.append({'openlp_core': core})
504 return service507 return service
505508
506 def save_file(self, field=None):509 def get_write_file_list(self):
510 """
511 Get a list of files used in the service and files that are missing.
512
513 :return: A list of files used in the service that exist, and a list of files that don't.
514 :rtype: (list[openlp.core.common.path.Path], list[openlp.core.common.path.Path])
515 """
516 write_list = []
517 missing_list = []
518 for item in self.service_items:
519 if item['service_item'].uses_file():
520 for frame in item['service_item'].get_frames():
521 path_from = item['service_item'].get_frame_path(frame=frame)
522 if path_from in write_list or path_from in missing_list:
523 continue
524 if not os.path.exists(path_from):
525 missing_list.append(Path(path_from))
526 else:
527 write_list.append(Path(path_from))
528 for audio_path in item['service_item'].background_audio:
529 if audio_path in write_list:
530 continue
531 write_list.append(audio_path)
532 return write_list, missing_list
533
534 def save_file(self):
507 """535 """
508 Save the current service file.536 Save the current service file.
509537
@@ -511,178 +539,74 @@
511 there be an error when saving. Audio files are also copied into the service manager directory, and then packaged539 there be an error when saving. Audio files are also copied into the service manager directory, and then packaged
512 into the zip file.540 into the zip file.
513 """541 """
514 if not self.file_name():542 file_path = self.file_name()
515 return self.save_file_as()543 self.log_debug('ServiceManager.save_file - {name}'.format(name=file_path))
516 temp_file, temp_file_name = mkstemp('.osz', 'openlp_')544 self.application.set_busy_cursor()
517 # We don't need the file handle.545
518 os.close(temp_file)
519 self.log_debug(temp_file_name)
520 path_file_name = str(self.file_name())
521 path, file_name = os.path.split(path_file_name)
522 base_name = os.path.splitext(file_name)[0]
523 service_file_name = '{name}.osj'.format(name=base_name)
524 self.log_debug('ServiceManager.save_file - {name}'.format(name=path_file_name))
525 Settings().setValue(self.main_window.service_manager_settings_section + '/last directory', Path(path))
526 service = self.create_basic_service()546 service = self.create_basic_service()
547
527 write_list = []548 write_list = []
528 missing_list = []549 missing_list = []
529 audio_files = []550
530 total_size = 0551 if not self._save_lite:
531 self.application.set_busy_cursor()552 write_list, missing_list = self.get_write_file_list()
532 # Number of items + 1 to zip it553
533 self.main_window.display_progress_bar(len(self.service_items) + 1)554 if missing_list:
534 # Get list of missing files, and list of files to write555 self.application.set_normal_cursor()
535 for item in self.service_items:556 title = translate('OpenLP.ServiceManager', 'Service File(s) Missing')
536 if not item['service_item'].uses_file():557 message = translate('OpenLP.ServiceManager',
537 continue558 'The following file(s) in the service are missing: {name}\n\n'
538 for frame in item['service_item'].get_frames():559 'These files will be removed if you continue to save.'
539 path_from = item['service_item'].get_frame_path(frame=frame)560 ).format(name='\n\t'.join(missing_list))
540 if path_from in write_list or path_from in missing_list:561 answer = QtWidgets.QMessageBox.critical(self, title, message,
541 continue562 QtWidgets.QMessageBox.StandardButtons(
542 if not os.path.exists(path_from):563 QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel))
543 missing_list.append(path_from)564 if answer == QtWidgets.QMessageBox.Cancel:
544 else:565 return False
545 write_list.append(path_from)
546 if missing_list:
547 self.application.set_normal_cursor()
548 title = translate('OpenLP.ServiceManager', 'Service File(s) Missing')
549 message = translate('OpenLP.ServiceManager',
550 'The following file(s) in the service are missing: {name}\n\n'
551 'These files will be removed if you continue to save.'
552 ).format(name="\n\t".join(missing_list))
553 answer = QtWidgets.QMessageBox.critical(self, title, message,
554 QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Ok |
555 QtWidgets.QMessageBox.Cancel))
556 if answer == QtWidgets.QMessageBox.Cancel:
557 self.main_window.finished_progress_bar()
558 return False
559 # Check if item contains a missing file.566 # Check if item contains a missing file.
560 for item in list(self.service_items):567 for item in list(self.service_items):
561 self.main_window.increment_progress_bar()568 if not self._save_lite:
562 item['service_item'].remove_invalid_frames(missing_list)569 item['service_item'].remove_invalid_frames(missing_list)
563 if item['service_item'].missing_frames():570 if item['service_item'].missing_frames():
564 self.service_items.remove(item)571 self.service_items.remove(item)
565 else:572 continue
566 service_item = item['service_item'].get_service_repr(self._save_lite)573 service_item = item['service_item'].get_service_repr(self._save_lite)
567 if service_item['header']['background_audio']:574 # Add the service item to the service.
568 for i, file_name in enumerate(service_item['header']['background_audio']):575 service.append({'serviceitem': service_item})
569 new_file = os.path.join('audio', item['service_item'].unique_identifier, str(file_name))
570 audio_files.append((file_name, new_file))
571 service_item['header']['background_audio'][i] = new_file
572 # Add the service item to the service.
573 service.append({'serviceitem': service_item})
574 self.repaint_service_list(-1, -1)576 self.repaint_service_list(-1, -1)
577 service_content = json.dumps(service, cls=OpenLPJsonEncoder)
578 service_content_size = len(bytes(service_content, encoding='utf-8'))
579 total_size = service_content_size
575 for file_item in write_list:580 for file_item in write_list:
576 file_size = os.path.getsize(file_item)581 total_size += file_item.stat().st_size
577 total_size += file_size
578 self.log_debug('ServiceManager.save_file - ZIP contents size is %i bytes' % total_size)582 self.log_debug('ServiceManager.save_file - ZIP contents size is %i bytes' % total_size)
579 service_content = json.dumps(service)583 self.main_window.display_progress_bar(total_size)
580 # Usual Zip file cannot exceed 2GiB, file with Zip64 cannot be extracted using unzip in UNIX.
581 allow_zip_64 = (total_size > 2147483648 + len(service_content))
582 self.log_debug('ServiceManager.save_file - allowZip64 is {text}'.format(text=allow_zip_64))
583 zip_file = None
584 success = True
585 self.main_window.increment_progress_bar()
586 try:584 try:
587 zip_file = zipfile.ZipFile(temp_file_name, 'w', zipfile.ZIP_STORED, allow_zip_64)585 with NamedTemporaryFile(dir=str(file_path.parent), prefix='.') as temp_file, \
588 # First we add service contents..586 zipfile.ZipFile(temp_file, 'w') as zip_file:
589 zip_file.writestr(service_file_name, service_content)587 # First we add service contents..
590 # Finally add all the listed media files.588 zip_file.writestr('service_data.osj', service_content)
591 for write_from in write_list:589 self.main_window.increment_progress_bar(service_content_size)
592 zip_file.write(write_from, write_from)590 # Finally add all the listed media files.
593 for audio_from, audio_to in audio_files:591 for write_path in write_list:
594 audio_from = str(audio_from)592 zip_file.write(str(write_path), str(write_path))
595 audio_to = str(audio_to)593 self.main_window.increment_progress_bar(write_path.stat().st_size)
596 if audio_from.startswith('audio'):594 with suppress(FileNotFoundError):
597 # When items are saved, they get new unique_identifier. Let's copy the file to the new location.595 file_path.unlink()
598 # Unused files can be ignored, OpenLP automatically cleans up the service manager dir on exit.596 os.link(temp_file.name, str(file_path))
599 audio_from = os.path.join(self.service_path, audio_from)597 Settings().setValue(self.main_window.service_manager_settings_section + '/last directory', file_path.parent)
600 save_file = os.path.join(self.service_path, audio_to)598 except (PermissionError, OSError) as error:
601 save_path = os.path.split(save_file)[0]599 self.log_exception('Failed to save service to disk: {name}'.format(name=file_path))
602 create_paths(Path(save_path))600 self.main_window.error_message(
603 if not os.path.exists(save_file):601 translate('OpenLP.ServiceManager', 'Error Saving File'),
604 shutil.copy(audio_from, save_file)602 translate('OpenLP.ServiceManager',
605 zip_file.write(audio_from, audio_to)603 'There was an error saving your file.\n\n{error}').format(error=error))
606 except OSError:
607 self.log_exception('Failed to save service to disk: {name}'.format(name=temp_file_name))
608 self.main_window.error_message(translate('OpenLP.ServiceManager', 'Error Saving File'),
609 translate('OpenLP.ServiceManager', 'There was an error saving your file.'))
610 success = False
611 finally:
612 if zip_file:
613 zip_file.close()
614 self.main_window.finished_progress_bar()
615 self.application.set_normal_cursor()
616 if success:
617 try:
618 shutil.copy(temp_file_name, path_file_name)
619 except (shutil.Error, PermissionError):
620 return self.save_file_as()
621 except OSError as ose:
622 QtWidgets.QMessageBox.critical(self, translate('OpenLP.ServiceManager', 'Error Saving File'),
623 translate('OpenLP.ServiceManager', 'An error occurred while writing the '
624 'service file: {error}').format(error=ose.strerror),
625 QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Ok))
626 success = False
627 self.main_window.add_recent_file(path_file_name)
628 self.set_modified(False)
629 delete_file(Path(temp_file_name))
630 return success
631
632 def save_local_file(self):
633 """
634 Save the current service file but leave all the file references alone to point to the current machine.
635 This format is not transportable as it will not contain any files.
636 """
637 if not self.file_name():
638 return self.save_file_as()604 return self.save_file_as()
639 temp_file, temp_file_name = mkstemp('.oszl', 'openlp_')
640 # We don't need the file handle.
641 os.close(temp_file)
642 self.log_debug(temp_file_name)
643 path_file_name = str(self.file_name())
644 path, file_name = os.path.split(path_file_name)
645 base_name = os.path.splitext(file_name)[0]
646 service_file_name = '{name}.osj'.format(name=base_name)
647 self.log_debug('ServiceManager.save_file - {name}'.format(name=path_file_name))
648 Settings().setValue(self.main_window.service_manager_settings_section + '/last directory', Path(path))
649 service = self.create_basic_service()
650 self.application.set_busy_cursor()
651 # Number of items + 1 to zip it
652 self.main_window.display_progress_bar(len(self.service_items) + 1)
653 for item in self.service_items:
654 self.main_window.increment_progress_bar()
655 service_item = item['service_item'].get_service_repr(self._save_lite)
656 # TODO: check for file item on save.
657 service.append({'serviceitem': service_item})
658 self.main_window.increment_progress_bar()
659 service_content = json.dumps(service)
660 zip_file = None
661 success = True
662 self.main_window.increment_progress_bar()
663 try:
664 zip_file = zipfile.ZipFile(temp_file_name, 'w', zipfile.ZIP_STORED, True)
665 # First we add service contents.
666 zip_file.writestr(service_file_name, service_content)
667 except OSError:
668 self.log_exception('Failed to save service to disk: {name}'.format(name=temp_file_name))
669 self.main_window.error_message(translate('OpenLP.ServiceManager', 'Error Saving File'),
670 translate('OpenLP.ServiceManager', 'There was an error saving your file.'))
671 success = False
672 finally:
673 if zip_file:
674 zip_file.close()
675 self.main_window.finished_progress_bar()605 self.main_window.finished_progress_bar()
676 self.application.set_normal_cursor()606 self.application.set_normal_cursor()
677 if success:607 self.main_window.add_recent_file(file_path)
678 try:608 self.set_modified(False)
679 shutil.copy(temp_file_name, path_file_name)609 return True
680 except (shutil.Error, PermissionError):
681 return self.save_file_as()
682 self.main_window.add_recent_file(path_file_name)
683 self.set_modified(False)
684 delete_file(Path(temp_file_name))
685 return success
686610
687 def save_file_as(self, field=None):611 def save_file_as(self, field=None):
688 """612 """
@@ -743,87 +667,49 @@
743 """667 """
744 if not self.file_name():668 if not self.file_name():
745 return self.save_file_as()669 return self.save_file_as()
746 if self._save_lite:670 return self.save_file()
747 return self.save_local_file()
748 else:
749 return self.save_file()
750671
751 def load_file(self, file_name):672 def load_file(self, file_path):
752 """673 """
753 Load an existing service file674 Load an existing service file
754 :param file_name:675 :param file_path:
755 """676 """
756 if not file_name:677 if not file_path.exists():
757 return False678 return False
758 file_name = str(file_name)679 service_data = None
759 if not os.path.exists(file_name):
760 return False
761 zip_file = None
762 file_to = None
763 self.application.set_busy_cursor()680 self.application.set_busy_cursor()
764 try:681 try:
765 zip_file = zipfile.ZipFile(file_name)682 with zipfile.ZipFile(str(file_path)) as zip_file:
766 for zip_info in zip_file.infolist():683 compressed_size = 0
767 try:684 for zip_info in zip_file.infolist():
768 ucs_file = zip_info.filename685 compressed_size += zip_info.compress_size
769 except UnicodeDecodeError:686 self.main_window.display_progress_bar(compressed_size)
770 self.log_exception('file_name "{name}" is not valid UTF-8'.format(name=zip_info.file_name))687 for zip_info in zip_file.infolist():
771 critical_error_message_box(message=translate('OpenLP.ServiceManager',688 self.log_debug('Extract file: {name}'.format(name=zip_info.filename))
772 'File is not a valid service.\n The content encoding is not UTF-8.'))689 # The json file has been called 'service_data.osj' since OpenLP 3.0
773 continue690 if zip_info.filename == 'service_data.osj' or zip_info.filename.endswith('osj'):
774 os_file = ucs_file.replace('/', os.path.sep)691 with zip_file.open(zip_info, 'r') as json_file:
775 os_file = os.path.basename(os_file)692 service_data = json_file.read()
776 self.log_debug('Extract file: {name}'.format(name=os_file))693 else:
777 zip_info.filename = os_file694 zip_info.filename = os.path.basename(zip_info.filename)
778 zip_file.extract(zip_info, self.service_path)695 zip_file.extract(zip_info, str(self.service_path))
779 if os_file.endswith('osj') or os_file.endswith('osd'):696 self.main_window.increment_progress_bar(zip_info.compress_size)
780 p_file = os.path.join(self.service_path, os_file)697 if service_data:
781 if 'p_file' in locals():698 items = json.loads(service_data, cls=OpenLPJsonDecoder)
782 file_to = open(p_file, 'r')
783 if p_file.endswith('osj'):
784 items = json.load(file_to)
785 else:
786 critical_error_message_box(message=translate('OpenLP.ServiceManager',
787 'The service file you are trying to open is in an old '
788 'format.\n Please save it using OpenLP 2.0.2 or '
789 'greater.'))
790 return
791 file_to.close()
792 self.new_file()699 self.new_file()
793 self.set_file_name(str_to_path(file_name))
794 self.main_window.display_progress_bar(len(items))
795 self.process_service_items(items)700 self.process_service_items(items)
796 delete_file(Path(p_file))701 self.set_file_name(file_path)
797 self.main_window.add_recent_file(file_name)702 self.main_window.add_recent_file(file_path)
798 self.set_modified(False)703 self.set_modified(False)
799 Settings().setValue('servicemanager/last file', Path(file_name))704 Settings().setValue('servicemanager/last file', file_path)
800 else:705 else:
801 critical_error_message_box(message=translate('OpenLP.ServiceManager', 'File is not a valid service.'))706 raise ValidationError(msg='No service data found')
802 self.log_error('File contains no service data')707 except (NameError, OSError, ValidationError, zipfile.BadZipFile) as e:
803 except (OSError, NameError):708 self.log_exception('Problem loading service file {name}'.format(name=file_path))
804 self.log_exception('Problem loading service file {name}'.format(name=file_name))709 critical_error_message_box(
805 critical_error_message_box(message=translate('OpenLP.ServiceManager',710 message=translate('OpenLP.ServiceManager',
806 'File could not be opened because it is corrupt.'))711 'The service file {file_path} could not be loaded because it is either corrupt, or '
807 except zipfile.BadZipFile:712 'not a valid OpenLP 2 or OpenLP 3 service file.'.format(file_path=file_path)))
808 if os.path.getsize(file_name) == 0:
809 self.log_exception('Service file is zero sized: {name}'.format(name=file_name))
810 QtWidgets.QMessageBox.information(self, translate('OpenLP.ServiceManager', 'Empty File'),
811 translate('OpenLP.ServiceManager',
812 'This service file does not contain '
813 'any data.'))
814 else:
815 self.log_exception('Service file is cannot be extracted as zip: {name}'.format(name=file_name))
816 QtWidgets.QMessageBox.information(self, translate('OpenLP.ServiceManager', 'Corrupt File'),
817 translate('OpenLP.ServiceManager',
818 'This file is either corrupt or it is not an OpenLP 2 '
819 'service file.'))
820 self.application.set_normal_cursor()
821 return
822 finally:
823 if file_to:
824 file_to.close()
825 if zip_file:
826 zip_file.close()
827 self.main_window.finished_progress_bar()713 self.main_window.finished_progress_bar()
828 self.application.set_normal_cursor()714 self.application.set_normal_cursor()
829 self.repaint_service_list(-1, -1)715 self.repaint_service_list(-1, -1)
@@ -838,7 +724,8 @@
838 self.main_window.increment_progress_bar()724 self.main_window.increment_progress_bar()
839 service_item = ServiceItem()725 service_item = ServiceItem()
840 if 'openlp_core' in item:726 if 'openlp_core' in item:
841 item = item.get('openlp_core')727 item = item['openlp_core']
728 self._save_lite = item.get('lite-service', False)
842 theme = item.get('service-theme', None)729 theme = item.get('service-theme', None)
843 if theme:730 if theme:
844 find_and_set_in_combo_box(self.theme_combo_box, theme, set_missing=False)731 find_and_set_in_combo_box(self.theme_combo_box, theme, set_missing=False)
@@ -861,9 +748,9 @@
861 Load the last service item from the service manager when the service was last closed. Can be blank if there was748 Load the last service item from the service manager when the service was last closed. Can be blank if there was
862 no service present.749 no service present.
863 """750 """
864 file_name = str_to_path(Settings().value('servicemanager/last file'))751 file_path = Settings().value('servicemanager/last file')
865 if file_name:752 if file_path:
866 self.load_file(file_name)753 self.load_file(file_path)
867754
868 def context_menu(self, point):755 def context_menu(self, point):
869 """756 """
870757
=== modified file 'tests/functional/openlp_core/lib/test_serviceitem.py'
--- tests/functional/openlp_core/lib/test_serviceitem.py 2017-12-28 08:22:55 +0000
+++ tests/functional/openlp_core/lib/test_serviceitem.py 2018-01-22 21:37:22 +0000
@@ -27,6 +27,7 @@
27from unittest.mock import MagicMock, patch27from unittest.mock import MagicMock, patch
2828
29from openlp.core.common import md5_hash29from openlp.core.common import md5_hash
30from openlp.core.common.path import Path
30from openlp.core.common.registry import Registry31from openlp.core.common.registry import Registry
31from openlp.core.common.settings import Settings32from openlp.core.common.settings import Settings
32from openlp.core.lib import ItemCapabilities, ServiceItem, ServiceItemType, FormattingTags33from openlp.core.lib import ItemCapabilities, ServiceItem, ServiceItemType, FormattingTags
@@ -351,5 +352,5 @@
351 '"Amazing Grace! how sweet the s" has been returned as the title'352 '"Amazing Grace! how sweet the s" has been returned as the title'
352 assert '’Twas grace that taught my hea' == service_item.get_frame_title(1), \353 assert '’Twas grace that taught my hea' == service_item.get_frame_title(1), \
353 '"’Twas grace that taught my hea" has been returned as the title'354 '"’Twas grace that taught my hea" has been returned as the title'
354 assert '/test/amazing_grace.mp3' == service_item.background_audio[0], \355 assert Path('/test/amazing_grace.mp3') == service_item.background_audio[0], \
355 '"/test/amazing_grace.mp3" should be in the background_audio list'356 '"/test/amazing_grace.mp3" should be in the background_audio list'
356357
=== modified file 'tests/functional/openlp_core/ui/test_mainwindow.py'
--- tests/functional/openlp_core/ui/test_mainwindow.py 2018-01-07 05:24:55 +0000
+++ tests/functional/openlp_core/ui/test_mainwindow.py 2018-01-22 21:37:22 +0000
@@ -23,6 +23,7 @@
23Package to test openlp.core.ui.mainwindow package.23Package to test openlp.core.ui.mainwindow package.
24"""24"""
25import os25import os
26from pathlib import Path
26from unittest import TestCase27from unittest import TestCase
27from unittest.mock import MagicMock, patch28from unittest.mock import MagicMock, patch
2829
@@ -84,14 +85,13 @@
84 """85 """
85 # GIVEN a service as an argument to openlp86 # GIVEN a service as an argument to openlp
86 service = os.path.join(TEST_RESOURCES_PATH, 'service', 'test.osz')87 service = os.path.join(TEST_RESOURCES_PATH, 'service', 'test.osz')
87 self.main_window.arguments = [service]
8888
89 # WHEN the argument is processed89 # WHEN the argument is processed
90 with patch.object(self.main_window.service_manager, 'load_file') as mocked_load_file:90 with patch.object(self.main_window.service_manager, 'load_file') as mocked_load_file:
91 self.main_window.open_cmd_line_files(service)91 self.main_window.open_cmd_line_files(service)
9292
93 # THEN the service from the arguments is loaded93 # THEN the service from the arguments is loaded
94 mocked_load_file.assert_called_with(service)94 mocked_load_file.assert_called_with(Path(service))
9595
96 @patch('openlp.core.ui.servicemanager.ServiceManager.load_file')96 @patch('openlp.core.ui.servicemanager.ServiceManager.load_file')
97 def test_cmd_line_arg(self, mocked_load_file):97 def test_cmd_line_arg(self, mocked_load_file):
@@ -242,3 +242,30 @@
242242
243 # THEN: projector_manager_dock.setVisible should had been called once243 # THEN: projector_manager_dock.setVisible should had been called once
244 mocked_dock.setVisible.assert_called_once_with(False)244 mocked_dock.setVisible.assert_called_once_with(False)
245
246 def test_increment_progress_bar_default_increment(self):
247 """
248 Test that increment_progress_bar increments the progress bar by 1 when called without the `increment` arg.
249 """
250 # GIVEN: A mocked progress bar
251 with patch.object(self.main_window, 'load_progress_bar', **{'value.return_value': 0}) as mocked_progress_bar:
252
253 # WHEN: Calling increment_progress_bar without the `increment` arg
254 self.main_window.increment_progress_bar()
255
256 # THEN: The progress bar value should have been incremented by 1
257 mocked_progress_bar.setValue.assert_called_once_with(1)
258
259 def test_increment_progress_bar_custom_increment(self):
260 """
261 Test that increment_progress_bar increments the progress bar by the `increment` arg when called with the
262 `increment` arg with a set value.
263 """
264 # GIVEN: A mocked progress bar
265 with patch.object(self.main_window, 'load_progress_bar', **{'value.return_value': 0}) as mocked_progress_bar:
266
267 # WHEN: Calling increment_progress_bar with `increment` set to 10
268 self.main_window.increment_progress_bar(increment=10)
269
270 # THEN: The progress bar value should have been incremented by 10
271 mocked_progress_bar.setValue.assert_called_once_with(10)
245272
=== modified file 'tests/functional/openlp_core/ui/test_servicemanager.py'
--- tests/functional/openlp_core/ui/test_servicemanager.py 2017-12-20 20:38:43 +0000
+++ tests/functional/openlp_core/ui/test_servicemanager.py 2018-01-22 21:37:22 +0000
@@ -623,10 +623,10 @@
623 # THEN: make_preview() should not have been called623 # THEN: make_preview() should not have been called
624 assert mocked_make_preview.call_count == 0, 'ServiceManager.make_preview() should not be called'624 assert mocked_make_preview.call_count == 0, 'ServiceManager.make_preview() should not be called'
625625
626 @patch('openlp.core.ui.servicemanager.shutil.copy')
627 @patch('openlp.core.ui.servicemanager.zipfile')626 @patch('openlp.core.ui.servicemanager.zipfile')
628 @patch('openlp.core.ui.servicemanager.ServiceManager.save_file_as')627 @patch('openlp.core.ui.servicemanager.ServiceManager.save_file_as')
629 def test_save_file_raises_permission_error(self, mocked_save_file_as, mocked_zipfile, mocked_shutil_copy):628 @patch('openlp.core.ui.servicemanager.os')
629 def test_save_file_raises_permission_error(self, mocked_os, mocked_save_file_as, mocked_zipfile):
630 """630 """
631 Test that when a PermissionError is raised when trying to save a file, it is handled correctly631 Test that when a PermissionError is raised when trying to save a file, it is handled correctly
632 """632 """
@@ -636,50 +636,22 @@
636 Registry().register('main_window', mocked_main_window)636 Registry().register('main_window', mocked_main_window)
637 Registry().register('application', MagicMock())637 Registry().register('application', MagicMock())
638 service_manager = ServiceManager(None)638 service_manager = ServiceManager(None)
639 service_manager._service_path = os.path.join('temp', 'filename.osz')639 service_manager._service_path = MagicMock()
640 service_manager._save_lite = False640 service_manager._save_lite = False
641 service_manager.service_items = []641 service_manager.service_items = []
642 service_manager.service_theme = 'Default'642 service_manager.service_theme = 'Default'
643 service_manager.service_manager_list = MagicMock()643 service_manager.service_manager_list = MagicMock()
644 mocked_save_file_as.return_value = True644 mocked_save_file_as.return_value = True
645 mocked_zipfile.ZipFile.return_value = MagicMock()645 mocked_zipfile.ZipFile.return_value = MagicMock()
646 mocked_shutil_copy.side_effect = PermissionError646 mocked_os.link.side_effect = PermissionError
647647
648 # WHEN: The service is saved and a PermissionError is thrown648 # WHEN: The service is saved and a PermissionError is raised
649 result = service_manager.save_file()649 result = service_manager.save_file()
650650
651 # THEN: The "save_as" method is called to save the service651 # THEN: The "save_as" method is called to save the service
652 assert result is True652 assert result is True
653 mocked_save_file_as.assert_called_with()653 mocked_save_file_as.assert_called_with()
654654
655 @patch('openlp.core.ui.servicemanager.shutil.copy')
656 @patch('openlp.core.ui.servicemanager.zipfile')
657 @patch('openlp.core.ui.servicemanager.ServiceManager.save_file_as')
658 def test_save_local_file_raises_permission_error(self, mocked_save_file_as, mocked_zipfile, mocked_shutil_copy):
659 """
660 Test that when a PermissionError is raised when trying to save a local file, it is handled correctly
661 """
662 # GIVEN: A service manager, a service to save
663 mocked_main_window = MagicMock()
664 mocked_main_window.service_manager_settings_section = 'servicemanager'
665 Registry().register('main_window', mocked_main_window)
666 Registry().register('application', MagicMock())
667 service_manager = ServiceManager(None)
668 service_manager._service_path = os.path.join('temp', 'filename.osz')
669 service_manager._save_lite = False
670 service_manager.service_items = []
671 service_manager.service_theme = 'Default'
672 mocked_save_file_as.return_value = True
673 mocked_zipfile.ZipFile.return_value = MagicMock()
674 mocked_shutil_copy.side_effect = PermissionError
675
676 # WHEN: The service is saved and a PermissionError is thrown
677 result = service_manager.save_local_file()
678
679 # THEN: The "save_as" method is called to save the service
680 assert result is True
681 mocked_save_file_as.assert_called_with()
682
683 @patch('openlp.core.ui.servicemanager.ServiceManager.regenerate_service_items')655 @patch('openlp.core.ui.servicemanager.ServiceManager.regenerate_service_items')
684 def test_theme_change_global(self, mocked_regenerate_service_items):656 def test_theme_change_global(self, mocked_regenerate_service_items):
685 """657 """