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

Proposed by Phill
Status: Merged
Approved by: Raoul Snyman
Approved revision: 2794
Merged at revision: 2784
Proposed branch: lp:~phill-ridout/openlp/fixes-mkII
Merge into: lp:openlp
Diff against target: 1063 lines (+167/-143)
47 files modified
openlp/core/common/__init__.py (+21/-3)
openlp/core/common/applocation.py (+1/-1)
openlp/core/common/httputils.py (+4/-4)
openlp/core/common/i18n.py (+1/-1)
openlp/core/common/mixins.py (+14/-0)
openlp/core/common/path.py (+1/-1)
openlp/core/common/settings.py (+4/-1)
openlp/core/lib/__init__.py (+1/-1)
openlp/core/lib/mediamanageritem.py (+4/-2)
openlp/core/ui/exceptionform.py (+1/-1)
openlp/core/ui/formattingtagcontroller.py (+1/-1)
openlp/core/ui/mainwindow.py (+2/-2)
openlp/core/ui/servicemanager.py (+28/-28)
openlp/core/ui/thememanager.py (+3/-3)
openlp/core/version.py (+2/-2)
openlp/core/widgets/edits.py (+13/-3)
openlp/core/widgets/views.py (+1/-1)
openlp/plugins/bibles/forms/booknameform.py (+1/-2)
openlp/plugins/bibles/lib/__init__.py (+3/-3)
openlp/plugins/bibles/lib/db.py (+1/-2)
openlp/plugins/images/lib/mediaitem.py (+2/-1)
openlp/plugins/presentations/lib/pptviewcontroller.py (+1/-1)
openlp/plugins/songs/forms/editsongform.py (+3/-3)
openlp/plugins/songs/lib/__init__.py (+2/-3)
openlp/plugins/songs/lib/importers/easyslides.py (+2/-1)
openlp/plugins/songs/lib/importers/mediashout.py (+1/-1)
openlp/plugins/songs/lib/importers/openoffice.py (+2/-2)
openlp/plugins/songs/lib/importers/opensong.py (+2/-1)
openlp/plugins/songs/lib/importers/songimport.py (+2/-21)
openlp/plugins/songs/lib/importers/songsoffellowship.py (+0/-1)
openlp/plugins/songs/lib/importers/zionworx.py (+5/-15)
openlp/plugins/songs/lib/openlyricsxml.py (+1/-1)
openlp/plugins/songusage/forms/songusagedetailform.py (+8/-2)
openlp/plugins/songusage/songusageplugin.py (+6/-9)
tests/functional/openlp_core/common/test_actions.py (+1/-0)
tests/functional/openlp_core/common/test_httputils.py (+1/-1)
tests/functional/openlp_core/common/test_i18n.py (+1/-1)
tests/functional/openlp_core/common/test_path.py (+4/-4)
tests/functional/openlp_core/lib/test_lib.py (+1/-1)
tests/functional/openlp_core/ui/test_first_time.py (+1/-1)
tests/functional/openlp_core/widgets/test_views.py (+0/-1)
tests/functional/openlp_plugins/presentations/test_presentationcontroller.py (+1/-1)
tests/interfaces/openlp_core/ui/test_projectormanager.py (+1/-1)
tests/interfaces/openlp_core/ui/test_projectorsourceform.py (+1/-1)
tests/interfaces/openlp_core/ui/test_thememanager.py (+1/-1)
tests/utils/__init__.py (+1/-1)
tests/utils/test_pylint.py (+9/-5)
To merge this branch: bzr merge lp:~phill-ridout/openlp/fixes-mkII
Reviewer Review Type Date Requested Status
Tim Bentley Pending
Raoul Snyman Pending
Review via email: mp+333573@code.launchpad.net

This proposal supersedes a proposal from 2017-11-10.

Description of the change

Fixed a number of bugs, and tests.

Failing on Code Analysis2, but this looks like fallout from the refactors (it hasn't passed since the beginning of october)

Also contains superflys branch lp:~raoul-snyman/openlp/fix-linting

--------------------------------
lp:~phill-ridout/openlp/fixes-mkII (revision 2794)
[SUCCESS] https://ci.openlp.io/job/Branch-01-Pull/2276/
[SUCCESS] https://ci.openlp.io/job/Branch-02-Functional-Tests/2178/
[SUCCESS] https://ci.openlp.io/job/Branch-03-Interface-Tests/2059/
[SUCCESS] https://ci.openlp.io/job/Branch-04a-Code_Analysis/1390/
[SUCCESS] https://ci.openlp.io/job/Branch-04b-Test_Coverage/1215/
[SUCCESS] https://ci.openlp.io/job/Branch-04c-Code_Analysis2/345/
[FAILURE] https://ci.openlp.io/job/Branch-05-AppVeyor-Tests/179/
Stopping after failure

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

Looks good.

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

Still approved

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

One small request....

review: Needs Fixing

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'openlp/core/common/__init__.py'
--- openlp/core/common/__init__.py 2017-10-07 07:05:07 +0000
+++ openlp/core/common/__init__.py 2017-11-10 23:08:30 +0000
@@ -43,9 +43,13 @@
4343
44FIRST_CAMEL_REGEX = re.compile('(.)([A-Z][a-z]+)')44FIRST_CAMEL_REGEX = re.compile('(.)([A-Z][a-z]+)')
45SECOND_CAMEL_REGEX = re.compile('([a-z0-9])([A-Z])')45SECOND_CAMEL_REGEX = re.compile('([a-z0-9])([A-Z])')
46CONTROL_CHARS = re.compile(r'[\x00-\x1F\x7F-\x9F]', re.UNICODE)46CONTROL_CHARS = re.compile(r'[\x00-\x1F\x7F-\x9F]')
47INVALID_FILE_CHARS = re.compile(r'[\\/:\*\?"<>\|\+\[\]%]', re.UNICODE)47INVALID_FILE_CHARS = re.compile(r'[\\/:\*\?"<>\|\+\[\]%]')
48IMAGES_FILTER = None48IMAGES_FILTER = None
49REPLACMENT_CHARS_MAP = str.maketrans({'\u2018': '\'', '\u2019': '\'', '\u201c': '"', '\u201d': '"', '\u2026': '...',
50 '\u2013': '-', '\u2014': '-', '\v': '\n\n', '\f': '\n\n'})
51NEW_LINE_REGEX = re.compile(r' ?(\r\n?|\n) ?')
52WHITESPACE_REGEX = re.compile(r'[ \t]+')
4953
5054
51def trace_error_handler(logger):55def trace_error_handler(logger):
@@ -339,7 +343,7 @@
339 if file_path.exists():343 if file_path.exists():
340 file_path.unlink()344 file_path.unlink()
341 return True345 return True
342 except (IOError, OSError):346 except OSError:
343 log.exception('Unable to delete file {file_path}'.format(file_path=file_path))347 log.exception('Unable to delete file {file_path}'.format(file_path=file_path))
344 return False348 return False
345349
@@ -436,3 +440,17 @@
436 return detector.result440 return detector.result
437 except OSError:441 except OSError:
438 log.exception('Error detecting file encoding')442 log.exception('Error detecting file encoding')
443
444
445def normalize_str(irreg_str):
446 """
447 Normalize the supplied string. Remove unicode control chars and tidy up white space.
448
449 :param str irreg_str: The string to normalize.
450 :return: The normalized string
451 :rtype: str
452 """
453 irreg_str = irreg_str.translate(REPLACMENT_CHARS_MAP)
454 irreg_str = CONTROL_CHARS.sub('', irreg_str)
455 irreg_str = NEW_LINE_REGEX.sub('\n', irreg_str)
456 return WHITESPACE_REGEX.sub(' ', irreg_str)
439457
=== modified file 'openlp/core/common/applocation.py'
--- openlp/core/common/applocation.py 2017-10-07 07:05:07 +0000
+++ openlp/core/common/applocation.py 2017-11-10 23:08:30 +0000
@@ -83,7 +83,7 @@
83 """83 """
84 # Check if we have a different data location.84 # Check if we have a different data location.
85 if Settings().contains('advanced/data path'):85 if Settings().contains('advanced/data path'):
86 path = Settings().value('advanced/data path')86 path = Path(Settings().value('advanced/data path'))
87 else:87 else:
88 path = AppLocation.get_directory(AppLocation.DataDir)88 path = AppLocation.get_directory(AppLocation.DataDir)
89 create_paths(path)89 create_paths(path)
9090
=== modified file 'openlp/core/common/httputils.py'
--- openlp/core/common/httputils.py 2017-10-07 07:05:07 +0000
+++ openlp/core/common/httputils.py 2017-11-10 23:08:30 +0000
@@ -97,8 +97,8 @@
97 response = requests.get(url, headers=headers, proxies=proxies, timeout=float(CONNECTION_TIMEOUT))97 response = requests.get(url, headers=headers, proxies=proxies, timeout=float(CONNECTION_TIMEOUT))
98 log.debug('Downloaded page {url}'.format(url=response.url))98 log.debug('Downloaded page {url}'.format(url=response.url))
99 break99 break
100 except IOError:100 except OSError:
101 # For now, catch IOError. All requests errors inherit from IOError101 # For now, catch OSError. All requests errors inherit from OSError
102 log.exception('Unable to connect to {url}'.format(url=url))102 log.exception('Unable to connect to {url}'.format(url=url))
103 response = None103 response = None
104 if retries >= CONNECTION_RETRIES:104 if retries >= CONNECTION_RETRIES:
@@ -127,7 +127,7 @@
127 try:127 try:
128 response = requests.head(url, timeout=float(CONNECTION_TIMEOUT), allow_redirects=True)128 response = requests.head(url, timeout=float(CONNECTION_TIMEOUT), allow_redirects=True)
129 return int(response.headers['Content-Length'])129 return int(response.headers['Content-Length'])
130 except IOError:130 except OSError:
131 if retries > CONNECTION_RETRIES:131 if retries > CONNECTION_RETRIES:
132 raise ConnectionError('Unable to download {url}'.format(url=url))132 raise ConnectionError('Unable to download {url}'.format(url=url))
133 else:133 else:
@@ -173,7 +173,7 @@
173 file_path.unlink()173 file_path.unlink()
174 return False174 return False
175 break175 break
176 except IOError:176 except OSError:
177 trace_error_handler(log)177 trace_error_handler(log)
178 if retries > CONNECTION_RETRIES:178 if retries > CONNECTION_RETRIES:
179 if file_path.exists():179 if file_path.exists():
180180
=== modified file 'openlp/core/common/i18n.py'
--- openlp/core/common/i18n.py 2017-10-07 07:05:07 +0000
+++ openlp/core/common/i18n.py 2017-11-10 23:08:30 +0000
@@ -53,7 +53,7 @@
5353
54Language = namedtuple('Language', ['id', 'name', 'code'])54Language = namedtuple('Language', ['id', 'name', 'code'])
55ICU_COLLATOR = None55ICU_COLLATOR = None
56DIGITS_OR_NONDIGITS = re.compile(r'\d+|\D+', re.UNICODE)56DIGITS_OR_NONDIGITS = re.compile(r'\d+|\D+')
57LANGUAGES = sorted([57LANGUAGES = sorted([
58 Language(1, translate('common.languages', '(Afan) Oromo', 'Language code: om'), 'om'),58 Language(1, translate('common.languages', '(Afan) Oromo', 'Language code: om'), 'om'),
59 Language(2, translate('common.languages', 'Abkhazian', 'Language code: ab'), 'ab'),59 Language(2, translate('common.languages', 'Abkhazian', 'Language code: ab'), 'ab'),
6060
=== modified file 'openlp/core/common/mixins.py'
--- openlp/core/common/mixins.py 2017-10-23 22:09:57 +0000
+++ openlp/core/common/mixins.py 2017-11-10 23:08:30 +0000
@@ -101,6 +101,20 @@
101 """101 """
102 This adds registry components to classes to use at run time.102 This adds registry components to classes to use at run time.
103 """103 """
104 _application = None
105 _plugin_manager = None
106 _image_manager = None
107 _media_controller = None
108 _service_manager = None
109 _preview_controller = None
110 _live_controller = None
111 _main_window = None
112 _renderer = None
113 _theme_manager = None
114 _settings_form = None
115 _alerts_manager = None
116 _projector_manager = None
117
104 @property118 @property
105 def application(self):119 def application(self):
106 """120 """
107121
=== modified file 'openlp/core/common/path.py'
--- openlp/core/common/path.py 2017-10-07 07:05:07 +0000
+++ openlp/core/common/path.py 2017-11-10 23:08:30 +0000
@@ -233,7 +233,7 @@
233 try:233 try:
234 if not path.exists():234 if not path.exists():
235 path.mkdir(parents=True)235 path.mkdir(parents=True)
236 except IOError:236 except OSError:
237 if not kwargs.get('do_not_log', False):237 if not kwargs.get('do_not_log', False):
238 log.exception('failed to check if directory exists or create directory')238 log.exception('failed to check if directory exists or create directory')
239239
240240
=== modified file 'openlp/core/common/settings.py'
--- openlp/core/common/settings.py 2017-10-07 07:05:07 +0000
+++ openlp/core/common/settings.py 2017-11-10 23:08:30 +0000
@@ -255,7 +255,10 @@
255 ('core/logo file', 'core/logo file', [(str_to_path, None)]),255 ('core/logo file', 'core/logo file', [(str_to_path, None)]),
256 ('presentations/last directory', 'presentations/last directory', [(str_to_path, None)]),256 ('presentations/last directory', 'presentations/last directory', [(str_to_path, None)]),
257 ('images/last directory', 'images/last directory', [(str_to_path, None)]),257 ('images/last directory', 'images/last directory', [(str_to_path, None)]),
258 ('media/last directory', 'media/last directory', [(str_to_path, None)])258 ('media/last directory', 'media/last directory', [(str_to_path, None)]),
259 ('songuasge/db password', 'songusage/db password', []),
260 ('songuasge/db hostname', 'songusage/db hostname', []),
261 ('songuasge/db database', 'songusage/db database', [])
259 ]262 ]
260263
261 @staticmethod264 @staticmethod
262265
=== modified file 'openlp/core/lib/__init__.py'
--- openlp/core/lib/__init__.py 2017-10-10 07:08:44 +0000
+++ openlp/core/lib/__init__.py 2017-11-10 23:08:30 +0000
@@ -104,7 +104,7 @@
104 # no BOM was found104 # no BOM was found
105 file_handle.seek(0)105 file_handle.seek(0)
106 content = file_handle.read()106 content = file_handle.read()
107 except (IOError, UnicodeError):107 except (OSError, UnicodeError):
108 log.exception('Failed to open text file {text}'.format(text=text_file_path))108 log.exception('Failed to open text file {text}'.format(text=text_file_path))
109 return content109 return content
110110
111111
=== modified file 'openlp/core/lib/mediamanageritem.py'
--- openlp/core/lib/mediamanageritem.py 2017-10-23 22:09:57 +0000
+++ openlp/core/lib/mediamanageritem.py 2017-11-10 23:08:30 +0000
@@ -92,7 +92,7 @@
92 Run some initial setup. This method is separate from __init__ in order to mock it out in tests.92 Run some initial setup. This method is separate from __init__ in order to mock it out in tests.
93 """93 """
94 self.hide()94 self.hide()
95 self.whitespace = re.compile(r'[\W_]+', re.UNICODE)95 self.whitespace = re.compile(r'[\W_]+')
96 visible_title = self.plugin.get_string(StringContent.VisibleName)96 visible_title = self.plugin.get_string(StringContent.VisibleName)
97 self.title = str(visible_title['title'])97 self.title = str(visible_title['title'])
98 Registry().register(self.plugin.name, self)98 Registry().register(self.plugin.name, self)
@@ -344,7 +344,9 @@
344 else:344 else:
345 new_files.append(file_name)345 new_files.append(file_name)
346 if new_files:346 if new_files:
347 self.validate_and_load(new_files, data['target'])347 if 'target' in data:
348 self.validate_and_load(new_files, data['target'])
349 self.validate_and_load(new_files)
348350
349 def dnd_move_internal(self, target):351 def dnd_move_internal(self, target):
350 """352 """
351353
=== modified file 'openlp/core/ui/exceptionform.py'
--- openlp/core/ui/exceptionform.py 2017-10-23 22:09:57 +0000
+++ openlp/core/ui/exceptionform.py 2017-11-10 23:08:30 +0000
@@ -155,7 +155,7 @@
155 try:155 try:
156 with file_path.open('w') as report_file:156 with file_path.open('w') as report_file:
157 report_file.write(report_text)157 report_file.write(report_text)
158 except IOError:158 except OSError:
159 log.exception('Failed to write crash report')159 log.exception('Failed to write crash report')
160160
161 def on_send_report_button_clicked(self):161 def on_send_report_button_clicked(self):
162162
=== modified file 'openlp/core/ui/formattingtagcontroller.py'
--- openlp/core/ui/formattingtagcontroller.py 2017-10-07 07:05:07 +0000
+++ openlp/core/ui/formattingtagcontroller.py 2017-11-10 23:08:30 +0000
@@ -43,7 +43,7 @@
43 r'(?P<tag>[^\s/!\?>]+)(?:\s+[^\s=]+="[^"]*")*\s*(?P<empty>/)?'43 r'(?P<tag>[^\s/!\?>]+)(?:\s+[^\s=]+="[^"]*")*\s*(?P<empty>/)?'
44 r'|(?P<cdata>!\[CDATA\[(?:(?!\]\]>).)*\]\])'44 r'|(?P<cdata>!\[CDATA\[(?:(?!\]\]>).)*\]\])'
45 r'|(?P<procinst>\?(?:(?!\?>).)*\?)'45 r'|(?P<procinst>\?(?:(?!\?>).)*\?)'
46 r'|(?P<comment>!--(?:(?!-->).)*--))>', re.UNICODE)46 r'|(?P<comment>!--(?:(?!-->).)*--))>')
47 self.html_regex = re.compile(r'^(?:[^<>]*%s)*[^<>]*$' % self.html_tag_regex.pattern)47 self.html_regex = re.compile(r'^(?:[^<>]*%s)*[^<>]*$' % self.html_tag_regex.pattern)
4848
49 def pre_save(self):49 def pre_save(self):
5050
=== added directory 'openlp/core/ui/lib'
=== modified file 'openlp/core/ui/mainwindow.py'
--- openlp/core/ui/mainwindow.py 2017-10-23 22:09:57 +0000
+++ openlp/core/ui/mainwindow.py 2017-11-10 23:08:30 +0000
@@ -180,7 +180,7 @@
180 triggers=self.service_manager_contents.on_load_service_clicked)180 triggers=self.service_manager_contents.on_load_service_clicked)
181 self.file_save_item = create_action(main_window, 'fileSaveItem', icon=':/general/general_save.png',181 self.file_save_item = create_action(main_window, 'fileSaveItem', icon=':/general/general_save.png',
182 can_shortcuts=True, category=UiStrings().File,182 can_shortcuts=True, category=UiStrings().File,
183 triggers=self.service_manager_contents.save_file)183 triggers=self.service_manager_contents.decide_save_method)
184 self.file_save_as_item = create_action(main_window, 'fileSaveAsItem', can_shortcuts=True,184 self.file_save_as_item = create_action(main_window, 'fileSaveAsItem', can_shortcuts=True,
185 category=UiStrings().File,185 category=UiStrings().File,
186 triggers=self.service_manager_contents.save_file_as)186 triggers=self.service_manager_contents.save_file_as)
@@ -1367,7 +1367,7 @@
1367 '- Please wait for copy to finish').format(path=self.new_data_path))1367 '- Please wait for copy to finish').format(path=self.new_data_path))
1368 dir_util.copy_tree(str(old_data_path), str(self.new_data_path))1368 dir_util.copy_tree(str(old_data_path), str(self.new_data_path))
1369 log.info('Copy successful')1369 log.info('Copy successful')
1370 except (IOError, os.error, DistutilsFileError) as why:1370 except (OSError, DistutilsFileError) as why:
1371 self.application.set_normal_cursor()1371 self.application.set_normal_cursor()
1372 log.exception('Data copy failed {err}'.format(err=str(why)))1372 log.exception('Data copy failed {err}'.format(err=str(why)))
1373 err_text = translate('OpenLP.MainWindow',1373 err_text = translate('OpenLP.MainWindow',
13741374
=== modified file 'openlp/core/ui/servicemanager.py'
--- openlp/core/ui/servicemanager.py 2017-10-23 22:09:57 +0000
+++ openlp/core/ui/servicemanager.py 2017-11-10 23:08:30 +0000
@@ -193,18 +193,6 @@
193 text=translate('OpenLP.ServiceManager', 'Move to &bottom'), icon=':/services/service_bottom.png',193 text=translate('OpenLP.ServiceManager', 'Move to &bottom'), icon=':/services/service_bottom.png',
194 tooltip=translate('OpenLP.ServiceManager', 'Move item to the end of the service.'),194 tooltip=translate('OpenLP.ServiceManager', 'Move item to the end of the service.'),
195 can_shortcuts=True, category=UiStrings().Service, triggers=self.on_service_end)195 can_shortcuts=True, category=UiStrings().Service, triggers=self.on_service_end)
196 self.down_action = self.order_toolbar.add_toolbar_action(
197 'down',
198 text=translate('OpenLP.ServiceManager', 'Move &down'), can_shortcuts=True,
199 tooltip=translate('OpenLP.ServiceManager', 'Moves the selection down the window.'), visible=False,
200 triggers=self.on_move_selection_down)
201 action_list.add_action(self.down_action)
202 self.up_action = self.order_toolbar.add_toolbar_action(
203 'up',
204 text=translate('OpenLP.ServiceManager', 'Move up'), can_shortcuts=True,
205 tooltip=translate('OpenLP.ServiceManager', 'Moves the selection up the window.'), visible=False,
206 triggers=self.on_move_selection_up)
207 action_list.add_action(self.up_action)
208 self.order_toolbar.addSeparator()196 self.order_toolbar.addSeparator()
209 self.delete_action = self.order_toolbar.add_toolbar_action(197 self.delete_action = self.order_toolbar.add_toolbar_action(
210 'delete', can_shortcuts=True,198 'delete', can_shortcuts=True,
@@ -300,8 +288,8 @@
300 self.theme_menu = QtWidgets.QMenu(translate('OpenLP.ServiceManager', '&Change Item Theme'))288 self.theme_menu = QtWidgets.QMenu(translate('OpenLP.ServiceManager', '&Change Item Theme'))
301 self.menu.addMenu(self.theme_menu)289 self.menu.addMenu(self.theme_menu)
302 self.service_manager_list.addActions([self.move_down_action, self.move_up_action, self.make_live_action,290 self.service_manager_list.addActions([self.move_down_action, self.move_up_action, self.make_live_action,
303 self.move_top_action, self.move_bottom_action, self.up_action,291 self.move_top_action, self.move_bottom_action, self.expand_action,
304 self.down_action, self.expand_action, self.collapse_action])292 self.collapse_action])
305 Registry().register_function('theme_update_list', self.update_theme_list)293 Registry().register_function('theme_update_list', self.update_theme_list)
306 Registry().register_function('config_screen_changed', self.regenerate_service_items)294 Registry().register_function('config_screen_changed', self.regenerate_service_items)
307 Registry().register_function('theme_update_global', self.theme_change)295 Registry().register_function('theme_update_global', self.theme_change)
@@ -474,6 +462,12 @@
474 Load a recent file as the service triggered by mainwindow recent service list.462 Load a recent file as the service triggered by mainwindow recent service list.
475 :param field:463 :param field:
476 """464 """
465 if self.is_modified():
466 result = self.save_modified_service()
467 if result == QtWidgets.QMessageBox.Cancel:
468 return False
469 elif result == QtWidgets.QMessageBox.Save:
470 self.decide_save_method()
477 sender = self.sender()471 sender = self.sender()
478 self.load_file(sender.data())472 self.load_file(sender.data())
479473
@@ -603,7 +597,7 @@
603 if not os.path.exists(save_file):597 if not os.path.exists(save_file):
604 shutil.copy(audio_from, save_file)598 shutil.copy(audio_from, save_file)
605 zip_file.write(audio_from, audio_to)599 zip_file.write(audio_from, audio_to)
606 except IOError:600 except OSError:
607 self.log_exception('Failed to save service to disk: {name}'.format(name=temp_file_name))601 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'),602 self.main_window.error_message(translate('OpenLP.ServiceManager', 'Error Saving File'),
609 translate('OpenLP.ServiceManager', 'There was an error saving your file.'))603 translate('OpenLP.ServiceManager', 'There was an error saving your file.'))
@@ -664,7 +658,7 @@
664 zip_file = zipfile.ZipFile(temp_file_name, 'w', zipfile.ZIP_STORED, True)658 zip_file = zipfile.ZipFile(temp_file_name, 'w', zipfile.ZIP_STORED, True)
665 # First we add service contents.659 # First we add service contents.
666 zip_file.writestr(service_file_name, service_content)660 zip_file.writestr(service_file_name, service_content)
667 except IOError:661 except OSError:
668 self.log_exception('Failed to save service to disk: {name}'.format(name=temp_file_name))662 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'),663 self.main_window.error_message(translate('OpenLP.ServiceManager', 'Error Saving File'),
670 translate('OpenLP.ServiceManager', 'There was an error saving your file.'))664 translate('OpenLP.ServiceManager', 'There was an error saving your file.'))
@@ -712,18 +706,23 @@
712 default_file_path = directory_path / default_file_path706 default_file_path = directory_path / default_file_path
713 # SaveAs from osz to oszl is not valid as the files will be deleted on exit which is not sensible or usable in707 # SaveAs from osz to oszl is not valid as the files will be deleted on exit which is not sensible or usable in
714 # the long term.708 # the long term.
709 lite_filter = translate('OpenLP.ServiceManager', 'OpenLP Service Files - lite (*.oszl)')
710 packaged_filter = translate('OpenLP.ServiceManager', 'OpenLP Service Files (*.osz)')
711
715 if self._file_name.endswith('oszl') or self.service_has_all_original_files:712 if self._file_name.endswith('oszl') or self.service_has_all_original_files:
716 file_path, filter_used = FileDialog.getSaveFileName(713 file_path, filter_used = FileDialog.getSaveFileName(
717 self.main_window, UiStrings().SaveService, default_file_path,714 self.main_window, UiStrings().SaveService, default_file_path,
718 translate('OpenLP.ServiceManager',715 '{packaged};; {lite}'.format(packaged=packaged_filter, lite=lite_filter))
719 'OpenLP Service Files (*.osz);; OpenLP Service Files - lite (*.oszl)'))
720 else:716 else:
721 file_path, filter_used = FileDialog.getSaveFileName(717 file_path, filter_used = FileDialog.getSaveFileName(
722 self.main_window, UiStrings().SaveService, file_path,718 self.main_window, UiStrings().SaveService, default_file_path,
723 translate('OpenLP.ServiceManager', 'OpenLP Service Files (*.osz);;'))719 '{packaged};;'.format(packaged=packaged_filter))
724 if not file_path:720 if not file_path:
725 return False721 return False
726 file_path.with_suffix('.osz')722 if filter_used == lite_filter:
723 file_path = file_path.with_suffix('.oszl')
724 else:
725 file_path = file_path.with_suffix('.osz')
727 self.set_file_name(file_path)726 self.set_file_name(file_path)
728 self.decide_save_method()727 self.decide_save_method()
729728
@@ -791,11 +790,11 @@
791 else:790 else:
792 critical_error_message_box(message=translate('OpenLP.ServiceManager', 'File is not a valid service.'))791 critical_error_message_box(message=translate('OpenLP.ServiceManager', 'File is not a valid service.'))
793 self.log_error('File contains no service data')792 self.log_error('File contains no service data')
794 except (IOError, NameError):793 except (OSError, NameError):
795 self.log_exception('Problem loading service file {name}'.format(name=file_name))794 self.log_exception('Problem loading service file {name}'.format(name=file_name))
796 critical_error_message_box(message=translate('OpenLP.ServiceManager',795 critical_error_message_box(message=translate('OpenLP.ServiceManager',
797 'File could not be opened because it is corrupt.'))796 'File could not be opened because it is corrupt.'))
798 except zipfile.BadZipfile:797 except zipfile.BadZipFile:
799 if os.path.getsize(file_name) == 0:798 if os.path.getsize(file_name) == 0:
800 self.log_exception('Service file is zero sized: {name}'.format(name=file_name))799 self.log_exception('Service file is zero sized: {name}'.format(name=file_name))
801 QtWidgets.QMessageBox.information(self, translate('OpenLP.ServiceManager', 'Empty File'),800 QtWidgets.QMessageBox.information(self, translate('OpenLP.ServiceManager', 'Empty File'),
@@ -1657,14 +1656,15 @@
1657 if start_pos == -1:1656 if start_pos == -1:
1658 return1657 return
1659 if item is None:1658 if item is None:
1660 end_pos = len(self.service_items)1659 end_pos = len(self.service_items) - 1
1661 else:1660 else:
1662 end_pos = get_parent_item_data(item) - 11661 end_pos = get_parent_item_data(item) - 1
1663 service_item = self.service_items[start_pos]1662 service_item = self.service_items[start_pos]
1664 self.service_items.remove(service_item)1663 if start_pos != end_pos:
1665 self.service_items.insert(end_pos, service_item)1664 self.service_items.remove(service_item)
1666 self.repaint_service_list(end_pos, child)1665 self.service_items.insert(end_pos, service_item)
1667 self.set_modified()1666 self.repaint_service_list(end_pos, child)
1667 self.set_modified()
1668 else:1668 else:
1669 # we are not over anything so drop1669 # we are not over anything so drop
1670 replace = False1670 replace = False
16711671
=== modified file 'openlp/core/ui/thememanager.py'
--- openlp/core/ui/thememanager.py 2017-10-23 22:09:57 +0000
+++ openlp/core/ui/thememanager.py 2017-11-10 23:08:30 +0000
@@ -604,7 +604,7 @@
604 else:604 else:
605 with full_name.open('wb') as out_file:605 with full_name.open('wb') as out_file:
606 out_file.write(theme_zip.read(zipped_file))606 out_file.write(theme_zip.read(zipped_file))
607 except (IOError, zipfile.BadZipfile):607 except (OSError, zipfile.BadZipFile):
608 self.log_exception('Importing theme from zip failed {name}'.format(name=file_path))608 self.log_exception('Importing theme from zip failed {name}'.format(name=file_path))
609 raise ValidationError609 raise ValidationError
610 except ValidationError:610 except ValidationError:
@@ -667,7 +667,7 @@
667 theme_path = theme_dir / '{file_name}.json'.format(file_name=name)667 theme_path = theme_dir / '{file_name}.json'.format(file_name=name)
668 try:668 try:
669 theme_path.write_text(theme_pretty)669 theme_path.write_text(theme_pretty)
670 except IOError:670 except OSError:
671 self.log_exception('Saving theme to file failed')671 self.log_exception('Saving theme to file failed')
672 if image_source_path and image_destination_path:672 if image_source_path and image_destination_path:
673 if self.old_background_image_path and image_destination_path != self.old_background_image_path:673 if self.old_background_image_path and image_destination_path != self.old_background_image_path:
@@ -675,7 +675,7 @@
675 if image_source_path != image_destination_path:675 if image_source_path != image_destination_path:
676 try:676 try:
677 copyfile(image_source_path, image_destination_path)677 copyfile(image_source_path, image_destination_path)
678 except IOError:678 except OSError:
679 self.log_exception('Failed to save theme image')679 self.log_exception('Failed to save theme image')
680 self.generate_and_save_image(name, theme)680 self.generate_and_save_image(name, theme)
681681
682682
=== modified file 'openlp/core/version.py'
--- openlp/core/version.py 2017-10-07 07:05:07 +0000
+++ openlp/core/version.py 2017-11-10 23:08:30 +0000
@@ -96,7 +96,7 @@
96 remote_version = response.text96 remote_version = response.text
97 log.debug('New version found: %s', remote_version)97 log.debug('New version found: %s', remote_version)
98 break98 break
99 except IOError:99 except OSError:
100 log.exception('Unable to connect to OpenLP server to download version file')100 log.exception('Unable to connect to OpenLP server to download version file')
101 retries += 1101 retries += 1
102 else:102 else:
@@ -182,7 +182,7 @@
182 try:182 try:
183 version_file = open(file_path, 'r')183 version_file = open(file_path, 'r')
184 full_version = str(version_file.read()).rstrip()184 full_version = str(version_file.read()).rstrip()
185 except IOError:185 except OSError:
186 log.exception('Error in version file.')186 log.exception('Error in version file.')
187 full_version = '0.0.0-bzr000'187 full_version = '0.0.0-bzr000'
188 finally:188 finally:
189189
=== modified file 'openlp/core/widgets/edits.py'
--- openlp/core/widgets/edits.py 2017-10-23 22:09:57 +0000
+++ openlp/core/widgets/edits.py 2017-11-10 23:08:30 +0000
@@ -27,6 +27,7 @@
2727
28from PyQt5 import QtCore, QtGui, QtWidgets28from PyQt5 import QtCore, QtGui, QtWidgets
2929
30from openlp.core.common import CONTROL_CHARS
30from openlp.core.common.i18n import UiStrings, translate31from openlp.core.common.i18n import UiStrings, translate
31from openlp.core.common.path import Path, path_to_str, str_to_path32from openlp.core.common.path import Path, path_to_str, str_to_path
32from openlp.core.common.settings import Settings33from openlp.core.common.settings import Settings
@@ -241,7 +242,7 @@
241 self.line_edit.editingFinished.connect(self.on_line_edit_editing_finished)242 self.line_edit.editingFinished.connect(self.on_line_edit_editing_finished)
242 self.update_button_tool_tips()243 self.update_button_tool_tips()
243244
244 @property245 @QtCore.pyqtProperty('QVariant')
245 def path(self):246 def path(self):
246 """247 """
247 A property getter method to return the selected path.248 A property getter method to return the selected path.
@@ -349,7 +350,7 @@
349 :rtype: None350 :rtype: None
350 """351 """
351 if self._path != path:352 if self._path != path:
352 self.path = path353 self._path = path
353 self.pathChanged.emit(path)354 self.pathChanged.emit(path)
354355
355356
@@ -470,12 +471,21 @@
470 cursor.insertText(html['start tag'])471 cursor.insertText(html['start tag'])
471 cursor.insertText(html['end tag'])472 cursor.insertText(html['end tag'])
472473
474 def insertFromMimeData(self, source):
475 """
476 Reimplement `insertFromMimeData` so that we can remove any control characters
477
478 :param QtCore.QMimeData source: The mime data to insert
479 :rtype: None
480 """
481 self.insertPlainText(CONTROL_CHARS.sub('', source.text()))
482
473483
474class Highlighter(QtGui.QSyntaxHighlighter):484class Highlighter(QtGui.QSyntaxHighlighter):
475 """485 """
476 Provides a text highlighter for pointing out spelling errors in text.486 Provides a text highlighter for pointing out spelling errors in text.
477 """487 """
478 WORDS = r'(?iu)[\w\']+'488 WORDS = r'(?i)[\w\']+'
479489
480 def __init__(self, *args):490 def __init__(self, *args):
481 """491 """
482492
=== modified file 'openlp/core/widgets/views.py'
--- openlp/core/widgets/views.py 2017-10-23 22:09:57 +0000
+++ openlp/core/widgets/views.py 2017-11-10 23:08:30 +0000
@@ -336,7 +336,7 @@
336 for file in listing:336 for file in listing:
337 files.append(os.path.join(local_file, file))337 files.append(os.path.join(local_file, file))
338 Registry().execute('{mime_data}_dnd'.format(mime_data=self.mime_data_text),338 Registry().execute('{mime_data}_dnd'.format(mime_data=self.mime_data_text),
339 {'files': files, 'target': self.itemAt(event.pos())})339 {'files': files})
340 else:340 else:
341 event.ignore()341 event.ignore()
342342
343343
=== modified file 'openlp/plugins/bibles/forms/booknameform.py'
--- openlp/plugins/bibles/forms/booknameform.py 2017-10-07 07:05:07 +0000
+++ openlp/plugins/bibles/forms/booknameform.py 2017-11-10 23:08:30 +0000
@@ -113,8 +113,7 @@
113 cor_book = self.corresponding_combo_box.currentText()113 cor_book = self.corresponding_combo_box.currentText()
114 for character in '\\.^$*+?{}[]()':114 for character in '\\.^$*+?{}[]()':
115 cor_book = cor_book.replace(character, '\\' + character)115 cor_book = cor_book.replace(character, '\\' + character)
116 books = [key for key in list(self.book_names.keys()) if re.match(cor_book, str(self.book_names[key]),116 books = [key for key in list(self.book_names.keys()) if re.match(cor_book, str(self.book_names[key]))]
117 re.UNICODE)]
118 books = [_f for _f in map(BiblesResourcesDB.get_book, books) if _f]117 books = [_f for _f in map(BiblesResourcesDB.get_book, books) if _f]
119 if books:118 if books:
120 self.book_id = books[0]['id']119 self.book_id = books[0]['id']
121120
=== modified file 'openlp/plugins/bibles/lib/__init__.py'
--- openlp/plugins/bibles/lib/__init__.py 2017-10-07 07:05:07 +0000
+++ openlp/plugins/bibles/lib/__init__.py 2017-11-10 23:08:30 +0000
@@ -224,13 +224,13 @@
224 range_regex = '(?:(?P<from_chapter>[0-9]+){sep_v})?' \224 range_regex = '(?:(?P<from_chapter>[0-9]+){sep_v})?' \
225 '(?P<from_verse>[0-9]+)(?P<range_to>{sep_r}(?:(?:(?P<to_chapter>' \225 '(?P<from_verse>[0-9]+)(?P<range_to>{sep_r}(?:(?:(?P<to_chapter>' \
226 '[0-9]+){sep_v})?(?P<to_verse>[0-9]+)|{sep_e})?)?'.format_map(REFERENCE_SEPARATORS)226 '[0-9]+){sep_v})?(?P<to_verse>[0-9]+)|{sep_e})?)?'.format_map(REFERENCE_SEPARATORS)
227 REFERENCE_MATCHES['range'] = re.compile(r'^\s*{range}\s*$'.format(range=range_regex), re.UNICODE)227 REFERENCE_MATCHES['range'] = re.compile(r'^\s*{range}\s*$'.format(range=range_regex))
228 REFERENCE_MATCHES['range_separator'] = re.compile(REFERENCE_SEPARATORS['sep_l'], re.UNICODE)228 REFERENCE_MATCHES['range_separator'] = re.compile(REFERENCE_SEPARATORS['sep_l'])
229 # full reference match: <book>(<range>(,(?!$)|(?=$)))+229 # full reference match: <book>(<range>(,(?!$)|(?=$)))+
230 REFERENCE_MATCHES['full'] = \230 REFERENCE_MATCHES['full'] = \
231 re.compile(r'^\s*(?!\s)(?P<book>[\d]*[.]?[^\d\.]+)\.*(?<!\s)\s*'231 re.compile(r'^\s*(?!\s)(?P<book>[\d]*[.]?[^\d\.]+)\.*(?<!\s)\s*'
232 r'(?P<ranges>(?:{range_regex}(?:{sep_l}(?!\s*$)|(?=\s*$)))+)\s*$'.format(232 r'(?P<ranges>(?:{range_regex}(?:{sep_l}(?!\s*$)|(?=\s*$)))+)\s*$'.format(
233 range_regex=range_regex, sep_l=REFERENCE_SEPARATORS['sep_l']), re.UNICODE)233 range_regex=range_regex, sep_l=REFERENCE_SEPARATORS['sep_l']))
234234
235235
236def get_reference_separator(separator_type):236def get_reference_separator(separator_type):
237237
=== modified file 'openlp/plugins/bibles/lib/db.py'
--- openlp/plugins/bibles/lib/db.py 2017-10-23 22:09:57 +0000
+++ openlp/plugins/bibles/lib/db.py 2017-11-10 23:08:30 +0000
@@ -307,8 +307,7 @@
307 book_escaped = book307 book_escaped = book
308 for character in RESERVED_CHARACTERS:308 for character in RESERVED_CHARACTERS:
309 book_escaped = book_escaped.replace(character, '\\' + character)309 book_escaped = book_escaped.replace(character, '\\' + character)
310 regex_book = re.compile('\\s*{book}\\s*'.format(book='\\s*'.join(book_escaped.split())),310 regex_book = re.compile('\\s*{book}\\s*'.format(book='\\s*'.join(book_escaped.split())), re.IGNORECASE)
311 re.UNICODE | re.IGNORECASE)
312 if language_selection == LanguageSelection.Bible:311 if language_selection == LanguageSelection.Bible:
313 db_book = self.get_book(book)312 db_book = self.get_book(book)
314 if db_book:313 if db_book:
315314
=== modified file 'openlp/plugins/images/lib/mediaitem.py'
--- openlp/plugins/images/lib/mediaitem.py 2017-10-23 22:09:57 +0000
+++ openlp/plugins/images/lib/mediaitem.py 2017-11-10 23:08:30 +0000
@@ -366,7 +366,7 @@
366 if validate_thumb(image.file_path, thumbnail_path):366 if validate_thumb(image.file_path, thumbnail_path):
367 icon = build_icon(thumbnail_path)367 icon = build_icon(thumbnail_path)
368 else:368 else:
369 icon = create_thumb(image.file_path, thumbnail_path)369 icon = create_thumb(str(image.file_path), str(thumbnail_path))
370 item_name = QtWidgets.QTreeWidgetItem([file_name])370 item_name = QtWidgets.QTreeWidgetItem([file_name])
371 item_name.setText(0, file_name)371 item_name.setText(0, file_name)
372 item_name.setIcon(0, icon)372 item_name.setIcon(0, icon)
@@ -390,6 +390,7 @@
390 :param files: A List of strings containing the filenames of the files to be loaded390 :param files: A List of strings containing the filenames of the files to be loaded
391 :param target_group: The QTreeWidgetItem of the group that will be the parent of the added files391 :param target_group: The QTreeWidgetItem of the group that will be the parent of the added files
392 """392 """
393 file_paths = [Path(file) for file in file_paths]
393 self.application.set_normal_cursor()394 self.application.set_normal_cursor()
394 self.load_list(file_paths, target_group)395 self.load_list(file_paths, target_group)
395 last_dir = file_paths[0].parent396 last_dir = file_paths[0].parent
396397
=== modified file 'openlp/plugins/presentations/lib/pptviewcontroller.py'
--- openlp/plugins/presentations/lib/pptviewcontroller.py 2017-10-10 07:08:44 +0000
+++ openlp/plugins/presentations/lib/pptviewcontroller.py 2017-11-10 23:08:30 +0000
@@ -70,7 +70,7 @@
70 try:70 try:
71 self.start_process()71 self.start_process()
72 return self.process.CheckInstalled()72 return self.process.CheckInstalled()
73 except WindowsError:73 except OSError:
74 return False74 return False
7575
76 def start_process(self):76 def start_process(self):
7777
=== modified file 'openlp/plugins/songs/forms/editsongform.py'
--- openlp/plugins/songs/forms/editsongform.py 2017-10-23 22:09:57 +0000
+++ openlp/plugins/songs/forms/editsongform.py 2017-11-10 23:08:30 +0000
@@ -105,9 +105,9 @@
105 self.topics_list_view.setSortingEnabled(False)105 self.topics_list_view.setSortingEnabled(False)
106 self.topics_list_view.setAlternatingRowColors(True)106 self.topics_list_view.setAlternatingRowColors(True)
107 self.audio_list_widget.setAlternatingRowColors(True)107 self.audio_list_widget.setAlternatingRowColors(True)
108 self.find_verse_split = re.compile('---\[\]---\n', re.UNICODE)108 self.find_verse_split = re.compile('---\[\]---\n')
109 self.whitespace = re.compile(r'\W+', re.UNICODE)109 self.whitespace = re.compile(r'\W+')
110 self.find_tags = re.compile(u'\{/?\w+\}', re.UNICODE)110 self.find_tags = re.compile(r'\{/?\w+\}')
111111
112 def _load_objects(self, cls, combo, cache):112 def _load_objects(self, cls, combo, cache):
113 """113 """
114114
=== modified file 'openlp/plugins/songs/lib/__init__.py'
--- openlp/plugins/songs/lib/__init__.py 2017-10-10 02:29:56 +0000
+++ openlp/plugins/songs/lib/__init__.py 2017-11-10 23:08:30 +0000
@@ -24,7 +24,6 @@
24"""24"""
2525
26import logging26import logging
27import os
28import re27import re
2928
30from PyQt5 import QtWidgets29from PyQt5 import QtWidgets
@@ -39,8 +38,8 @@
3938
40log = logging.getLogger(__name__)39log = logging.getLogger(__name__)
4140
42WHITESPACE = re.compile(r'[\W_]+', re.UNICODE)41WHITESPACE = re.compile(r'[\W_]+')
43APOSTROPHE = re.compile('[\'`’ʻ′]', re.UNICODE)42APOSTROPHE = re.compile(r'[\'`’ʻ′]')
44# PATTERN will look for the next occurence of one of these symbols:43# PATTERN will look for the next occurence of one of these symbols:
45# \controlword - optionally preceded by \*, optionally followed by a number44# \controlword - optionally preceded by \*, optionally followed by a number
46# \'## - where ## is a pair of hex digits, representing a single character45# \'## - where ## is a pair of hex digits, representing a single character
4746
=== modified file 'openlp/plugins/songs/lib/importers/easyslides.py'
--- openlp/plugins/songs/lib/importers/easyslides.py 2017-09-30 20:16:30 +0000
+++ openlp/plugins/songs/lib/importers/easyslides.py 2017-11-10 23:08:30 +0000
@@ -25,6 +25,7 @@
2525
26from lxml import etree, objectify26from lxml import etree, objectify
2727
28from openlp.core.common import normalize_str
28from openlp.plugins.songs.lib import VerseType29from openlp.plugins.songs.lib import VerseType
29from openlp.plugins.songs.lib.importers.songimport import SongImport30from openlp.plugins.songs.lib.importers.songimport import SongImport
3031
@@ -225,7 +226,7 @@
225 verses[reg].setdefault(vt, {})226 verses[reg].setdefault(vt, {})
226 verses[reg][vt].setdefault(vn, {})227 verses[reg][vt].setdefault(vn, {})
227 verses[reg][vt][vn].setdefault(inst, [])228 verses[reg][vt][vn].setdefault(inst, [])
228 verses[reg][vt][vn][inst].append(self.tidy_text(line))229 verses[reg][vt][vn][inst].append(normalize_str(line))
229 # done parsing230 # done parsing
230 versetags = []231 versetags = []
231 # we use our_verse_order to ensure, we insert lyrics in the same order232 # we use our_verse_order to ensure, we insert lyrics in the same order
232233
=== modified file 'openlp/plugins/songs/lib/importers/mediashout.py'
--- openlp/plugins/songs/lib/importers/mediashout.py 2017-10-07 07:05:07 +0000
+++ openlp/plugins/songs/lib/importers/mediashout.py 2017-11-10 23:08:30 +0000
@@ -101,7 +101,7 @@
101 self.song_book_name = song.SongID101 self.song_book_name = song.SongID
102 for verse in verses:102 for verse in verses:
103 tag = VERSE_TAGS[verse.Type] + str(verse.Number) if verse.Type < len(VERSE_TAGS) else 'O'103 tag = VERSE_TAGS[verse.Type] + str(verse.Number) if verse.Type < len(VERSE_TAGS) else 'O'
104 self.add_verse(self.tidy_text(verse.Text), tag)104 self.add_verse(verse.Text, tag)
105 for order in verse_order:105 for order in verse_order:
106 if order.Type < len(VERSE_TAGS):106 if order.Type < len(VERSE_TAGS):
107 self.verse_order_list.append(VERSE_TAGS[order.Type] + str(order.Number))107 self.verse_order_list.append(VERSE_TAGS[order.Type] + str(order.Number))
108108
=== modified file 'openlp/plugins/songs/lib/importers/openoffice.py'
--- openlp/plugins/songs/lib/importers/openoffice.py 2017-10-10 02:29:56 +0000
+++ openlp/plugins/songs/lib/importers/openoffice.py 2017-11-10 23:08:30 +0000
@@ -24,7 +24,7 @@
2424
25from PyQt5 import QtCore25from PyQt5 import QtCore
2626
27from openlp.core.common import is_win, get_uno_command, get_uno_instance27from openlp.core.common import get_uno_command, get_uno_instance, is_win, normalize_str
28from openlp.core.common.i18n import translate28from openlp.core.common.i18n import translate
29from .songimport import SongImport29from .songimport import SongImport
3030
@@ -241,7 +241,7 @@
241241
242 :param text: The text.242 :param text: The text.
243 """243 """
244 song_texts = self.tidy_text(text).split('\f')244 song_texts = normalize_str(text).split('\f')
245 self.set_defaults()245 self.set_defaults()
246 for song_text in song_texts:246 for song_text in song_texts:
247 if song_text.strip():247 if song_text.strip():
248248
=== modified file 'openlp/plugins/songs/lib/importers/opensong.py'
--- openlp/plugins/songs/lib/importers/opensong.py 2017-10-10 02:29:56 +0000
+++ openlp/plugins/songs/lib/importers/opensong.py 2017-11-10 23:08:30 +0000
@@ -25,6 +25,7 @@
25from lxml import objectify25from lxml import objectify
26from lxml.etree import Error, LxmlError26from lxml.etree import Error, LxmlError
2727
28from openlp.core.common import normalize_str
28from openlp.core.common.i18n import translate29from openlp.core.common.i18n import translate
29from openlp.core.common.settings import Settings30from openlp.core.common.settings import Settings
30from openlp.plugins.songs.lib import VerseType31from openlp.plugins.songs.lib import VerseType
@@ -262,7 +263,7 @@
262 post=this_line[offset + column:])263 post=this_line[offset + column:])
263 offset += len(chord) + 2264 offset += len(chord) + 2
264 # Tidy text and remove the ____s from extended words265 # Tidy text and remove the ____s from extended words
265 this_line = self.tidy_text(this_line)266 this_line = normalize_str(this_line)
266 this_line = this_line.replace('_', '')267 this_line = this_line.replace('_', '')
267 this_line = this_line.replace('||', '\n[---]\n')268 this_line = this_line.replace('||', '\n[---]\n')
268 this_line = this_line.strip()269 this_line = this_line.strip()
269270
=== modified file 'openlp/plugins/songs/lib/importers/songimport.py'
--- openlp/plugins/songs/lib/importers/songimport.py 2017-10-23 22:09:57 +0000
+++ openlp/plugins/songs/lib/importers/songimport.py 2017-11-10 23:08:30 +0000
@@ -25,6 +25,7 @@
2525
26from PyQt5 import QtCore26from PyQt5 import QtCore
2727
28from openlp.core.common import normalize_str
28from openlp.core.common.applocation import AppLocation29from openlp.core.common.applocation import AppLocation
29from openlp.core.common.i18n import translate30from openlp.core.common.i18n import translate
30from openlp.core.common.path import copyfile, create_paths31from openlp.core.common.path import copyfile, create_paths
@@ -130,26 +131,6 @@
130 def register(self, import_wizard):131 def register(self, import_wizard):
131 self.import_wizard = import_wizard132 self.import_wizard = import_wizard
132133
133 def tidy_text(self, text):
134 """
135 Get rid of some dodgy unicode and formatting characters we're not interested in. Some can be converted to ascii.
136 """
137 text = text.replace('\u2018', '\'')
138 text = text.replace('\u2019', '\'')
139 text = text.replace('\u201c', '"')
140 text = text.replace('\u201d', '"')
141 text = text.replace('\u2026', '...')
142 text = text.replace('\u2013', '-')
143 text = text.replace('\u2014', '-')
144 # Replace vertical tab with 2 linebreaks
145 text = text.replace('\v', '\n\n')
146 # Replace form feed (page break) with 2 linebreaks
147 text = text.replace('\f', '\n\n')
148 # Remove surplus blank lines, spaces, trailing/leading spaces
149 text = re.sub(r'[ \t]+', ' ', text)
150 text = re.sub(r' ?(\r\n?|\n) ?', '\n', text)
151 return text
152
153 def process_song_text(self, text):134 def process_song_text(self, text):
154 """135 """
155 Process the song text from import136 Process the song text from import
@@ -368,7 +349,7 @@
368 verse_tag = VerseType.tags[VerseType.Other]349 verse_tag = VerseType.tags[VerseType.Other]
369 log.info('Versetype {old} changing to {new}'.format(old=verse_def, new=new_verse_def))350 log.info('Versetype {old} changing to {new}'.format(old=verse_def, new=new_verse_def))
370 verse_def = new_verse_def351 verse_def = new_verse_def
371 sxml.add_verse_to_lyrics(verse_tag, verse_def[1:], verse_text, lang)352 sxml.add_verse_to_lyrics(verse_tag, verse_def[1:], normalize_str(verse_text), lang)
372 song.lyrics = str(sxml.extract_xml(), 'utf-8')353 song.lyrics = str(sxml.extract_xml(), 'utf-8')
373 if not self.verse_order_list and self.verse_order_list_generated_useful:354 if not self.verse_order_list and self.verse_order_list_generated_useful:
374 self.verse_order_list = self.verse_order_list_generated355 self.verse_order_list = self.verse_order_list_generated
375356
=== modified file 'openlp/plugins/songs/lib/importers/songsoffellowship.py'
--- openlp/plugins/songs/lib/importers/songsoffellowship.py 2016-12-31 11:01:36 +0000
+++ openlp/plugins/songs/lib/importers/songsoffellowship.py 2017-11-10 23:08:30 +0000
@@ -194,7 +194,6 @@
194 :param text_portion: A Piece of text194 :param text_portion: A Piece of text
195 """195 """
196 text = text_portion.getString()196 text = text_portion.getString()
197 text = self.tidy_text(text)
198 if text.strip() == '':197 if text.strip() == '':
199 return text198 return text
200 if text_portion.CharWeight == BOLD:199 if text_portion.CharWeight == BOLD:
201200
=== modified file 'openlp/plugins/songs/lib/importers/zionworx.py'
--- openlp/plugins/songs/lib/importers/zionworx.py 2017-10-10 02:29:56 +0000
+++ openlp/plugins/songs/lib/importers/zionworx.py 2017-11-10 23:08:30 +0000
@@ -30,9 +30,6 @@
3030
31log = logging.getLogger(__name__)31log = logging.getLogger(__name__)
3232
33# Used to strip control chars (except 10=LF, 13=CR)
34CONTROL_CHARS_MAP = dict.fromkeys(list(range(10)) + [11, 12] + list(range(14, 32)) + [127])
35
3633
37class ZionWorxImport(SongImport):34class ZionWorxImport(SongImport):
38 """35 """
@@ -95,12 +92,12 @@
95 return92 return
96 self.set_defaults()93 self.set_defaults()
97 try:94 try:
98 self.title = self._decode(record['Title1'])95 self.title = record['Title1']
99 if record['Title2']:96 if record['Title2']:
100 self.alternate_title = self._decode(record['Title2'])97 self.alternate_title = record['Title2']
101 self.parse_author(self._decode(record['Writer']))98 self.parse_author(record['Writer'])
102 self.add_copyright(self._decode(record['Copyright']))99 self.add_copyright(record['Copyright'])
103 lyrics = self._decode(record['Lyrics'])100 lyrics = record['Lyrics']
104 except UnicodeDecodeError as e:101 except UnicodeDecodeError as e:
105 self.log_error(translate('SongsPlugin.ZionWorxImport', 'Record {index}').format(index=index),102 self.log_error(translate('SongsPlugin.ZionWorxImport', 'Record {index}').format(index=index),
106 translate('SongsPlugin.ZionWorxImport', 'Decoding error: {error}').format(error=e))103 translate('SongsPlugin.ZionWorxImport', 'Decoding error: {error}').format(error=e))
@@ -122,10 +119,3 @@
122 if not self.finish():119 if not self.finish():
123 self.log_error(translate('SongsPlugin.ZionWorxImport', 'Record %d') % index +120 self.log_error(translate('SongsPlugin.ZionWorxImport', 'Record %d') % index +
124 (': "' + title + '"' if title else ''))121 (': "' + title + '"' if title else ''))
125
126 def _decode(self, str):
127 """
128 Strips all control characters (except new lines).
129 """
130 # ZionWorx has no option for setting the encoding for its songs, so we assume encoding is always the same.
131 return str.translate(CONTROL_CHARS_MAP)
132122
=== modified file 'openlp/plugins/songs/lib/openlyricsxml.py'
--- openlp/plugins/songs/lib/openlyricsxml.py 2017-10-10 02:29:56 +0000
+++ openlp/plugins/songs/lib/openlyricsxml.py 2017-11-10 23:08:30 +0000
@@ -281,7 +281,7 @@
281 # Process the formatting tags.281 # Process the formatting tags.
282 # Have we any tags in song lyrics?282 # Have we any tags in song lyrics?
283 tags_element = None283 tags_element = None
284 match = re.search('\{/?\w+\}', song.lyrics, re.UNICODE)284 match = re.search(r'\{/?\w+\}', song.lyrics)
285 if match:285 if match:
286 # Named 'format_' - 'format' is built-in function in Python.286 # Named 'format_' - 'format' is built-in function in Python.
287 format_ = etree.SubElement(song_xml, 'format')287 format_ = etree.SubElement(song_xml, 'format')
288288
=== modified file 'openlp/plugins/songusage/forms/songusagedetailform.py'
--- openlp/plugins/songusage/forms/songusagedetailform.py 2017-10-23 22:09:57 +0000
+++ openlp/plugins/songusage/forms/songusagedetailform.py 2017-11-10 23:08:30 +0000
@@ -54,8 +54,14 @@
54 """54 """
55 We need to set up the screen55 We need to set up the screen
56 """56 """
57 self.from_date_calendar.setSelectedDate(Settings().value(self.plugin.settings_section + '/from date'))57 to_date = Settings().value(self.plugin.settings_section + '/to date')
58 self.to_date_calendar.setSelectedDate(Settings().value(self.plugin.settings_section + '/to date'))58 if not (isinstance(to_date, QtCore.QDate) and to_date.isValid()):
59 to_date = QtCore.QDate.currentDate()
60 from_date = Settings().value(self.plugin.settings_section + '/from date')
61 if not (isinstance(from_date, QtCore.QDate) and from_date.isValid()):
62 from_date = to_date.addYears(-1)
63 self.from_date_calendar.setSelectedDate(from_date)
64 self.to_date_calendar.setSelectedDate(to_date)
59 self.report_path_edit.path = Settings().value(self.plugin.settings_section + '/last directory export')65 self.report_path_edit.path = Settings().value(self.plugin.settings_section + '/last directory export')
6066
61 def on_report_path_edit_path_changed(self, file_path):67 def on_report_path_edit_path_changed(self, file_path):
6268
=== modified file 'openlp/plugins/songusage/songusageplugin.py'
--- openlp/plugins/songusage/songusageplugin.py 2017-10-07 07:05:07 +0000
+++ openlp/plugins/songusage/songusageplugin.py 2017-11-10 23:08:30 +0000
@@ -38,20 +38,17 @@
3838
39log = logging.getLogger(__name__)39log = logging.getLogger(__name__)
4040
41YEAR = QtCore.QDate().currentDate().year()41TODAY = QtCore.QDate.currentDate()
42if QtCore.QDate().currentDate().month() < 9:
43 YEAR -= 1
44
4542
46__default_settings__ = {43__default_settings__ = {
47 'songusage/db type': 'sqlite',44 'songusage/db type': 'sqlite',
48 'songusage/db username': '',45 'songusage/db username': '',
49 'songuasge/db password': '',46 'songusage/db password': '',
50 'songuasge/db hostname': '',47 'songusage/db hostname': '',
51 'songuasge/db database': '',48 'songusage/db database': '',
52 'songusage/active': False,49 'songusage/active': False,
53 'songusage/to date': QtCore.QDate(YEAR, 8, 31),50 'songusage/to date': TODAY,
54 'songusage/from date': QtCore.QDate(YEAR - 1, 9, 1),51 'songusage/from date': TODAY.addYears(-1),
55 'songusage/last directory export': None52 'songusage/last directory export': None
56}53}
5754
5855
=== modified file 'tests/functional/openlp_core/common/test_actions.py'
--- tests/functional/openlp_core/common/test_actions.py 2017-10-07 07:05:07 +0000
+++ tests/functional/openlp_core/common/test_actions.py 2017-11-10 23:08:30 +0000
@@ -153,6 +153,7 @@
153 """153 """
154 Prepare the tests154 Prepare the tests
155 """155 """
156 self.setup_application()
156 self.action_list = ActionList.get_instance()157 self.action_list = ActionList.get_instance()
157 self.build_settings()158 self.build_settings()
158 self.settings = Settings()159 self.settings = Settings()
159160
=== modified file 'tests/functional/openlp_core/common/test_httputils.py'
--- tests/functional/openlp_core/common/test_httputils.py 2017-09-25 20:34:05 +0000
+++ tests/functional/openlp_core/common/test_httputils.py 2017-11-10 23:08:30 +0000
@@ -233,7 +233,7 @@
233 Test socket timeout gets caught233 Test socket timeout gets caught
234 """234 """
235 # GIVEN: Mocked urlopen to fake a network disconnect in the middle of a download235 # GIVEN: Mocked urlopen to fake a network disconnect in the middle of a download
236 mocked_requests.get.side_effect = IOError236 mocked_requests.get.side_effect = OSError
237237
238 # WHEN: Attempt to retrieve a file238 # WHEN: Attempt to retrieve a file
239 url_get_file(MagicMock(), url='http://localhost/test', file_path=Path(self.tempfile))239 url_get_file(MagicMock(), url='http://localhost/test', file_path=Path(self.tempfile))
240240
=== modified file 'tests/functional/openlp_core/common/test_i18n.py'
--- tests/functional/openlp_core/common/test_i18n.py 2017-10-07 07:05:07 +0000
+++ tests/functional/openlp_core/common/test_i18n.py 2017-11-10 23:08:30 +0000
@@ -155,7 +155,7 @@
155 assert first_instance is second_instance, 'Two UiStrings objects should be the same instance'155 assert first_instance is second_instance, 'Two UiStrings objects should be the same instance'
156156
157157
158def test_translate(self):158def test_translate():
159 """159 """
160 Test the translate() function160 Test the translate() function
161 """161 """
162162
=== modified file 'tests/functional/openlp_core/common/test_path.py'
--- tests/functional/openlp_core/common/test_path.py 2017-10-07 07:05:07 +0000
+++ tests/functional/openlp_core/common/test_path.py 2017-11-10 23:08:30 +0000
@@ -371,13 +371,13 @@
371 @patch('openlp.core.common.path.log')371 @patch('openlp.core.common.path.log')
372 def test_create_paths_dir_io_error(self, mocked_logger):372 def test_create_paths_dir_io_error(self, mocked_logger):
373 """373 """
374 Test the create_paths() when an IOError is raised374 Test the create_paths() when an OSError is raised
375 """375 """
376 # GIVEN: A `Path` to check with patched out mkdir and exists methods376 # GIVEN: A `Path` to check with patched out mkdir and exists methods
377 mocked_path = MagicMock()377 mocked_path = MagicMock()
378 mocked_path.exists.side_effect = IOError('Cannot make directory')378 mocked_path.exists.side_effect = OSError('Cannot make directory')
379379
380 # WHEN: An IOError is raised when checking the if the path exists.380 # WHEN: An OSError is raised when checking the if the path exists.
381 create_paths(mocked_path)381 create_paths(mocked_path)
382382
383 # THEN: The Error should have been logged383 # THEN: The Error should have been logged
@@ -385,7 +385,7 @@
385385
386 def test_create_paths_dir_value_error(self):386 def test_create_paths_dir_value_error(self):
387 """387 """
388 Test the create_paths() when an error other than IOError is raised388 Test the create_paths() when an error other than OSError is raised
389 """389 """
390 # GIVEN: A `Path` to check with patched out mkdir and exists methods390 # GIVEN: A `Path` to check with patched out mkdir and exists methods
391 mocked_path = MagicMock()391 mocked_path = MagicMock()
392392
=== modified file 'tests/functional/openlp_core/lib/test_lib.py'
--- tests/functional/openlp_core/lib/test_lib.py 2017-10-10 07:08:44 +0000
+++ tests/functional/openlp_core/lib/test_lib.py 2017-11-10 23:08:30 +0000
@@ -168,7 +168,7 @@
168 patch.object(Path, 'open'):168 patch.object(Path, 'open'):
169 file_path = Path('testfile.txt')169 file_path = Path('testfile.txt')
170 file_path.is_file.return_value = True170 file_path.is_file.return_value = True
171 file_path.open.side_effect = IOError()171 file_path.open.side_effect = OSError()
172172
173 # WHEN: get_text_file_string is called173 # WHEN: get_text_file_string is called
174 result = get_text_file_string(file_path)174 result = get_text_file_string(file_path)
175175
=== modified file 'tests/functional/openlp_core/ui/test_first_time.py'
--- tests/functional/openlp_core/ui/test_first_time.py 2017-09-20 16:55:21 +0000
+++ tests/functional/openlp_core/ui/test_first_time.py 2017-11-10 23:08:30 +0000
@@ -40,7 +40,7 @@
40 Test get_web_page will attempt CONNECTION_RETRIES+1 connections - bug 140903140 Test get_web_page will attempt CONNECTION_RETRIES+1 connections - bug 1409031
41 """41 """
42 # GIVEN: Initial settings and mocks42 # GIVEN: Initial settings and mocks
43 mocked_requests.get.side_effect = IOError('Unable to connect')43 mocked_requests.get.side_effect = OSError('Unable to connect')
4444
45 # WHEN: A webpage is requested45 # WHEN: A webpage is requested
46 try:46 try:
4747
=== modified file 'tests/functional/openlp_core/widgets/test_views.py'
--- tests/functional/openlp_core/widgets/test_views.py 2017-10-23 22:09:57 +0000
+++ tests/functional/openlp_core/widgets/test_views.py 2017-11-10 23:08:30 +0000
@@ -627,4 +627,3 @@
627 assert widget.allow_internal_dnd is False627 assert widget.allow_internal_dnd is False
628 assert widget.indentation() == 0628 assert widget.indentation() == 0
629 assert widget.isAnimated() is True629 assert widget.isAnimated() is True
630
631630
=== modified file 'tests/functional/openlp_plugins/presentations/test_presentationcontroller.py'
--- tests/functional/openlp_plugins/presentations/test_presentationcontroller.py 2017-10-07 07:05:07 +0000
+++ tests/functional/openlp_plugins/presentations/test_presentationcontroller.py 2017-11-10 23:08:30 +0000
@@ -144,7 +144,7 @@
144 # GIVEN: A mocked open, get_thumbnail_folder and exists144 # GIVEN: A mocked open, get_thumbnail_folder and exists
145 with patch('openlp.plugins.presentations.lib.presentationcontroller.Path.read_text') as mocked_read_text, \145 with patch('openlp.plugins.presentations.lib.presentationcontroller.Path.read_text') as mocked_read_text, \
146 patch(FOLDER_TO_PATCH) as mocked_get_thumbnail_folder:146 patch(FOLDER_TO_PATCH) as mocked_get_thumbnail_folder:
147 mocked_read_text.side_effect = IOError()147 mocked_read_text.side_effect = OSError()
148 mocked_get_thumbnail_folder.return_value = Path('test')148 mocked_get_thumbnail_folder.return_value = Path('test')
149149
150 # WHEN: calling get_titles_and_notes150 # WHEN: calling get_titles_and_notes
151151
=== modified file 'tests/interfaces/openlp_core/ui/test_projectormanager.py'
--- tests/interfaces/openlp_core/ui/test_projectormanager.py 2017-10-07 07:05:07 +0000
+++ tests/interfaces/openlp_core/ui/test_projectormanager.py 2017-11-10 23:08:30 +0000
@@ -42,8 +42,8 @@
42 """42 """
43 Create the UI and setup necessary options43 Create the UI and setup necessary options
44 """44 """
45 self.setup_application()
45 self.build_settings()46 self.build_settings()
46 self.setup_application()
47 Registry.create()47 Registry.create()
48 with patch('openlp.core.lib.projector.db.init_url') as mocked_init_url:48 with patch('openlp.core.lib.projector.db.init_url') as mocked_init_url:
49 if os.path.exists(TEST_DB):49 if os.path.exists(TEST_DB):
5050
=== modified file 'tests/interfaces/openlp_core/ui/test_projectorsourceform.py'
--- tests/interfaces/openlp_core/ui/test_projectorsourceform.py 2017-10-07 07:05:07 +0000
+++ tests/interfaces/openlp_core/ui/test_projectorsourceform.py 2017-11-10 23:08:30 +0000
@@ -64,8 +64,8 @@
64 Set up anything necessary for all tests64 Set up anything necessary for all tests
65 """65 """
66 mocked_init_url.return_value = 'sqlite:///{}'.format(TEST_DB)66 mocked_init_url.return_value = 'sqlite:///{}'.format(TEST_DB)
67 self.setup_application()
67 self.build_settings()68 self.build_settings()
68 self.setup_application()
69 Registry.create()69 Registry.create()
70 # Do not try to recreate if we've already been created from a previous test70 # Do not try to recreate if we've already been created from a previous test
71 if not hasattr(self, 'projectordb'):71 if not hasattr(self, 'projectordb'):
7272
=== modified file 'tests/interfaces/openlp_core/ui/test_thememanager.py'
--- tests/interfaces/openlp_core/ui/test_thememanager.py 2017-10-10 01:08:09 +0000
+++ tests/interfaces/openlp_core/ui/test_thememanager.py 2017-11-10 23:08:30 +0000
@@ -41,8 +41,8 @@
41 """41 """
42 Create the UI42 Create the UI
43 """43 """
44 self.setup_application()
44 self.build_settings()45 self.build_settings()
45 self.setup_application()
46 Registry.create()46 Registry.create()
47 self.theme_manager = ThemeManager()47 self.theme_manager = ThemeManager()
4848
4949
=== modified file 'tests/utils/__init__.py'
--- tests/utils/__init__.py 2016-12-31 11:01:36 +0000
+++ tests/utils/__init__.py 2017-11-10 23:08:30 +0000
@@ -36,7 +36,7 @@
36 try:36 try:
37 items = json.load(open_file)37 items = json.load(open_file)
38 first_line = items[row]38 first_line = items[row]
39 except IOError:39 except OSError:
40 first_line = ''40 first_line = ''
41 finally:41 finally:
42 open_file.close()42 open_file.close()
4343
=== modified file 'tests/utils/test_pylint.py'
--- tests/utils/test_pylint.py 2016-12-31 11:01:36 +0000
+++ tests/utils/test_pylint.py 2017-11-10 23:08:30 +0000
@@ -58,17 +58,21 @@
58 # GIVEN: Some checks to disable and enable, and the pylint script58 # GIVEN: Some checks to disable and enable, and the pylint script
59 disabled_checks = 'import-error,no-member'59 disabled_checks = 'import-error,no-member'
60 enabled_checks = 'missing-format-argument-key,unused-format-string-argument,bad-format-string'60 enabled_checks = 'missing-format-argument-key,unused-format-string-argument,bad-format-string'
61 if is_win() or 'arch' in platform.dist()[0].lower():61 pylint_kwargs = {
62 pylint_script = 'pylint'62 'return_std': True
63 else:63 }
64 pylint_script = 'pylint3'64 if version < '1.7.0':
65 if is_win() or 'arch' in platform.dist()[0].lower():
66 pylint_kwargs.update({'script': 'pylint'})
67 else:
68 pylint_kwargs.update({'script': 'pylint3'})
6569
66 # WHEN: Running pylint70 # WHEN: Running pylint
67 (pylint_stdout, pylint_stderr) = \71 (pylint_stdout, pylint_stderr) = \
68 lint.py_run('openlp --errors-only --disable={disabled} --enable={enabled} '72 lint.py_run('openlp --errors-only --disable={disabled} --enable={enabled} '
69 '--reports=no --output-format=parseable'.format(disabled=disabled_checks,73 '--reports=no --output-format=parseable'.format(disabled=disabled_checks,
70 enabled=enabled_checks),74 enabled=enabled_checks),
71 return_std=True, script=pylint_script)75 **pylint_kwargs)
72 stdout = pylint_stdout.read()76 stdout = pylint_stdout.read()
73 stderr = pylint_stderr.read()77 stderr = pylint_stderr.read()
74 filtered_stdout = self._filter_tolerated_errors(stdout)78 filtered_stdout = self._filter_tolerated_errors(stdout)