Skip to content
Snippets Groups Projects
Commit d7b0555b authored by Matthias Stefan Bodenbenner's avatar Matthias Stefan Bodenbenner
Browse files

9.1.0 - added license field to semantics

parent 76a523ce
Branches
Tags 9.1.0
No related merge requests found
[![Build](https://git-ce.rwth-aachen.de/wzl-mq-ms/forschung-lehre/lava/unified-device-interface/python/badges/master/pipeline.svg)](https://git-ce.rwth-aachen.de/wzl-mq-ms/forschung-lehre/lava/unified-device-interface/python/commits/master)
# Python Unified Device Interface
Current stable version: 9.0.1
Current stable version: 9.1.0
## Installation
1. Install the WZL-UDI package via pip
......@@ -58,6 +58,9 @@ Funded by the Deutsche Forschungsgemeinschaft (DFG, German Research Foundation)
## Recent changes
**9.1.0** - 2024-01-17
- the license for profiles, metadata and data is now provided anc can be specified in the config file
**9.0.1** - 2024-01-11
- bug fix of semantic name resolution
......
......@@ -4,7 +4,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
long_description = fh.read()
setup(name='wzl-udi',
version='9.0.1',
version='9.1.0',
url='https://git-ce.rwth-aachen.de/wzl-mq-public/soil/python',
project_urls={
"Bug Tracker": "https://git-ce.rwth-aachen.de/wzl-mq-public/soil/python/-/issues",
......
......@@ -19,7 +19,7 @@ from .error import ChildNotFoundException
from .function import Function
from .measurement import Measurement
from .parameter import Parameter
from .semantics import Namespaces
from .semantics import Namespaces, Semantics
from ..utils import root_logger
from ..utils.constants import HTTP_GET
from ..utils.error import SerialisationException, DeviceException, UserException
......@@ -399,12 +399,22 @@ class Component(Element):
try:
return super().resolve_semantic_path(suffix)
except ChildNotFoundException:
# check if the path fits one of the components children
for child in self.children:
try:
return child.resolve_semantic_path(suffix)
except ChildNotFoundException:
continue
# check if the profile of this component imports a shape which matches the path
imported_profiles = list(self._metadata_profile.objects(predicate=Namespaces.owl.imports))
for imported_profile in imported_profiles:
if imported_profile.toPython().replace(Semantics.namespace, '') == suffix:
# TODO implement loading and returning of the base components profile
# (is not present as element, so might require adaption of the method signature,
# might also be called recursively, if the base component has own bases which are queried)
raise ChildNotFoundException('Profiles of base components can currently not be returned.')
raise ChildNotFoundException('Could not resolve the semantic path.')
@property
......
......@@ -6,6 +6,7 @@ from typing import Any, Dict, List
import rdflib
from .error import ChildNotFoundException
from .semantics import Namespaces, Semantics
from ..utils.constants import BASE_UUID_PATTERN, HTTP_GET
from ..utils.error import SerialisationException
......@@ -101,12 +102,15 @@ class Element(ABC):
shape_filename = os.path.join(profiles_path, f"{self._profilename}.shacl.ttl")
self._metadata_profile = rdflib.Graph()
self._metadata_profile.parse(shape_filename)
self._metadata_profile.add((rdflib.URIRef(Semantics.namespace[self._profilename]), Namespaces.dcterms.license,
Semantics.profile_license))
# load metadata
self._semantic_name = f'{parent_name}{self.uuid[4:].capitalize()}'
metadata_filename = os.path.join(metadata_path, f"{self._semantic_name}.ttl")
self._metadata = rdflib.Graph()
self._metadata.parse(metadata_filename)
self._metadata.add((rdflib.URIRef(self._semantic_name), Namespaces.schema.license, Semantics.metadata_license))
@abstractmethod
def serialize_semantics(self, kind: str) -> rdflib.Graph:
......
......@@ -197,6 +197,7 @@ class Measurement(Figure):
data_graph.add((observation_subject, Namespaces.sosa.hasResult,
Semantics.namespace[f'{self._semantic_name}Measurement']))
data_graph.add((observation_subject, Namespaces.sosa.madeBySensor, sensor_triples[0][2]))
data_graph.add((observation_subject, Namespaces.schema.license, Semantics.data_license))
# create result node
unit_triples = list(self._metadata.triples((None, Namespaces.qudt.applicableUnit, None)))
......@@ -207,6 +208,7 @@ class Measurement(Figure):
data_graph.add((measurement_subject, Namespaces.rdf.type, rdflib.URIRef(Namespaces.soil.Measurement)))
data_graph.add((measurement_subject, Namespaces.sosa.isResultOf, observation_subject))
data_graph.add((measurement_subject, Namespaces.qudt.unit, unit_triples[0][2]))
data_graph.add((measurement_subject, Namespaces.schema.license, Semantics.data_license))
rdf_value = Figure.serialize_value(data_graph, self.__getitem__('value', 0))
......
import re
import urllib
import rdflib
from ..utils.constants import URL_PATTERN
class Semantics(object):
prefix: str = None
url: str = None
namespace: rdflib.Namespace = None
profile_license: rdflib.term.Identifier = rdflib.URIRef("https://spdx.org/licenses/CC-BY-4.0.html")
metadata_license: rdflib.term.Identifier = rdflib.URIRef("https://spdx.org/licenses/CC-BY-NC-ND-4.0.html")
data_license: rdflib.term.Identifier = rdflib.Literal("All rights reserved.")
def __init__(self, config: dict[str, str]):
Semantics.prefix = config['prefix']
Semantics.url = config['url']
Semantics.namespace = rdflib.Namespace(config['url'])
if 'profile-license' in config:
if re.match(URL_PATTERN, config['profile-license']):
Semantics.profile_license = rdflib.URIRef(config['profile-license'])
else:
Semantics.profile_license = rdflib.Literal(config['profile-license'])
if 'metadata-license' in config:
if re.match(URL_PATTERN, config['metadata-license']):
Semantics.metadata_license = rdflib.URIRef(config['metadata-license'])
else:
Semantics.metadata_license = rdflib.Literal(config['metadata-license'])
if 'data-license' in config:
if re.match(URL_PATTERN, config['data-license']):
Semantics.data_license = rdflib.URIRef(config['data-license'])
else:
Semantics.data_license = rdflib.Literal(config['data-license'])
class Namespaces(object):
dcterms = rdflib.namespace.DCTERMS
m4i = rdflib.Namespace('http://w3id.org/nfdi4ing/metadata4ing#')
owl = rdflib.Namespace('http://www.w3.org/2002/07/owl#')
quantitykind = rdflib.Namespace('http://qudt.org/vocab/quantitykind/')
qudt = rdflib.Namespace('http://qudt.org/schema/qudt/')
rdf = rdflib.namespace.RDF
......
......@@ -298,6 +298,7 @@ class StreamScheduler(object):
# try to send semantic data package
try:
url, semantic_data = job.semantic_data(self._model)
url = url.replace('https://', '').replace('http://', '')
if self._dataformat == 'json':
message = semantic_data.serialize(format='json-ld')
elif self._dataformat == 'xml':
......
import re
HTTP_GET = 0
HTTP_OPTIONS = 1
BASE_UUID_PATTERN = r'[0-9A-Za-z-_]{3,}'
URL_PATTERN = re.compile(
r'^(?:http|ftp)s?://' # http:// or https://
r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|' # domain...
r'localhost|' # localhost...
r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' # ...or ip
r'(?::\d+)?' # optional port
r'(?:/?|[/?]\S+)$', re.IGNORECASE)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment