Merge 2.4 base - fix conflicts

This commit is contained in:
Ken Roberts 2016-06-25 10:32:32 -07:00
commit 319870aa83
24 changed files with 810 additions and 189 deletions

View File

@ -219,7 +219,8 @@ def qmd5_hash(salt, data=None):
log.debug('qmd5_hash(salt="%s"' % salt)
hash_obj = QHash(QHash.Md5)
hash_obj.addData(salt)
hash_obj.addData(data)
if data:
hash_obj.addData(data)
hash_value = hash_obj.result().toHex()
log.debug('qmd5_hash() returning "%s"' % hash_value)
return hash_value.data()

View File

@ -297,7 +297,11 @@ PJLINK_ERST_STATUS = {'0': ERROR_STRING[E_OK],
PJLINK_POWR_STATUS = {'0': S_STANDBY,
'1': S_ON,
'2': S_COOLDOWN,
'3': S_WARMUP}
'3': S_WARMUP,
S_STANDBY: '0',
S_ON: '1',
S_COOLDOWN: '2',
S_WARMUP: '3'}
PJLINK_DEFAULT_SOURCES = {'1': translate('OpenLP.DB', 'RGB'),
'2': translate('OpenLP.DB', 'Video'),

View File

@ -49,7 +49,7 @@ from codecs import decode
from PyQt5.QtCore import pyqtSignal, pyqtSlot
from PyQt5.QtNetwork import QAbstractSocket, QTcpSocket
from openlp.core.common import translate, qmd5_hash
from openlp.core.common import translate, md5_hash
from openlp.core.lib.projector.constants import *
# Shortcuts
@ -58,7 +58,7 @@ SocketSTate = QAbstractSocket.SocketState
PJLINK_PREFIX = '%'
PJLINK_CLASS = '1'
PJLINK_HEADER = '%s%s' % (PJLINK_PREFIX, PJLINK_CLASS)
PJLINK_HEADER = '{prefix}{linkclass}'.format(prefix=PJLINK_PREFIX, linkclass=PJLINK_CLASS)
PJLINK_SUFFIX = CR
@ -91,7 +91,7 @@ class PJLink1(QTcpSocket):
:param poll_time: Time (in seconds) to poll connected projector
:param socket_timeout: Time (in seconds) to abort the connection if no response
"""
log.debug('PJlink(args="%s" kwargs="%s")' % (args, kwargs))
log.debug('PJlink(args={args} kwargs={kwargs})'.format(args=args, kwargs=kwargs))
self.name = name
self.ip = ip
self.port = port
@ -147,7 +147,7 @@ class PJLink1(QTcpSocket):
"""
Reset projector-specific information to default
"""
log.debug('(%s) reset_information() connect status is %s' % (self.ip, self.state()))
log.debug('({ip}) reset_information() connect status is {state}'.format(ip=self.ip, state=self.state()))
self.power = S_OFF
self.pjlink_name = None
self.manufacturer = None
@ -160,8 +160,10 @@ class PJLink1(QTcpSocket):
self.source = None
self.other_info = None
if hasattr(self, 'timer'):
log.debug('({ip}): Calling timer.stop()'.format(ip=self.ip))
self.timer.stop()
if hasattr(self, 'socket_timer'):
log.debug('({ip}): Calling socket_timer.stop()'.format(ip=self.ip))
self.socket_timer.stop()
self.send_queue = []
self.send_busy = False
@ -170,7 +172,7 @@ class PJLink1(QTcpSocket):
"""
Connects signals to methods when thread is started.
"""
log.debug('(%s) Thread starting' % self.ip)
log.debug('({ip}) Thread starting'.format(ip=self.ip))
self.i_am_running = True
self.connected.connect(self.check_login)
self.disconnected.connect(self.disconnect_from_host)
@ -180,7 +182,7 @@ class PJLink1(QTcpSocket):
"""
Cleanups when thread is stopped.
"""
log.debug('(%s) Thread stopped' % self.ip)
log.debug('({ip}) Thread stopped'.format(ip=self.ip))
try:
self.connected.disconnect(self.check_login)
except TypeError:
@ -206,7 +208,7 @@ class PJLink1(QTcpSocket):
Aborts connection and closes socket in case of brain-dead projectors.
Should normally be called by socket_timer().
"""
log.debug('(%s) socket_abort() - Killing connection' % self.ip)
log.debug('({ip}) socket_abort() - Killing connection'.format(ip=self.ip))
self.disconnect_from_host(abort=True)
def poll_loop(self):
@ -216,7 +218,7 @@ class PJLink1(QTcpSocket):
"""
if self.state() != self.ConnectedState:
return
log.debug('(%s) Updating projector status' % self.ip)
log.debug('({ip}) Updating projector status'.format(ip=self.ip))
# Reset timer in case we were called from a set command
if self.timer.interval() < self.poll_time:
# Reset timer to 5 seconds
@ -276,11 +278,17 @@ class PJLink1(QTcpSocket):
self.status_connect = S_CONNECTED
self.projector_status = status
(status_code, status_message) = self._get_status(self.status_connect)
log.debug('(%s) status_connect: %s: %s' % (self.ip, status_code, status_message if msg is None else msg))
log.debug('({ip}) status_connect: {code}: "{message}"'.format(ip=self.ip,
code=status_code,
message=status_message if msg is None else msg))
(status_code, status_message) = self._get_status(self.projector_status)
log.debug('(%s) projector_status: %s: %s' % (self.ip, status_code, status_message if msg is None else msg))
log.debug('({ip}) projector_status: {code}: "{message}"'.format(ip=self.ip,
code=status_code,
message=status_message if msg is None else msg))
(status_code, status_message) = self._get_status(self.error_status)
log.debug('(%s) error_status: %s: %s' % (self.ip, status_code, status_message if msg is None else msg))
log.debug('({ip}) error_status: {code}: "{message}"'.format(ip=self.ip,
code=status_code,
message=status_message if msg is None else msg))
self.changeStatus.emit(self.ip, status, message)
@pyqtSlot()
@ -289,29 +297,31 @@ class PJLink1(QTcpSocket):
Processes the initial connection and authentication (if needed).
Starts poll timer if connection is established.
NOTE: Qt md5 hash function doesn't work with projector authentication. Use the python md5 hash function.
:param data: Optional data if called from another routine
"""
log.debug('(%s) check_login(data="%s")' % (self.ip, data))
log.debug('({ip}) check_login(data="{data}")'.format(ip=self.ip, data=data))
if data is None:
# Reconnected setup?
if not self.waitForReadyRead(2000):
# Possible timeout issue
log.error('(%s) Socket timeout waiting for login' % self.ip)
log.error('({ip}) Socket timeout waiting for login'.format(ip=self.ip))
self.change_status(E_SOCKET_TIMEOUT)
return
read = self.readLine(self.maxSize)
dontcare = self.readLine(self.maxSize) # Clean out the trailing \r\n
if read is None:
log.warn('(%s) read is None - socket error?' % self.ip)
log.warn('({ip}) read is None - socket error?'.format(ip=self.ip))
return
elif len(read) < 8:
log.warn('(%s) Not enough data read)' % self.ip)
log.warn('({ip}) Not enough data read)'.format(ip=self.ip))
return
data = decode(read, 'ascii')
# Possibility of extraneous data on input when reading.
# Clean out extraneous characters in buffer.
dontcare = self.readLine(self.maxSize)
log.debug('(%s) check_login() read "%s"' % (self.ip, data.strip()))
log.debug('({ip}) check_login() read "{data}"'.format(ip=self.ip, data=data.strip()))
# At this point, we should only have the initial login prompt with
# possible authentication
# PJLink initial login will be:
@ -326,26 +336,35 @@ class PJLink1(QTcpSocket):
else:
# Process initial connection
data_check = data.strip().split(' ')
log.debug('(%s) data_check="%s"' % (self.ip, data_check))
log.debug('({ip}) data_check="{data}"'.format(ip=self.ip, data=data_check))
# Check for projector reporting an error
if data_check[1].upper() == 'ERRA':
# Authentication error
self.disconnect_from_host()
self.change_status(E_AUTHENTICATION)
log.debug('(%s) emitting projectorAuthentication() signal' % self.name)
log.debug('({ip}) emitting projectorAuthentication() signal'.format(ip=self.name))
return
elif data_check[1] == '0' and self.pin is not None:
# Pin set and no authentication needed
log.warning('({ip}) Regular connection but PIN set'.format(ip=self.name))
self.disconnect_from_host()
self.change_status(E_AUTHENTICATION)
log.debug('(%s) emitting projectorNoAuthentication() signal' % self.name)
log.debug('({ip}) Emitting projectorNoAuthentication() signal'.format(ip=self.name))
self.projectorNoAuthentication.emit(self.name)
return
elif data_check[1] == '1':
# Authenticated login with salt
log.debug('(%s) Setting hash with salt="%s"' % (self.ip, data_check[2]))
log.debug('(%s) pin="%s"' % (self.ip, self.pin))
salt = qmd5_hash(salt=data_check[2].encode('ascii'), data=self.pin.encode('ascii'))
if self.pin is None:
log.warning('({ip}) Authenticated connection but no pin set'.format(ip=self.name))
self.disconnect_from_host()
self.change_status(E_AUTHENTICATION)
log.debug('({ip}) Emitting projectorAuthentication() signal'.format(ip=self.name))
self.projectorAuthentication.emit(self.name)
return
else:
log.debug('({ip}) Setting hash with salt="{data}"'.format(ip=self.ip, data=data_check[2]))
log.debug('({ip}) pin="{data}"'.format(ip=self.ip, data=self.pin))
salt = md5_hash(salt=data_check[2].encode('ascii'), data=self.pin.encode('ascii'))
else:
salt = None
# We're connected at this point, so go ahead and do regular I/O
@ -355,7 +374,7 @@ class PJLink1(QTcpSocket):
self.send_command(cmd='CLSS', salt=salt)
self.waitForReadyRead()
if (not self.no_poll) and (self.state() == self.ConnectedState):
log.debug('(%s) Starting timer' % self.ip)
log.debug('({ip}) Starting timer'.format(ip=self.ip))
self.timer.setInterval(2000) # Set 2 seconds for initial information
self.timer.start()
@ -364,15 +383,15 @@ class PJLink1(QTcpSocket):
"""
Socket interface to retrieve data.
"""
log.debug('(%s) get_data(): Reading data' % self.ip)
log.debug('({ip}) get_data(): Reading data'.format(ip=self.ip))
if self.state() != self.ConnectedState:
log.debug('(%s) get_data(): Not connected - returning' % self.ip)
log.debug('({ip}) get_data(): Not connected - returning'.format(ip=self.ip))
self.send_busy = False
return
read = self.readLine(self.maxSize)
if read == -1:
# No data available
log.debug('(%s) get_data(): No data available (-1)' % self.ip)
log.debug('({ip}) get_data(): No data available (-1)'.format(ip=self.ip))
self.send_busy = False
self.projectorReceivedData.emit()
return
@ -382,11 +401,11 @@ class PJLink1(QTcpSocket):
data = data_in.strip()
if len(data) < 7:
# Not enough data for a packet
log.debug('(%s) get_data(): Packet length < 7: "%s"' % (self.ip, data))
log.debug('({ip}) get_data(): Packet length < 7: "{data}"'.format(ip=self.ip, data=data))
self.send_busy = False
self.projectorReceivedData.emit()
return
log.debug('(%s) get_data(): Checking new data "%s"' % (self.ip, data))
log.debug('({ip}) get_data(): Checking new data "{data}"'.format(ip=self.ip, data=data))
if data.upper().startswith('PJLINK'):
# Reconnected from remote host disconnect ?
self.check_login(data)
@ -394,7 +413,7 @@ class PJLink1(QTcpSocket):
self.projectorReceivedData.emit()
return
elif '=' not in data:
log.warn('(%s) get_data(): Invalid packet received' % self.ip)
log.warn('({ip}) get_data(): Invalid packet received'.format(ip=self.ip))
self.send_busy = False
self.projectorReceivedData.emit()
return
@ -402,15 +421,15 @@ class PJLink1(QTcpSocket):
try:
(prefix, class_, cmd, data) = (data_split[0][0], data_split[0][1], data_split[0][2:], data_split[1])
except ValueError as e:
log.warn('(%s) get_data(): Invalid packet - expected header + command + data' % self.ip)
log.warn('(%s) get_data(): Received data: "%s"' % (self.ip, read))
log.warn('({ip}) get_data(): Invalid packet - expected header + command + data'.format(ip=self.ip))
log.warn('({ip}) get_data(): Received data: "{data}"'.format(ip=self.ip, data=data_in.strip()))
self.change_status(E_INVALID_DATA)
self.send_busy = False
self.projectorReceivedData.emit()
return
if not (self.pjlink_class in PJLINK_VALID_CMD and cmd in PJLINK_VALID_CMD[self.pjlink_class]):
log.warn('(%s) get_data(): Invalid packet - unknown command "%s"' % (self.ip, cmd))
log.warn('({ip}) get_data(): Invalid packet - unknown command "{data}"'.format(ip=self.ip, data=cmd))
self.send_busy = False
self.projectorReceivedData.emit()
return
@ -424,7 +443,7 @@ class PJLink1(QTcpSocket):
:param err: Error code
"""
log.debug('(%s) get_error(err=%s): %s' % (self.ip, err, self.errorString()))
log.debug('({ip}) get_error(err={error}): {data}'.format(ip=self.ip, error=err, data=self.errorString()))
if err <= 18:
# QSocket errors. Redefined in projector.constants so we don't mistake
# them for system errors
@ -453,7 +472,7 @@ class PJLink1(QTcpSocket):
:param queue: Option to force add to queue rather than sending directly
"""
if self.state() != self.ConnectedState:
log.warn('(%s) send_command(): Not connected - returning' % self.ip)
log.warn('({ip}) send_command(): Not connected - returning'.format(ip=self.ip))
self.send_queue = []
return
self.projectorNetwork.emit(self.ip, S_NETWORK_SENDING)
@ -467,18 +486,19 @@ class PJLink1(QTcpSocket):
out = '%s%s%s %s%s' % (salt, PJLINK_HEADER, cmd, opts, CR)
if out in self.send_queue:
# Already there, so don't add
log.debug('(%s) send_command(out="%s") Already in queue - skipping' % (self.ip, out.strip()))
log.debug('({ip}) send_command(out="{data}") Already in queue - skipping'.format(ip=self.ip,
data=out.strip()))
elif not queue and len(self.send_queue) == 0:
# Nothing waiting to send, so just send it
log.debug('(%s) send_command(out="%s") Sending data' % (self.ip, out.strip()))
log.debug('({ip}) send_command(out="{data}") Sending data'.format(ip=self.ip, data=out.strip()))
return self._send_command(data=out)
else:
log.debug('(%s) send_command(out="%s") adding to queue' % (self.ip, out.strip()))
log.debug('({ip}) send_command(out="{data}") adding to queue'.format(ip=self.ip, data=out.strip()))
self.send_queue.append(out)
self.projectorReceivedData.emit()
log.debug('(%s) send_command(): send_busy is %s' % (self.ip, self.send_busy))
log.debug('({ip}) send_command(): send_busy is {data}'.format(ip=self.ip, data=self.send_busy))
if not self.send_busy:
log.debug('(%s) send_command() calling _send_string()')
log.debug('({ip}) send_command() calling _send_string()'.format(ip=self.ip))
self._send_command()
@pyqtSlot()
@ -488,10 +508,10 @@ class PJLink1(QTcpSocket):
:param data: Immediate data to send
"""
log.debug('(%s) _send_string()' % self.ip)
log.debug('(%s) _send_string(): Connection status: %s' % (self.ip, self.state()))
log.debug('({ip}) _send_string()'.format(ip=self.ip))
log.debug('({ip}) _send_string(): Connection status: {data}'.format(ip=self.ip, data=self.state()))
if self.state() != self.ConnectedState:
log.debug('(%s) _send_string() Not connected - abort' % self.ip)
log.debug('({ip}) _send_string() Not connected - abort'.format(ip=self.ip))
self.send_queue = []
self.send_busy = False
return
@ -500,18 +520,18 @@ class PJLink1(QTcpSocket):
return
if data is not None:
out = data
log.debug('(%s) _send_string(data=%s)' % (self.ip, out.strip()))
log.debug('({ip}) _send_string(data="{data}")'.format(ip=self.ip, data=out.strip()))
elif len(self.send_queue) != 0:
out = self.send_queue.pop(0)
log.debug('(%s) _send_string(queued data=%s)' % (self.ip, out.strip()))
log.debug('({ip}) _send_string(queued data="{data}"%s)'.format(ip=self.ip, data=out.strip()))
else:
# No data to send
log.debug('(%s) _send_string(): No data to send' % self.ip)
log.debug('({ip}) _send_string(): No data to send'.format(ip=self.ip))
self.send_busy = False
return
self.send_busy = True
log.debug('(%s) _send_string(): Sending "%s"' % (self.ip, out.strip()))
log.debug('(%s) _send_string(): Queue = %s' % (self.ip, self.send_queue))
log.debug('({ip}) _send_string(): Sending "{data}"'.format(ip=self.ip, data=out.strip()))
log.debug('({ip}) _send_string(): Queue = {data}'.format(ip=self.ip, data=self.send_queue))
self.socket_timer.start()
try:
self.projectorNetwork.emit(self.ip, S_NETWORK_SENDING)
@ -523,7 +543,9 @@ class PJLink1(QTcpSocket):
translate('OpenLP.PJLink1', 'Error while sending data to projector'))
except SocketError as e:
self.disconnect_from_host(abort=True)
self.changeStatus(E_NETWORK, '%s : %s' % (e.error(), e.errorString()))
self.changeStatus(E_NETWORK,
translate('OpenLP.PJLink1', '{code} : {string}').format(code=e.error(),
string=e.errorString()))
def process_command(self, cmd, data):
"""
@ -532,19 +554,21 @@ class PJLink1(QTcpSocket):
:param cmd: Command to process
:param data: Data being processed
"""
log.debug('(%s) Processing command "%s"' % (self.ip, cmd))
log.debug('({ip}) Processing command "{data}"'.format(ip=self.ip, data=cmd))
if data in PJLINK_ERRORS:
# Oops - projector error
log.error('({ip}) Projector returned error "{data}"'.format(ip=self.ip, data=data))
if data.upper() == 'ERRA':
# Authentication error
self.disconnect_from_host()
self.change_status(E_AUTHENTICATION)
log.debug('(%s) emitting projectorAuthentication() signal' % self.ip)
log.debug('({ip}) emitting projectorAuthentication() signal'.format(ip=self.ip))
self.projectorAuthentication.emit(self.name)
elif data.upper() == 'ERR1':
# Undefined command
self.change_status(E_UNDEFINED, '%s "%s"' %
(translate('OpenLP.PJLink1', 'Undefined command:'), cmd))
self.change_status(E_UNDEFINED, '{error} "{data}"'.format(error=translate('OpenLP.PJLink1',
'Undefined command:'),
data=cmd))
elif data.upper() == 'ERR2':
# Invalid parameter
self.change_status(E_PARAMETER)
@ -559,7 +583,7 @@ class PJLink1(QTcpSocket):
return
# Command succeeded - no extra information
elif data.upper() == 'OK':
log.debug('(%s) Command returned OK' % self.ip)
log.debug('({ip}) Command returned OK'.format(ip=self.ip))
# A command returned successfully, recheck data
self.send_busy = False
self.projectorReceivedData.emit()
@ -568,7 +592,7 @@ class PJLink1(QTcpSocket):
if cmd in self.PJLINK1_FUNC:
self.PJLINK1_FUNC[cmd](data)
else:
log.warn('(%s) Invalid command %s' % (self.ip, cmd))
log.warn('({ip}) Invalid command {data}'.format(ip=self.ip, data=cmd))
self.send_busy = False
self.projectorReceivedData.emit()
@ -587,7 +611,7 @@ class PJLink1(QTcpSocket):
fill = {'Hours': int(data_dict[0]), 'On': False if data_dict[1] == '0' else True}
except ValueError:
# In case of invalid entry
log.warn('(%s) process_lamp(): Invalid data "%s"' % (self.ip, data))
log.warn('({ip}) process_lamp(): Invalid data "{data}"'.format(ip=self.ip, data=data))
return
lamps.append(fill)
data_dict.pop(0) # Remove lamp hours
@ -614,7 +638,7 @@ class PJLink1(QTcpSocket):
self.send_command('INST')
else:
# Log unknown status response
log.warn('Unknown power response: %s' % data)
log.warn('({ip}) Unknown power response: {data}'.format(ip=self.ip, data=data))
return
def process_avmt(self, data):
@ -639,7 +663,7 @@ class PJLink1(QTcpSocket):
shutter = True
mute = True
else:
log.warn('Unknown shutter response: %s' % data)
log.warn('({ip}) Unknown shutter response: {data}'.format(ip=self.ip, data=data))
update_icons = shutter != self.shutter
update_icons = update_icons or mute != self.mute
self.shutter = shutter
@ -656,6 +680,7 @@ class PJLink1(QTcpSocket):
:param data: Currently selected source
"""
self.source = data
log.info('({ip}) Setting data source to "{data}"'.format(ip=self.ip, data=self.source))
return
def process_clss(self, data):
@ -674,7 +699,8 @@ class PJLink1(QTcpSocket):
else:
clss = data
self.pjlink_class = clss
log.debug('(%s) Setting pjlink_class for this projector to "%s"' % (self.ip, self.pjlink_class))
log.debug('({ip}) Setting pjlink_class for this projector to "{data}"'.format(ip=self.ip,
data=self.pjlink_class))
return
def process_name(self, data):
@ -685,6 +711,7 @@ class PJLink1(QTcpSocket):
:param data: Projector name
"""
self.pjlink_name = data
log.debug('({ip}) Setting projector PJLink name to "{data}"'.format(ip=self.ip, data=self.pjlink_name))
return
def process_inf1(self, data):
@ -695,6 +722,7 @@ class PJLink1(QTcpSocket):
:param data: Projector manufacturer
"""
self.manufacturer = data
log.debug('({ip}) Setting projector manufacturer data to "{data}"'.format(ip=self.ip, data=self.manufacturer))
return
def process_inf2(self, data):
@ -705,6 +733,7 @@ class PJLink1(QTcpSocket):
:param data: Model name
"""
self.model = data
log.debug('({ip}) Setting projector model to "{data}"'.format(ip=self.ip, data=self.model))
return
def process_info(self, data):
@ -715,6 +744,7 @@ class PJLink1(QTcpSocket):
:param data: Projector other info
"""
self.other_info = data
log.debug('({ip}) Setting projector other_info to "{data}"'.format(ip=self.ip, data=self.other_info))
return
def process_inst(self, data):
@ -731,6 +761,8 @@ class PJLink1(QTcpSocket):
sources.sort()
self.source_available = sources
self.projectorUpdateIcons.emit()
log.debug('({ip}) Setting projector sources_available to "{data}"'.format(ip=self.ip,
data=self.source_available))
return
def process_erst(self, data):
@ -780,7 +812,7 @@ class PJLink1(QTcpSocket):
Initiate connection to projector.
"""
if self.state() == self.ConnectedState:
log.warn('(%s) connect_to_host(): Already connected - returning' % self.ip)
log.warn('({ip}) connect_to_host(): Already connected - returning'.format(ip=self.ip))
return
self.change_status(S_CONNECTING)
self.connectToHost(self.ip, self.port if type(self.port) is int else int(self.port))
@ -792,9 +824,9 @@ class PJLink1(QTcpSocket):
"""
if abort or self.state() != self.ConnectedState:
if abort:
log.warn('(%s) disconnect_from_host(): Aborting connection' % self.ip)
log.warn('({ip}) disconnect_from_host(): Aborting connection'.format(ip=self.ip))
else:
log.warn('(%s) disconnect_from_host(): Not connected - returning' % self.ip)
log.warn('({ip}) disconnect_from_host(): Not connected - returning'.format(ip=self.ip))
self.reset_information()
self.disconnectFromHost()
try:
@ -804,8 +836,8 @@ class PJLink1(QTcpSocket):
if abort:
self.change_status(E_NOT_CONNECTED)
else:
log.debug('(%s) disconnect_from_host() Current status %s' % (self.ip,
self._get_status(self.status_connect)[0]))
log.debug('({ip}) disconnect_from_host() '
'Current status {data}'.format(ip=self.ip, data=self._get_status(self.status_connect)[0]))
if self.status_connect != E_NOT_CONNECTED:
self.change_status(S_NOT_CONNECTED)
self.reset_information()
@ -815,60 +847,70 @@ class PJLink1(QTcpSocket):
"""
Send command to retrieve available source inputs.
"""
log.debug('({ip}) Sending INST command'.format(ip=self.ip))
return self.send_command(cmd='INST')
def get_error_status(self):
"""
Send command to retrieve currently known errors.
"""
log.debug('({ip}) Sending ERST command'.format(ip=self.ip))
return self.send_command(cmd='ERST')
def get_input_source(self):
"""
Send command to retrieve currently selected source input.
"""
log.debug('({ip}) Sending INPT command'.format(ip=self.ip))
return self.send_command(cmd='INPT')
def get_lamp_status(self):
"""
Send command to return the lap status.
"""
log.debug('({ip}) Sending LAMP command'.format(ip=self.ip))
return self.send_command(cmd='LAMP')
def get_manufacturer(self):
"""
Send command to retrieve manufacturer name.
"""
log.debug('({ip}) Sending INF1 command'.format(ip=self.ip))
return self.send_command(cmd='INF1')
def get_model(self):
"""
Send command to retrieve the model name.
"""
log.debug('({ip}) Sending INF2 command'.format(ip=self.ip))
return self.send_command(cmd='INF2')
def get_name(self):
"""
Send command to retrieve name as set by end-user (if set).
"""
log.debug('({ip}) Sending NAME command'.format(ip=self.ip))
return self.send_command(cmd='NAME')
def get_other_info(self):
"""
Send command to retrieve extra info set by manufacturer.
"""
log.debug('({ip}) Sending INFO command'.format(ip=self.ip))
return self.send_command(cmd='INFO')
def get_power_status(self):
"""
Send command to retrieve power status.
"""
log.debug('({ip}) Sending POWR command'.format(ip=self.ip))
return self.send_command(cmd='POWR')
def get_shutter_status(self):
"""
Send command to retrieve shutter status.
"""
log.debug('({ip}) Sending AVMT command'.format(ip=self.ip))
return self.send_command(cmd='AVMT')
def set_input_source(self, src=None):
@ -878,12 +920,12 @@ class PJLink1(QTcpSocket):
:param src: Video source to select in projector
"""
log.debug('(%s) set_input_source(src=%s)' % (self.ip, src))
log.debug('({ip}) set_input_source(src="{data}")'.format(ip=self.ip, data=src))
if self.source_available is None:
return
elif src not in self.source_available:
return
log.debug('(%s) Setting input source to %s' % (self.ip, src))
log.debug('({ip}) Setting input source to "{data}"'.format(ip=self.ip, data=src))
self.send_command(cmd='INPT', opts=src)
self.poll_loop()
@ -891,6 +933,7 @@ class PJLink1(QTcpSocket):
"""
Send command to turn power to on.
"""
log.debug('({ip}) Setting POWR to 1 (on)'.format(ip=self.ip))
self.send_command(cmd='POWR', opts='1')
self.poll_loop()
@ -898,6 +941,7 @@ class PJLink1(QTcpSocket):
"""
Send command to turn power to standby.
"""
log.debug('({ip}) Setting POWR to 0 (standby)'.format(ip=self.ip))
self.send_command(cmd='POWR', opts='0')
self.poll_loop()
@ -905,6 +949,7 @@ class PJLink1(QTcpSocket):
"""
Send command to set shutter to closed position.
"""
log.debug('({ip}) Setting AVMT to 11 (shutter closed)'.format(ip=self.ip))
self.send_command(cmd='AVMT', opts='11')
self.poll_loop()
@ -912,5 +957,6 @@ class PJLink1(QTcpSocket):
"""
Send command to set shutter to open position.
"""
log.debug('({ip}) Setting AVMT to "10" (shutter open)'.format(ip=self.ip))
self.send_command(cmd='AVMT', opts='10')
self.poll_loop()

View File

@ -59,6 +59,7 @@ class MediaShoutImport(SongImport):
songs = cursor.fetchall()
self.import_wizard.progress_bar.setMaximum(len(songs))
for song in songs:
topics = []
if self.stop_import_flag:
break
cursor.execute('SELECT Type, Number, Text FROM Verses WHERE Record = %s ORDER BY Type, Number'
@ -66,9 +67,10 @@ class MediaShoutImport(SongImport):
verses = cursor.fetchall()
cursor.execute('SELECT Type, Number, POrder FROM PlayOrder WHERE Record = %s ORDER BY POrder' % song.Record)
verse_order = cursor.fetchall()
cursor.execute('SELECT Name FROM Themes INNER JOIN SongThemes ON SongThemes.ThemeId = Themes.ThemeId '
'WHERE SongThemes.Record = %s' % song.Record)
topics = cursor.fetchall()
if cursor.tables(table='TableName', tableType='TABLE').fetchone():
cursor.execute('SELECT Name FROM Themes INNER JOIN SongThemes ON SongThemes.ThemeId = Themes.ThemeId '
'WHERE SongThemes.Record = %s' % song.Record)
topics = cursor.fetchall()
cursor.execute('SELECT Name FROM Groups INNER JOIN SongGroups ON SongGroups.GroupId = Groups.GroupId '
'WHERE SongGroups.Record = %s' % song.Record)
topics += cursor.fetchall()

View File

@ -156,8 +156,8 @@ class OpenSongImport(SongImport):
ustring = str(root.__getattr__(attr))
if isinstance(fn_or_string, str):
if attr in ['ccli']:
ustring = ''.join(re.findall('\d+', ustring))
if ustring:
ustring = ''.join(re.findall('\d+', ustring))
setattr(self, fn_or_string, int(ustring))
else:
setattr(self, fn_or_string, None)

View File

@ -54,7 +54,13 @@ class PresentationManagerImport(SongImport):
# Open file with detected encoding and remove encoding declaration
text = open(file_path, mode='r', encoding=encoding).read()
text = re.sub('.+\?>\n', '', text)
tree = etree.fromstring(text, parser=etree.XMLParser(recover=True))
try:
tree = etree.fromstring(text, parser=etree.XMLParser(recover=True))
except ValueError:
self.log_error(file_path,
translate('SongsPlugin.PresentationManagerImport',
'File is not in XML-format, which is the only format supported.'))
continue
root = objectify.fromstring(etree.tostring(tree))
self.process_song(root)

View File

@ -116,7 +116,13 @@ class SongShowPlusImport(SongImport):
null, verse_name_length, = struct.unpack("BB", song_data.read(2))
verse_name = self.decode(song_data.read(verse_name_length))
length_descriptor_size, = struct.unpack("B", song_data.read(1))
log.debug(length_descriptor_size)
log.debug('length_descriptor_size: %d' % length_descriptor_size)
# In the case of song_numbers the number is in the data from the
# current position to the next block starts
if block_key == SONG_NUMBER:
sn_bytes = song_data.read(length_descriptor_size - 1)
self.song_number = int.from_bytes(sn_bytes, byteorder='little')
continue
# Detect if/how long the length descriptor is
if length_descriptor_size == 12 or length_descriptor_size == 20:
length_descriptor, = struct.unpack("I", song_data.read(4))

View File

@ -73,6 +73,14 @@ class VideoPsalmImport(SongImport):
processed_content += c
c = next(file_content_it)
processed_content += '"' + c
# Remove control characters
elif (c < chr(32)):
processed_content += ' '
# Handle escaped characters
elif c == '\\':
processed_content += c
c = next(file_content_it)
processed_content += c
else:
processed_content += c
songbook = json.loads(processed_content.strip())
@ -117,6 +125,6 @@ class VideoPsalmImport(SongImport):
if not self.finish():
self.log_error('Could not import %s' % self.title)
except Exception as e:
self.log_error(translate('SongsPlugin.VideoPsalmImport', 'File %s' % file.name),
self.log_error(self.import_source,
translate('SongsPlugin.VideoPsalmImport', 'Error: %s') % e)
song_file.close()

View File

@ -4548,7 +4548,7 @@ Dateiendung nicht unterstützt.</translation>
<message>
<location filename="../../openlp/plugins/songs/lib/mediaitem.py" line="109"/>
<source>&amp;Clone</source>
<translation>&amp;Klonen</translation>
<translation>&amp;Duplizieren</translation>
</message>
<message>
<location filename="../../openlp/core/lib/mediamanageritem.py" line="379"/>

View File

@ -1592,7 +1592,8 @@ Complete</source>
<location filename="../../openlp/plugins/bibles/forms/bibleupgradeform.py" line="549"/>
<source>Upgrading Bible(s): %(success)d successful%(failed_text)s
Please note that verses from Web Bibles will be downloaded on demand and so an Internet connection is required.</source>
<translation type="unfinished"/>
<translation>Aktualizowanie Biblii: %(success)d z powodzeniem %(failed_text)s
Należy pamiętać, że wersety z Biblii online będą pobierane na żądnie, więc połączenie z internetem jest wymagane.</translation>
</message>
</context>
<context>
@ -1772,7 +1773,7 @@ Wtyczka Slajdu Tekstowego umożliwia umieszczanie zwykłych ciągów znaków na
<message>
<location filename="../../openlp/plugins/custom/lib/mediaitem.py" line="190"/>
<source>Are you sure you want to delete the &quot;%d&quot; selected custom slide(s)?</source>
<translation type="unfinished"/>
<translation>Czy na pewno chcesz usunąć %n zaznaczonych slajdów niestandardowych?</translation>
</message>
</context>
<context>
@ -1998,7 +1999,7 @@ Czy mimo to chcesz dodać inne?</translation>
<message>
<location filename="../../openlp/core/ui/media/systemplayer.py" line="249"/>
<source>This media player uses your operating system to provide media capabilities.</source>
<translation type="unfinished"/>
<translation>Ten odtwarzacz multimediów wykorzystuje twój system operacyjny dla zapewnienia zdolności multimedialnych</translation>
</message>
</context>
<context>
@ -2426,147 +2427,147 @@ OpenLP jest pisany i wspierany przez wolontariuszy. Jeśli chciałbyś, aby pows
<message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="157"/>
<source>Project Lead</source>
<translation type="unfinished"/>
<translation>Prowadzenie projektu</translation>
</message>
<message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="158"/>
<source>Developers</source>
<translation type="unfinished"/>
<translation>Deweloperzy</translation>
</message>
<message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="159"/>
<source>Contributors</source>
<translation type="unfinished"/>
<translation>Uczestnicy</translation>
</message>
<message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="160"/>
<source>Packagers</source>
<translation type="unfinished"/>
<translation>Paczki</translation>
</message>
<message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="161"/>
<source>Testers</source>
<translation type="unfinished"/>
<translation>Testerzy</translation>
</message>
<message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="162"/>
<source>Translators</source>
<translation type="unfinished"/>
<translation>Tłumacze</translation>
</message>
<message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="163"/>
<source>Afrikaans (af)</source>
<translation type="unfinished"/>
<translation>Afrykanerski (af)</translation>
</message>
<message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="164"/>
<source>Czech (cs)</source>
<translation type="unfinished"/>
<translation>Czeski (cs)</translation>
</message>
<message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="165"/>
<source>Danish (da)</source>
<translation type="unfinished"/>
<translation>Duński (da)</translation>
</message>
<message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="166"/>
<source>German (de)</source>
<translation type="unfinished"/>
<translation>Niemiecki (de)</translation>
</message>
<message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="167"/>
<source>Greek (el)</source>
<translation type="unfinished"/>
<translation>Grecki (el)</translation>
</message>
<message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="168"/>
<source>English, United Kingdom (en_GB)</source>
<translation type="unfinished"/>
<translation>Angielski, UK (en_GB)</translation>
</message>
<message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="169"/>
<source>English, South Africa (en_ZA)</source>
<translation type="unfinished"/>
<translation>Angielski, RPA (en_ZA)</translation>
</message>
<message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="170"/>
<source>Spanish (es)</source>
<translation type="unfinished"/>
<translation>Hiszpański (es)</translation>
</message>
<message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="171"/>
<source>Estonian (et)</source>
<translation type="unfinished"/>
<translation>Estoński (et)</translation>
</message>
<message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="172"/>
<source>Finnish (fi)</source>
<translation type="unfinished"/>
<translation>Fiński (fi)</translation>
</message>
<message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="173"/>
<source>French (fr)</source>
<translation type="unfinished"/>
<translation>Francuski (fr)</translation>
</message>
<message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="174"/>
<source>Hungarian (hu)</source>
<translation type="unfinished"/>
<translation>Węgierski (hu)</translation>
</message>
<message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="175"/>
<source>Indonesian (id)</source>
<translation type="unfinished"/>
<translation>Indonezyjski (id)</translation>
</message>
<message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="176"/>
<source>Japanese (ja)</source>
<translation type="unfinished"/>
<translation>Japoński (ja)</translation>
</message>
<message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="177"/>
<source>Norwegian Bokmål (nb)</source>
<translation type="unfinished"/>
<translation>Norweski (nb)</translation>
</message>
<message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="178"/>
<source>Dutch (nl)</source>
<translation type="unfinished"/>
<translation>Holenderski (nl)</translation>
</message>
<message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="179"/>
<source>Polish (pl)</source>
<translation type="unfinished"/>
<translation>Polski (pl)</translation>
</message>
<message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="180"/>
<source>Portuguese, Brazil (pt_BR)</source>
<translation type="unfinished"/>
<translation>Portugalski (pt_BR)</translation>
</message>
<message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="181"/>
<source>Russian (ru)</source>
<translation type="unfinished"/>
<translation>Rosyjski (ru)</translation>
</message>
<message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="182"/>
<source>Swedish (sv)</source>
<translation type="unfinished"/>
<translation>Szwedzki (sv)</translation>
</message>
<message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="183"/>
<source>Tamil(Sri-Lanka) (ta_LK)</source>
<translation type="unfinished"/>
<translation>Tamilski (Sri-Lanka) (ta_LK)</translation>
</message>
<message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="184"/>
<source>Chinese(China) (zh_CN)</source>
<translation type="unfinished"/>
<translation>Chiński(Chiny) (zh_CN)</translation>
</message>
<message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="185"/>
<source>Documentation</source>
<translation type="unfinished"/>
<translation>Dokumentacja</translation>
</message>
<message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="186"/>
@ -2577,7 +2578,12 @@ OpenLP jest pisany i wspierany przez wolontariuszy. Jeśli chciałbyś, aby pows
Oxygen Icons: http://techbase.kde.org/Projects/Oxygen/
MuPDF: http://www.mupdf.com/
</source>
<translation type="unfinished"/>
<translation>Stworzono przy użyciu:
Python: http://www.python.org/
Qt5: http://qt.io
PyQt5: http://www.riverbankcomputing.co.uk/software/pyqt/intro
Oxygen Icons: http://techbase.kde.org/Projects/Oxygen/
MuPDF: http://www.mupdf.com/</translation>
</message>
<message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="192"/>
@ -2592,13 +2598,19 @@ OpenLP jest pisany i wspierany przez wolontariuszy. Jeśli chciałbyś, aby pows
on the cross, setting us free from sin. We
bring this software to you for free because
He has set us free.</source>
<translation type="unfinished"/>
<translation>Końcowe podziękowanie:
&quot;Dla Boga, który tak umiłował świat, że Syna swego, jednorodzonego dał, aby każdy, kto w niego wierzy, nie zginął, ale miał życie wieczne&quot; J 3,16
Na koniec ale nie mniej ważnie, końcowe podziękowania dla Boga, naszego Ojca, za zesłanie nam swego Syna, by umarł za nas na krzyżu i uczynił nas wolnymi od grzechu.
Dostarczamy to oprogramowanie za darmo, ponieważ On uczynił nas wolnymi.
</translation>
</message>
<message>
<location filename="../../openlp/core/ui/aboutdialog.py" line="303"/>
<source>Copyright © 2004-2016 %s
Portions copyright © 2004-2016 %s</source>
<translation type="unfinished"/>
<translation>Copyright © 2004-2016 %s
Portions copyright © 2004-2016 %s</translation>
</message>
</context>
<context>
@ -3221,12 +3233,12 @@ Katalog z danymi zostanie przywrucony jak OpenLP zostanie zamknięty.</translati
<location filename="../../openlp/core/ui/exceptiondialog.py" line="92"/>
<source>Please enter a description of what you were doing to cause this error. If possible, write in English.
(Minimum 20 characters)</source>
<translation type="unfinished"/>
<translation>Proszę opisać czynności wykonywane bezpośrednio przed wystąpieniem błędu. Jeśli to możliwe, napisz w języku angielskim (min. 20 znaków).</translation>
</message>
<message>
<location filename="../../openlp/core/ui/exceptiondialog.py" line="96"/>
<source>Oops! OpenLP hit a problem, and couldn&apos;t recover. The text in the box below contains information that might be helpful to the OpenLP developers, so please e-mail it to bugs@openlp.org, along with a detailed description of what you were doing when the problem occurred. Also attach any files that triggered the problem.</source>
<translation type="unfinished"/>
<translation>Oops! OpenLP napotkał problem i już nie wstał... Poniższe pole tekstowe zawiera informacje, które mogą być pomocne dla programistów OpenLP. Proszę wysłać e-mail na adres bugs@openlp.org wraz z dokładnym opisem czynności jakie były wykonywane w momencie wystąpienia błędu.</translation>
</message>
</context>
<context>
@ -3629,7 +3641,7 @@ By anulować kreatora pierwszego uruchomienia całkowicie (i nie uruchamiać Ope
<message>
<location filename="../../openlp/core/ui/formattingtagcontroller.py" line="168"/>
<source>End tag %(end)s does not match end tag for start tag %(start)s</source>
<translation type="unfinished"/>
<translation>Końcowy znacznik %s nie pasuje do początkowego %s</translation>
</message>
</context>
<context>
@ -4409,47 +4421,47 @@ Proces został zatrzymany i nie wykonano żadnych zmian.</translation>
<message>
<location filename="../../openlp/core/ui/mainwindow.py" line="393"/>
<source>&amp;Recent Services</source>
<translation type="unfinished"/>
<translation>Ostatnio &amp;używane</translation>
</message>
<message>
<location filename="../../openlp/core/ui/mainwindow.py" line="404"/>
<source>&amp;New Service</source>
<translation type="unfinished"/>
<translation>Nowy plan nabożeństwa</translation>
</message>
<message>
<location filename="../../openlp/core/ui/mainwindow.py" line="407"/>
<source>&amp;Open Service</source>
<translation type="unfinished"/>
<translation>Otwórz plan nabożeństwa.</translation>
</message>
<message>
<location filename="../../openlp/core/ui/mainwindow.py" line="410"/>
<source>&amp;Save Service</source>
<translation type="unfinished"/>
<translation>Zapisz plan nabożeństwa</translation>
</message>
<message>
<location filename="../../openlp/core/ui/mainwindow.py" line="413"/>
<source>Save Service &amp;As...</source>
<translation type="unfinished"/>
<translation>Zapisz plan nabożeństwa jako</translation>
</message>
<message>
<location filename="../../openlp/core/ui/mainwindow.py" line="460"/>
<source>&amp;Manage Plugins</source>
<translation type="unfinished"/>
<translation>Zarządzaj profilami</translation>
</message>
<message>
<location filename="../../openlp/core/ui/mainwindow.py" line="1101"/>
<source>Exit OpenLP</source>
<translation type="unfinished"/>
<translation>Wyjdź z OpenLP</translation>
</message>
<message>
<location filename="../../openlp/core/ui/mainwindow.py" line="1101"/>
<source>Are you sure you want to exit OpenLP?</source>
<translation type="unfinished"/>
<translation>Czy na pewno chcesz zamknąć OpenLP?</translation>
</message>
<message>
<location filename="../../openlp/core/ui/mainwindow.py" line="1108"/>
<source>&amp;Exit OpenLP</source>
<translation type="unfinished"/>
<translation>Wyjdź z OpenLP</translation>
</message>
</context>
<context>
@ -4620,7 +4632,7 @@ Nieobsługiwany przyrostek.</translation>
<location filename="../../openlp/core/ui/media/playertab.py" line="256"/>
<source>NOTE: To use VLC you must install the %s version</source>
<comment>Will insert &quot;32bit&quot; or &quot;64bit&quot;</comment>
<translation type="unfinished"/>
<translation>Uwaga: By używać VLC musisz zainstalować %s wersję</translation>
</message>
</context>
<context>
@ -4663,7 +4675,7 @@ Nieobsługiwany przyrostek.</translation>
<message>
<location filename="../../openlp/core/ui/plugindialog.py" line="81"/>
<source>Manage Plugins</source>
<translation type="unfinished"/>
<translation>Zarządzaj wtyczkami </translation>
</message>
</context>
<context>
@ -5291,7 +5303,7 @@ Nieobsługiwany przyrostek.</translation>
<message>
<location filename="../../openlp/core/ui/projector/manager.py" line="653"/>
<source>Shutter is</source>
<translation type="unfinished"/>
<translation>Przesłona</translation>
</message>
<message>
<location filename="../../openlp/core/ui/projector/manager.py" line="653"/>
@ -5351,12 +5363,12 @@ Nieobsługiwany przyrostek.</translation>
<message>
<location filename="../../openlp/core/ui/projector/manager.py" line="476"/>
<source>Delete projector (%s) %s?</source>
<translation type="unfinished"/>
<translation>Usunąć projektor (%s) %s?</translation>
</message>
<message>
<location filename="../../openlp/core/ui/projector/manager.py" line="478"/>
<source>Are you sure you want to delete this projector?</source>
<translation type="unfinished"/>
<translation>Czy napewno chcesz usunąć ten projektor?</translation>
</message>
</context>
<context>
@ -5379,7 +5391,7 @@ Nieobsługiwany przyrostek.</translation>
<message>
<location filename="../../openlp/core/lib/projector/pjlink1.py" line="767"/>
<source>Cover</source>
<translation type="unfinished"/>
<translation>Osłona</translation>
</message>
<message>
<location filename="../../openlp/core/lib/projector/pjlink1.py" line="771"/>
@ -6456,14 +6468,15 @@ Zostaną one usunięte, jeśli będziesz kontynuował/</translation>
<message>
<location filename="../../openlp/core/ui/thememanager.py" line="768"/>
<source>Unable to delete theme</source>
<translation type="unfinished"/>
<translation>Nie można usunąć motywu</translation>
</message>
<message>
<location filename="../../openlp/core/ui/thememanager.py" line="768"/>
<source>Theme is currently used
%s</source>
<translation type="unfinished"/>
<translation>Motyw, który jest obecnie używany
%s</translation>
</message>
</context>
<context>
@ -7510,19 +7523,19 @@ Proszę zaznaczyć go ręcznie.</translation>
<message>
<location filename="../../openlp/core/common/uistrings.py" line="125"/>
<source>Replace live background is not available when the WebKit player is disabled.</source>
<translation type="unfinished"/>
<translation>Zastępowanie tła live jest niedostępne gdy odtwarzacz WebKit jest zablokowany</translation>
</message>
<message>
<location filename="../../openlp/plugins/songs/lib/ui.py" line="38"/>
<source>Songbook</source>
<comment>Singular</comment>
<translation type="unfinished"/>
<translation>Śpiewnik</translation>
</message>
<message>
<location filename="../../openlp/plugins/songs/lib/ui.py" line="39"/>
<source>Songbooks</source>
<comment>Plural</comment>
<translation type="unfinished"/>
<translation>Śpiewniki</translation>
</message>
</context>
<context>
@ -7970,12 +7983,12 @@ Proszę zaznaczyć go ręcznie.</translation>
<message>
<location filename="../../openlp/plugins/remotes/lib/remotetab.py" line="198"/>
<source>iOS App</source>
<translation type="unfinished"/>
<translation>Aplikacja iOS</translation>
</message>
<message>
<location filename="../../openlp/plugins/remotes/lib/remotetab.py" line="199"/>
<source>Scan the QR code or click &lt;a href=&quot;%s&quot;&gt;download&lt;/a&gt; to install the iOS app from the App Store.</source>
<translation type="unfinished"/>
<translation>Zeskanuj kod QR lub kliknij &lt;a href=&quot;%s&quot;&gt;pobierz&lt;/a&gt;, aby zainstalować aplikację z App Store.</translation>
</message>
</context>
<context>
@ -8712,42 +8725,43 @@ Proszę, wpisz zwrotki oddzielając je spacją.</translation>
<location filename="../../openlp/plugins/songs/forms/editsongform.py" line="189"/>
<source>There are no verses corresponding to &quot;%(invalid)s&quot;. Valid entries are %(valid)s.
Please enter the verses separated by spaces.</source>
<translation type="unfinished"/>
<translation>Nie ma zwrotek o oznaczeniu &quot;%(invalid)s&quot;. Właściwymi oznaczeniami %(valid)s.
Proszę, wpisz zwrotki oddzielając je spacją.</translation>
</message>
<message>
<location filename="../../openlp/plugins/songs/forms/editsongdialog.py" line="317"/>
<source>&amp;Manage Authors, Topics, Songbooks</source>
<translation type="unfinished"/>
<translation>Zarządzaj autorami, &amp;tematami, śpiewnikami</translation>
</message>
<message>
<location filename="../../openlp/plugins/songs/forms/editsongdialog.py" line="322"/>
<source>Add &amp;to Song</source>
<translation type="unfinished"/>
<translation>Dodaj do &amp;pieśni</translation>
</message>
<message>
<location filename="../../openlp/plugins/songs/forms/editsongdialog.py" line="323"/>
<source>Re&amp;move</source>
<translation type="unfinished"/>
<translation>Usuń</translation>
</message>
<message>
<location filename="../../openlp/plugins/songs/forms/editsongdialog.py" line="324"/>
<source>Authors, Topics &amp;&amp; Songbooks</source>
<translation type="unfinished"/>
<translation>Autorzy, tematy i śpiewniki</translation>
</message>
<message>
<location filename="../../openlp/plugins/songs/forms/editsongform.py" line="683"/>
<source>Add Songbook</source>
<translation type="unfinished"/>
<translation>Dodaj Śpiewnik</translation>
</message>
<message>
<location filename="../../openlp/plugins/songs/forms/editsongform.py" line="683"/>
<source>This Songbook does not exist, do you want to add it?</source>
<translation type="unfinished"/>
<translation>Ten śpiewnik nie istnieje, czy chcesz go dodać?</translation>
</message>
<message>
<location filename="../../openlp/plugins/songs/forms/editsongform.py" line="700"/>
<source>This Songbook is already in the list.</source>
<translation type="unfinished"/>
<translation>Ten Śpiewnik już jest na liście</translation>
</message>
<message>
<location filename="../../openlp/plugins/songs/forms/editsongform.py" line="707"/>
@ -8764,7 +8778,9 @@ Please enter the verses separated by spaces.</source>
<source>Unable to find the following file:
%s
Do you want to remove the entry from the song?</source>
<translation type="unfinished"/>
<translation>Nie można znaleźć pliku:
%s
Czy chcesz usunąć wpis z pieśni?</translation>
</message>
</context>
<context>
@ -9094,37 +9110,37 @@ Do you want to remove the entry from the song?</source>
<message>
<location filename="../../openlp/plugins/songs/lib/importer.py" line="194"/>
<source>OpenLyrics or OpenLP 2 Exported Song</source>
<translation type="unfinished"/>
<translation>OpenLyrics lub wyeksportowane pieśni OpenLP 2.0</translation>
</message>
<message>
<location filename="../../openlp/plugins/songs/lib/importer.py" line="201"/>
<source>OpenLP 2 Databases</source>
<translation type="unfinished"/>
<translation>Bazy danych OpenLP 2.0</translation>
</message>
<message>
<location filename="../../openlp/plugins/songs/lib/importer.py" line="255"/>
<source>LyriX Files</source>
<translation type="unfinished"/>
<translation>Pliki LyriX</translation>
</message>
<message>
<location filename="../../openlp/plugins/songs/lib/importer.py" line="256"/>
<source>LyriX (Exported TXT-files)</source>
<translation type="unfinished"/>
<translation>LyriX (Eksportowane pliki txt)</translation>
</message>
<message>
<location filename="../../openlp/plugins/songs/lib/importer.py" line="343"/>
<source>VideoPsalm Files</source>
<translation type="unfinished"/>
<translation>pliki VideoPsalm</translation>
</message>
<message>
<location filename="../../openlp/plugins/songs/lib/importer.py" line="344"/>
<source>VideoPsalm</source>
<translation type="unfinished"/>
<translation>VideoPsalm</translation>
</message>
<message>
<location filename="../../openlp/plugins/songs/lib/importer.py" line="345"/>
<source>The VideoPsalm songbooks are normally located in %s</source>
<translation type="unfinished"/>
<translation>Śpiewniki VideoPsalm normalnie zlokalizowane w %s</translation>
</message>
</context>
<context>
@ -9132,7 +9148,7 @@ Do you want to remove the entry from the song?</source>
<message>
<location filename="../../openlp/plugins/songs/lib/importers/lyrix.py" line="109"/>
<source>Error: %s</source>
<translation type="unfinished"/>
<translation>Błąd: %s</translation>
</message>
</context>
<context>
@ -9204,12 +9220,12 @@ Do you want to remove the entry from the song?</source>
<message>
<location filename="../../openlp/plugins/songs/lib/mediaitem.py" line="364"/>
<source>Are you sure you want to delete the &quot;%d&quot; selected song(s)?</source>
<translation type="unfinished"/>
<translation>Czy na pewno chcesz usunąć &quot;%d&quot; wybranych pieśn(i)?</translation>
</message>
<message>
<location filename="../../openlp/plugins/songs/lib/mediaitem.py" line="142"/>
<source>Search Songbooks...</source>
<translation type="unfinished"/>
<translation>Szukaj Śpiewników</translation>
</message>
</context>
<context>
@ -9225,7 +9241,7 @@ Do you want to remove the entry from the song?</source>
<message>
<location filename="../../openlp/plugins/songs/lib/importers/openlp.py" line="102"/>
<source>Not a valid OpenLP 2 song database.</source>
<translation type="unfinished"/>
<translation>Niewłaściwa baza pieśni Openlp 2.0.</translation>
</message>
</context>
<context>
@ -9297,7 +9313,7 @@ Do you want to remove the entry from the song?</source>
<message>
<location filename="../../openlp/plugins/songs/forms/songbookdialog.py" line="66"/>
<source>Songbook Maintenance</source>
<translation type="unfinished"/>
<translation>Zarządzanie pieśniami</translation>
</message>
</context>
<context>
@ -9605,7 +9621,7 @@ Zapisywanie nazwy użytkownika i hasła jest NIEBEZPIECZNE, twoje hasło jest za
<message>
<location filename="../../openlp/plugins/songs/forms/songselectdialog.py" line="244"/>
<source>Stop</source>
<translation type="unfinished"/>
<translation>Stop</translation>
</message>
</context>
<context>
@ -9702,7 +9718,7 @@ Zapisywanie nazwy użytkownika i hasła jest NIEBEZPIECZNE, twoje hasło jest za
<message>
<location filename="../../openlp/plugins/songs/lib/importers/videopsalm.py" line="121"/>
<source>Error: %s</source>
<translation type="unfinished"/>
<translation>Błąd: %s</translation>
</message>
</context>
<context>
@ -9710,7 +9726,7 @@ Zapisywanie nazwy użytkownika i hasła jest NIEBEZPIECZNE, twoje hasło jest za
<message>
<location filename="../../openlp/plugins/songs/lib/importers/wordsofworship.py" line="109"/>
<source>Invalid Words of Worship song file. Missing &quot;%s&quot; header.WoW File\nSong Words</source>
<translation type="unfinished"/>
<translation>Niewłaściwy plik pieśni Words of Worship. Brakujący &quot;%s&quot; nagłówek. WoW File\nSong Words </translation>
</message>
<message>
<location filename="../../openlp/plugins/songs/lib/importers/wordsofworship.py" line="119"/>

View File

@ -2329,7 +2329,7 @@ Deseja continuar adicionando as outras imagens mesmo assim?</translation>
<message>
<location filename="../../openlp/core/ui/mainwindow.py" line="435"/>
<source>&amp;Projector Manager</source>
<translation>Gerenciador de projetor</translation>
<translation>Gerenciador de &amp;Projetor</translation>
</message>
</context>
<context>
@ -7122,7 +7122,7 @@ See http://docs.python.org/library/datetime.html#strftime-strptime-behavior for
<message>
<location filename="../../openlp/core/common/uistrings.py" line="73"/>
<source>&amp;Delete</source>
<translation>&amp;Excluir</translation>
<translation>E&amp;xcluir</translation>
</message>
<message>
<location filename="../../openlp/core/common/uistrings.py" line="74"/>
@ -7989,7 +7989,7 @@ Por favor, tente selecionar individualmente.</translation>
<message>
<location filename="../../openlp/plugins/remotes/lib/remotetab.py" line="198"/>
<source>iOS App</source>
<translation>iOS App</translation>
<translation>Aplicativo iOS</translation>
</message>
<message>
<location filename="../../openlp/plugins/remotes/lib/remotetab.py" line="199"/>
@ -8027,12 +8027,12 @@ Por favor, tente selecionar individualmente.</translation>
<message>
<location filename="../../openlp/plugins/songusage/songusageplugin.py" line="103"/>
<source>Toggle Tracking</source>
<translation>Alternar Registro</translation>
<translation>Liga/Desliga Registro</translation>
</message>
<message>
<location filename="../../openlp/plugins/songusage/songusageplugin.py" line="117"/>
<source>Toggle the tracking of song usage.</source>
<translation>Alternar o registro de uso das músicas.</translation>
<translation>Liga/Desliga o registro de uso das músicas.</translation>
</message>
<message>
<location filename="../../openlp/plugins/songusage/songusageplugin.py" line="237"/>
@ -8043,19 +8043,19 @@ Por favor, tente selecionar individualmente.</translation>
<location filename="../../openlp/plugins/songusage/songusageplugin.py" line="248"/>
<source>SongUsage</source>
<comment>name singular</comment>
<translation>UsoDaMúsica</translation>
<translation>Música Utilizada</translation>
</message>
<message>
<location filename="../../openlp/plugins/songusage/songusageplugin.py" line="249"/>
<source>SongUsage</source>
<comment>name plural</comment>
<translation>UsoDaMúsica</translation>
<translation>Músicas Utilizadas</translation>
</message>
<message>
<location filename="../../openlp/plugins/songusage/songusageplugin.py" line="253"/>
<source>SongUsage</source>
<comment>container title</comment>
<translation>UsoDaMúsica</translation>
<translation>Música Utilizada</translation>
</message>
<message>
<location filename="../../openlp/plugins/songusage/songusageplugin.py" line="153"/>
@ -8548,7 +8548,7 @@ A codificação é responsável pela correta representação dos caracteres.</tr
<message>
<location filename="../../openlp/plugins/songs/forms/editsongdialog.py" line="304"/>
<source>Alt&amp;ernate title:</source>
<translation>Título &amp;Alternativo:</translation>
<translation>Tít&amp;ulo Alternativo:</translation>
</message>
<message>
<location filename="../../openlp/plugins/songs/forms/editsongdialog.py" line="305"/>
@ -8558,12 +8558,12 @@ A codificação é responsável pela correta representação dos caracteres.</tr
<message>
<location filename="../../openlp/plugins/songs/forms/editsongdialog.py" line="306"/>
<source>&amp;Verse order:</source>
<translation>Ordem das &amp;estrofes:</translation>
<translation>&amp;Ordem das estrofes:</translation>
</message>
<message>
<location filename="../../openlp/plugins/songs/forms/editsongdialog.py" line="309"/>
<source>Ed&amp;it All</source>
<translation>&amp;Editar Todos</translation>
<translation>Ed&amp;itar Todos</translation>
</message>
<message>
<location filename="../../openlp/plugins/songs/forms/editsongdialog.py" line="311"/>
@ -8573,7 +8573,7 @@ A codificação é responsável pela correta representação dos caracteres.</tr
<message>
<location filename="../../openlp/plugins/songs/forms/editsongdialog.py" line="314"/>
<source>&amp;Add to Song</source>
<translation>&amp;Adicionar à Música</translation>
<translation>&amp;Adicionar Autor</translation>
</message>
<message>
<location filename="../../openlp/plugins/songs/forms/editsongdialog.py" line="338"/>
@ -8583,7 +8583,7 @@ A codificação é responsável pela correta representação dos caracteres.</tr
<message>
<location filename="../../openlp/plugins/songs/forms/editsongdialog.py" line="319"/>
<source>A&amp;dd to Song</source>
<translation>A&amp;dicionar uma Música</translation>
<translation>Adicionar &amp;Tópico</translation>
</message>
<message>
<location filename="../../openlp/plugins/songs/forms/editsongdialog.py" line="320"/>
@ -9247,7 +9247,7 @@ Você deseja remover a entrada da música?</translation>
<message>
<location filename="../../openlp/plugins/songs/lib/importers/openlp.py" line="102"/>
<source>Not a valid OpenLP 2 song database.</source>
<translation>Não é uma base de dados de músicas válida do OpenLP 2.</translation>
<translation>Não é uma base de dados de músicas válido do OpenLP 2.</translation>
</message>
</context>
<context>

View File

@ -23,13 +23,12 @@
Package to test the openlp.core.ui.projector.networkutils package.
"""
import os
from unittest import TestCase
from openlp.core.common import verify_ip_address, md5_hash, qmd5_hash
from tests.resources.projector.data import TEST_PIN, TEST_SALT, TEST_HASH
salt = TEST_SALT
pin = TEST_PIN
test_hash = TEST_HASH
@ -148,7 +147,7 @@ class testProjectorUtilities(TestCase):
hash_ = qmd5_hash(salt=salt.encode('ascii'), data=pin.encode('ascii'))
# THEN: Validate return has is same
self.assertEquals(hash_.decode('ascii'), test_hash, 'Qt-MD5 should have returned a good hash')
self.assertEquals(hash_, test_hash.encode('ascii'), 'Qt-MD5 should have returned a good hash')
def test_qmd5_hash_bad(self):
"""
@ -158,7 +157,7 @@ class testProjectorUtilities(TestCase):
hash_ = qmd5_hash(salt=pin.encode('ascii'), data=salt.encode('ascii'))
# THEN: return data is different
self.assertNotEquals(hash_.decode('ascii'), test_hash, 'Qt-MD5 should have returned a bad hash')
self.assertNotEquals(hash_, test_hash, 'Qt-MD5 should have returned a bad hash')
def test_md5_non_ascii_string(self):
"""

View File

@ -26,13 +26,29 @@ Package to test the openlp.core.lib.projector.pjlink1 package.
from unittest import TestCase
from openlp.core.lib.projector.pjlink1 import PJLink1
from openlp.core.lib.projector.constants import E_PARAMETER, ERROR_STRING, S_OFF, S_STANDBY, S_WARMUP, S_ON, \
S_COOLDOWN, PJLINK_POWR_STATUS
from tests.functional import patch
from tests.resources.projector.data import TEST_PIN, TEST_SALT, TEST_CONNECT_AUTHENTICATE
from tests.resources.projector.data import TEST_PIN, TEST_SALT, TEST_CONNECT_AUTHENTICATE, TEST_HASH
pjlink_test = PJLink1(name='test', ip='127.0.0.1', pin=TEST_PIN, no_poll=True)
class DummyTimer(object):
'''
Dummy class to fake timers
'''
def __init__(self, *args, **kwargs):
pass
def start(self, *args, **kwargs):
pass
def stop(self, *args, **kwargs):
pass
class TestPJLink(TestCase):
"""
Tests for the PJLink module
@ -41,13 +57,10 @@ class TestPJLink(TestCase):
@patch.object(pjlink_test, 'send_command')
@patch.object(pjlink_test, 'waitForReadyRead')
@patch('openlp.core.common.qmd5_hash')
def authenticated_connection_call_test(self,
mock_qmd5_hash,
mock_waitForReadyRead,
mock_send_command,
def test_authenticated_connection_call(self, mock_qmd5_hash, mock_waitForReadyRead, mock_send_command,
mock_readyRead):
"""
Fix for projector connect with PJLink authentication exception. Ticket 92187.
Ticket 92187: Fix for projector connect with PJLink authentication exception.
"""
# GIVEN: Test object
pjlink = pjlink_test
@ -61,9 +74,23 @@ class TestPJLink(TestCase):
self.assertTrue(mock_qmd5_hash.called_with(TEST_PIN,
"Connection request should have been called with TEST_PIN"))
def non_standard_class_reply_test(self):
def test_projector_class(self):
"""
bugfix 1550891 - CLSS request returns non-standard 'Class N' reply
Test class version from projector
"""
# GIVEN: Test object
pjlink = pjlink_test
# WHEN: Process class response
pjlink.process_clss('1')
# THEN: Projector class should be set to 1
self.assertEquals(pjlink.pjlink_class, '1',
'Projector should have returned class=1')
def test_non_standard_class_reply(self):
"""
Bugfix 1550891: CLSS request returns non-standard 'Class N' reply
"""
# GIVEN: Test object
pjlink = pjlink_test
@ -88,3 +115,284 @@ class TestPJLink(TestCase):
# THEN: Projector class should be set to 1
self.assertEquals(pjlink.pjlink_class, '1',
'Projector should have returned class=1')
@patch.object(pjlink_test, 'change_status')
def test_status_change(self, mock_change_status):
"""
Test process_command call with ERR2 (Parameter) status
"""
# GIVEN: Test object
pjlink = pjlink_test
# WHEN: process_command is called with "ERR2" status from projector
pjlink.process_command('POWR', 'ERR2')
# THEN: change_status should have called change_status with E_UNDEFINED
# as first parameter
mock_change_status.called_with(E_PARAMETER,
'change_status should have been called with "{}"'.format(
ERROR_STRING[E_PARAMETER]))
@patch.object(pjlink_test, 'process_inpt')
def test_projector_return_ok(self, mock_process_inpt):
"""
Test projector calls process_inpt command when process_command is called with INPT option
"""
# GIVEN: Test object
pjlink = pjlink_test
# WHEN: process_command is called with INST command and 31 input:
pjlink.process_command('INPT', '31')
# THEN: process_inpt method should have been called with 31
mock_process_inpt.called_with('31',
"process_inpt should have been called with 31")
@patch.object(pjlink_test, 'projectorReceivedData')
def test_projector_process_lamp(self, mock_projectorReceivedData):
"""
Test status lamp on/off and hours
"""
# GIVEN: Test object
pjlink = pjlink_test
# WHEN: Call process_command with lamp data
pjlink.process_command('LAMP', '22222 1')
# THEN: Lamp should have been set with status=ON and hours=22222
self.assertEquals(pjlink.lamp[0]['On'], True,
'Lamp power status should have been set to TRUE')
self.assertEquals(pjlink.lamp[0]['Hours'], 22222,
'Lamp hours should have been set to 22222')
@patch.object(pjlink_test, 'projectorReceivedData')
def test_projector_process_multiple_lamp(self, mock_projectorReceivedData):
"""
Test status multiple lamp on/off and hours
"""
# GIVEN: Test object
pjlink = pjlink_test
# WHEN: Call process_command with lamp data
pjlink.process_command('LAMP', '11111 1 22222 0 33333 1')
# THEN: Lamp should have been set with proper lamp status
self.assertEquals(len(pjlink.lamp), 3,
'Projector should have 3 lamps specified')
self.assertEquals(pjlink.lamp[0]['On'], True,
'Lamp 1 power status should have been set to TRUE')
self.assertEquals(pjlink.lamp[0]['Hours'], 11111,
'Lamp 1 hours should have been set to 11111')
self.assertEquals(pjlink.lamp[1]['On'], False,
'Lamp 2 power status should have been set to FALSE')
self.assertEquals(pjlink.lamp[1]['Hours'], 22222,
'Lamp 2 hours should have been set to 22222')
self.assertEquals(pjlink.lamp[2]['On'], True,
'Lamp 3 power status should have been set to TRUE')
self.assertEquals(pjlink.lamp[2]['Hours'], 33333,
'Lamp 3 hours should have been set to 33333')
@patch.object(pjlink_test, 'projectorReceivedData')
def test_projector_process_power_on(self, mock_projectorReceivedData):
"""
Test status power to ON
"""
# GIVEN: Test object and preset
pjlink = pjlink_test
pjlink.power = S_STANDBY
# WHEN: Call process_command with turn power on command
pjlink.process_command('POWR', PJLINK_POWR_STATUS[S_ON])
# THEN: Power should be set to ON
self.assertEquals(pjlink.power, S_ON, 'Power should have been set to ON')
@patch.object(pjlink_test, 'projectorReceivedData')
def test_projector_process_power_off(self, mock_projectorReceivedData):
"""
Test status power to STANDBY
"""
# GIVEN: Test object and preset
pjlink = pjlink_test
pjlink.power = S_ON
# WHEN: Call process_command with turn power on command
pjlink.process_command('POWR', PJLINK_POWR_STATUS[S_STANDBY])
# THEN: Power should be set to STANDBY
self.assertEquals(pjlink.power, S_STANDBY, 'Power should have been set to STANDBY')
@patch.object(pjlink_test, 'projectorUpdateIcons')
def test_projector_process_avmt_closed_unmuted(self, mock_projectorReceivedData):
"""
Test avmt status shutter closed and audio muted
"""
# GIVEN: Test object
pjlink = pjlink_test
pjlink.shutter = False
pjlink.mute = True
# WHEN: Called with setting shutter closed and mute off
pjlink.process_avmt('11')
# THEN: Shutter should be True and mute should be False
self.assertTrue(pjlink.shutter, 'Shutter should have been set to closed')
self.assertFalse(pjlink.mute, 'Audio should be off')
@patch.object(pjlink_test, 'projectorUpdateIcons')
def test_projector_process_avmt_open_muted(self, mock_projectorReceivedData):
"""
Test avmt status shutter open and mute on
"""
# GIVEN: Test object
pjlink = pjlink_test
pjlink.shutter = True
pjlink.mute = False
# WHEN: Called with setting shutter closed and mute on
pjlink.process_avmt('21')
# THEN: Shutter should be closed and mute should be True
self.assertFalse(pjlink.shutter, 'Shutter should have been set to closed')
self.assertTrue(pjlink.mute, 'Audio should be off')
@patch.object(pjlink_test, 'projectorUpdateIcons')
def test_projector_process_avmt_open_unmuted(self, mock_projectorReceivedData):
"""
Test avmt status shutter open and mute off off
"""
# GIVEN: Test object
pjlink = pjlink_test
pjlink.shutter = True
pjlink.mute = True
# WHEN: Called with setting shutter to closed and mute on
pjlink.process_avmt('30')
# THEN: Shutter should be closed and mute should be True
self.assertFalse(pjlink.shutter, 'Shutter should have been set to open')
self.assertFalse(pjlink.mute, 'Audio should be on')
@patch.object(pjlink_test, 'projectorUpdateIcons')
def test_projector_process_avmt_closed_muted(self, mock_projectorReceivedData):
"""
Test avmt status shutter closed and mute off
"""
# GIVEN: Test object
pjlink = pjlink_test
pjlink.shutter = False
pjlink.mute = False
# WHEN: Called with setting shutter to closed and mute on
pjlink.process_avmt('31')
# THEN: Shutter should be closed and mute should be True
self.assertTrue(pjlink.shutter, 'Shutter should have been set to closed')
self.assertTrue(pjlink.mute, 'Audio should be on')
def test_projector_process_input(self):
"""
Test input source status shows current input
"""
# GIVEN: Test object
pjlink = pjlink_test
pjlink.source = '0'
# WHEN: Called with input source
pjlink.process_inpt('1')
# THEN: Input selected should reflect current input
self.assertEquals(pjlink.source, '1', 'Input source should be set to "1"')
def test_projector_reset_information(self):
"""
Test reset_information() resets all information and stops timers
"""
# GIVEN: Test object and test data
pjlink = pjlink_test
pjlink.power = S_ON
pjlink.pjlink_name = 'OPENLPTEST'
pjlink.manufacturer = 'PJLINK'
pjlink.model = '1'
pjlink.shutter = True
pjlink.mute = True
pjlink.lamp = True
pjlink.fan = True
pjlink.source_available = True
pjlink.other_info = 'ANOTHER TEST'
pjlink.send_queue = True
pjlink.send_busy = True
pjlink.timer = DummyTimer()
pjlink.socket_timer = DummyTimer()
# WHEN: reset_information() is called
with patch.object(pjlink.timer, 'stop') as mock_timer:
with patch.object(pjlink.socket_timer, 'stop') as mock_socket_timer:
pjlink.reset_information()
# THEN: All information should be reset and timers stopped
self.assertEquals(pjlink.power, S_OFF, 'Projector power should be OFF')
self.assertIsNone(pjlink.pjlink_name, 'Projector pjlink_name should be None')
self.assertIsNone(pjlink.manufacturer, 'Projector manufacturer should be None')
self.assertIsNone(pjlink.model, 'Projector model should be None')
self.assertIsNone(pjlink.shutter, 'Projector shutter should be None')
self.assertIsNone(pjlink.mute, 'Projector shuttter should be None')
self.assertIsNone(pjlink.lamp, 'Projector lamp should be None')
self.assertIsNone(pjlink.fan, 'Projector fan should be None')
self.assertIsNone(pjlink.source_available, 'Projector source_available should be None')
self.assertIsNone(pjlink.source, 'Projector source should be None')
self.assertIsNone(pjlink.other_info, 'Projector other_info should be None')
self.assertEquals(pjlink.send_queue, [], 'Projector send_queue should be an empty list')
self.assertFalse(pjlink.send_busy, 'Projector send_busy should be False')
self.assertTrue(mock_timer.called, 'Projector timer.stop() should have been called')
self.assertTrue(mock_socket_timer.called, 'Projector socket_timer.stop() should have been called')
@patch.object(pjlink_test, 'send_command')
@patch.object(pjlink_test, 'waitForReadyRead')
@patch.object(pjlink_test, 'projectorAuthentication')
@patch.object(pjlink_test, 'timer')
@patch.object(pjlink_test, 'socket_timer')
def test_bug_1593882_no_pin_authenticated_connection(self, mock_socket_timer,
mock_timer,
mock_authentication,
mock_ready_read,
mock_send_command):
"""
Test bug 1593882 no pin and authenticated request exception
"""
# GIVEN: Test object and mocks
pjlink = pjlink_test
pjlink.pin = None
mock_ready_read.return_value = True
# WHEN: call with authentication request and pin not set
pjlink.check_login(data=TEST_CONNECT_AUTHENTICATE)
# THEN: No Authentication signal should have been sent
mock_authentication.emit.assert_called_with(pjlink.name)
@patch.object(pjlink_test, 'waitForReadyRead')
@patch.object(pjlink_test, 'state')
@patch.object(pjlink_test, '_send_command')
@patch.object(pjlink_test, 'timer')
@patch.object(pjlink_test, 'socket_timer')
def test_bug_1593883_pjlink_authentication(self, mock_socket_timer,
mock_timer,
mock_send_command,
mock_state,
mock_waitForReadyRead):
"""
Test bugfix 1593883 pjlink authentication
"""
# GIVEN: Test object and data
pjlink = pjlink_test
pjlink.pin = TEST_PIN
mock_state.return_value = pjlink.ConnectedState
mock_waitForReadyRead.return_value = True
# WHEN: Athenticated connection is called
pjlink.check_login(data=TEST_CONNECT_AUTHENTICATE)
# THEN: send_command should have the proper authentication
self.assertEquals("{test}".format(test=mock_send_command.call_args),
"call(data='{hash}%1CLSS ?\\r')".format(hash=TEST_HASH))

View File

@ -22,13 +22,13 @@
"""
This module contains tests for the OpenSong song importer.
"""
import os
from unittest import TestCase
from tests.helpers.songfileimport import SongImportTestHelper
from openlp.plugins.songs.lib.importers.opensong import OpenSongImport
from openlp.core.common import Registry
from tests.helpers.songfileimport import SongImportTestHelper
from tests.functional import patch, MagicMock
TEST_PATH = os.path.abspath(
@ -54,6 +54,8 @@ class TestOpenSongFileImport(SongImportTestHelper):
self.load_external_result_data(os.path.join(TEST_PATH, 'One, Two, Three, Four, Five.json')))
self.file_import([os.path.join(TEST_PATH, 'Amazing Grace2')],
self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace.json')))
self.file_import([os.path.join(TEST_PATH, 'Amazing Grace with bad CCLI')],
self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace without CCLI.json')))
class TestOpenSongImport(TestCase):

View File

@ -52,6 +52,8 @@ class TestSongShowPlusFileImport(SongImportTestHelper):
self.load_external_result_data(os.path.join(TEST_PATH, 'Beautiful Garden Of Prayer.json')))
self.file_import([os.path.join(TEST_PATH, 'a mighty fortress is our god.sbsong')],
self.load_external_result_data(os.path.join(TEST_PATH, 'a mighty fortress is our god.json')))
self.file_import([os.path.join(TEST_PATH, 'cleanse-me.sbsong')],
self.load_external_result_data(os.path.join(TEST_PATH, 'cleanse-me.json')))
class TestSongShowPlusImport(TestCase):

View File

@ -46,3 +46,5 @@ class TestVideoPsalmFileImport(SongImportTestHelper):
"""
self.file_import(os.path.join(TEST_PATH, 'videopsalm-as-safe-a-stronghold.json'),
self.load_external_result_data(os.path.join(TEST_PATH, 'as-safe-a-stronghold.json')))
self.file_import(os.path.join(TEST_PATH, 'videopsalm-as-safe-a-stronghold2.json'),
self.load_external_result_data(os.path.join(TEST_PATH, 'as-safe-a-stronghold2.json')))

View File

@ -29,6 +29,7 @@ from unittest import TestCase
from openlp.plugins.songs.lib.importers.opensong import OpenSongImport
from openlp.core.common import Registry
from tests.functional import patch, MagicMock, call
log = logging.getLogger(__name__)
@ -36,7 +37,7 @@ log = logging.getLogger(__name__)
class SongImportTestHelper(TestCase):
"""
This class is designed to be a helper class to reduce repition when testing the import of song files.
This class is designed to be a helper class to reduce repetition when testing the import of song files.
"""
def __init__(self, *args, **kwargs):
super(SongImportTestHelper, self).__init__(*args, **kwargs)

View File

@ -53,4 +53,4 @@
<key_line></key_line>
<time_sig></time_sig>
<style index="default_style"></style>
</song>
</song>

View File

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?>
<song>
<title>Amazing Grace (Demonstration)</title>
<author>John Newton, Edwin Excell &amp; John P. Rees</author>
<copyright>Public Domain </copyright>
<presentation>V1 V2 V3 V4 V5</presentation>
<capo print="false"></capo>
<tempo></tempo>
<ccli>GE</ccli>
<theme>God: Assurance/Grace/Salvation</theme>
<alttheme>Worship: Praise</alttheme>
<user1> </user1>
<user2> </user2>
<user3> </user3>
<lyrics>[V]
;Test the chords format
;Chords beging with .
;Verses begin with their verse number
;Link words with _
;Comments begin with ;
. D D7 G D
1A______ma________zing grace! How sweet the sound!
2'Twas grace that taught my heart to fear,
3The Lord has pro____mised good to me,
4Thro' ma________ny dan____gers, toils and snares
5When we've been there ten thou__sand years,
. Bm E A A7
1That saved a wretch like me!
2And grace my fears re___lieved.
3His Word my hope se___cures.
4I have al___rea____dy come.
5Bright shi___ning as the sun,
. D D7 G D
1I once was lost, but now am found;
2How pre___cious did that grace ap____pear,
3He will my shield and por___tion be
4'Tis grace that brought me safe thus far,
5We've no less days to sing God's praise,
. Bm A G D
1Was blind, but now I see.
2The hour I first be_lieved.
3As long as life en_dures.
4And grace will lead me home.
5Than when we first be_gun.
</lyrics>
<hymn_number>Demonstration Songs 0</hymn_number>
<key></key>
<aka></aka>
<key_line></key_line>
<time_sig></time_sig>
<style index="default_style"></style>
</song>

View File

@ -0,0 +1,42 @@
{
"authors": [
"John Newton",
"Edwin Excell",
"John P. Rees"
],
"ccli_number": null,
"comments": "\n\n\n",
"copyright": "Public Domain ",
"song_book_name": "Demonstration Songs",
"song_number": 0,
"title": "Amazing Grace (Demonstration)",
"topics": [
"Assurance",
"Grace",
"Praise",
"Salvation"
],
"verse_order_list": [],
"verses": [
[
"Amazing grace! How sweet the sound!\nThat saved a wretch like me!\nI once was lost, but now am found;\nWas blind, but now I see.",
"v1"
],
[
"'Twas grace that taught my heart to fear,\nAnd grace my fears relieved.\nHow precious did that grace appear,\nThe hour I first believed.",
"v2"
],
[
"The Lord has promised good to me,\nHis Word my hope secures.\nHe will my shield and portion be\nAs long as life endures.",
"v3"
],
[
"Thro' many dangers, toils and snares\nI have already come.\n'Tis grace that brought me safe thus far,\nAnd grace will lead me home.",
"v4"
],
[
"When we've been there ten thousand years,\nBright shining as the sun,\nWe've no less days to sing God's praise,\nThan when we first begun.",
"v5"
]
]
}

View File

@ -0,0 +1,38 @@
{
"authors": [
"J. Edwin Orr"
],
"ccli_number": 56307,
"comments": "",
"copyright": "Public Domain ",
"song_book_name": "",
"song_number": 438,
"title": "Cleanse Me [438]",
"topics": [
"Cleansing",
"Communion",
"Consecration",
"Holiness",
"Holy Spirit",
"Revival"
],
"verse_order_list": [],
"verses": [
[
"Search me, O God,\r\nAnd know my heart today;\r\nTry me, O Savior,\r\nKnow my thoughts, I pray.\r\nSee if there be\r\nSome wicked way in me;\r\nCleanse me from every sin\r\nAnd set me free.",
"v1"
],
[
"I praise Thee, Lord,\r\nFor cleansing me from sin;\r\nFulfill Thy Word,\r\nAnd make me pure within.\r\nFill me with fire\r\nWhere once I burned with shame;\r\nGrant my desire\r\nTo magnify Thy name.",
"v2"
],
[
"Lord, take my life,\r\nAnd make it wholly Thine;\r\nFill my poor heart\r\nWith Thy great love divine.\r\nTake all my will,\r\nMy passion, self and pride;\r\nI now surrender, Lord\r\nIn me abide.",
"v3"
],
[
"O Holy Ghost,\r\nRevival comes from Thee;\r\nSend a revival,\r\nStart the work in me.\r\nThy Word declares\r\nThou wilt supply our need;\r\nFor blessings now,\r\nO Lord, I humbly plead.",
"v4"
]
]
}

Binary file not shown.

View File

@ -0,0 +1,35 @@
{
"authors": [
["Martin Luther", "words"],
["Unknown", "music"]
],
"ccli_number": "12345",
"comments": "This is\nthe first comment\nThis is\nthe second comment\nThis is\nthe third comment\n",
"copyright": "Public Domain",
"song_book_name": "SongBook1",
"song_number": 0,
"title": "A Safe Stronghold Our God is Still",
"topics": [
"tema1",
"tema2"
],
"verse_order_list": [],
"verses": [
[
"As safe a stronghold our God is still,\nA trusty shield and weapon;\nHell help us clear from all the ill\nThat hath us now oertaken.\nThe ancient prince of hell\nHath risen with purpose fell;\nStrong mail of craft and power\nHe weareth in this hour;\nOn earth is not His fellow.",
"v"
],
[
"With \"force\" of arms we nothing can,\nFull soon were we down-ridden;\nBut for us fights \\ the proper Man,\nWhom God Himself hath bidden.\nAsk ye: Who is this same?\nChrist Jesus is His name,\nThe Lord Sabaoths Son;\nHe, and no other one,\nShall conquer in the battle.",
"v"
],
[
"And were this world all devils oer,\nAnd watching to devour us,\nWe lay it not to heart so sore;\nNot they can overpower us.\nAnd let the prince of ill\nLook grim as eer he will,\nHe harms us not a whit;\nFor why? his doom is writ;\nA word shall quickly slay him.",
"v"
],
[
"Gods word, for all their craft and force,\nOne moment will not linger,\nBut, spite of hell, shall have its course;\nTis written by His finger.\nAnd though they take our life,\nGoods, honour, children, wife,\nYet is their profit small:\nThese things shall vanish all;\nThe city of God remaineth.",
"v"
]
]
}

View File

@ -0,0 +1,47 @@
{Abbreviation:"SB1",Copyright:"Public domain",Songs:[{ID:3,Composer:"Unknown",Author:"Martin Luther",Copyright:"Public
Domain",Theme:"tema1
tema2",CCLI:"12345",Alias:"A safe stronghold",Memo1:"This is
the first comment
",Memo2:"This is
the second comment
",Memo3:"This is
the third comment
",Reference:"reference",Guid:"jtCkrJdPIUOmECjaQylg/g",Verses:[{
Text:"As safe a stronghold our God is still,
A trusty shield and weapon;
Hell help us clear from all the ill
That hath us now oertaken.
The ancient prince of hell
Hath risen with purpose fell;
Strong mail of craft and power
He weareth in this hour;
On earth is not His fellow."},{ID:2,
Text:"With \"force\" of arms we nothing can,
Full soon were we down-ridden;
But for us fights \\ the proper Man,
Whom God Himself hath bidden.
Ask ye: Who is this same?
Christ Jesus is His name,
The Lord Sabaoths Son;
He, and no other one,
Shall conquer in the battle."},{ID:3,
Text:"And were this world all devils oer,
And watching to devour us,
We lay it not to heart so sore;
Not they can overpower us.
And let the prince of ill
Look grim as eer he will,
He harms us not a whit;
For why? his doom is writ;
A word shall quickly slay him."},{ID:4,
Text:"Gods word, for all their craft and force,
One moment will not linger,
But, spite of hell, shall have its course;
Tis written by His finger.
And though they take our life,
Goods, honour, children, wife,
Yet is their profit small:
These things shall vanish all;
The city of God remaineth."}],AudioFile:"282.mp3",IsAudioFileEnabled:1,
Text:"A Safe Stronghold Our God is Still"}],Guid:"khiHU2blX0Kb41dGdbDLhA",VersionDate:"20121012000000",
Text:"SongBook1"}