Skip to content
Snippets Groups Projects

Revert "Revert "Merge branch 'f-extend-yaml-model' into 'dev'""

Merged Florian Spreckelsen requested to merge f-extend-yaml-model into dev
Files
5
@@ -25,7 +25,8 @@ import yaml
@@ -25,7 +25,8 @@ import yaml
from .data_model import DataModel
from .data_model import DataModel
# Keywords which are allowed in data model descriptions.
# Keywords which are allowed in data model descriptions.
KEYWORDS = ["parent",
KEYWORDS = ["parent", # deprecated, use inherit_from_* instead:
 
# https://gitlab.com/caosdb/caosdb-advanced-user-tools/-/issues/36
"importance",
"importance",
"datatype", # for example TEXT, INTEGER or REFERENCE
"datatype", # for example TEXT, INTEGER or REFERENCE
"unit",
"unit",
@@ -35,8 +36,12 @@ KEYWORDS = ["parent",
@@ -35,8 +36,12 @@ KEYWORDS = ["parent",
"suggested_properties",
"suggested_properties",
"inherit_from_recommended",
"inherit_from_recommended",
"inherit_from_suggested",
"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.
# These KEYWORDS are not forbidden as properties, but merely ignored.
KEYWORDS_IGNORED = [
KEYWORDS_IGNORED = [
"unit",
"unit",
@@ -109,6 +114,10 @@ def parse_model_from_string(string):
@@ -109,6 +114,10 @@ def parse_model_from_string(string):
class Parser(object):
class Parser(object):
def __init__(self):
def __init__(self):
 
"""Initialize an empty parser object and initialize the dictionary of entities and the list of
 
treated elements.
 
 
"""
self.model = {}
self.model = {}
self.treated = []
self.treated = []
@@ -177,13 +186,11 @@ class Parser(object):
@@ -177,13 +186,11 @@ class Parser(object):
ymlmodel["extern"] = []
ymlmodel["extern"] = []
for name in ymlmodel["extern"]:
for name in ymlmodel["extern"]:
if db.execute_query("COUNT Property {}".format(name)) > 0:
for role in ("Property", "RecordType", "Record", "File"):
self.model[name] = db.execute_query(
if db.execute_query("COUNT {} {}".format(role, name)) > 0:
"FIND Property WITH name={}".format(name), unique=True)
self.model[name] = db.execute_query(
"FIND {} WITH name={}".format(role, name), unique=True)
elif db.execute_query("COUNT RecordType {}".format(name)) > 0:
break
self.model[name] = db.execute_query(
"FIND RecordType WITH name={}".format(name), unique=True)
else:
else:
raise Exception("Did not find {}".format(name))
raise Exception("Did not find {}".format(name))
@@ -235,6 +242,8 @@ class Parser(object):
@@ -235,6 +242,8 @@ class Parser(object):
""" adds names of Properties and RecordTypes to the model dictionary
""" adds names of Properties and RecordTypes to the model dictionary
Properties are also initialized.
Properties are also initialized.
 
 
name is the key of the yaml element and definition the value.
"""
"""
if name == "__line__":
if name == "__line__":
@@ -258,9 +267,29 @@ class Parser(object):
@@ -258,9 +267,29 @@ class Parser(object):
# and create the new property
# and create the new property
self.model[name] = db.Property(name=name,
self.model[name] = db.Property(name=name,
datatype=definition["datatype"])
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":
 
# TODO(fspreck) Implement files at some later point in time
 
raise NotImplementedError(
 
"The definition of file objects is not yet implemented.")
 
 
# 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",
for prop_type in ["recommended_properties",
"suggested_properties", "obligatory_properties"]:
"suggested_properties", "obligatory_properties"]:
@@ -284,7 +313,25 @@ class Parser(object):
@@ -284,7 +313,25 @@ class Parser(object):
raise
raise
def _add_to_recordtype(self, ent_name, props, importance):
def _add_to_recordtype(self, ent_name, props, importance):
"""Add properties to a RecordType."""
"""Add properties to a RecordType.
 
 
Parameters
 
----------
 
ent_name : str
 
The name of the entity to which the properties shall be added.
 
 
props : dict [str -> dict or :doc:`Entity`]
 
The properties, indexed by their names. Properties may be given as :doc:`Entity` objects
 
or as dictionaries.
 
 
importance
 
The importance as used in :doc:`Entity.add_property`.
 
 
Returns
 
-------
 
None
 
 
"""
for n, e in props.items():
for n, e in props.items():
if n in KEYWORDS:
if n in KEYWORDS:
@@ -297,15 +344,28 @@ class Parser(object):
@@ -297,15 +344,28 @@ class Parser(object):
continue
continue
n = self._stringify(n)
n = self._stringify(n)
if (isinstance(e, dict) and "datatype" in e
if isinstance(e, dict):
and (_get_listdatatype(e["datatype"]) is not None)):
if "datatype" in e and _get_listdatatype(e["datatype"]) is not None:
self.model[ent_name].add_property(
# Reuse the existing datatype for lists.
name=n,
datatype = db.LIST(_get_listdatatype(e["datatype"]))
importance=importance,
else:
datatype=db.LIST(_get_listdatatype(e["datatype"])))
# Ignore a possible e["datatype"] here if it's not a list
 
# since it has been treated in the definition of the
 
# property (entity) already
 
datatype = None
 
if "value" in e:
 
value = e["value"]
 
else:
 
value = None
 
else:
else:
self.model[ent_name].add_property(name=n,
value = e
importance=importance)
datatype = None
 
 
self.model[ent_name].add_property(name=n,
 
value=value,
 
importance=importance,
 
datatype=datatype)
def _inherit(self, name, prop, inheritance):
def _inherit(self, name, prop, inheritance):
if not isinstance(prop, list):
if not isinstance(prop, list):
@@ -328,6 +388,10 @@ class Parser(object):
@@ -328,6 +388,10 @@ class Parser(object):
if definition is None:
if definition is None:
return
return
 
# for setting values of properties directly:
 
if not isinstance(definition, dict):
 
return
 
if ("datatype" in definition
if ("datatype" in definition
and definition["datatype"].startswith("LIST")):
and definition["datatype"].startswith("LIST")):
@@ -344,6 +408,9 @@ class Parser(object):
@@ -344,6 +408,9 @@ class Parser(object):
if prop_name == "unit":
if prop_name == "unit":
self.model[name].unit = prop
self.model[name].unit = prop
 
elif prop_name == "value":
 
self.model[name].value = prop
 
elif prop_name == "description":
elif prop_name == "description":
self.model[name].description = prop
self.model[name].description = prop
@@ -372,6 +439,10 @@ class Parser(object):
@@ -372,6 +439,10 @@ class Parser(object):
elif prop_name == "datatype":
elif prop_name == "datatype":
continue
continue
 
# role has already been used
 
elif prop_name == "role":
 
continue
 
elif prop_name == "inherit_from_obligatory":
elif prop_name == "inherit_from_obligatory":
self._inherit(name, prop, db.OBLIGATORY)
self._inherit(name, prop, db.OBLIGATORY)
elif prop_name == "inherit_from_recommended":
elif prop_name == "inherit_from_recommended":
@@ -432,7 +503,8 @@ class Parser(object):
@@ -432,7 +503,8 @@ class Parser(object):
continue
continue
raise ValueError("Property {} has an unknown datatype: {}".format(value.name, value.datatype))
raise ValueError("Property {} has an unknown datatype: {}".format(
 
value.name, value.datatype))
def _set_recordtypes(self):
def _set_recordtypes(self):
""" properties are defined in first iteration; set remaining as RTs """
""" properties are defined in first iteration; set remaining as RTs """
Loading