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

Proposed by Phill
Status: Superseded
Proposed branch: lp:~phill-ridout/openlp/fixes-mkII
Merge into: lp:openlp
Diff against target: 1064 lines (+169/-142)
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 (+6/-0)
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 Approve
Review via email: mp+333491@code.launchpad.net

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

This proposal has been superseded by 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)

Add this to your merge proposal:
--------------------------------
lp:~phill-ridout/openlp/fixes-mkII (revision 2792)
[SUCCESS] https://ci.openlp.io/job/Branch-01-Pull/2263/
[SUCCESS] https://ci.openlp.io/job/Branch-02-Functional-Tests/2165/
[SUCCESS] https://ci.openlp.io/job/Branch-03-Interface-Tests/2046/
[SUCCESS] https://ci.openlp.io/job/Branch-04a-Code_Analysis/1387/
[SUCCESS] https://ci.openlp.io/job/Branch-04b-Test_Coverage/1212/
[FAILURE] https://ci.openlp.io/job/Branch-04c-Code_Analysis2/342/
Stopping after failure

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

Looks good.

review: Approve
lp:~phill-ridout/openlp/fixes-mkII updated
2793. By Phill

Merge superflys branch

2794. By Phill

Small modification to upgrade settings as per superflys request

Unmerged revisions

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 20:40:07 +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 20:40:07 +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 20:40:07 +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 20:40:07 +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 20:40:07 +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 20:40:07 +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 20:40:07 +0000
@@ -258,6 +258,12 @@
258 ('media/last directory', 'media/last directory', [(str_to_path, None)])258 ('media/last directory', 'media/last directory', [(str_to_path, None)])
259 ]259 ]
260260
261 __setting_upgrade_3__ = [
262 ('songuasge/db password', 'songusage/db password', []),
263 ('songuasge/db hostname', 'songusage/db hostname', []),
264 ('songuasge/db database', 'songusage/db database', [])
265 ]
266
261 @staticmethod267 @staticmethod
262 def extend_default_settings(default_values):268 def extend_default_settings(default_values):
263 """269 """
264270
=== 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 20:40:07 +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 20:40:07 +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 20:40:07 +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 20:40:07 +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 20:40:07 +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 20:40:07 +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 20:40:07 +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 20:40:07 +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 20:40:07 +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 20:40:07 +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 20:40:07 +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 20:40:07 +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 20:40:07 +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 20:40:07 +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 20:40:07 +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 20:40:07 +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 20:40:07 +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 20:40:07 +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 20:40:07 +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 20:40:07 +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 20:40:07 +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 20:40:07 +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 20:40:07 +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 20:40:07 +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 20:40:07 +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 20:40:07 +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 20:40:07 +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 20:40:07 +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 20:40:07 +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 20:40:07 +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 20:40:07 +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 20:40:07 +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 20:40:07 +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 20:40:07 +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 20:40:07 +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 20:40:07 +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 20:40:07 +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 20:40:07 +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 20:40:07 +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 20:40:07 +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)