Managing concept properties in ConceptEvaluator

This commit is contained in:
2019-11-16 18:11:29 +01:00
parent 3a1dea19e8
commit 7fa509555d
13 changed files with 808 additions and 57 deletions
+34 -7
View File
@@ -23,9 +23,13 @@ class BuiltinConcepts(Enum):
INVALID_RETURN_VALUE = 14 # the return value of an evaluator is not correct
BEFORE_PARSING = 15 # activated before evaluation by the parsers
PARSING = 16 # activated during the parsing. It contains the text to parse
AFTER_PARSING = 17 # activated when the parsing process seems to be finished
CONCEPT_ALREADY_DEFINED = 18 # when you try to add the same concept twice
NOP = 19 # no operation concept. Does nothing
AFTER_PARSING = 17 # after parsing
BEFORE_EVALUATION = 18 # before evalution
EVALUATION = 19 # activated when the parsing process seems to be finished
AFTER_EVALUATION = 20 # activated when the parsing process seems to be finished
CONCEPT_ALREADY_DEFINED = 21 # when you try to add the same concept twice
NOP = 22 # no operation concept. Does nothing
PROPERTY_EVAL_ERROR = 23
"""
@@ -198,11 +202,34 @@ class BeforeParsingConcept(Concept):
super().__init__(BuiltinConcepts.BEFORE_PARSING, True, True, BuiltinConcepts.BEFORE_PARSING)
class ParsingConcept(Concept):
class EvaluationConcept(Concept):
def __init__(self):
super().__init__(BuiltinConcepts.PARSING, True, True, BuiltinConcepts.PARSING)
super().__init__(BuiltinConcepts.EVALUATION, True, True, BuiltinConcepts.EVALUATION)
class AfterParsingConcept(Concept):
class AfterEvaluationConcept(Concept):
def __init__(self):
super().__init__(BuiltinConcepts.AFTER_PARSING, True, True, BuiltinConcepts.AFTER_PARSING)
super().__init__(BuiltinConcepts.AFTER_EVALUATION, True, True, BuiltinConcepts.AFTER_EVALUATION)
class PropertyEvalError(Concept):
def __init__(self, property_name=None, concept=None, error=None):
super().__init__(BuiltinConcepts.PROPERTY_EVAL_ERROR, True, False, BuiltinConcepts.PROPERTY_EVAL_ERROR)
self.set_prop("concept", concept)
self.set_prop("error", error)
self.body = property_name
def __repr__(self):
return f"PropertyEvalError(property={self.property_name}, concept={self.concept}), error={self.error})"
@property
def concept(self):
return self.props["concept"].value
@property
def error(self):
return self.props["error"].value
@property
def property_name(self):
return self.body
+1 -1
View File
@@ -69,7 +69,7 @@ class Concept:
# check the attributes
for prop in self.props_to_serialize:
if getattr(self, prop) != getattr(other, prop):
print(prop)
# print(prop) # use full to know which id does not match
return False
# check the props (Concept variables)
+59 -22
View File
@@ -150,19 +150,24 @@ class Sheerka(Concept):
evt_digest = self.sdp.save_event(Event(text))
exec_context = ExecutionContext(self.key, evt_digest, self)
before_parsing = self.ret(self.eval.__name__, True, self.new(BuiltinConcepts.BEFORE_PARSING))
# Before parsing
before_parsing = self.new(BuiltinConcepts.BEFORE_PARSING)
return_values = self.process(exec_context, [], [before_parsing])
return_values = core.utils.remove_from_list(return_values, [before_parsing])
return_values = core.utils.remove_from_list(return_values, lambda x: x.value == before_parsing)
# parse
parsing_results = self.parse(exec_context, text)
return_values.extend(parsing_results)
processing_parsing = self.ret(self.eval.__name__, True, self.new(BuiltinConcepts.PARSING))
return_values = self.process(exec_context, return_values, [processing_parsing])
return_values = core.utils.remove_from_list(return_values, [processing_parsing])
after_parsing = self.ret(self.eval.__name__, True, self.new(BuiltinConcepts.AFTER_PARSING))
return_values = self.process(exec_context, return_values, [after_parsing])
return_values = core.utils.remove_from_list(return_values, [after_parsing])
# evaluate
evaluating = self.new(BuiltinConcepts.EVALUATION)
return_values = self.process(exec_context, return_values, [evaluating])
return_values = core.utils.remove_from_list(return_values, lambda x: x.value == evaluating)
# post evaluation
after_evaluation = self.new(BuiltinConcepts.AFTER_EVALUATION)
return_values = self.process(exec_context, return_values, [after_evaluation])
return_values = core.utils.remove_from_list(return_values, lambda x: x.value == after_evaluation)
return return_values
@@ -207,17 +212,17 @@ class Sheerka(Concept):
result.append(res)
return result
def process(self, context, return_values, contextual_concepts=None):
contextual_concepts_values = [c.value for c in contextual_concepts] if contextual_concepts else []
log.debug(f"Processing parsing result. context concept={contextual_concepts_values}")
def process(self, context, return_values, initial_concepts=None):
log.debug(f"Processing parsing result. context concept={initial_concepts}")
# return_values must be a list
if not isinstance(return_values, list):
return_values = [return_values]
# adds contextual concepts
if contextual_concepts:
return_values.extend(contextual_concepts)
if initial_concepts:
for concept in initial_concepts:
return_values.append(self.ret(context.who, True, concept))
# group the evaluators by priority and sort them
# The first one to be applied will be the one with the highest priority
@@ -261,6 +266,8 @@ class Sheerka(Concept):
else:
if evaluator.matches(context, original_items):
results = evaluator.eval(context, original_items)
if results is None:
continue
if not isinstance(results, list):
results = [results]
for result in results:
@@ -277,6 +284,24 @@ class Sheerka(Concept):
return return_values
def chain_process(self, context, return_values, initial_concepts):
"""
Executes process for all initial contexts
:param context:
:param return_values:
:param initial_concepts:
:return:
"""
for concept in initial_concepts:
if isinstance(concept, BuiltinConcepts):
concept = self.new(BuiltinConcepts)
init = [self.ret(context.who, True, concept)]
return_values = self.process(context, return_values, [init])
return_values = core.utils.remove_from_list(return_values, lambda x: x.value == init)
return return_values
def create_new_concept(self, context, concept):
"""
Adds a new concept to the system
@@ -319,13 +344,14 @@ class Sheerka(Concept):
for part_key in ConceptParts:
source = getattr(concept, part_key.value)
if source is None or not isinstance(source, str) or source == "":
# the only sources that I am sure to parse are strings
# I refuse empty strings for performance, I don't want to handle useless NOPConcepts
continue
else:
concept.codes[part_key] = self.parse(context, source)
ret_val = self.expect_one(context, self.parse(context, source))
concept.codes[part_key] = ret_val
for prop in concept.props:
concept.codes[prop] = self.parse(context, concept.props[prop].value)
def add_in_cache(self, concept):
"""
@@ -334,7 +360,16 @@ class Sheerka(Concept):
:param concept:
:return:
"""
# sanity check
if concept.key is None:
concept.init_key()
if concept.key is None:
raise KeyError()
self.concepts_cache[concept.key] = concept
return concept
def get(self, concept_key):
"""
@@ -455,7 +490,7 @@ class Sheerka(Concept):
base_class = core.utils.get_class("parsers.BaseParser.BaseParser")
for c in core.utils.get_classes_recursive("parsers"):
#if issubclass(c, base_class) and c != base_class:
# if issubclass(c, base_class) and c != base_class:
res.append(c)
return res
@@ -470,9 +505,11 @@ class ExecutionContext:
"""
To keep track of the execution of a request
"""
who: object
event_digest: str
sheerka: Sheerka
who: object # who is asking
event_digest: str # what was the (original) trigger
sheerka: Sheerka # sheerka
desc: str = None # human description of what is going on
obj: Concept = None # what is the subject of the execution context (if known)
def push(self, who):
return ExecutionContext(who, self.event_digest, self.sheerka)
def push(self, who, desc=None, obj=None):
return ExecutionContext(who, self.event_digest, self.sheerka, desc=desc, obj=obj)
+8 -4
View File
@@ -106,7 +106,6 @@ def get_classes_from_package(package_name):
def get_sub_classes(package_name, base_class_name):
pkg = __import__(package_name)
prefix = pkg.__name__ + "."
for (module_loader, name, ispkg) in pkgutil.iter_modules(pkg.__path__, prefix):
@@ -123,8 +122,13 @@ def remove_from_list(lst, to_remove):
:param to_remove:
:return:
"""
for item in to_remove:
if item in lst:
lst.remove(item)
flagged = []
for item in lst:
if to_remove(item):
flagged.append(item)
for item in flagged:
lst.remove(item)
return lst