match_condition should return immediately (no score-based fuzzy search) if an event condition is an exact match

This commit is contained in:
Fabio Manganiello 2022-08-04 01:04:00 +02:00
parent cbf8ba7fdd
commit 7c87238fec
Signed by: blacklight
GPG Key ID: D90FBA7F76362774
1 changed files with 56 additions and 31 deletions

View File

@ -2,6 +2,7 @@ import copy
import hashlib import hashlib
import json import json
import re import re
import sys
import time import time
import uuid import uuid
@ -13,15 +14,23 @@ from platypush.utils import get_event_class_by_type
class Event(Message): class Event(Message):
""" Event message class """ """Event message class"""
# If this class property is set to false then the logging of these events # If this class property is set to false then the logging of these events
# will be disabled. Logging is usually disabled for events with a very # will be disabled. Logging is usually disabled for events with a very
# high frequency that would otherwise pollute the logs e.g. camera capture # high frequency that would otherwise pollute the logs e.g. camera capture
# events # events
# pylint: disable=redefined-builtin # pylint: disable=redefined-builtin
def __init__(self, target=None, origin=None, id=None, timestamp=None, def __init__(
disable_logging=False, disable_web_clients_notification=False, **kwargs): self,
target=None,
origin=None,
id=None,
timestamp=None,
disable_logging=False,
disable_web_clients_notification=False,
**kwargs
):
""" """
Params: Params:
target -- Target node [String] target -- Target node [String]
@ -34,22 +43,27 @@ class Event(Message):
self.id = id if id else self._generate_id() self.id = id if id else self._generate_id()
self.target = target if target else Config.get('device_id') self.target = target if target else Config.get('device_id')
self.origin = origin if origin else Config.get('device_id') self.origin = origin if origin else Config.get('device_id')
self.type = '{}.{}'.format(self.__class__.__module__, self.type = '{}.{}'.format(self.__class__.__module__, self.__class__.__name__)
self.__class__.__name__)
self.args = kwargs self.args = kwargs
self.disable_logging = disable_logging self.disable_logging = disable_logging
self.disable_web_clients_notification = disable_web_clients_notification self.disable_web_clients_notification = disable_web_clients_notification
for arg, value in self.args.items(): for arg, value in self.args.items():
if arg not in [ if arg not in [
'id', 'args', 'origin', 'target', 'type', 'timestamp', 'disable_logging' 'id',
'args',
'origin',
'target',
'type',
'timestamp',
'disable_logging',
] and not arg.startswith('_'): ] and not arg.startswith('_'):
self.__setattr__(arg, value) self.__setattr__(arg, value)
@classmethod @classmethod
def build(cls, msg): def build(cls, msg):
""" Builds an event message from a JSON UTF-8 string/bytearray, a """Builds an event message from a JSON UTF-8 string/bytearray, a
dictionary, or another Event """ dictionary, or another Event"""
msg = super().parse(msg) msg = super().parse(msg)
event_type = msg['args'].pop('type') event_type = msg['args'].pop('type')
@ -64,8 +78,10 @@ class Event(Message):
@staticmethod @staticmethod
def _generate_id(): def _generate_id():
""" Generate a unique event ID """ """Generate a unique event ID"""
return hashlib.md5(str(uuid.uuid1()).encode()).hexdigest() # lgtm [py/weak-sensitive-data-hashing] return hashlib.md5(
str(uuid.uuid1()).encode()
).hexdigest() # lgtm [py/weak-sensitive-data-hashing]
def matches_condition(self, condition): def matches_condition(self, condition):
""" """
@ -120,7 +136,13 @@ class Event(Message):
""" """
result = EventMatchResult(is_match=False) result = EventMatchResult(is_match=False)
event_tokens = re.split(r'\s+', self.args[argname].strip().lower()) if self.args.get(argname) == condition_value:
# In case of an exact match, return immediately
result.is_match = True
result.score = sys.maxsize
return result
event_tokens = re.split(r'\s+', self.args.get(argname, '').strip().lower())
condition_tokens = re.split(r'\s+', condition_value.strip().lower()) condition_tokens = re.split(r'\s+', condition_value.strip().lower())
while event_tokens and condition_tokens: while event_tokens and condition_tokens:
@ -148,9 +170,11 @@ class Event(Message):
else: else:
result.parsed_args[argname] += ' ' + event_token result.parsed_args[argname] += ' ' + event_token
if (len(condition_tokens) == 1 and len(event_tokens) == 1) \ if (len(condition_tokens) == 1 and len(event_tokens) == 1) or (
or (len(event_tokens) > 1 and len(condition_tokens) > 1 len(event_tokens) > 1
and event_tokens[1] == condition_tokens[1]): and len(condition_tokens) > 1
and event_tokens[1] == condition_tokens[1]
):
# Stop appending tokens to this argument, as the next # Stop appending tokens to this argument, as the next
# condition will be satisfied as well # condition will be satisfied as well
condition_tokens.pop(0) condition_tokens.pop(0)
@ -173,30 +197,30 @@ class Event(Message):
args = copy.deepcopy(self.args) args = copy.deepcopy(self.args)
flatten(args) flatten(args)
return json.dumps({ return json.dumps(
'type': 'event', {
'target': self.target, 'type': 'event',
'origin': self.origin if hasattr(self, 'origin') else None, 'target': self.target,
'id': self.id if hasattr(self, 'id') else None, 'origin': self.origin if hasattr(self, 'origin') else None,
'_timestamp': self.timestamp, 'id': self.id if hasattr(self, 'id') else None,
'args': { '_timestamp': self.timestamp,
'type': self.type, 'args': {'type': self.type, **args},
**args
}, },
}, cls=self.Encoder) cls=self.Encoder,
)
class EventMatchResult(object): class EventMatchResult:
""" When comparing an event against an event condition, you want to """When comparing an event against an event condition, you want to
return this object. It contains the match status (True or False), return this object. It contains the match status (True or False),
any parsed arguments, and a match_score that identifies how "strong" any parsed arguments, and a match_score that identifies how "strong"
the match is - in case of multiple event matches, the ones with the the match is - in case of multiple event matches, the ones with the
highest score will win """ highest score will win"""
def __init__(self, is_match, score=0, parsed_args=None): def __init__(self, is_match, score=0, parsed_args=None):
self.is_match = is_match self.is_match = is_match
self.score = score self.score = score
self.parsed_args = {} if not parsed_args else parsed_args self.parsed_args = parsed_args or {}
def flatten(args): def flatten(args):
@ -213,4 +237,5 @@ def flatten(args):
elif isinstance(arg, (dict, list)): elif isinstance(arg, (dict, list)):
flatten(args[i]) flatten(args[i])
# vim:sw=4:ts=4:et: # vim:sw=4:ts=4:et: