Skip to content
Snippets Groups Projects
Commit 634c632d authored by Florian Spreckelsen's avatar Florian Spreckelsen
Browse files

Merge branch 'dev' into f-fix-inttests

parents ca9d1f08 751a0d04
No related branches found
No related tags found
2 merge requests!39Release 0.4.0,!35Pipeline streamlining
Pipeline #20050 passed
......@@ -11,6 +11,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- CFood that creates a Record for each line in a csv file
- `generic_analysis.py` allows to easily call scripts to perform analyses in
server side scripting [EXPERIMENTAL]
- New keyword "role" in yaml data model that allows creation of Records and Files.
- It is now possible to set values of properties and default values of properties
directly in the yaml model.
### Changed ###
......
......@@ -25,7 +25,7 @@ import yaml
from .data_model import DataModel
# Keywords which are allowed in data model descriptions.
KEYWORDS = ["parent",
KEYWORDS = ["parent", # TODO: can we remove that, see: #36
"importance",
"datatype", # for example TEXT, INTEGER or REFERENCE
"unit",
......@@ -35,8 +35,11 @@ KEYWORDS = ["parent",
"suggested_properties",
"inherit_from_recommended",
"inherit_from_suggested",
"inherit_from_obligatory", ]
"inherit_from_obligatory",
"role",
"value", ]
# TODO: check whether it's really ignored
# These KEYWORDS are not forbidden as properties, but merely ignored.
KEYWORDS_IGNORED = [
"unit",
......@@ -109,6 +112,10 @@ def parse_model_from_string(string):
class Parser(object):
def __init__(self):
"""
Initialize an empty parer object and initialize
the dictionary of entities and the list of treated elements.
"""
self.model = {}
self.treated = []
......@@ -177,13 +184,11 @@ class Parser(object):
ymlmodel["extern"] = []
for name in ymlmodel["extern"]:
if db.execute_query("COUNT Property {}".format(name)) > 0:
self.model[name] = db.execute_query(
"FIND Property WITH name={}".format(name), unique=True)
elif db.execute_query("COUNT RecordType {}".format(name)) > 0:
self.model[name] = db.execute_query(
"FIND RecordType WITH name={}".format(name), unique=True)
for role in ("Property", "RecordType", "Record", "File"):
if db.execute_query("COUNT {} {}".format(role, name)) > 0:
self.model[name] = db.execute_query(
"FIND {} WITH name={}".format(role, name), unique=True)
break
else:
raise Exception("Did not find {}".format(name))
......@@ -235,6 +240,8 @@ class Parser(object):
""" adds names of Properties and RecordTypes to the model dictionary
Properties are also initialized.
name is the key of the yaml element and definition the value.
"""
if name == "__line__":
......@@ -258,9 +265,25 @@ class Parser(object):
# and create the new property
self.model[name] = db.Property(name=name,
datatype=definition["datatype"])
elif (self.model[name] is None and isinstance(definition, dict)
and "role" in definition):
if definition["role"] == "RecordType":
self.model[name] = db.RecordType(name=name)
elif definition["role"] == "Record":
self.model[name] = db.Record(name=name)
elif definition["role"] == "File":
self.model[name] = db.File(name=name)
elif definition["role"] == "Property":
self.model[name] = db.Property(name=name)
else:
raise RuntimeError("Unknown role {} in definition of entity.".format(
definition["role"]))
# add other definitions recursively
# for setting values of properties directly:
if not isinstance(definition, dict):
return
# add other definitions recursively
for prop_type in ["recommended_properties",
"suggested_properties", "obligatory_properties"]:
......@@ -303,8 +326,12 @@ class Parser(object):
name=n,
importance=importance,
datatype=db.LIST(_get_listdatatype(e["datatype"])))
elif e is None:
self.model[ent_name].add_property(name=n,
importance=importance)
else:
self.model[ent_name].add_property(name=n,
value=e,
importance=importance)
def _inherit(self, name, prop, inheritance):
......@@ -328,6 +355,10 @@ class Parser(object):
if definition is None:
return
# for setting values of properties directly:
if not isinstance(definition, dict):
return
if ("datatype" in definition
and definition["datatype"].startswith("LIST")):
......@@ -344,6 +375,9 @@ class Parser(object):
if prop_name == "unit":
self.model[name].unit = prop
elif prop_name == "value":
self.model[name].value = prop
elif prop_name == "description":
self.model[name].description = prop
......@@ -372,6 +406,10 @@ class Parser(object):
elif prop_name == "datatype":
continue
# role has already been used
elif prop_name == "role":
continue
elif prop_name == "inherit_from_obligatory":
self._inherit(name, prop, db.OBLIGATORY)
elif prop_name == "inherit_from_recommended":
......
......@@ -15,6 +15,8 @@ def to_file(string):
return f.name
# TODO: check purpose of this function... add documentation
def parse_str(string):
parse_model_from_yaml(to_file(string))
......@@ -68,7 +70,8 @@ RT2:
a:
"""
self.assertRaises(TwiceDefinedException, lambda: parse_model_from_yaml(to_file(string)))
self.assertRaises(TwiceDefinedException,
lambda: parse_model_from_yaml(to_file(string)))
def test_typical_case(self):
string = """
......@@ -103,7 +106,8 @@ RT5:
- RT1:
- RT2:
"""
self.assertRaises(ValueError, lambda: parse_model_from_yaml(to_file(string)))
self.assertRaises(
ValueError, lambda: parse_model_from_yaml(to_file(string)))
def test_unknown_kwarg(self):
string = """
......@@ -111,7 +115,8 @@ RT1:
datetime:
p1:
"""
self.assertRaises(ValueError, lambda: parse_model_from_yaml(to_file(string)))
self.assertRaises(
ValueError, lambda: parse_model_from_yaml(to_file(string)))
def test_definition_in_inheritance(self):
string = """
......@@ -121,7 +126,8 @@ RT2:
- RT1:
description: "tach"
"""
self.assertRaises(ValueError, lambda: parse_model_from_yaml(to_file(string)))
self.assertRaises(
ValueError, lambda: parse_model_from_yaml(to_file(string)))
def test_inheritance(self):
string = """
......@@ -301,6 +307,8 @@ class ExternTest(unittest.TestCase):
class ErrorMessageTest(unittest.TestCase):
"""Tests for understandable error messages."""
# Note: This was changed with implementation of role keyword
@unittest.expectedFailure
def test_non_dict(self):
"""When a value is given, where a list or mapping is expected."""
recordtype_value = """
......@@ -328,3 +336,66 @@ A:
with self.assertRaises(YamlDefinitionError) as yde:
parse_str(string)
assert("line {}".format(line) in yde.exception.args[0])
def test_define_role():
model = """
A:
role: Record
"""
entities = parse_model_from_string(model)
assert "A" in entities
assert isinstance(entities["A"], db.Record)
assert entities["A"].role == "Record"
model = """
A:
role: Record
inherit_from_obligatory:
- C
obligatory_properties:
b:
b:
datatype: INTEGER
C:
obligatory_properties:
b:
D:
role: RecordType
"""
entities = parse_model_from_string(model)
for l, ent in (("A", "Record"), ("b", "Property"),
("C", "RecordType"), ("D", "RecordType")):
assert l in entities
assert isinstance(entities[l], getattr(db, ent))
assert entities[l].role == ent
assert entities["A"].parents[0].name == "C"
assert entities["A"].name == "A"
assert entities["A"].properties[0].name == "b"
assert entities["A"].properties[0].value is None
assert entities["C"].properties[0].name == "b"
assert entities["C"].properties[0].value is None
model = """
A:
role: Record
obligatory_properties:
b: 42
b:
datatype: INTEGER
"""
entities = parse_model_from_string(model)
assert entities["A"].get_property("b").value == 42
assert entities["b"].value is None
model = """
b:
datatype: INTEGER
value: 18
"""
entities = parse_model_from_string(model)
assert entities["b"].value == 18
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment