Merge branch 'master' of gitlab.com:openlp/openlp into master

This commit is contained in:
Tim 2020-10-20 12:38:31 +01:00
commit dc0081f82a
No known key found for this signature in database
GPG Key ID: 3D454289AF831A6D
17 changed files with 158 additions and 88 deletions

View File

@ -75,8 +75,6 @@ class RemoteVersionWorker(ThreadWorker):
self.no_internet.emit()
if version_info and (QtCore.QVersionNumber.fromString(version_info['latest']['version']) >
QtCore.QVersionNumber.fromString(self.current_version)):
Registry().get('settings').setValue('api/last version test', date.today().strftime('%Y-%m-%d'))
Registry().get('settings_form').api_tab.master_version = version_info['latest']['version']
self.new_version.emit(version_info['latest']['version'])
self.quit.emit()

View File

@ -43,6 +43,17 @@ def controller_live_items():
return jsonify(live_item)
@controller_views.route('/live-item')
def controller_live_item():
log.debug('controller-v2-live-item')
live_controller = Registry().get('live_controller')
current_item = live_controller.service_item
live_item = {}
if current_item:
live_item = current_item.to_dict(True, live_controller.selected_row)
return jsonify(live_item)
@controller_views.route('/show', methods=['POST'])
@login_required
def controller_set():

View File

@ -280,6 +280,7 @@ class DownloadWorker(ThreadWorker):
log.exception('Unable to download %s', url)
self.download_failed.emit()
finally:
time.sleep(1)
self.quit.emit()
@QtCore.pyqtSlot()

View File

@ -358,6 +358,13 @@ class Manager(object):
self.db_url = init_url(plugin_name, str(db_file_path)) # TOdO :PATHLIB
else:
self.db_url = init_url(plugin_name)
if not session:
try:
self.session = init_schema(self.db_url)
except (SQLAlchemyError, DBAPIError):
handle_db_error(plugin_name, db_file_path)
else:
self.session = session
if upgrade_mod:
try:
db_ver, up_ver = upgrade_db(self.db_url, upgrade_mod)
@ -373,13 +380,6 @@ class Manager(object):
db_up=up_ver,
db_name=self.db_url))
return
if not session:
try:
self.session = init_schema(self.db_url)
except (SQLAlchemyError, DBAPIError):
handle_db_error(plugin_name, db_file_path)
else:
self.session = session
def save_object(self, object_instance, commit=True):
"""

View File

@ -870,10 +870,13 @@ class ServiceItem(RegistryProperties):
return os.path.dirname(self.slides[0]['thumbnail'])
return None
def to_dict(self):
def to_dict(self, active=False, item_no=0):
"""
Convert the service item into a dictionary
Images and thumbnails are put in dict as data uri strings.
:param boolean active: Do I filter list for only the active item
:param int item_no: the index of the active item
"""
data_dict = {
'title': self.title,
@ -883,6 +886,7 @@ class ServiceItem(RegistryProperties):
'footer': self.raw_footer,
'audit': self.audit,
'notes': self.notes,
'data': self.data_string or {},
'fromPlugin': self.from_plugin,
'capabilities': self.capabilities,
'backgroundAudio': [str(file_path) for file_path in self.background_audio],
@ -890,45 +894,48 @@ class ServiceItem(RegistryProperties):
'slides': []
}
for index, frame in enumerate(self.get_frames()):
item = {
'tag': index + 1,
'title': self.title,
'selected': False
}
if self.is_text():
if frame['verse']:
item['tag'] = str(frame['verse'])
item['text'] = frame['text']
item['html'] = self.rendered_slides[index]['text']
item['chords'] = self.rendered_slides[index]['chords']
item['footer'] = self.rendered_slides[index]['footer']
elif self.is_image() and not frame.get('image', '') and \
Registry().get('settings_thread').value('api/thumbnails'):
thumbnail_path = os.path.join('images', 'thumbnails', frame['title'])
full_thumbnail_path = AppLocation.get_data_path() / thumbnail_path
if not full_thumbnail_path.exists():
create_thumb(Path(self.get_frame_path(index)), full_thumbnail_path, False)
item['img'] = image_to_data_uri(full_thumbnail_path)
item['text'] = str(frame['title'])
item['html'] = str(frame['title'])
if active and index is not item_no:
continue
else:
# presentations and other things
if self.is_capable(ItemCapabilities.HasDisplayTitle):
item['title'] = str(frame['display_title'])
if self.is_capable(ItemCapabilities.HasNotes):
item['slide_notes'] = str(frame['notes'])
if self.is_capable(ItemCapabilities.HasThumbnails) and \
item = {
'tag': index + 1,
'title': self.title,
'selected': False
}
if self.is_text():
if frame['verse']:
item['tag'] = str(frame['verse'])
item['text'] = frame['text']
item['html'] = self.rendered_slides[index]['text']
item['chords'] = self.rendered_slides[index]['chords']
item['footer'] = self.rendered_slides[index]['footer']
elif self.is_image() and not frame.get('image', '') and \
Registry().get('settings_thread').value('api/thumbnails'):
# If the file is under our app directory tree send the portion after the match
data_path = str(AppLocation.get_data_path())
try:
relative_file = frame['image'].relative_to(data_path)
except ValueError:
log.warning('Service item "{title}" is missing a thumbnail or has an invalid thumbnail path'
.format(title=self.title))
else:
item['img'] = image_to_data_uri(AppLocation.get_data_path() / relative_file)
item['text'] = str(frame['title'])
item['html'] = str(frame['title'])
data_dict['slides'].append(item)
thumbnail_path = os.path.join('images', 'thumbnails', frame['title'])
full_thumbnail_path = AppLocation.get_data_path() / thumbnail_path
if not full_thumbnail_path.exists():
create_thumb(Path(self.get_frame_path(index)), full_thumbnail_path, False)
item['img'] = image_to_data_uri(full_thumbnail_path)
item['text'] = str(frame['title'])
item['html'] = str(frame['title'])
else:
# presentations and other things
if self.is_capable(ItemCapabilities.HasDisplayTitle):
item['title'] = str(frame['display_title'])
if self.is_capable(ItemCapabilities.HasNotes):
item['slide_notes'] = str(frame['notes'])
if self.is_capable(ItemCapabilities.HasThumbnails) and \
Registry().get('settings_thread').value('api/thumbnails'):
# If the file is under our app directory tree send the portion after the match
data_path = str(AppLocation.get_data_path())
try:
relative_file = frame['image'].relative_to(data_path)
except ValueError:
log.warning('Service item "{title}" is missing a thumbnail or has an invalid thumbnail path'
.format(title=self.title))
else:
item['img'] = image_to_data_uri(AppLocation.get_data_path() / relative_file)
item['text'] = str(frame['title'])
item['html'] = str(frame['title'])
data_dict['slides'].append(item)
return data_dict

View File

@ -186,7 +186,6 @@ class FirstTimeForm(QtWidgets.QWizard, UiFirstTimeWizard, RegistryProperties):
self.has_web_access = True
self.application.process_events()
self.downloading = translate('OpenLP.FirstTimeWizard', 'Downloading {name}...')
self.application.set_normal_cursor()
self.is_index_downloaded = True
def _parse_config(self, web_config):
@ -246,7 +245,11 @@ class FirstTimeForm(QtWidgets.QWizard, UiFirstTimeWizard, RegistryProperties):
self.presentation_check_box.setChecked(
self.plugin_manager.get_plugin_by_name('presentations').is_active())
self.image_check_box.setChecked(self.plugin_manager.get_plugin_by_name('images').is_active())
self.media_check_box.setChecked(self.plugin_manager.get_plugin_by_name('media').is_active())
# temp fix for #677 when we have an error
try:
self.media_check_box.setChecked(self.plugin_manager.get_plugin_by_name('media').is_active())
except Exception:
self.media_check_box.setEnabled(False)
self.custom_check_box.setChecked(self.plugin_manager.get_plugin_by_name('custom').is_active())
self.song_usage_check_box.setChecked(self.plugin_manager.get_plugin_by_name('songusage').is_active())
self.alert_check_box.setChecked(self.plugin_manager.get_plugin_by_name('alerts').is_active())
@ -326,6 +329,7 @@ class FirstTimeForm(QtWidgets.QWizard, UiFirstTimeWizard, RegistryProperties):
# Was the thread created.
if self.thumbnail_download_threads:
while any([not is_thread_finished(thread_name) for thread_name in self.thumbnail_download_threads]):
self.application.process_events()
time.sleep(0.1)
self.application.set_normal_cursor()
Registry().remove_function('config_screen_changed', self.screen_selection_widget.load)

View File

@ -23,7 +23,7 @@ This is the main window, where all the action happens.
"""
import os
import shutil
from datetime import datetime
from datetime import datetime, date
from pathlib import Path
from tempfile import gettempdir
@ -626,6 +626,8 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, LogMixin, RegistryPropert
:param version: The Version to be displayed.
"""
Registry().get('settings').setValue('api/last version test', date.today().strftime('%Y-%m-%d'))
Registry().get('settings_form').api_tab.master_version = version
version_text = translate('OpenLP.MainWindow', 'Version {version} of the web remote is now available for '
'download.\nTo download this version, go to the Remote settings and click the Upgrade '
'button.').format(version=version)

View File

@ -27,10 +27,10 @@ import sys
from collections import OrderedDict
from datetime import date
import requests
from PyQt5 import QtCore
from openlp.core.common.applocation import AppLocation
from openlp.core.common.httputils import get_web_page
from openlp.core.common.registry import Registry
from openlp.core.threading import ThreadWorker, run_thread
@ -104,9 +104,9 @@ class VersionWorker(ThreadWorker):
retries = 0
while retries < 3:
try:
response = requests.get(download_url, headers=headers)
if response.status_code == 200:
remote_version = response.text.strip()
response = get_web_page(download_url, headers=headers)
if response:
remote_version = response.strip()
log.debug('New version found: %s', remote_version)
break
except OSError:

View File

@ -64,6 +64,12 @@ class CustomPlugin(Plugin):
'the same way songs are. This plugin provides greater freedom over the songs plugin.')
return about_text
def check_pre_conditions(self):
"""
Check the plugin can run.
"""
return self.db_manager.session is not None
def uses_theme(self, theme):
"""
Called to find out if the custom plugin is currently using a theme.

View File

@ -63,6 +63,12 @@ class ImagePlugin(Plugin):
'provided by the theme.')
return about_text
def check_pre_conditions(self):
"""
Check the plugin can run.
"""
return self.manager.session is not None
def set_plugin_text_strings(self):
"""
Called to define all translatable texts of the plugin.

View File

@ -64,6 +64,12 @@ class MediaPlugin(Plugin):
"""
pass
def check_pre_conditions(self):
"""
Check the plugin can run and the media controller is available.
"""
return State().is_module_active('mediacontroller')
@staticmethod
def about():
"""

View File

@ -616,8 +616,10 @@ class SongMediaItem(MediaManagerItem):
author_list = self.generate_footer(service_item, song)
service_item.data_string = {
'title': song.search_title,
'alternate_title': song.alternate_title,
'authors': ', '.join(author_list),
'ccli_number': song.ccli_number
'ccli_number': song.ccli_number,
'copyright': song.copyright
}
service_item.xml_version = self.open_lyrics.song_to_xml(song)
# Add the audio file to the service item.

View File

@ -24,7 +24,7 @@ from openlp.core.common.registry import Registry
from pathlib import Path
def test_retrieve_live_item(flask_client, settings):
def test_retrieve_live_items(flask_client, settings):
"""
Test the live-item endpoint with a mocked service item
"""

View File

@ -49,6 +49,8 @@ def test_bootstrap_initialise(settings, state):
Test the PluginManager.bootstrap_initialise() method
"""
# GIVEN: A plugin manager with some mocked out methods
State().add_service('mediacontroller', 0)
State().update_pre_conditions('mediacontroller', True)
manager = PluginManager()
with patch.object(manager, 'hook_settings_tabs') as mocked_hook_settings_tabs, \
@ -474,6 +476,25 @@ def test_get_plugin_by_name_exists(registry, state):
assert result == mocked_plugin, 'The result for get_plugin_by_name should be the mocked plugin'
def test_get_plugin_by_name_disabled(registry, state):
"""
Test running the get_plugin_by_name() method to find a plugin that is disabled
"""
# GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Active
mocked_plugin = MagicMock()
mocked_plugin.name = 'Mocked Plugin'
plugin_manager = PluginManager()
Registry().register('mock_plugin', mocked_plugin)
State().add_service("mock", 1, is_plugin=True, status=PluginStatus.Disabled)
State().flush_preconditions()
# WHEN: We run finalise_plugins()
result = plugin_manager.get_plugin_by_name('Mocked Plugin')
# THEN: The is_active() and finalise() methods should have been called
assert result == mocked_plugin, 'The result for get_plugin_by_name should be the mocked plugin'
def test_new_service_created_with_disabled_plugin(registry, state):
"""
Test running the new_service_created() method with a disabled plugin

View File

@ -804,7 +804,8 @@ def test_to_dict_text_item(state_media, settings, service_item_env):
],
'theme': None,
'title': 'Amazing Grace',
'type': 'ServiceItemType.Text'
'type': 'ServiceItemType.Text',
'data': {'authors': 'John Newton', 'title': 'amazing grace@'}
}
assert result == expected_dict
@ -823,7 +824,7 @@ def test_to_dict_image_item(mocked_image_to_data_uri, mocked_get_data_path, stat
FormattingTags.load_tags()
line = convert_file_service_item(TEST_PATH, 'serviceitem_image_2.osj')
mocked_get_data_path.return_value = Path('/path/to/')
mocked_image_to_data_uri.side_effect = lambda x: 'your image uri at: {}'.format(x)
mocked_image_to_data_uri.side_effect = lambda x: 'your image uri at: {}'.format(x.as_posix())
with patch('openlp.core.lib.serviceitem.sha256_file_hash') as mocked_sha256_file_hash:
mocked_sha256_file_hash.return_value = '3a7ccbdb0b5a3db169c4692d7aad0ec8'
@ -854,7 +855,8 @@ def test_to_dict_image_item(mocked_image_to_data_uri, mocked_get_data_path, stat
],
'theme': -1,
'title': 'Images',
'type': 'ServiceItemType.Image'
'type': 'ServiceItemType.Image',
'data': {}
}
assert result == expected_dict
@ -875,7 +877,7 @@ def test_to_dict_presentation_item(mocked_image_uri, mocked_get_data_path, state
image = Path('thumbnails/abcd/slide1.png')
display_title = 'DisplayTitle'
notes = 'Note1\nNote2\n'
mocked_image_uri.side_effect = lambda x: 'your img uri at: {}'.format(x)
mocked_image_uri.side_effect = lambda x: 'your img uri at: {}'.format(x.as_posix())
# WHEN: adding presentation to service_item
with patch('openlp.core.lib.serviceitem.sha256_file_hash') as mocked_sha256_file_hash,\
@ -909,6 +911,7 @@ def test_to_dict_presentation_item(mocked_image_uri, mocked_get_data_path, state
],
'theme': None,
'title': '',
'type': 'ServiceItemType.Command'
'type': 'ServiceItemType.Command',
'data': {}
}
assert result == expected_dict

View File

@ -47,8 +47,8 @@ def test_worker_init():
@patch('openlp.core.version.platform')
@patch('openlp.core.version.requests')
def test_worker_start(mock_requests, mock_platform):
@patch('openlp.core.version.get_web_page')
def test_worker_start(mock_get_web_page, mock_platform):
"""
Test the VersionWorkder.start() method
"""
@ -57,7 +57,7 @@ def test_worker_start(mock_requests, mock_platform):
current_version = {'full': '2.0', 'version': '2.0', 'build': None}
mock_platform.system.return_value = 'Linux'
mock_platform.release.return_value = '4.12.0-1-amd64'
mock_requests.get.return_value = MagicMock(text='2.4.6', status_code=200)
mock_get_web_page.return_value = '2.4.6'
worker = VersionWorker(last_check_date, current_version)
# WHEN: The worker is run
@ -68,14 +68,14 @@ def test_worker_start(mock_requests, mock_platform):
# THEN: The check completes and the signal is emitted
expected_download_url = 'https://get.openlp.org/versions/version.txt'
expected_headers = {'User-Agent': 'OpenLP/2.0 Linux/4.12.0-1-amd64; '}
mock_requests.get.assert_called_once_with(expected_download_url, headers=expected_headers)
mock_get_web_page.assert_called_once_with(expected_download_url, headers=expected_headers)
mock_new_version.emit.assert_called_once_with('2.4.6')
mock_quit.emit.assert_called_once_with()
@patch('openlp.core.version.platform')
@patch('openlp.core.version.requests')
def test_worker_start_fail(mock_requests, mock_platform):
@patch('openlp.core.version.get_web_page')
def test_worker_start_fail(mock_get_web_page, mock_platform):
"""
Test the Version Workder.start() method with no response
"""
@ -84,7 +84,7 @@ def test_worker_start_fail(mock_requests, mock_platform):
current_version = {'full': '2.0', 'version': '2.0', 'build': None}
mock_platform.system.return_value = 'Linux'
mock_platform.release.return_value = '4.12.0-1-amd64'
mock_requests.get.return_value = MagicMock(text='2.4.6', status_code=500)
mock_get_web_page.return_value = None
worker = VersionWorker(last_check_date, current_version)
# WHEN: The worker is run
@ -95,14 +95,14 @@ def test_worker_start_fail(mock_requests, mock_platform):
# THEN: The check completes and the signal is emitted
expected_download_url = 'https://get.openlp.org/versions/version.txt'
expected_headers = {'User-Agent': 'OpenLP/2.0 Linux/4.12.0-1-amd64; '}
mock_requests.get.assert_called_once_with(expected_download_url, headers=expected_headers)
mock_get_web_page.assert_called_once_with(expected_download_url, headers=expected_headers)
mock_new_version.emit.assert_not_called()
mock_quit.emit.assert_called_once_with()
@patch('openlp.core.version.platform')
@patch('openlp.core.version.requests')
def test_worker_start_dev_version(mock_requests, mock_platform):
@patch('openlp.core.version.get_web_page')
def test_worker_start_dev_version(mock_get_web_page, mock_platform):
"""
Test the VersionWorkder.start() method for dev versions
"""
@ -111,7 +111,7 @@ def test_worker_start_dev_version(mock_requests, mock_platform):
current_version = {'full': '2.1.3', 'version': '2.1.3', 'build': None}
mock_platform.system.return_value = 'Linux'
mock_platform.release.return_value = '4.12.0-1-amd64'
mock_requests.get.return_value = MagicMock(text='2.4.6', status_code=200)
mock_get_web_page.return_value = '2.4.6'
worker = VersionWorker(last_check_date, current_version)
# WHEN: The worker is run
@ -122,14 +122,14 @@ def test_worker_start_dev_version(mock_requests, mock_platform):
# THEN: The check completes and the signal is emitted
expected_download_url = 'https://get.openlp.org/versions/dev_version.txt'
expected_headers = {'User-Agent': 'OpenLP/2.1.3 Linux/4.12.0-1-amd64; '}
mock_requests.get.assert_called_once_with(expected_download_url, headers=expected_headers)
mock_get_web_page.assert_called_once_with(expected_download_url, headers=expected_headers)
mock_new_version.emit.assert_called_once_with('2.4.6')
mock_quit.emit.assert_called_once_with()
@patch('openlp.core.version.platform')
@patch('openlp.core.version.requests')
def test_worker_start_nightly_version(mock_requests, mock_platform):
@patch('openlp.core.version.get_web_page')
def test_worker_start_nightly_version(mock_get_web_page, mock_platform):
"""
Test the VersionWorkder.start() method for nightlies
"""
@ -138,7 +138,7 @@ def test_worker_start_nightly_version(mock_requests, mock_platform):
current_version = {'full': '2.1-bzr2345', 'version': '2.1', 'build': '2345'}
mock_platform.system.return_value = 'Linux'
mock_platform.release.return_value = '4.12.0-1-amd64'
mock_requests.get.return_value = MagicMock(text='2.4.6', status_code=200)
mock_get_web_page.return_value = '2.4.6'
worker = VersionWorker(last_check_date, current_version)
# WHEN: The worker is run
@ -149,14 +149,14 @@ def test_worker_start_nightly_version(mock_requests, mock_platform):
# THEN: The check completes and the signal is emitted
expected_download_url = 'https://get.openlp.org/versions/nightly_version.txt'
expected_headers = {'User-Agent': 'OpenLP/2.1-bzr2345 Linux/4.12.0-1-amd64; '}
mock_requests.get.assert_called_once_with(expected_download_url, headers=expected_headers)
mock_get_web_page.assert_called_once_with(expected_download_url, headers=expected_headers)
mock_new_version.emit.assert_called_once_with('2.4.6')
mock_quit.emit.assert_called_once_with()
@patch('openlp.core.version.platform')
@patch('openlp.core.version.requests')
def test_worker_empty_response(mock_requests, mock_platform):
@patch('openlp.core.version.get_web_page')
def test_worker_empty_response(mock_get_web_page, mock_platform):
"""
Test the VersionWorkder.start() method for empty responses
"""
@ -165,7 +165,7 @@ def test_worker_empty_response(mock_requests, mock_platform):
current_version = {'full': '2.1-bzr2345', 'version': '2.1', 'build': '2345'}
mock_platform.system.return_value = 'Linux'
mock_platform.release.return_value = '4.12.0-1-amd64'
mock_requests.get.return_value = MagicMock(text='\n')
mock_get_web_page.return_value = '\n'
worker = VersionWorker(last_check_date, current_version)
# WHEN: The worker is run
@ -176,14 +176,14 @@ def test_worker_empty_response(mock_requests, mock_platform):
# THEN: The check completes and the signal is emitted
expected_download_url = 'https://get.openlp.org/versions/nightly_version.txt'
expected_headers = {'User-Agent': 'OpenLP/2.1-bzr2345 Linux/4.12.0-1-amd64; '}
mock_requests.get.assert_called_once_with(expected_download_url, headers=expected_headers)
mock_get_web_page.assert_called_once_with(expected_download_url, headers=expected_headers)
assert mock_new_version.emit.call_count == 0
mock_quit.emit.assert_called_once_with()
@patch('openlp.core.version.platform')
@patch('openlp.core.version.requests')
def test_worker_start_connection_error(mock_requests, mock_platform):
@patch('openlp.core.version.get_web_page')
def test_worker_start_connection_error(mock_get_web_page, mock_platform):
"""
Test the VersionWorkder.start() method when a ConnectionError happens
"""
@ -192,7 +192,7 @@ def test_worker_start_connection_error(mock_requests, mock_platform):
current_version = {'full': '2.0', 'version': '2.0', 'build': None}
mock_platform.system.return_value = 'Linux'
mock_platform.release.return_value = '4.12.0-1-amd64'
mock_requests.get.side_effect = ConnectionError('Could not connect')
mock_get_web_page.side_effect = ConnectionError('Could not connect')
worker = VersionWorker(last_check_date, current_version)
# WHEN: The worker is run
@ -203,8 +203,8 @@ def test_worker_start_connection_error(mock_requests, mock_platform):
# THEN: The check completes and the signal is emitted
expected_download_url = 'https://get.openlp.org/versions/version.txt'
expected_headers = {'User-Agent': 'OpenLP/2.0 Linux/4.12.0-1-amd64; '}
mock_requests.get.assert_called_with(expected_download_url, headers=expected_headers)
assert mock_requests.get.call_count == 3
mock_get_web_page.assert_called_with(expected_download_url, headers=expected_headers)
assert mock_get_web_page.call_count == 3
mocked_no_internet.emit.assert_called_once_with()
mocked_quit.emit.assert_called_once_with()

View File

@ -23,6 +23,7 @@ Test the media plugin
"""
from unittest.mock import patch
from openlp.core.state import State
from openlp.plugins.media.mediaplugin import MediaPlugin
@ -32,6 +33,8 @@ def test_initialise(mock_initialise, state, settings):
Test that the initialise() method overwrites the built-in one, but still calls it
"""
# GIVEN: A media plugin instance
State().add_service('mediacontroller', 0)
State().update_pre_conditions('mediacontroller', True)
media_plugin = MediaPlugin()
# WHEN: initialise() is called