Skip to content

Commit

Permalink
fix: restrict the creation of classes without cardinalities (DEV-305) (
Browse files Browse the repository at this point in the history
…#136)

* integrate cardinality adding into resource creation

* fix code smells

* remove test files

* update error message to be more readable

* fix order of entity creation

* add error message when cardinalities are empty

* Improve code after review

* Remove unused code
  • Loading branch information
irinaschubert committed Jan 4, 2022
1 parent 9ce6722 commit 5604a5b
Show file tree
Hide file tree
Showing 2 changed files with 156 additions and 273 deletions.
271 changes: 156 additions & 115 deletions knora/dsplib/utils/onto_create_ontology.py
@@ -1,4 +1,5 @@
"""This module handles the ontology creation and upload to a DSP server. This includes the creation and upload of lists."""
"""This module handles the ontology creation and upload to a DSP server. This includes the creation of the project,
groups, users, lists, resource classes, properties and cardinalities. """
import json
from typing import Dict, List, Optional, Set

Expand Down Expand Up @@ -39,7 +40,7 @@ def create_ontology(input_file: str,
user: str,
password: str,
verbose: bool,
dump: bool) -> bool:
dump: bool) -> None:
"""
Creates the ontology from a json input file on a DSP server
Expand All @@ -55,6 +56,9 @@ def create_ontology(input_file: str,
Returns:
True if successful
"""

knora_api_prefix = "knora-api:"

# read the ontology from the input file
with open(input_file) as f:
onto_json_str = f.read()
Expand Down Expand Up @@ -90,7 +94,7 @@ def create_ontology(input_file: str,
# try to read the project to check if it exists
project = Project(con=con, shortcode=data_model["project"]["shortcode"]).read()

# update the project with data from data_model
# update the project with data from the ontology (data_model)
if project.shortname != data_model["project"]["shortname"]:
project.shortname = data_model["project"]["shortname"]
if project.longname != data_model["project"]["longname"]:
Expand All @@ -116,11 +120,10 @@ def create_ontology(input_file: str,
status=True).create()
except BaseError as err:
print("Creating project failed: ", err.message)
return False
exit(1)
if verbose:
print("Created project:")
project.print()
assert project is not None

# create the lists
list_root_nodes = create_lists(input_file, lists_file, server, user, password, verbose)
Expand All @@ -143,7 +146,7 @@ def create_ontology(input_file: str,
new_groups[new_group.name] = new_group
if verbose:
print("Created group:")
new_group.print() # project.set_default_permissions(new_group.id)
new_group.print()

except BaseError as err:
print(f"ERROR while trying to create group '{group.name}'. The error message was: {err.message}")
Expand Down Expand Up @@ -270,7 +273,6 @@ def create_ontology(input_file: str,
except BaseError as err:
tmp_user.print()
print("Updating user failed:", err.message)
return False

# update group and project membership
# Note: memberships are NOT removed here, just added
Expand Down Expand Up @@ -301,151 +303,190 @@ def create_ontology(input_file: str,
in_groups=group_ids).create()
except BaseError as err:
print("Creating user failed:", err.message)
return False
if verbose:
print("New user:")
new_user.print()

# create the ontologies
if verbose:
print("Create ontologies...")
ontologies = data_model["project"]["ontologies"]
ontologies = data_model.get("project").get("ontologies")
for ontology in ontologies:
newontology = Ontology(con=con,
project=project,
label=ontology["label"],
name=ontology["name"]).create()
last_modification_date = newontology.lastModificationDate
if verbose:
print("Created empty ontology:")
newontology.print()
new_ontology = None
last_modification_date = None
try:
new_ontology = Ontology(con=con,
project=project,
label=ontology.get("label"),
name=ontology.get("name")).create()
last_modification_date = new_ontology.lastModificationDate
if verbose:
print("Created ontology:")
new_ontology.print()
except BaseError as err:
print(f"ERROR while trying to create ontology. The error message was {err.message}")
exit(1)
except Exception as exception:
print(f"ERROR while trying to create ontology. The error message was {exception}")
exit(1)

# add the prefixes defined in the json file
for prefix, iri in context:
if not prefix in newontology.context:
if prefix not in new_ontology.context:
s = iri.iri + ("#" if iri.hashtag else "")
newontology.context.add_context(prefix, s)
new_ontology.context.add_context(prefix, s)

# create the empty resource classes
resclasses = ontology["resources"]
newresclasses: Dict[str, ResourceClass] = {}
for resclass in resclasses:
resname = resclass.get("name")
super_classes = resclass.get("super")
new_res_classes: Dict[str, ResourceClass] = {}
for res_class in ontology.get("resources"):
res_name = res_class.get("name")
super_classes = res_class.get("super")
if isinstance(super_classes, str):
super_classes = [super_classes]
reslabel = LangString(resclass.get("labels"))
rescomment = resclass.get("comments")
if rescomment is not None:
rescomment = LangString(rescomment)
res_label = LangString(res_class.get("labels"))
res_comment = res_class.get("comments")
if res_comment:
res_comment = LangString(res_comment)
# if no cardinalities are submitted, don't create the class
if not res_class.get("cardinalities"):
print(f"ERROR while trying to add cardinalities to class '{res_name}'. No cardinalities submitted. At"
f"least one direct cardinality is required to create a class with dsp-tools.")
continue

new_res_class = None
try:
last_modification_date, newresclass = ResourceClass(con=con,
context=newontology.context,
ontology_id=newontology.id,
name=resname,
superclasses=super_classes,
label=reslabel,
comment=rescomment).create(last_modification_date)
newontology.lastModificationDate = last_modification_date
last_modification_date, new_res_class = ResourceClass(con=con,
context=new_ontology.context,
ontology_id=new_ontology.id,
name=res_name,
superclasses=super_classes,
label=res_label,
comment=res_comment).create(
last_modification_date)
except BaseError as err:
print("Creating resource class failed:", err.message)
exit(1)
newresclasses[newresclass.id] = newresclass
print(
f"ERROR while trying to create resource class {res_name}. The error message was {err.message}")
except Exception as exception:
print(
f"ERROR while trying to create resource class {res_name}. The error message was {exception}")
new_res_classes[new_res_class.id] = new_res_class
new_ontology.lastModificationDate = last_modification_date

if verbose:
print("New resource class:")
newresclass.print()
print("Created resource class:")
new_res_class.print()

# create the property classes
propclasses = ontology["properties"]
newpropclasses: Dict[str, ResourceClass] = {}
for propclass in propclasses:
propname = propclass.get("name")
proplabel = LangString(propclass.get("labels"))
# get the super-property/ies if defined. Valid forms are:
# - "prefix:superproperty" : fully qualified name of property in another ontology. The prefix has to
# be defined in the prefixes part.
# - "superproperty" : Use of super-property defined in the knora-api ontology
# if omitted, "knora-api:hasValue" is assumed
if propclass.get("super") is not None:
super_props = list(map(lambda a: a if ':' in a else "knora-api:" + a, propclass["super"]))
for prop_class in ontology.get("properties"):
prop_name = prop_class.get("name")
prop_label = LangString(prop_class.get("labels"))

# get the super-property/ies if defined, valid forms are:
# - "prefix:super-property" : fully qualified name of property in another ontology. The prefix has to be
# defined in the prefixes part.
# - "super-property" : super-property defined in the knora-api ontology
# - if omitted, "knora-api:hasValue" is assumed

if prop_class.get("super"):
super_props = []
for super_class in prop_class.get("super"):
if ':' in super_class:
super_props.append(super_class)
else:
super_props.append(knora_api_prefix + super_class)
else:
super_props = ["knora-api:hasValue"]
# get the "object" if defined. Valid forms are:
# - "prefix:object_name" : fully qualified object. The prefix has to be defined in the prefixes part.
# - ":object_name" : The object is defined in the current ontology.
# - "object_name" : The object is defined in "knora-api"
if propclass.get("object") is not None:
tmp = propclass["object"].split(':')

# get the "object" if defined, valid forms are:
# - "prefix:object_name" : fully qualified object. The prefix has to be defined in the prefixes part.
# - ":object_name" : The object is defined in the current ontology.
# - "object_name" : The object is defined in "knora-api"

if prop_class.get("object"):
tmp = prop_class.get("object").split(':')
if len(tmp) > 1:
if tmp[0]:
object = propclass["object"] # fully qualified name
prop_object = prop_class.get("object") # fully qualified name
else:
if verbose:
newontology.print()
object = newontology.name + ':' + tmp[1]
prop_object = new_ontology.name + ':' + tmp[1] # object refers to actual ontology
else:
object = "knora-api:" + propclass["object"]
else:
object = None

if propclass.get("subject") is not None:
subject = propclass["subject"]
prop_object = knora_api_prefix + prop_class.get("object") # object refers to knora-api
else:
subject = None
gui_element = propclass.get("gui_element")
gui_attributes = propclass.get("gui_attributes")
if gui_attributes is not None and gui_attributes.get("hlist") is not None:
gui_attributes['hlist'] = "<" + list_root_nodes[gui_attributes['hlist']]["id"] + ">"
propcomment = propclass.get("comment")
if propcomment is not None:
propcomment = LangString(propcomment)
prop_object = None
prop_subject = prop_class.get("subject")
gui_element = prop_class.get("gui_element")
gui_attributes = prop_class.get("gui_attributes")
if gui_attributes and gui_attributes.get("hlist"):
gui_attributes["hlist"] = "<" + list_root_nodes[gui_attributes["hlist"]]["id"] + ">"
prop_comment = prop_class.get("comment")
if prop_comment:
prop_comment = LangString(prop_comment)
else:
propcomment = "no comment given"
prop_comment = "no comment given"

new_prop_class = None
try:
last_modification_date, newpropclass = PropertyClass(con=con,
context=newontology.context,
label=proplabel,
name=propname,
ontology_id=newontology.id,
superproperties=super_props,
object=object,
subject=subject,
gui_element="salsah-gui:" + gui_element,
gui_attributes=gui_attributes,
comment=propcomment).create(last_modification_date)
newontology.lastModificationDate = last_modification_date
last_modification_date, new_prop_class = PropertyClass(con=con,
context=new_ontology.context,
label=prop_label,
name=prop_name,
ontology_id=new_ontology.id,
superproperties=super_props,
object=prop_object,
subject=prop_subject,
gui_element="salsah-gui:" + gui_element,
gui_attributes=gui_attributes,
comment=prop_comment).create(
last_modification_date)
except BaseError as err:
print("Creating property class failed:", err.message)
return False
newpropclasses[newpropclass.id] = newpropclass
print(
f"ERROR while trying to create property class {prop_name}. The error message was: {err.message}"
)
except Exception as exception:
print(
f"ERROR while trying to create property class {prop_name}. The error message was: {exception}")

new_ontology.lastModificationDate = last_modification_date
if verbose:
print("New property class:")
newpropclass.print()
print("Created property:")
new_prop_class.print()

# Add cardinalities
# Add cardinality/ies to class
switcher = {
"1": Cardinality.C_1,
"0-1": Cardinality.C_0_1,
"0-n": Cardinality.C_0_n,
"1-n": Cardinality.C_1_n
}
for resclass in resclasses:
for cardinfo in resclass["cardinalities"]:
rc = newresclasses.get(newontology.id + '#' + resclass["name"])
cardinality = switcher[cardinfo["cardinality"]]
tmp = cardinfo["propname"].split(':')
if len(tmp) > 1:
if tmp[0]:
propid = cardinfo["propname"] # fully qualified name

for res_class in ontology.get("resources"):
if res_class.get("cardinalities"):
for card_info in res_class.get("cardinalities"):
rc = new_res_classes.get(new_ontology.id + '#' + res_class.get("name"))
cardinality = switcher[card_info.get("cardinality")]
prop_name_for_card = card_info.get("propname")
tmp = prop_name_for_card.split(":")
if len(tmp) > 1:
if tmp[0]:
prop_id = prop_name_for_card # fully qualified name
else:
prop_id = new_ontology.name + ":" + tmp[1] # prop name refers to actual ontology
else:
propid = newontology.name + ':' + tmp[1]
else:
propid = "knora-api:" + cardinfo["propname"]
gui_order = cardinfo.get('gui_order')
last_modification_date = rc.addProperty(
property_id=propid,
cardinality=cardinality,
gui_order=gui_order,
last_modification_date=last_modification_date)
newontology.lastModificationDate = last_modification_date
return True
prop_id = knora_api_prefix + prop_name_for_card # prop name refers to knora-api

try:
last_modification_date = rc.addProperty(
property_id=prop_id,
cardinality=cardinality,
gui_order=card_info.get("gui_order"),
last_modification_date=last_modification_date)
except BaseError as err:
print(
f"ERROR while trying to add cardinality {prop_id} to resource class {res_class.get('name')}."
f"The error message was {err.message}")
except Exception as exception:
print(
f"ERROR while trying to add cardinality {prop_id} to resource class {res_class.get('name')}."
f"The error message was {exception}")

new_ontology.lastModificationDate = last_modification_date

0 comments on commit 5604a5b

Please sign in to comment.