Fixed #101 : Implement PLURIAL
Fixed #103 : Implement PlurialNodeParser Fixed #104 : Implement dynamic concept Fixed #107 : PrepareEvalxxxEvaluator: context hints are lost on a second evaluation
This commit is contained in:
@@ -1537,6 +1537,14 @@ class TestSheerkaConceptManager(TestUsingMemoryBasedSheerka):
|
||||
weights = sheerka.get_weights(BuiltinConcepts.PRECEDENCE, comparison_context=CONCEPT_COMPARISON_CONTEXT)
|
||||
assert weights == {'c:one|1001:': 2, 'c:two|1002:': 1}
|
||||
|
||||
def test_i_can_get_a_dynamic_concept_by_id_when_allow_dynamic(self):
|
||||
sheerka, context, foo = self.init_concepts("foo")
|
||||
|
||||
dynamic_foo = sheerka.new_dynamic(foo, "SUFFIX")
|
||||
|
||||
assert sheerka.isinstance(sheerka.get_by_id(dynamic_foo.id), BuiltinConcepts.UNKNOWN_CONCEPT)
|
||||
assert sheerka.isinstance(sheerka.get_by_id(dynamic_foo.id, allow_dynamic=True), foo)
|
||||
|
||||
|
||||
class TestSheerkaConceptManagerUsingFileBasedSheerka(TestUsingFileBasedSheerka):
|
||||
def test_i_can_add_several_concepts(self):
|
||||
|
||||
@@ -518,6 +518,29 @@ class TestSheerkaEvaluateConcept(TestUsingMemoryBasedSheerka):
|
||||
evaluated = sheerka.evaluate_concept(self.get_context(sheerka, False, False), concept)
|
||||
assert evaluated.key == concept.key
|
||||
|
||||
def test_i_can_evaluate_context_hint_multiple_times(self):
|
||||
"""
|
||||
Previous behaviour (that we want to change)
|
||||
When def concept foo as global_truth(xxx) pre yyy
|
||||
the context hint for the global truth is set during the initialisation of the ast
|
||||
if the body is computed straight away it's ok,
|
||||
But if the concept is evaluated a second time, the asts is not computed again, so the context hint is not set
|
||||
:return:
|
||||
"""
|
||||
sheerka, context, foo = self.init_concepts(
|
||||
Concept("foo", body="global_truth(in_context(BuiltinConcepts.EVAL_GLOBAL_TRUTH_REQUESTED))")
|
||||
)
|
||||
|
||||
foo_instance = sheerka.new("foo")
|
||||
evaluated = sheerka.evaluate_concept(context, foo_instance, eval_body=False)
|
||||
|
||||
assert ConceptParts.BODY in evaluated.get_compiled()
|
||||
assert evaluated.body == NotInit
|
||||
assert not evaluated.get_hints().is_evaluated
|
||||
|
||||
evaluated = sheerka.evaluate_concept(context, foo_instance, eval_body=True) # evaluate the body this time
|
||||
assert isinstance(evaluated.body, bool) and evaluated.body
|
||||
|
||||
def test_i_can_apply_intermediate_where_condition_using_python(self):
|
||||
sheerka, context, one_1, one_str, plus = self.init_concepts(
|
||||
Concept("one", body="1"),
|
||||
@@ -818,10 +841,14 @@ class TestSheerkaEvaluateConcept(TestUsingMemoryBasedSheerka):
|
||||
(Concept("foo"), False, []),
|
||||
(Concept("foo", pre="pre", post="post", ret="ret", where="where"), False, ["#pre#", "#post#"]),
|
||||
(Concept("foo", pre="pr", post="p", ret="r", where="w"), True,
|
||||
["#pre#", "#ret#", "#post#", "variables", "#body#"]),
|
||||
["#pre#", "variables", "#body#", "#ret#", "#post#"]),
|
||||
|
||||
(Concept("foo", pre="pre", body="body"), False, ["#pre#"]),
|
||||
(Concept("foo", pre="pre", body="body"), True, ["#pre#", "variables", "#body#"]),
|
||||
(Concept("foo", pre="a").def_var("a"), False, ["variables", "#pre#"]),
|
||||
(Concept("foo", pre="self"), False, ["#body#", "#pre#"]),
|
||||
(Concept("foo", pre="self + a").def_var("a"), False, ["variables", "#body#", "#pre#"]),
|
||||
|
||||
(Concept("foo", pre="self + a", ret="ret").def_var("a"), False, ["variables", "#body#", "#pre#"]),
|
||||
(Concept("foo", pre="self + a", ret="ret").def_var("a"), True, ["variables", "#body#", "#pre#", "#ret#"]),
|
||||
(Concept("foo", body="body"), False, [])
|
||||
|
||||
@@ -9,7 +9,10 @@ class TestSheerkaHasAManager(TestUsingMemoryBasedSheerka):
|
||||
|
||||
king_instance = sheerka.new("king")
|
||||
res = sheerka.set_hasa(context, king_instance, kingdom)
|
||||
|
||||
assert res.status
|
||||
assert king_instance.get_prop(BuiltinConcepts.HASA) == {kingdom}
|
||||
assert sheerka.hasa(king_instance, kingdom)
|
||||
|
||||
# when global truth is not activated, only the current instance is modified
|
||||
another_king = sheerka.get_by_key("king")
|
||||
@@ -56,3 +59,17 @@ class TestSheerkaHasAManager(TestUsingMemoryBasedSheerka):
|
||||
assert res.body.property_name == BuiltinConcepts.HASA
|
||||
assert res.body.property_value == kingdom
|
||||
assert res.body.concept == sheerka.new("king")
|
||||
|
||||
def test_i_can_set_hase_twice_when_global_truth_is_true_the_second_time(self):
|
||||
sheerka, context, king, kingdom = self.init_concepts("king", "kingdom")
|
||||
global_truth_context = self.get_context(sheerka, global_truth=True)
|
||||
|
||||
king_instance = sheerka.new("king")
|
||||
sheerka.set_hasa(context, king_instance, kingdom)
|
||||
|
||||
# set it again with global_truth = True
|
||||
res = sheerka.set_hasa(global_truth_context, king_instance, kingdom)
|
||||
|
||||
assert res.status
|
||||
assert sheerka.hasa(king_instance, kingdom)
|
||||
assert sheerka.hasa(sheerka.new("king"), kingdom) # try another instance
|
||||
|
||||
@@ -116,7 +116,8 @@ class TestSheerkaIsAManager(TestUsingMemoryBasedSheerka):
|
||||
|
||||
context.add_to_private_hints(BuiltinConcepts.EVAL_GLOBAL_TRUTH_REQUESTED)
|
||||
|
||||
sheerka.set_isa(context, blue_instance, color)
|
||||
res = sheerka.set_isa(context, blue_instance, color)
|
||||
assert res.status
|
||||
assert sheerka.isa(blue_instance, color)
|
||||
assert sheerka.isaset(context, color)
|
||||
assert sheerka.isinset(blue_instance, color)
|
||||
@@ -423,6 +424,20 @@ class TestSheerkaIsAManager(TestUsingMemoryBasedSheerka):
|
||||
foo = sheerka.get_by_id(foo.id)
|
||||
assert not sheerka.isa(foo, group2)
|
||||
|
||||
def test_i_can_set_isa_twice_when_global_truth_is_true_the_second_time(self):
|
||||
sheerka, context, blue, color = self.init_concepts(Concept("blue"), Concept("color"))
|
||||
global_truth_context = self.get_context(sheerka, global_truth=True)
|
||||
|
||||
blue_instance = sheerka.new("blue")
|
||||
sheerka.set_isa(context, blue_instance, color)
|
||||
|
||||
# set it again with global_truth = True
|
||||
res = sheerka.set_isa(global_truth_context, blue_instance, color)
|
||||
|
||||
assert res.status
|
||||
assert sheerka.isa(blue_instance, color)
|
||||
assert sheerka.isa(sheerka.new("blue"), color) # try another instance
|
||||
|
||||
|
||||
class TestSheerkaSetsManagerUsingFileBasedSheerka(TestUsingFileBasedSheerka):
|
||||
def test_i_can_add_concept_to_set_and_retrieve_it_in_another_session(self):
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
from core.builtin_concepts_ids import BuiltinConcepts
|
||||
from core.global_symbols import NotFound
|
||||
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
||||
|
||||
|
||||
class TestSheerkaPluralManager(TestUsingMemoryBasedSheerka):
|
||||
def test_i_can_set_plural(self):
|
||||
sheerka, context, man, men = self.init_concepts("man", "men")
|
||||
|
||||
men_instance = sheerka.new("men")
|
||||
res = sheerka.set_plural(context, men_instance, man)
|
||||
|
||||
assert res.status
|
||||
assert men_instance.get_prop(BuiltinConcepts.PLURAL) == man
|
||||
assert sheerka.is_plural(men_instance)
|
||||
assert sheerka.is_plural(men_instance, man)
|
||||
|
||||
# global truth is not set
|
||||
another_instance = sheerka.new("men")
|
||||
assert another_instance.get_prop(BuiltinConcepts.PLURAL) is None
|
||||
|
||||
def test_i_can_set_plural_when_global_truth_is_set(self):
|
||||
sheerka, context, man, men = self.init_concepts("man", "men", global_truth=True)
|
||||
|
||||
assert sheerka.known_plural(man) == NotFound
|
||||
|
||||
res = sheerka.set_plural(context, men, man)
|
||||
|
||||
assert res.status
|
||||
assert sheerka.is_plural(men)
|
||||
|
||||
another_instance = sheerka.new("men")
|
||||
assert sheerka.is_plural(another_instance)
|
||||
assert sheerka.known_plural(man) == men.id
|
||||
|
||||
def test_i_cannot_set_plural_twice(self):
|
||||
sheerka, context, man, men = self.init_concepts("man", "men")
|
||||
men_instance = sheerka.new("men")
|
||||
sheerka.set_plural(context, men_instance, man)
|
||||
|
||||
res = sheerka.set_plural(context, men_instance, man)
|
||||
|
||||
assert not res.status
|
||||
assert sheerka.isinstance(res.body, BuiltinConcepts.PROPERTY_ALREADY_DEFINED)
|
||||
assert res.body.concept == men_instance
|
||||
assert res.body.property_name == BuiltinConcepts.PLURAL
|
||||
assert res.body.property_value == man
|
||||
|
||||
def test_i_cannot_set_plural_twice_when_global_truth(self):
|
||||
sheerka, context, man, men = self.init_concepts("man", "men", global_truth=True)
|
||||
sheerka.set_plural(context, men, man)
|
||||
|
||||
another_instance = sheerka.new("men")
|
||||
res = sheerka.set_plural(context, another_instance, man)
|
||||
|
||||
assert not res.status
|
||||
assert sheerka.isinstance(res.body, BuiltinConcepts.PROPERTY_ALREADY_DEFINED)
|
||||
assert res.body.concept == another_instance
|
||||
assert res.body.property_name == BuiltinConcepts.PLURAL
|
||||
assert res.body.property_value == man
|
||||
|
||||
def test_i_can_set_plural_twice_when_global_truth_is_true_the_second_time(self):
|
||||
sheerka, context, man, men = self.init_concepts("man", "men")
|
||||
global_truth_context = self.get_context(sheerka, global_truth=True)
|
||||
|
||||
men_instance = sheerka.new("men")
|
||||
sheerka.set_plural(context, men_instance, man)
|
||||
|
||||
# set it again with global_truth = True
|
||||
res = sheerka.set_plural(global_truth_context, men_instance, man)
|
||||
|
||||
assert res.status
|
||||
assert men_instance.get_prop(BuiltinConcepts.PLURAL) == man
|
||||
|
||||
# and it's now true for all newly created context
|
||||
assert sheerka.is_plural(sheerka.new("men"), man)
|
||||
@@ -0,0 +1,59 @@
|
||||
from core.builtin_concepts_ids import BuiltinConcepts
|
||||
from core.concept import Concept, ConceptParts
|
||||
from evaluators.PrepareEvalCommon import PrepareEvalCommon
|
||||
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
||||
|
||||
|
||||
class TestPrepareEvalCommon(TestUsingMemoryBasedSheerka):
|
||||
def test_i_can_update_the_current_root(self):
|
||||
sheerka, context = self.init_concepts()
|
||||
|
||||
PrepareEvalCommon.update_context_hints(context, "prop_name", ["to_put_in_context"])
|
||||
assert context.in_context("to_put_in_context")
|
||||
|
||||
def test_i_can_update_process_input(self):
|
||||
sheerka, context = self.init_concepts()
|
||||
|
||||
process_input_context = context.push(BuiltinConcepts.PROCESS_INPUT, "some input", desc=f"some desc")
|
||||
level1 = process_input_context.push(BuiltinConcepts.TESTING, "some stuff")
|
||||
level2 = level1.push(BuiltinConcepts.TESTING, "some stuff") # another level for the fun
|
||||
|
||||
PrepareEvalCommon.update_context_hints(level2, "prop_name", ["to_put_in_context"])
|
||||
assert not context.in_context("to_put_in_context")
|
||||
assert process_input_context.in_context("to_put_in_context")
|
||||
assert not level1.in_context("to_put_in_context")
|
||||
assert not level2.in_context("to_put_in_context")
|
||||
|
||||
def test_i_can_update_process_when_evaluating_a_concept(self):
|
||||
sheerka, context, foo = self.init_concepts("foo")
|
||||
|
||||
eval_context = context.push(BuiltinConcepts.EVALUATING_CONCEPT, foo, desc=f"some desc")
|
||||
parsing_prop_context = eval_context.push(BuiltinConcepts.PARSING, {"prop": ConceptParts.BODY})
|
||||
level1 = parsing_prop_context.push(BuiltinConcepts.TESTING, "some stuff")
|
||||
level2 = level1.push(BuiltinConcepts.TESTING, "some stuff") # another level for the fun
|
||||
|
||||
PrepareEvalCommon.update_context_hints(level2, "prop_name", ["to_put_in_context"])
|
||||
assert not context.in_context("to_put_in_context")
|
||||
assert not eval_context.in_context("to_put_in_context")
|
||||
assert not parsing_prop_context.in_context("to_put_in_context")
|
||||
assert not level1.in_context("to_put_in_context")
|
||||
assert not level2.in_context("to_put_in_context")
|
||||
assert foo.get_compiled_context_hints() == {ConceptParts.BODY: ["to_put_in_context"]}
|
||||
|
||||
def test_i_can_update_for_the_correct_variable(self):
|
||||
# when source is the name of the variable,
|
||||
# use this name, rather than the attribute being parsed
|
||||
sheerka, context, foo = self.init_concepts(Concept("foo").def_var("var_name"))
|
||||
|
||||
eval_context = context.push(BuiltinConcepts.EVALUATING_CONCEPT, foo, desc=f"some desc")
|
||||
parsing_prop_context = eval_context.push(BuiltinConcepts.PARSING, {"prop": ConceptParts.BODY})
|
||||
level1 = parsing_prop_context.push(BuiltinConcepts.TESTING, "some stuff")
|
||||
level2 = level1.push(BuiltinConcepts.TESTING, "some stuff") # another level for the fun
|
||||
|
||||
PrepareEvalCommon.update_context_hints(level2, "var_name", ["to_put_in_context"])
|
||||
assert not context.in_context("to_put_in_context")
|
||||
assert not eval_context.in_context("to_put_in_context")
|
||||
assert not parsing_prop_context.in_context("to_put_in_context")
|
||||
assert not level1.in_context("to_put_in_context")
|
||||
assert not level2.in_context("to_put_in_context")
|
||||
assert foo.get_compiled_context_hints() == {"var_name": ["to_put_in_context"]}
|
||||
@@ -44,3 +44,14 @@ class TestSheerkaNonRegMemory2(TestUsingMemoryBasedSheerka):
|
||||
|
||||
assert res[0].status
|
||||
assert sheerka.isa(sheerka.new("one"), sheerka.new("number"))
|
||||
|
||||
# def test_i_can_define_plural(self):
|
||||
# init = [
|
||||
# "def concept man",
|
||||
# "def concept men as set_plural(man) ret man auto_eval True",
|
||||
# ]
|
||||
# sheerka = self.init_scenario(init)
|
||||
#
|
||||
# res = sheerka.evaluate_user_input("men")
|
||||
# assert res[0].status
|
||||
|
||||
|
||||
@@ -151,8 +151,8 @@ __default__
|
||||
init = [
|
||||
"def concept one as 1",
|
||||
"def concept two as 2",
|
||||
"one",
|
||||
"two"
|
||||
"eval one",
|
||||
"eval two"
|
||||
]
|
||||
sheerka = self.init_scenario(init)
|
||||
capsys.readouterr()
|
||||
|
||||
@@ -3,6 +3,7 @@ import pytest
|
||||
from core.builtin_concepts import BuiltinConcepts
|
||||
from core.concept import Concept
|
||||
from core.sheerka.services.SheerkaExecute import ParserInput
|
||||
from parsers.BaseNodeParser import SourceCodeWithConceptNode
|
||||
from parsers.BaseParser import ErrorSink
|
||||
from parsers.FunctionParser import FunctionParser
|
||||
from parsers.PythonParser import PythonErrorNode
|
||||
@@ -192,6 +193,17 @@ class TestFunctionParser(TestUsingMemoryBasedSheerka):
|
||||
assert expression.python_node is not None
|
||||
assert expression.return_value is not None
|
||||
|
||||
def test_i_can_parse_when_the_parameter_is_a_dynamic_concept(self):
|
||||
sheerka, context, parser = self.init_parser()
|
||||
|
||||
text = "func(ones)"
|
||||
res = parser.parse(context, ParserInput(text))
|
||||
|
||||
assert res.status
|
||||
assert isinstance(res.body.body, SourceCodeWithConceptNode)
|
||||
assert res.body.body.python_node.source == 'func(__C__ones__1001___PLURAL__C__)'
|
||||
assert "__C__ones__1001___PLURAL__C__" in res.body.body.python_node.objects
|
||||
|
||||
@pytest.mark.parametrize("text, expected_error_type", [
|
||||
("one", BuiltinConcepts.NOT_FOR_ME), # no function found
|
||||
("$*!", BuiltinConcepts.NOT_FOR_ME), # no function found
|
||||
|
||||
@@ -229,11 +229,13 @@ class TestSequenceNodeParser(TestUsingMemoryBasedSheerka):
|
||||
sheerka, context, parser = self.init_parser(concepts_map)
|
||||
|
||||
res = parser.parse(context, ParserInput("a special concept"))
|
||||
assert res.status
|
||||
lexer_nodes = res.body.body
|
||||
expected_array = compute_expected_array(concepts_map, "a special concept", ["a special concept"])
|
||||
compare_with_test_object(lexer_nodes, expected_array)
|
||||
|
||||
res = parser.parse(context, ParserInput("isa"))
|
||||
assert res.status
|
||||
lexer_nodes = res.body.body
|
||||
expected_array = compute_expected_array(concepts_map, "isa", ["isa"])
|
||||
compare_with_test_object(lexer_nodes, expected_array)
|
||||
@@ -442,3 +444,22 @@ class TestSequenceNodeParser(TestUsingMemoryBasedSheerka):
|
||||
for node in res.body.body:
|
||||
if hasattr(node, "concept"):
|
||||
assert node.concept.get_hints().use_copy
|
||||
|
||||
def test_i_can_parse_plural(self):
|
||||
concepts_map = {
|
||||
"boy": Concept("boy"),
|
||||
}
|
||||
|
||||
sheerka, context, parser = self.init_parser(concepts_map)
|
||||
boy = concepts_map['boy']
|
||||
|
||||
res = parser.parse(context, ParserInput("boys"))
|
||||
assert res.status
|
||||
lexer_nodes = res.body.body
|
||||
assert len(lexer_nodes) == 1
|
||||
|
||||
concept_found = lexer_nodes[0].concept
|
||||
assert concept_found.id == f"{boy.id}-{BuiltinConcepts.PLURAL}"
|
||||
assert concept_found.name == "boys"
|
||||
assert concept_found.key == "boys"
|
||||
assert concept_found.get_prop(BuiltinConcepts.PLURAL) == boy
|
||||
|
||||
@@ -159,7 +159,7 @@ class TestSheerkaPickleHandler(TestUsingMemoryBasedSheerka):
|
||||
to_string = sheerkapickle.encode(sheerka, ref_concept)
|
||||
decoded = sheerkapickle.decode(sheerka, to_string)
|
||||
assert decoded == ref_concept
|
||||
assert to_string == '{"_sheerka/obj": "core.concept.Concept", "concept/id": ["my_key", "1001"]}'
|
||||
assert to_string == '{"_sheerka/obj": "core.concept.Concept", "concept/id": "1001"}'
|
||||
|
||||
# same test, modify a value and check if this modification is correctly saved
|
||||
concept = Concept().update_from(sheerka.get_by_id(ref_concept.id))
|
||||
@@ -167,26 +167,7 @@ class TestSheerkaPickleHandler(TestUsingMemoryBasedSheerka):
|
||||
to_string = sheerkapickle.encode(sheerka, concept)
|
||||
decoded = sheerkapickle.decode(sheerka, to_string)
|
||||
assert decoded == concept
|
||||
assert to_string == '{"_sheerka/obj": "core.concept.Concept", "concept/id": ["my_key", "1001"], "values": [["#body#", {"_sheerka/obj": "core.concept.Concept", "meta.name": "bar"}]]}'
|
||||
|
||||
# def test_i_can_encode_decode_when_variable_is_a_concept(self):
|
||||
# sheerka = self.get_sheerka()
|
||||
#
|
||||
# foo = Concept("foo")
|
||||
# sheerka.create_new_concept(self.get_context(sheerka), foo)
|
||||
#
|
||||
# concept = Concept("my_name")
|
||||
# sheerka.create_new_concept(self.get_context(sheerka), concept)
|
||||
# concept.def_var(foo, "a value")
|
||||
# concept.set_value(foo, "another value")
|
||||
# concept.get_metadata().full_serialization = True
|
||||
#
|
||||
# to_string = sheerkapickle.encode(sheerka, concept)
|
||||
# decoded = sheerkapickle.decode(sheerka, to_string)
|
||||
# assert decoded == concept
|
||||
# assert to_string == '{"_sheerka/obj": "core.concept.Concept", "meta.name": "my_name", "meta.key": "my_name", ' + \
|
||||
# '"meta.variables": [[{"_sheerka/obj": "core.concept.Concept", "concept/id": ["foo", "1001"]}, "a value"]], ' + \
|
||||
# '"meta.id": "1002", "values": [[{"_sheerka/id": 1}, "another value"]]}'
|
||||
assert to_string == '{"_sheerka/obj": "core.concept.Concept", "concept/id": "1001", "values": [["#body#", {"_sheerka/obj": "core.concept.Concept", "meta.name": "bar"}]]}'
|
||||
|
||||
def test_i_can_manage_reference_of_the_same_object(self):
|
||||
sheerka = self.get_sheerka()
|
||||
@@ -331,7 +312,7 @@ class TestSheerkaPickleHandler(TestUsingMemoryBasedSheerka):
|
||||
def test_i_can_encode_decode_rule(self):
|
||||
sheerka = self.get_sheerka()
|
||||
|
||||
rule = Rule("print", "my rule", "True","Hello world")
|
||||
rule = Rule("print", "my rule", "True", "Hello world")
|
||||
rule.metadata.id = "1"
|
||||
|
||||
to_string = sheerkapickle.encode(sheerka, rule)
|
||||
@@ -339,3 +320,21 @@ class TestSheerkaPickleHandler(TestUsingMemoryBasedSheerka):
|
||||
|
||||
assert to_string == '{"_sheerka/obj": "core.rule.Rule", "rule/id": "1", "name": "my rule", "predicate": "True", "action_type": "print", "action": "Hello world"}'
|
||||
assert decoded == rule
|
||||
|
||||
def test_i_can_encode_decode_dynamic_concept(self):
|
||||
sheerka, context, foo = self.init_concepts("foo", global_truth=True, create_new=True)
|
||||
sheerka.set_attr(context, foo, "attr", "attr_value")
|
||||
sheerka.set_property(context, foo, "prop", "prop_value", all_concepts=True)
|
||||
|
||||
foo_instance = sheerka.new(foo)
|
||||
dynamic_foo = sheerka.new_dynamic(foo_instance,
|
||||
"SUFFIX",
|
||||
"new_name",
|
||||
props={"new_prop": "value"},
|
||||
attrs={"new_attr": "value"})
|
||||
|
||||
to_string = sheerkapickle.encode(sheerka, dynamic_foo)
|
||||
decoded = sheerkapickle.decode(sheerka, to_string)
|
||||
|
||||
assert decoded == dynamic_foo
|
||||
assert to_string == '{"_sheerka/obj": "core.concept.Concept", "concept/id": "1001-SUFFIX", "meta.name": "new_name", "meta.key": "new_name", "meta.props": {"prop": "prop_value", "new_prop": "value"}, "meta.id": "1001-SUFFIX", "values": [["new_attr", "value"]]}'
|
||||
|
||||
Reference in New Issue
Block a user