From 3ea37800b7bd049537f2a511c93b9d79e7e64cae Mon Sep 17 00:00:00 2001 From: Ken Roberts Date: Sat, 27 May 2017 11:21:24 -0700 Subject: [PATCH 1/3] Fix db.Manager upgrade/PJlink2 update D --- openlp/core/lib/db.py | 15 ++-- openlp/core/lib/projector/constants.py | 80 +++++++++++++------ openlp/core/lib/projector/db.py | 5 +- openlp/core/lib/projector/pjlink1.py | 42 +++++++--- openlp/core/lib/projector/upgrade.py | 73 +++++++++++++++++ openlp/core/ui/lib/pathedit.py | 8 +- openlp/core/ui/projector/manager.py | 14 ++++ .../presentations/presentationplugin.py | 2 +- .../songusage/forms/songusagedetaildialog.py | 2 +- .../openlp_core_lib/test_projector_pjlink1.py | 15 ---- .../openlp_core_lib/test_projectordb.py | 59 +++++++++++--- tests/resources/projector/data.py | 4 +- 12 files changed, 235 insertions(+), 84 deletions(-) create mode 100644 openlp/core/lib/projector/upgrade.py diff --git a/openlp/core/lib/db.py b/openlp/core/lib/db.py index 3e2187b77..6407f7a78 100644 --- a/openlp/core/lib/db.py +++ b/openlp/core/lib/db.py @@ -144,6 +144,7 @@ def upgrade_db(url, upgrade): :param url: The url of the database to upgrade. :param upgrade: The python module that contains the upgrade instructions. """ + log.debug('Checking upgrades for DB {db}'.format(db=url)) session, metadata = init_db(url) class Metadata(BaseModel): @@ -160,17 +161,15 @@ def upgrade_db(url, upgrade): metadata_table.create(checkfirst=True) mapper(Metadata, metadata_table) version_meta = session.query(Metadata).get('version') - if version_meta is None: - # Tables have just been created - fill the version field with the most recent version - if session.query(Metadata).get('dbversion'): - version = 0 - else: - version = upgrade.__version__ + if version_meta: + version = int(version_meta.value) + else: + # Due to issues with other checks, if the version is not set in the DB then default to 0 + # and let the upgrade function handle the checks + version = 0 version_meta = Metadata.populate(key='version', value=version) session.add(version_meta) session.commit() - else: - version = int(version_meta.value) if version > upgrade.__version__: session.remove() return version, upgrade.__version__ diff --git a/openlp/core/lib/projector/constants.py b/openlp/core/lib/projector/constants.py index 5177aa691..6c9beaf8d 100644 --- a/openlp/core/lib/projector/constants.py +++ b/openlp/core/lib/projector/constants.py @@ -59,33 +59,61 @@ TIMEOUT = 30.0 PJLINK_MAX_PACKET = 136 # NOTE: Change format to account for some commands are both class 1 and 2 PJLINK_VALID_CMD = { - 'ACKN': ['2', ], # UDP Reply to 'SRCH' - 'AVMT': ['1', ], # Shutter option - 'CLSS': ['1', ], # PJLink class support query - 'ERST': ['1', '2'], # Error status option - 'FILT': ['2', ], # Get current filter usage time - 'FREZ': ['2', ], # Set freeze/unfreeze picture being projected - 'INF1': ['1', ], # Manufacturer name query - 'INF2': ['1', ], # Product name query - 'INFO': ['1', ], # Other information query - 'INNM': ['2', ], # Get Video source input terminal name - 'INPT': ['1', ], # Video sources option - 'INST': ['1', ], # Input sources available query - 'IRES': ['2', ], # Get Video source resolution - 'LAMP': ['1', ], # Lamp(s) query (Includes fans) - 'LKUP': ['2', ], # UPD Linkup status notification - 'MVOL': ['2', ], # Set microphone volume - 'NAME': ['1', ], # Projector name query - 'PJLINK': ['1', ], # Initial connection - 'POWR': ['1', ], # Power option - 'RFIL': ['2', ], # Get replacement air filter model number - 'RLMP': ['2', ], # Get lamp replacement model number - 'RRES': ['2', ], # Get projector recommended video resolution - 'SNUM': ['2', ], # Get projector serial number - 'SRCH': ['2', ], # UDP broadcast search for available projectors on local network - 'SVER': ['2', ], # Get projector software version - 'SVOL': ['2', ] # Set speaker volume + 'ACKN': {'version': ['2', ], # UDP Reply to 'SRCH' + 'description': 'Acknowledge a PJLink SRCH command - returns MAC address.'}, + 'AVMT': {'version': ['1', ], + 'description': 'Blank/unblank video and/or mute audio.'}, + 'CLSS': {'version': ['1', ], + 'description': 'Query projector PJLink class support.'}, + 'ERST': {'version': ['1', '2'], + 'description': 'Query error status from projector. ' + 'Returns fan/lamp/temp/cover/filter/other error status.'}, + 'FILT': {'version': ['2', ], # Assume (!) time in hours + 'description': 'Query number of hours on filter.'}, + 'FREZ': {'version': ['2', ], + 'description': 'Freeze or unfreeze current image being projected.'}, + 'INF1': {'version': ['1', ], + 'description': 'Query projector manufacturer name.'}, + 'INF2': {'version': ['1', ], + 'description': 'Query projector product name.'}, + 'INFO': {'version': ['1', ], + 'description': 'Query projector for other information set by manufacturer.'}, + 'INNM': {'version': ['2', ], + 'description': 'Query specified input source name'}, + 'INPT': {'version': ['1', ], + 'description': 'Switch to specified video source.'}, + 'INST': {'version': ['1', ], + 'description': 'Query available input sources.'}, + 'IRES': {'version:': ['2', ], + 'description': 'Query current input resolution.'}, + 'LAMP': {'version': ['1', ], + 'description': 'Query lamp time and on/off status. Multiple lamps supported.'}, + 'LKUP': {'version': ['2', ], + 'description': 'UDP Status notify. Returns MAC address.'}, + 'MVOL': {'version': ['2', ], + 'description': 'Adjust microphone volume by 1 step.'}, + 'NAME': {'version': ['1', ], + 'description': 'Query customer-set projector name.'}, + 'PJLINK': {'version': ['1', ], + 'description': 'Initial connection with authentication/no authentication request.'}, + 'POWR': {'version': ['1', ], + 'description': 'Turn lamp on or off/standby.'}, + 'RFIL': {'version': ['2', ], + 'description': 'Query replacement air filter model number.'}, + 'RLMP': {'version': ['2', ], + 'description': 'Query replacement lamp model number.'}, + 'RRES': {'version': ['2', ], + 'description': 'Query recommended resolution.'}, + 'SNUM': {'version': ['2', ], + 'description': 'Query projector serial number.'}, + 'SRCH': {'version': ['2', ], + 'description': 'UDP broadcast search request for available projectors.'}, + 'SVER': {'version': ['2', ], + 'description': 'Query projector software version number.'}, + 'SVOL': {'version': ['2', ], + 'description': 'Adjust speaker volume by 1 step.'} } + # Error and status codes S_OK = E_OK = 0 # E_OK included since I sometimes forget # Error codes. Start at 200 so we don't duplicate system error codes. diff --git a/openlp/core/lib/projector/db.py b/openlp/core/lib/projector/db.py index 11b84b019..24340406f 100644 --- a/openlp/core/lib/projector/db.py +++ b/openlp/core/lib/projector/db.py @@ -44,6 +44,7 @@ from sqlalchemy.orm import relationship from openlp.core.lib.db import Manager, init_db, init_url from openlp.core.lib.projector.constants import PJLINK_DEFAULT_CODES +from openlp.core.lib.projector import upgrade Base = declarative_base(MetaData()) @@ -243,7 +244,9 @@ class ProjectorDB(Manager): """ def __init__(self, *args, **kwargs): log.debug('ProjectorDB().__init__(args="{arg}", kwargs="{kwarg}")'.format(arg=args, kwarg=kwargs)) - super().__init__(plugin_name='projector', init_schema=self.init_schema) + super(ProjectorDB, self).__init__(plugin_name='projector', + init_schema=self.init_schema, + upgrade_mod=upgrade) log.debug('ProjectorDB() Initialized using db url {db}'.format(db=self.db_url)) log.debug('Session: {session}'.format(session=self.session)) diff --git a/openlp/core/lib/projector/pjlink1.py b/openlp/core/lib/projector/pjlink1.py index 2252c566c..0a3ab8cd4 100644 --- a/openlp/core/lib/projector/pjlink1.py +++ b/openlp/core/lib/projector/pjlink1.py @@ -186,10 +186,15 @@ class PJLink(QtNetwork.QTcpSocket): self.pjlink_name = None self.manufacturer = None self.model = None + self.serial_no = None + self.sw_version = None self.shutter = None self.mute = None self.lamp = None + self.model_lamp = None self.fan = None + self.filter_time = None + self.model_filter = None self.source_available = None self.source = None self.other_info = None @@ -451,14 +456,14 @@ class PJLink(QtNetwork.QTcpSocket): return data_split = data.split('=') try: - (prefix, class_, cmd, data) = (data_split[0][0], data_split[0][1], data_split[0][2:], data_split[1]) + (prefix, version, cmd, data) = (data_split[0][0], data_split[0][1], data_split[0][2:], data_split[1]) except ValueError as e: log.warning('({ip}) get_data(): Invalid packet - expected header + command + data'.format(ip=self.ip)) log.warning('({ip}) get_data(): Received data: "{data}"'.format(ip=self.ip, data=data_in.strip())) self.change_status(E_INVALID_DATA) self.receive_data_signal() return - if not (cmd in PJLINK_VALID_CMD and class_ in PJLINK_VALID_CMD[cmd]): + if cmd not in PJLINK_VALID_CMD: log.warning('({ip}) get_data(): Invalid packet - unknown command "{data}"'.format(ip=self.ip, data=cmd)) self.receive_data_signal() return @@ -507,14 +512,25 @@ class PJLink(QtNetwork.QTcpSocket): log.warning('({ip}) send_command(): Not connected - returning'.format(ip=self.ip)) self.send_queue = [] return + if cmd not in PJLINK_VALID_CMD: + log.error('({ip}) send_command(): Invalid command requested - ignoring.'.format(ip=self.ip)) + return self.projectorNetwork.emit(S_NETWORK_SENDING) log.debug('({ip}) send_command(): Building cmd="{command}" opts="{data}"{salt}'.format(ip=self.ip, command=cmd, data=opts, salt='' if salt is None else ' with hash')) - # TODO: Check for class of command rather than default to projector PJLink class - header = PJLINK_HEADER.format(linkclass=self.pjlink_class) + cmd_ver = PJLINK_VALID_CMD[cmd]['version'] + if self.pjlink_class in cmd_ver: + header = PJLINK_HEADER.format(linkclass=self.pjlink_class) + elif len(cmd_ver) == 1 and (int(cmd_ver[0]) < int(self.pjlink_class)): + # Typically a class 1 only command + header = PJLINK_HEADER.format(linkclass=cmd_ver[0]) + else: + # NOTE: Once we get to version 3 then think about looping + log.error('({ip}): send_command(): PJLink class check issue? aborting'.format(ip=self.ip)) + return out = '{salt}{header}{command} {options}{suffix}'.format(salt="" if salt is None else salt, header=header, command=cmd, @@ -589,10 +605,13 @@ class PJLink(QtNetwork.QTcpSocket): cmd=cmd, data=data)) # Check if we have a future command not available yet - if cmd in self.pjlink_future: - self._not_implemented(cmd) + if cmd not in PJLINK_VALID_CMD: + log.error('({ip}) Unknown command received - ignoring'.format(ip=self.ip)) return - if data in PJLINK_ERRORS: + elif cmd not in self.pjlink_functions: + log.warn('({ip}) Future command received - unable to process yet'.format(ip=self.ip)) + return + elif data in PJLINK_ERRORS: # Oops - projector error log.error('({ip}) Projector returned error "{data}"'.format(ip=self.ip, data=data)) if data.upper() == 'ERRA': @@ -624,14 +643,11 @@ class PJLink(QtNetwork.QTcpSocket): self.send_busy = False self.projectorReceivedData.emit() return - - if cmd in self.pjlink_functions: - log.debug('({ip}) Calling function for {cmd}'.format(ip=self.ip, cmd=cmd)) - self.pjlink_functions[cmd](data) - else: - log.warning('({ip}) Invalid command {data}'.format(ip=self.ip, data=cmd)) + # Command checks already passed + log.debug('({ip}) Calling function for {cmd}'.format(ip=self.ip, cmd=cmd)) self.send_busy = False self.projectorReceivedData.emit() + self.pjlink_functions[cmd](data) def process_lamp(self, data): """ diff --git a/openlp/core/lib/projector/upgrade.py b/openlp/core/lib/projector/upgrade.py new file mode 100644 index 000000000..2fc18ccaa --- /dev/null +++ b/openlp/core/lib/projector/upgrade.py @@ -0,0 +1,73 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2017 OpenLP Developers # +# --------------------------------------------------------------------------- # +# This program is free software; you can redistribute it and/or modify it # +# under the terms of the GNU General Public License as published by the Free # +# Software Foundation; version 2 of the License. # +# # +# This program is distributed in the hope that it will be useful, but WITHOUT # +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # +# more details. # +# # +# You should have received a copy of the GNU General Public License along # +# with this program; if not, write to the Free Software Foundation, Inc., 59 # +# Temple Place, Suite 330, Boston, MA 02111-1307 USA # +############################################################################### +""" +The :mod:`upgrade` module provides a way for the database and schema that is the +backend for the Songs plugin +""" +import logging + +# Not all imports used at this time, but keep for future upgrades +from sqlalchemy import Column, types +from sqlalchemy.sql.expression import null + +from openlp.core.common.db import drop_columns +from openlp.core.lib.db import get_upgrade_op + +log = logging.getLogger(__name__) + +# Initial projector DB was unversioned +__version__ = 2 + +log.debug('Projector DB upgrade module loading') + + +def upgrade_1(session, metadata): + """ + Version 1 upgrade - old db might/might not be versioned. + """ + pass + + +def upgrade_2(session, metadata): + """ + Version 2 upgrade. + + Update Projector() table to include new data defined in PJLink version 2 changes + + serial_no: Column(String(30)) + sw_version: Column(String(30)) + model_filter: Column(String(30)) + model_lamp: Column(String(30)) + + :param session: DB session instance + :param metadata: Metadata of current DB + """ + + new_op = get_upgrade_op(session) + if 'serial_no' not in [t.name for t in metadata.tables.values()]: + log.debug("Upgrading projector DB to version '2'") + new_op.add_column('projector', Column('serial_no', types.String(30), server_default=null())) + new_op.add_column('projector', Column('sw_version', types.String(30), server_default=null())) + new_op.add_column('projector', Column('model_filter', types.String(30), server_default=null())) + new_op.add_column('projector', Column('model_lamp', types.String(30), server_default=null())) + else: + log_warn("Skipping upgrade_2 of projector DB") diff --git a/openlp/core/ui/lib/pathedit.py b/openlp/core/ui/lib/pathedit.py index 238bcb00d..c489daa33 100755 --- a/openlp/core/ui/lib/pathedit.py +++ b/openlp/core/ui/lib/pathedit.py @@ -46,16 +46,16 @@ class PathEdit(QtWidgets.QWidget): :param parent: The parent of the widget. This is just passed to the super method. :type parent: QWidget or None - + :param dialog_caption: Used to customise the caption in the QFileDialog. :param dialog_caption: str - + :param default_path: The default path. This is set as the path when the revert button is clicked :type default_path: str :param show_revert: Used to determin if the 'revert button' should be visible. :type show_revert: bool - + :return: None :rtype: None """ @@ -72,7 +72,7 @@ class PathEdit(QtWidgets.QWidget): Set up the widget :param show_revert: Show or hide the revert button :type show_revert: bool - + :return: None :rtype: None """ diff --git a/openlp/core/ui/projector/manager.py b/openlp/core/ui/projector/manager.py index 747a63a85..e47b3c1f9 100644 --- a/openlp/core/ui/projector/manager.py +++ b/openlp/core/ui/projector/manager.py @@ -662,6 +662,20 @@ class ProjectorManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, UiProjecto message = '%s%s: %s
' % (message, translate('OpenLP.ProjectorManager', 'Current source input is'), projector.link.source) + if projector.link.pjlink_class == '2': + # Information only available for PJLink Class 2 projectors + message += '{title}: {data}

'.format(title=translate('OpenLP.ProjectorManager', + 'Serial Number'), + data=projector.serial_no) + message += '{title}: {data}

'.format(title=translate('OpenLP.ProjectorManager', + 'Software Version'), + data=projector.sw_version) + message += '{title}: {data}

'.format(title=translate('OpenLP.ProjectorManager', + 'Lamp type'), + data=projector.model_lamp) + message += '{title}: {data}

'.format(title=translate('OpenLP.ProjectorManager', + 'Filter type'), + data=projector.model_filter) count = 1 for item in projector.link.lamp: message += '{title} {count} {status} '.format(title=translate('OpenLP.ProjectorManager', diff --git a/openlp/plugins/presentations/presentationplugin.py b/openlp/plugins/presentations/presentationplugin.py index 884f155a2..210f8a531 100644 --- a/openlp/plugins/presentations/presentationplugin.py +++ b/openlp/plugins/presentations/presentationplugin.py @@ -1,4 +1,4 @@ - # -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- # vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 ############################################################################### diff --git a/openlp/plugins/songusage/forms/songusagedetaildialog.py b/openlp/plugins/songusage/forms/songusagedetaildialog.py index 74c8c89c8..082173bf5 100644 --- a/openlp/plugins/songusage/forms/songusagedetaildialog.py +++ b/openlp/plugins/songusage/forms/songusagedetaildialog.py @@ -69,7 +69,7 @@ class Ui_SongUsageDetailDialog(object): self.file_horizontal_layout.setSpacing(8) self.file_horizontal_layout.setContentsMargins(8, 8, 8, 8) self.file_horizontal_layout.setObjectName('file_horizontal_layout') - self.report_path_edit = PathEdit(self.file_group_box, path_type = PathType.Directories, show_revert=False) + self.report_path_edit = PathEdit(self.file_group_box, path_type=PathType.Directories, show_revert=False) self.file_horizontal_layout.addWidget(self.report_path_edit) self.vertical_layout.addWidget(self.file_group_box) self.button_box = create_button_box(song_usage_detail_dialog, 'button_box', ['cancel', 'ok']) diff --git a/tests/functional/openlp_core_lib/test_projector_pjlink1.py b/tests/functional/openlp_core_lib/test_projector_pjlink1.py index cd62da060..e5fb7566f 100644 --- a/tests/functional/openlp_core_lib/test_projector_pjlink1.py +++ b/tests/functional/openlp_core_lib/test_projector_pjlink1.py @@ -384,21 +384,6 @@ class TestPJLink(TestCase): self.assertEquals("{test}".format(test=mock_send_command.call_args), "call(data='{hash}%1CLSS ?\\r')".format(hash=TEST_HASH)) - @patch.object(pjlink_test, '_not_implemented') - def not_implemented_test(self, mock_not_implemented): - """ - Test PJLink._not_implemented method being called - """ - # GIVEN: test object - pjlink = pjlink_test - test_cmd = 'TESTMEONLY' - - # WHEN: A future command is called that is not implemented yet - pjlink.process_command(test_cmd, "Garbage data for test only") - - # THEN: PJLink.__not_implemented should have been called with test_cmd - mock_not_implemented.assert_called_with(test_cmd) - @patch.object(pjlink_test, 'disconnect_from_host') def socket_abort_test(self, mock_disconnect): """ diff --git a/tests/functional/openlp_core_lib/test_projectordb.py b/tests/functional/openlp_core_lib/test_projectordb.py index 6b63ad493..ec72f41bb 100644 --- a/tests/functional/openlp_core_lib/test_projectordb.py +++ b/tests/functional/openlp_core_lib/test_projectordb.py @@ -27,11 +27,15 @@ PREREQUISITE: add_record() and get_all() functions validated. """ import os import shutil -from unittest import TestCase +from tempfile import mkdtemp + +from unittest import TestCase, skip from unittest.mock import MagicMock, patch -from openlp.core.lib.projector.db import Manufacturer, Model, Projector, ProjectorDB, ProjectorSource, Source +from openlp.core.lib.projector import upgrade +from openlp.core.lib.db import upgrade_db from openlp.core.lib.projector.constants import PJLINK_PORT +from openlp.core.lib.projector.db import Manufacturer, Model, Projector, ProjectorDB, ProjectorSource, Source from tests.resources.projector.data import TEST_DB_PJLINK1, TEST_DB, TEST1_DATA, TEST2_DATA, TEST3_DATA from tests.utils.constants import TEST_RESOURCES_PATH @@ -85,6 +89,42 @@ def add_records(projector_db, test): return added +class TestProjectorDBUpdate(TestCase): + """ + Test case for upgrading Projector DB. + NOTE: Separate class so I don't have to look for upgrade tests. + """ + def setUp(self): + """ + Setup for tests + """ + self.tmp_folder = mkdtemp(prefix='openlp_') + + def tearDown(self): + """ + Clean up after tests + """ + # Ignore errors since windows can have problems with locked files + shutil.rmtree(self.tmp_folder, ignore_errors=True) + + def test_upgrade_old_projector_db(self): + """ + Test that we can upgrade an old song db to the current schema + """ + # GIVEN: An old song db + old_db = os.path.join(TEST_RESOURCES_PATH, "projector", TEST_DB_PJLINK1) + tmp_db = os.path.join(self.tmp_folder, TEST_DB) + shutil.copyfile(old_db, tmp_db) + db_url = 'sqlite:///{db}'.format(db=tmp_db) + + # WHEN: upgrading the db + updated_to_version, latest_version = upgrade_db(db_url, upgrade) + + # THEN: the song db should have been upgraded to the latest version + self.assertEqual(updated_to_version, latest_version, + 'The projector DB should have been upgrade to the latest version') + + class TestProjectorDB(TestCase): """ Test case for ProjectorDB @@ -94,7 +134,9 @@ class TestProjectorDB(TestCase): """ Set up anything necessary for all tests """ - mocked_init_url.return_value = 'sqlite:///{db}'.format(db=TEST_DB) + self.tmp_folder = mkdtemp(prefix='openlp_') + tmpdb_url = 'sqlite:///{db}'.format(db=os.path.join(self.tmp_folder, TEST_DB)) + mocked_init_url.return_value = tmpdb_url self.projector = ProjectorDB() def tearDown(self): @@ -103,15 +145,8 @@ class TestProjectorDB(TestCase): """ self.projector.session.close() self.projector = None - retries = 0 - while retries < 5: - try: - if os.path.exists(TEST_DB): - os.unlink(TEST_DB) - break - except: - time.sleep(1) - retries += 1 + # Ignore errors since windows can have problems with locked files + shutil.rmtree(self.tmp_folder, ignore_errors=True) def test_find_record_by_ip(self): """ diff --git a/tests/resources/projector/data.py b/tests/resources/projector/data.py index 358719015..d9baa80d0 100644 --- a/tests/resources/projector/data.py +++ b/tests/resources/projector/data.py @@ -29,7 +29,7 @@ from tempfile import gettempdir # Test data TEST_DB_PJLINK1 = 'projector_pjlink1.sqlite' -TEST_DB = os.path.join(gettempdir(), 'openlp-test-projectordb.sql') +TEST_DB = 'openlp-test-projectordb.sqlite' TEST_SALT = '498e4a67' @@ -39,8 +39,6 @@ TEST_HASH = '5d8409bc1c3fa39749434aa3a5c38682' TEST_CONNECT_AUTHENTICATE = 'PJLink 1 {salt}'.format(salt=TEST_SALT) -TEST_DB = os.path.join(gettempdir(), 'openlp-test-projectordb.sql') - TEST1_DATA = dict(ip='111.111.111.111', port='1111', pin='1111', From 80369c8b0bde26f02973726cae09dfe1ae0827a4 Mon Sep 17 00:00:00 2001 From: Ken Roberts Date: Tue, 30 May 2017 16:26:37 -0700 Subject: [PATCH 2/3] pylint fixes - minor cleanups --- openlp/core/lib/projector/pjlink1.py | 4 ++-- openlp/core/lib/projector/upgrade.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/openlp/core/lib/projector/pjlink1.py b/openlp/core/lib/projector/pjlink1.py index 0a3ab8cd4..dfc261f0a 100644 --- a/openlp/core/lib/projector/pjlink1.py +++ b/openlp/core/lib/projector/pjlink1.py @@ -129,7 +129,7 @@ class PJLink(QtNetwork.QTcpSocket): self.ip = ip self.port = port self.pin = pin - super(PJLink, self).__init__() + super().__init__() self.dbid = None self.location = None self.notes = None @@ -467,7 +467,7 @@ class PJLink(QtNetwork.QTcpSocket): log.warning('({ip}) get_data(): Invalid packet - unknown command "{data}"'.format(ip=self.ip, data=cmd)) self.receive_data_signal() return - if int(self.pjlink_class) < int(class_): + if int(self.pjlink_class) < int(version): log.warn('({ip}) get_data(): Projector returned class reply higher ' 'than projector stated class'.format(ip=self.ip)) return self.process_command(cmd, data) diff --git a/openlp/core/lib/projector/upgrade.py b/openlp/core/lib/projector/upgrade.py index 2fc18ccaa..e4a3849a6 100644 --- a/openlp/core/lib/projector/upgrade.py +++ b/openlp/core/lib/projector/upgrade.py @@ -21,7 +21,7 @@ ############################################################################### """ The :mod:`upgrade` module provides a way for the database and schema that is the -backend for the Songs plugin +backend for the projector setup. """ import logging @@ -70,4 +70,4 @@ def upgrade_2(session, metadata): new_op.add_column('projector', Column('model_filter', types.String(30), server_default=null())) new_op.add_column('projector', Column('model_lamp', types.String(30), server_default=null())) else: - log_warn("Skipping upgrade_2 of projector DB") + log.warn("Skipping upgrade_2 of projector DB") From 0fc49e3df177230c80a066564f95f9c0d1916c49 Mon Sep 17 00:00:00 2001 From: Ken Roberts Date: Thu, 1 Jun 2017 15:35:57 -0700 Subject: [PATCH 3/3] Translations and more minor cleanups --- openlp/core/lib/projector/constants.py | 112 +++++++++++++----- openlp/core/lib/projector/db.py | 10 +- openlp/core/lib/projector/upgrade.py | 1 + .../test_projector_constants.py | 2 +- .../openlp_core_lib/test_projectordb.py | 8 +- 5 files changed, 94 insertions(+), 39 deletions(-) diff --git a/openlp/core/lib/projector/constants.py b/openlp/core/lib/projector/constants.py index 6c9beaf8d..38331f500 100644 --- a/openlp/core/lib/projector/constants.py +++ b/openlp/core/lib/projector/constants.py @@ -57,61 +57,113 @@ LF = chr(0x0A) # \n PJLINK_PORT = 4352 TIMEOUT = 30.0 PJLINK_MAX_PACKET = 136 -# NOTE: Change format to account for some commands are both class 1 and 2 +# NOTE: Changed format to account for some commands are both class 1 and 2 PJLINK_VALID_CMD = { - 'ACKN': {'version': ['2', ], # UDP Reply to 'SRCH' - 'description': 'Acknowledge a PJLink SRCH command - returns MAC address.'}, + 'ACKN': {'version': ['2', ], + 'description': translate('OpenLP.PJLinkConstants', + 'Acknowledge a PJLink SRCH command - returns MAC address.') + }, 'AVMT': {'version': ['1', ], - 'description': 'Blank/unblank video and/or mute audio.'}, + 'description': translate('OpenLP.PJLinkConstants', + 'Blank/unblank video and/or mute audio.') + }, 'CLSS': {'version': ['1', ], - 'description': 'Query projector PJLink class support.'}, + 'description': translate('OpenLP.PJLinkConstants', + 'Query projector PJLink class support.') + }, 'ERST': {'version': ['1', '2'], - 'description': 'Query error status from projector. ' - 'Returns fan/lamp/temp/cover/filter/other error status.'}, - 'FILT': {'version': ['2', ], # Assume (!) time in hours - 'description': 'Query number of hours on filter.'}, + 'description': translate('OpenLP.PJLinkConstants', + 'Query error status from projector. ' + 'Returns fan/lamp/temp/cover/filter/other error status.') + }, + 'FILT': {'version': ['2', ], + 'description': translate('OpenLP.PJLinkConstants', + 'Query number of hours on filter.') + }, 'FREZ': {'version': ['2', ], - 'description': 'Freeze or unfreeze current image being projected.'}, + 'description': translate('OpenLP.PJLinkConstants', + 'Freeze or unfreeze current image being projected.') + }, 'INF1': {'version': ['1', ], - 'description': 'Query projector manufacturer name.'}, + 'description': translate('OpenLP.PJLinkConstants', + 'Query projector manufacturer name.') + }, 'INF2': {'version': ['1', ], - 'description': 'Query projector product name.'}, + 'description': translate('OpenLP.PJLinkConstants', + 'Query projector product name.') + }, 'INFO': {'version': ['1', ], - 'description': 'Query projector for other information set by manufacturer.'}, + 'description': translate('OpenLP.PJLinkConstants', + 'Query projector for other information set by manufacturer.') + }, 'INNM': {'version': ['2', ], - 'description': 'Query specified input source name'}, + 'description': translate('OpenLP.PJLinkConstants', + 'Query specified input source name') + }, 'INPT': {'version': ['1', ], - 'description': 'Switch to specified video source.'}, + 'description': translate('OpenLP.PJLinkConstants', + 'Switch to specified video source.') + }, 'INST': {'version': ['1', ], - 'description': 'Query available input sources.'}, + 'description': translate('OpenLP.PJLinkConstants', + 'Query available input sources.') + }, 'IRES': {'version:': ['2', ], - 'description': 'Query current input resolution.'}, + 'description': translate('OpenLP.PJLinkConstants', + 'Query current input resolution.') + }, 'LAMP': {'version': ['1', ], - 'description': 'Query lamp time and on/off status. Multiple lamps supported.'}, + 'description': translate('OpenLP.PJLinkConstants', + 'Query lamp time and on/off status. Multiple lamps supported.') + }, 'LKUP': {'version': ['2', ], - 'description': 'UDP Status notify. Returns MAC address.'}, + 'description': translate('OpenLP.PJLinkConstants', + 'UDP Status notify. Includes MAC address.') + }, 'MVOL': {'version': ['2', ], - 'description': 'Adjust microphone volume by 1 step.'}, + 'description': translate('OpenLP.PJLinkConstants', + 'Adjust microphone volume by 1 step.') + }, 'NAME': {'version': ['1', ], - 'description': 'Query customer-set projector name.'}, + 'description': translate('OpenLP.PJLinkConstants', + 'Query customer-set projector name.') + }, 'PJLINK': {'version': ['1', ], - 'description': 'Initial connection with authentication/no authentication request.'}, + 'description': translate('OpenLP.PJLinkConstants', + 'Initial connection with authentication/no authentication request.') + }, 'POWR': {'version': ['1', ], - 'description': 'Turn lamp on or off/standby.'}, + 'description': translate('OpenLP.PJLinkConstants', + 'Turn lamp on or off/standby.') + }, 'RFIL': {'version': ['2', ], - 'description': 'Query replacement air filter model number.'}, + 'description': translate('OpenLP.PJLinkConstants', + 'Query replacement air filter model number.') + }, 'RLMP': {'version': ['2', ], - 'description': 'Query replacement lamp model number.'}, + 'description': translate('OpenLP.PJLinkConstants', + 'Query replacement lamp model number.') + }, 'RRES': {'version': ['2', ], - 'description': 'Query recommended resolution.'}, + 'description': translate('OpenLP.PJLinkConstants', + 'Query recommended resolution.') + }, 'SNUM': {'version': ['2', ], - 'description': 'Query projector serial number.'}, + 'description': translate('OpenLP.PJLinkConstants', + 'Query projector serial number.') + }, 'SRCH': {'version': ['2', ], - 'description': 'UDP broadcast search request for available projectors.'}, + 'description': translate('OpenLP.PJLinkConstants', + 'UDP broadcast search request for available projectors.') + }, 'SVER': {'version': ['2', ], - 'description': 'Query projector software version number.'}, + 'description': translate('OpenLP.PJLinkConstants', + 'Query projector software version number.') + }, 'SVOL': {'version': ['2', ], - 'description': 'Adjust speaker volume by 1 step.'} + 'description': translate('OpenLP.PJLinkConstants', + 'Adjust speaker volume by 1 step.') + } } # Error and status codes diff --git a/openlp/core/lib/projector/db.py b/openlp/core/lib/projector/db.py index 24340406f..89b807f21 100644 --- a/openlp/core/lib/projector/db.py +++ b/openlp/core/lib/projector/db.py @@ -167,13 +167,14 @@ class Projector(CommonBase, Base): """ Return basic representation of Source table entry. """ - return '< Projector(id="{data}", ip="{ip}", port="{port}", pin="{pin}", name="{name}", ' \ + return '< Projector(id="{data}", ip="{ip}", port="{port}", mac_adx="{mac}", pin="{pin}", name="{name}", ' \ 'location="{location}", notes="{notes}", pjlink_name="{pjlink_name}", ' \ 'manufacturer="{manufacturer}", model="{model}", serial_no="{serial}", other="{other}", ' \ 'sources="{sources}", source_list="{source_list}", model_filter="{mfilter}", ' \ 'model_lamp="{mlamp}", sw_version="{sw_ver}") >'.format(data=self.id, ip=self.ip, port=self.port, + mac=self.mac_adx, pin=self.pin, name=self.name, location=self.location, @@ -190,6 +191,7 @@ class Projector(CommonBase, Base): sw_ver=self.sw_version) ip = Column(String(100)) port = Column(String(8)) + mac_adx = Column(String(18)) pin = Column(String(20)) name = Column(String(20)) location = Column(String(30)) @@ -244,9 +246,9 @@ class ProjectorDB(Manager): """ def __init__(self, *args, **kwargs): log.debug('ProjectorDB().__init__(args="{arg}", kwargs="{kwarg}")'.format(arg=args, kwarg=kwargs)) - super(ProjectorDB, self).__init__(plugin_name='projector', - init_schema=self.init_schema, - upgrade_mod=upgrade) + super().__init__(plugin_name='projector', + init_schema=self.init_schema, + upgrade_mod=upgrade) log.debug('ProjectorDB() Initialized using db url {db}'.format(db=self.db_url)) log.debug('Session: {session}'.format(session=self.session)) diff --git a/openlp/core/lib/projector/upgrade.py b/openlp/core/lib/projector/upgrade.py index e4a3849a6..4d2f4532e 100644 --- a/openlp/core/lib/projector/upgrade.py +++ b/openlp/core/lib/projector/upgrade.py @@ -65,6 +65,7 @@ def upgrade_2(session, metadata): new_op = get_upgrade_op(session) if 'serial_no' not in [t.name for t in metadata.tables.values()]: log.debug("Upgrading projector DB to version '2'") + new_op.add_column('projector', Column('mac_adx', types.String(18), server_default=null())) new_op.add_column('projector', Column('serial_no', types.String(30), server_default=null())) new_op.add_column('projector', Column('sw_version', types.String(30), server_default=null())) new_op.add_column('projector', Column('model_filter', types.String(30), server_default=null())) diff --git a/tests/functional/openlp_core_lib/test_projector_constants.py b/tests/functional/openlp_core_lib/test_projector_constants.py index 4a8e7a9e1..019c18888 100644 --- a/tests/functional/openlp_core_lib/test_projector_constants.py +++ b/tests/functional/openlp_core_lib/test_projector_constants.py @@ -29,7 +29,7 @@ class TestProjectorConstants(TestCase): """ Test specific functions in the projector constants module. """ - def build_pjlink_video_label_test(self): + def test_build_pjlink_video_label(self): """ Test building PJLINK_DEFAULT_CODES dictionary """ diff --git a/tests/functional/openlp_core_lib/test_projectordb.py b/tests/functional/openlp_core_lib/test_projectordb.py index ec72f41bb..d4ff4e75c 100644 --- a/tests/functional/openlp_core_lib/test_projectordb.py +++ b/tests/functional/openlp_core_lib/test_projectordb.py @@ -306,10 +306,10 @@ class TestProjectorDB(TestCase): # THEN: __repr__ should return a proper string self.assertEqual(str(projector), - '< Projector(id="0", ip="127.0.0.1", port="4352", pin="None", name="Test One", ' - 'location="Somewhere over the rainbow", notes="Not again", pjlink_name="TEST", ' - 'manufacturer="IN YOUR DREAMS", model="OpenLP", serial_no="None", other="None", ' - 'sources="None", source_list="[]", model_filter="None", model_lamp="None", ' + '< Projector(id="0", ip="127.0.0.1", port="4352", mac_adx="None", pin="None", ' + 'name="Test One", location="Somewhere over the rainbow", notes="Not again", ' + 'pjlink_name="TEST", manufacturer="IN YOUR DREAMS", model="OpenLP", serial_no="None", ' + 'other="None", sources="None", source_list="[]", model_filter="None", model_lamp="None", ' 'sw_version="None") >', 'Projector.__repr__() should have returned a proper representation string')