Skip to content
Snippets Groups Projects

Development

347 files
+ 13661
773
Compare changes
  • Side-by-side
  • Inline

Files

+ 39
230
###############################################################################
# Coscine Python3 Client
# Coscine Python Client
# Copyright (c) 2018-2021 RWTH Aachen University
# Contact: coscine@itc.rwth-aachen.de
# Git: https://git.rwth-aachen.de/coscine/docs/public/wiki/-/tree/master/
# Git: https://git.rwth-aachen.de/coscine/docs/public/coscine-python-client
# Please direct bug reports, feature requests or questions at the URL above
# by opening an issue.
###############################################################################
# This python wrapper implements a client for the Coscine API.
# Coscine is an open source project at RWTH Aachen University for
# the management of research data.
# Visit https://coscine.rwth-aachen.de for more information.
# Visit https://coscine.de for more information.
###############################################################################
import os
from collections.abc import MutableMapping
from collections import OrderedDict
from .exceptions import *
from .form import Form
###############################################################################
class MetadataForm(MutableMapping):
class MetadataForm(Form):
"""
Coscine Metadata Input Form
Can be used to generate and manipulate json-ld formatted metadata
"""
###############################################################################
def __init__(self, handle, project, resource):
self.store = {}
self._keys = {}
self.vocabulary = {}
self.handle = handle
self.profile = handle.get_application_profile(resource, parse=True)
for entry in self.profile["graph"]:
if "class" in entry:
uri = entry["class"]
name = entry["name"][self.handle.lang]
data = handle.get_instance(project, uri)
lang = self.handle.lang
vocabulary = {}
if lang not in data:
lang = "en"
for entry in data[lang]:
vocabulary[entry["name"]] = entry["value"]
self.vocabulary[name] = vocabulary
self.reset()
###############################################################################
def __getitem__(self, key):
return self.store[key]
###############################################################################
def __setitem__(self, key, value):
if key not in self._keys:
raise KeyError(key)
elif self.is_controlled(key):
vocabulary = self.get_vocabulary(key)
if type(value) is list:
self.store[key] = []
for val in value:
if val in vocabulary:
self.store[key].append(vocabulary[val])
else:
raise VocabularyError(val)
else:
if value in vocabulary:
self.store[key] = vocabulary[value]
else:
raise VocabularyError(value)
else:
self.store[key] = value
###############################################################################
def __delitem__(self, key):
del self.store[key]
###############################################################################
def __iter__(self):
return iter(self.store)
###############################################################################
def __len__(self):
return len(self.store)
def __init__(self, profile: dict, lang: str, entries: list, vocabulary: dict):
super().__init__("Metadata Form", lang, entries, vocabulary)
self.profile = profile
###############################################################################
def __repr__(self):
entries = []
for key in self._keys:
R = " "
C = " "
value = ""
if self.is_required(key):
R = "R"
if self.is_controlled(key):
C = "C"
if key in self.store:
value = " = %s" % self.store[key]
text = " [%s%s] %s%s" % (R, C, key, value)
entries.append(text)
format = \
"_______________________________\n\n" \
" Coscine Metadata Form\n" \
"_______________________________\n\n" \
" [R: Required] [C: Controlled]\n" \
"-------------------------------\n" \
+ "\n".join(entries) + \
"\n_______________________________\n"
return format
###############################################################################
def is_required(self, key):
"""
Determines wether a key is required
Parameters
-----------
key : str
Returns
--------
bool
"""
def parse(self, data: dict):
info = self._keys[key]
if "minCount" in info and info["minCount"] > 0:
return True
else:
return False
###############################################################################
def is_controlled(self, key):
"""
Determines wether a key is controlled by a vocabulary
Parameters
-----------
key : str
Returns
--------
bool
"""
if key in self.vocabulary:
return True
else:
return False
###############################################################################
def get_vocabulary(self, key):
"""
Returns the vocabulary associated with a controlled key
Parameters
-----------
key : str
Returns
--------
dict
"""
if self.is_controlled(key):
return self._keys[key]["vocabulary"]
else:
msg = "Key [%s] is not controlled by a vocabulary!" % key
raise CoscineException(msg)
###############################################################################
def keys(self):
"""
Enumerates the keys of the input form
"""
keys = []
for key in self._keys:
keys.append(key)
return keys
###############################################################################
def reset(self):
"""
Resets the input form to default
Parses JSON-LD metadata into a Metadata Input Form
"""
self.store.clear()
for entry in self.profile["graph"]:
name = entry["name"][self.handle.lang]
self._keys[name] = entry
if self.is_controlled(name):
self._keys[name]["vocabulary"] = self.vocabulary[name]
# Sort the keys according to their application profile order
tuples = sorted(self._keys.items(), key = lambda x: x[1]["order"])
self._keys = OrderedDict(tuples)
k = list(data.keys())[0]
for path in data[k]:
for entry in self.profile["graph"]:
if path == entry["path"]:
key = entry["name"][self._lang]
value = data[k][path][0]["value"]
if self.is_controlled(key):
voc = self.get_vocabulary(key)
keys = list(voc.keys())
values = list(voc.values())
index = values.index(value)
value = keys[index]
self.store[key] = value
break
###############################################################################
def generate(self):
"""
Generates JSON-LD formatted metadata representation
Raises
------
RequirementError
When one or more required have not been set
Returns
--------
JSON-LD formatted metadata
"""
def generate(self) -> dict:
metadata = {}
RDFTYPE = "http://www.w3.org/1999/02/22-rdf-syntax-ns#type"
# Set application profile type used for the metadata
# Set application profile type used by metadata
metadata[RDFTYPE] = [{
"type": "uri",
"value": self.profile["id"]
@@ -247,16 +62,24 @@ class MetadataForm(MutableMapping):
# Set metadata fields
for key in self._keys:
entry = self._keys[key]
field = entry["field"]
if key not in self.store:
if self.is_required(key):
missing.append(key)
else:
field = self._keys[key]
path = field["path"]
path = entry["path"]
value = self.store[key]
if entry["flags"] & MetadataForm.LIST:
if type(value) is not list and type(value) is not tuple:
raise ValueError("Expected iterable value for key %s" % key)
if self.is_controlled(key):
voc = self.get_vocabulary(key)
value = voc[value]
metadata[path] = [{
"value": self.store[key],
"datatype": field["datatype"],
"type": field["type"]
"value": value,
"datatype": entry["datatype"],
"type": entry["type"]
}]
# Check missing field list
@@ -266,17 +89,3 @@ class MetadataForm(MutableMapping):
return metadata
###############################################################################
def parse(self, data):
"""
Parses JSON-LD metadata into a Metadata Input Form
"""
for path in data:
for entry in self.profile["graph"]:
if path in entry["path"]:
self.store[entry["name"][self.handle.lang]] = data[path][0]["value"]
break
###############################################################################
\ No newline at end of file
Loading