Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(create-ontology): within an ontology, references to the ontology itself are not possible (DEV-135) #130

Merged
merged 12 commits into from Dec 1, 2021
Merged
6 changes: 3 additions & 3 deletions .github/workflows/ckeck-pr-title.yml
Expand Up @@ -12,9 +12,9 @@ jobs:
# check PR title
- uses: deepakputhraya/action-pr-title@master
with:
regex: '([a-z])+(\(([a-z\-_ ])+\))?!?: [a-z]([a-zA-Z-\.\d \(\)\[\]#_])+$' # Regex the title should match.
allowed_prefixes: 'fix,refactor,feat,docs,chore,style,test' # title should start with the given prefix
disallowed_prefixes: 'feature,hotfix' # title should not start with the given prefix
regex: '([a-z])+(\(([a-z\-_ ])+\))?!?: [a-z]([a-zA-Z-\.\d \(\)\[\]#_,])+$' # Regex the title should match.
allowed_prefixes: "fix,refactor,feat,docs,chore,style,test" # title should start with the given prefix
disallowed_prefixes: "feature,hotfix" # title should not start with the given prefix
prefix_case_sensitive: true # title prefix are case insensitive
min_length: 7 # Min length of the title
max_length: 120 # Max length of the title
3 changes: 3 additions & 0 deletions .gitignore
Expand Up @@ -68,3 +68,6 @@ out.json
# bazel
/bazel-*
/.ijwb/

# for testing in development
tmp/
2 changes: 1 addition & 1 deletion Makefile
Expand Up @@ -54,7 +54,7 @@ install-requirements: ## install requirements
.PHONY: install
install: ## install from source (runs setup.py)
python3 -m pip install --upgrade pip
pip3 install .
pip3 install -e .

.PHONY: test
test: clean local-tmp clone-dsp-repo dsp-stack ## run all tests
Expand Down
39 changes: 37 additions & 2 deletions docs/dsp-tools-create.md
Expand Up @@ -48,6 +48,8 @@ A complete data model definition for DSP looks like this:

### "prefixes" object

(optional)

`"prefixes": { "prefix": "<iri>", ...}`

The `prefixes` object contains the prefixes of external ontologies that are used in the current project. All prefixes
Expand All @@ -64,14 +66,21 @@ full qualified IRI each time it is used. So, instead of writing a property calle
}
```

Note that prefixes can be defined for the ontologies defined in this file, but this is only necessary if the ontology
needs to be referred to explicitly by another ontology within the same file.

### "$schema" object

(required)

The `$schema` object refers to the JSON schema for DSP data model definitions and is mandatory.

`"$schema": "https://raw.githubusercontent.com/dasch-swiss/dsp-tools/main/knora/dsplib/schemas/ontology.json"`

### "project" object

(required)

`"project": {"key": "<value>", ...}`

The `project` object contains all resources and properties of the ontology as well as some information about the
Expand Down Expand Up @@ -128,38 +137,50 @@ In the following section all fields of the `project` object are explained in det

### Shortcode

(required)

`"shortcode": "<4-hex-characters>"`

The shortcode has to be unique and is represented by a 4 digit hexadecimal string. The shortcode has to be provided by the DaSCH.

### Shortname

(required)

`"shortname": "<string>"`

The shortname has to be unique. It should be in the form of a [xsd:NCNAME](https://www.w3.org/TR/xmlschema11-2/#NCName). This means a
string without blanks or special characters but `-` and `_` are allowed (although not as first character).

### Longname

(required)

`"longname": "<string>"`

The longname is a string that provides the full name of the project.

### Descriptions

(required)

`"descriptions": {"<lang>": "<string>", ...}`

The description is represented as a collection of strings with language tags (currently "en", "de", "fr" and "it" are
supported). It is the description of the project.

### Keywords

(required)

`"keywords": ["<string>", "<string>", ...]`

Keywords are represented as an array of strings and are used to describe and/or tag the project.

### Lists

(optional)

`"lists": [<list-definition>,<list-definition>,...]`

Lists can be used to provide controlled vocabularies and can be "flat" or "hierarchical". One advantage of the use of
Expand Down Expand Up @@ -331,6 +352,8 @@ The `lists` element is optional. If not used, it should be omitted.

### Groups

(optional)

`"groups": [<group-definition>, <group-definition>,...]`

The `groups` object contains groups definitions. This is used to specify the permissions a user gets. A project may
Expand Down Expand Up @@ -364,6 +387,8 @@ The `groups` element is optional. If not used, it should be omitted. It is curre

### Users

(optional)

`"users": [<user-definition>, <user-definition>,...]`

This object contains user definitions. A user has the following elements:
Expand Down Expand Up @@ -410,6 +435,8 @@ The `users` element is optional. If not used, it should be omitted.

### Ontologies

(required)

An ontology is a formal representation of a set of terminologies which finally represent real world objects.
Dependencies, attributes and relations of and between the individual components of the set are recorded in a logical,
formal language. In contrast to a taxonomy, which defines a mere hierarchical structure within a range of terms, an
Expand Down Expand Up @@ -467,19 +494,25 @@ Example of an `ontologies` object:

#### Name

(required)

`"name": "<string>"`

The ontology's (short) name should be in the form of a [xsd:NCNAME](https://www.w3.org/TR/xmlschema11-2/#NCName). This
means a string without blanks or special characters but `-` and `_` are allowed (although not as first character).

#### Label

(required)

`"label": "<string>"`

A string that provides the full name of the ontology.

#### Properties

(required)

`"properties": [<property-definition>, <property-definition>, ...]`

A `properties` array contains all properties used to describe resources in the ontology. A property has to be of a
Expand Down Expand Up @@ -896,11 +929,11 @@ Represents a node of a (possibly hierarchical) list

- `Radio`: A GUI element for _ListValue_. A set of radio buttons. This works only with flat lists.
- _gui_attributes_:
- `hlist=<list-name>` (mandatory): The reference of a [list](#lists) root node
- `hlist=<list-name>` (required): The reference of a [list](#lists) root node
- `List`: A GUI element for _ListValue_. A list of values to select one from. This GUI element should be chosen for
hierarchical lists or flat lists that could be expanded to hierarchical lists in the future.
- _gui_attributes_:
- `hlist=<list-name>` (mandatory): The reference of a [list](#lists) root node
- `hlist=<list-name>` (required): The reference of a [list](#lists) root node

*Example:*

Expand Down Expand Up @@ -1029,6 +1062,8 @@ Example of a `properties` object:

#### Resources

(required)

The resource classes are the primary entities of the data model. They are the actual objects inside a terminology space.
A resource class can be seen as a template for the representation of a real object that is represented in the DSP. A
resource class defines properties (_data fields_). For each of these properties a data type as well as the cardinality
Expand Down
2 changes: 1 addition & 1 deletion knora/dsplib/models/connection.py
Expand Up @@ -69,7 +69,7 @@ def get_token(self) -> str:
def token(self) -> str:
return self._token

def start_logging(self):
def start_logging(self) -> None:
self._log = True

def stop_logging(self):
Expand Down
30 changes: 15 additions & 15 deletions knora/dsplib/models/helpers.py
Expand Up @@ -3,7 +3,7 @@
from dataclasses import dataclass
from enum import Enum, unique
from traceback import format_exc
from typing import NewType, List, Dict, Optional, Any, Union, Pattern
from typing import NewType, List, Dict, Optional, Any, Tuple, Union, Pattern

from pystrict import strict

Expand All @@ -26,7 +26,7 @@ class OntoInfo:
ContextType = NewType("ContextType", Dict[str, OntoInfo])


def LINE():
def LINE() -> int:
return sys._getframe(1).f_lineno


Expand Down Expand Up @@ -94,7 +94,7 @@ def __init__(self, context: 'Context'):
self._prefixes = [x for x in self._context.context]
self._index = 0

def __next__(self):
def __next__(self) -> Tuple[Optional[str], Optional[OntoInfo]]:
if len(self._context.context) == 0 and self._index == 0:
return None, None
elif self._index < len(self._context.context):
Expand All @@ -112,9 +112,9 @@ class Context:
"""
_context: ContextType
_rcontext: Dict[str, str]
_exp: Pattern
_exp: Pattern[str]

common_ontologies: ContextType = {
common_ontologies = ContextType({
"foaf": OntoInfo("http://xmlns.com/foaf/0.1/", False),
"dc": OntoInfo("http://purl.org/dc/elements/1.1/", False),
"dcterms": OntoInfo("http://purl.org/dc/terms/", False),
Expand All @@ -123,19 +123,19 @@ class Context:
"bibtex": OntoInfo("http://purl.org/net/nknouf/ns/bibtex", True),
"bibo": OntoInfo("http://purl.org/ontology/bibo/", False),
"cidoc": OntoInfo("http://purl.org/NET/cidoc-crm/core", True)
}
})

knora_ontologies: ContextType = {
knora_ontologies = ContextType({
"knora-api": OntoInfo("http://api.knora.org/ontology/knora-api/v2", True),
"salsah-gui": OntoInfo("http://api.knora.org/ontology/salsah-gui/v2", True)
}
})

base_ontologies: ContextType = {
base_ontologies = ContextType({
"rdf": OntoInfo("http://www.w3.org/1999/02/22-rdf-syntax-ns", True),
"rdfs": OntoInfo("http://www.w3.org/2000/01/rdf-schema", True),
"owl": OntoInfo("http://www.w3.org/2002/07/owl", True),
"xsd": OntoInfo("http://www.w3.org/2001/XMLSchema", True)
}
})

def __is_iri(self, val: str) -> bool:
"""
Expand Down Expand Up @@ -164,7 +164,7 @@ def __init__(self, context: Optional[Dict[str, str]] = None):
cleaned_input: Dict[str, str] = {prefix: onto for (prefix, onto) in context.items()
if self.base_ontologies.get(prefix) is None and self.knora_ontologies.get(
prefix) is None}
self._context = {}
self._context = ContextType({})
for prefix, onto in cleaned_input.items():
self._context[prefix] = OntoInfo(onto[:-1], True) if onto.endswith('#') else OntoInfo(onto, False)
#
Expand All @@ -180,14 +180,14 @@ def __init__(self, context: Optional[Dict[str, str]] = None):
if self._context.get(cc[0]) is None:
self._context[cc[0]] = cc[1]
else:
BalduinLandolt marked this conversation as resolved.
Show resolved Hide resolved
self._context = {
self._context = ContextType({
"rdf": OntoInfo("http://www.w3.org/1999/02/22-rdf-syntax-ns", True),
"rdfs": OntoInfo("http://www.w3.org/2000/01/rdf-schema", True),
"owl": OntoInfo("http://www.w3.org/2002/07/owl", True),
"xsd": OntoInfo("http://www.w3.org/2001/XMLSchema", True),
"knora-api": OntoInfo("http://api.knora.org/ontology/knora-api/v2", True),
"salsah-gui": OntoInfo("http://api.knora.org/ontology/salsah-gui/v2", True)
}
})
self._rcontext = dict(map(lambda x: (x[1].iri, x[0]), self._context.items()))

def __len__(self) -> int:
Expand All @@ -196,7 +196,7 @@ def __len__(self) -> int:
def __getitem__(self, key: str) -> OntoInfo:
return self._context[key]

def __setitem__(self, key: str, value: OntoInfo):
def __setitem__(self, key: str, value: OntoInfo) -> None:
self._context[key] = value
self._rcontext[value.iri] = key

Expand Down Expand Up @@ -283,7 +283,7 @@ def iri_from_prefix(self, prefix: str) -> Optional[str]:

def prefix_from_iri(self, iri: str) -> Optional[str]:
"""
Get the IRI from a full context that has or has not a a trailing "#". It first searches in the normal list
Get the IRI from a full context that has or has not a trailing "#". It first searches in the normal list
of contexted. If the iri is not found there, it looks in the list common (external) ontologies. If the
ontology is found there, this ontology is added to the list of known ontology and it's prefix is returned.
If nothing is found, None is returns
Expand Down
2 changes: 1 addition & 1 deletion knora/dsplib/models/project.py
Expand Up @@ -515,7 +515,7 @@ def getAllProjects(con: Connection) -> List['Project']:
raise BaseError("Request got no projects!")
return list(map(lambda a: Project.fromJsonObj(con, a), result['projects']))

def print(self):
def print(self) -> None:
"""
print info to stdout

Expand Down
5 changes: 2 additions & 3 deletions knora/dsplib/models/resourceclass.py
Expand Up @@ -669,12 +669,11 @@ def toJsonObj(self, lastModificationDate: LastModificationDate, action: Actions,
def resolve_resref(resref: str):
tmp = resref.split(':')
if len(tmp) > 1:
if tmp[0]:
if tmp[0] and self._context.iri_from_prefix(tmp[0]) != self._ontology_id:
self._context.add_context(tmp[0])
return {"@id": resref} # fully qualified name in the form "prefix:name"
else:
return {"@id": self._context.prefix_from_iri(self._ontology_id) + ':' + tmp[
1]} # ":name" in current ontology
return {"@id": self._context.prefix_from_iri(self._ontology_id) + ':' + tmp[1]} # ":name" in current ontology
else:
return {"@id": "knora-api:" + resref} # no ":", must be from knora-api!

Expand Down
5 changes: 5 additions & 0 deletions knora/dsplib/schemas/ontology.json
Expand Up @@ -221,12 +221,14 @@
},
"properties": {
"type": "array",
"minItems": 1,
"items": {
"$ref": "#/definitions/property"
}
},
"resources": {
"type": "array",
"minItems": 1,
"items": {
"$ref": "#/definitions/resource"
}
Expand Down Expand Up @@ -482,6 +484,7 @@
},
"keywords": {
"type": "array",
"minItems": 1,
"items": {
"type": "string"
}
Expand Down Expand Up @@ -512,6 +515,7 @@
},
"ontologies": {
"type": "array",
"minItems": 1,
"items": {
"$ref": "#/definitions/ontology"
}
Expand All @@ -521,6 +525,7 @@
"shortcode",
"shortname",
"longname",
"descriptions",
"ontologies",
"keywords"
],
Expand Down
6 changes: 3 additions & 3 deletions knora/dsplib/utils/onto_create_lists.py
@@ -1,5 +1,5 @@
import json
from typing import List
from typing import Any, Dict, List

from .expand_all_lists import expand_lists_from_excel
from .onto_validate import validate_ontology
Expand All @@ -8,7 +8,7 @@
from ..models.project import Project


def list_creator(con: Connection, project: Project, parent_node: ListNode, nodes: List[dict]):
def list_creator(con: Connection, project: Project, parent_node: ListNode, nodes: List[Dict[Any, Any]]) -> List[Dict[Any, Any]]:
"""
Creates the list on the DSP server

Expand All @@ -35,7 +35,7 @@ def list_creator(con: Connection, project: Project, parent_node: ListNode, nodes


def create_lists(input_file: str, lists_file: str, server: str, user: str, password: str, verbose: bool,
dump: bool = False):
dump: bool = False) -> Dict[str, Any]:
"""
Creates the lists on the DSP server

Expand Down