Mercurial > repos > guerler > springsuite
comparison planemo/lib/python3.7/site-packages/schema_salad/jsonld_context.py @ 1:56ad4e20f292 draft
"planemo upload commit 6eee67778febed82ddd413c3ca40b3183a3898f1"
| author | guerler |
|---|---|
| date | Fri, 31 Jul 2020 00:32:28 -0400 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 0:d30785e31577 | 1:56ad4e20f292 |
|---|---|
| 1 from __future__ import absolute_import | |
| 2 | |
| 3 import logging | |
| 4 from typing import ( | |
| 5 Any, | |
| 6 Dict, | |
| 7 Iterable, | |
| 8 List, | |
| 9 MutableMapping, | |
| 10 MutableSequence, | |
| 11 Optional, | |
| 12 Tuple, | |
| 13 Union, | |
| 14 cast, | |
| 15 ) | |
| 16 | |
| 17 import rdflib | |
| 18 import rdflib.namespace | |
| 19 import six | |
| 20 from rdflib import Graph, URIRef | |
| 21 from rdflib.namespace import RDF, RDFS | |
| 22 from six.moves import urllib | |
| 23 from typing_extensions import Text # pylint: disable=unused-import | |
| 24 | |
| 25 from .exceptions import SchemaException | |
| 26 from .ref_resolver import ContextType # pylint: disable=unused-import | |
| 27 from .utils import aslist, json_dumps | |
| 28 | |
| 29 # move to a regular typing import when Python 3.3-3.6 is no longer supported | |
| 30 | |
| 31 | |
| 32 _logger = logging.getLogger("salad") | |
| 33 | |
| 34 | |
| 35 def pred( | |
| 36 datatype, # type: MutableMapping[Text, Union[Dict[Text, Text], Text]] | |
| 37 field, # type: Optional[Dict[Text, Any]] | |
| 38 name, # type: str | |
| 39 context, # type: ContextType | |
| 40 defaultBase, # type: str | |
| 41 namespaces, # type: Dict[Text, rdflib.namespace.Namespace] | |
| 42 ): # type: (...) -> Union[Dict[Text, Text], Text] | |
| 43 split = urllib.parse.urlsplit(name) | |
| 44 | |
| 45 vee = None # type: Optional[Text] | |
| 46 | |
| 47 if split.scheme != "": | |
| 48 vee = name | |
| 49 (ns, ln) = rdflib.namespace.split_uri(six.text_type(vee)) | |
| 50 name = ln | |
| 51 if ns[0:-1] in namespaces: | |
| 52 vee = six.text_type(namespaces[ns[0:-1]][ln]) | |
| 53 _logger.debug("name, v %s %s", name, vee) | |
| 54 | |
| 55 v = None # type: Optional[Dict[Text, Any]] | |
| 56 | |
| 57 if field is not None and "jsonldPredicate" in field: | |
| 58 if isinstance(field["jsonldPredicate"], MutableMapping): | |
| 59 v = {} | |
| 60 for k, val in field["jsonldPredicate"].items(): | |
| 61 v[("@" + k[1:] if k.startswith("_") else k)] = val | |
| 62 if "@id" not in v: | |
| 63 v["@id"] = vee | |
| 64 else: | |
| 65 v = field["jsonldPredicate"] | |
| 66 elif "jsonldPredicate" in datatype: | |
| 67 if isinstance(datatype["jsonldPredicate"], Iterable): | |
| 68 for d in datatype["jsonldPredicate"]: | |
| 69 if isinstance(d, MutableMapping): | |
| 70 if d["symbol"] == name: | |
| 71 v = d["predicate"] | |
| 72 else: | |
| 73 raise SchemaException( | |
| 74 "entries in the jsonldPredicate List must be " "Dictionaries" | |
| 75 ) | |
| 76 else: | |
| 77 raise SchemaException("jsonldPredicate must be a List of Dictionaries.") | |
| 78 | |
| 79 ret = v or vee | |
| 80 | |
| 81 if not ret: | |
| 82 ret = defaultBase + name | |
| 83 | |
| 84 if name in context: | |
| 85 if context[name] != ret: | |
| 86 raise SchemaException( | |
| 87 "Predicate collision on {}, '{}' != '{}'".format( | |
| 88 name, context[name], ret | |
| 89 ) | |
| 90 ) | |
| 91 else: | |
| 92 _logger.debug("Adding to context '%s' %s (%s)", name, ret, type(ret)) | |
| 93 context[name] = ret | |
| 94 | |
| 95 return ret | |
| 96 | |
| 97 | |
| 98 def process_type( | |
| 99 t, # type: MutableMapping[Text, Any] | |
| 100 g, # type: Graph | |
| 101 context, # type: ContextType | |
| 102 defaultBase, # type: str | |
| 103 namespaces, # type: Dict[Text, rdflib.namespace.Namespace] | |
| 104 defaultPrefix, # type: str | |
| 105 ): # type: (...) -> None | |
| 106 if t["type"] not in ("record", "enum"): | |
| 107 return | |
| 108 | |
| 109 if "name" in t: | |
| 110 recordname = t["name"] | |
| 111 | |
| 112 _logger.debug("Processing %s %s\n", t.get("type"), t) | |
| 113 | |
| 114 classnode = URIRef(recordname) | |
| 115 g.add((classnode, RDF.type, RDFS.Class)) | |
| 116 | |
| 117 split = urllib.parse.urlsplit(recordname) | |
| 118 predicate = recordname | |
| 119 if t.get("inVocab", True): | |
| 120 if split.scheme: | |
| 121 (ns, ln) = rdflib.namespace.split_uri(six.text_type(recordname)) | |
| 122 predicate = recordname | |
| 123 recordname = ln | |
| 124 else: | |
| 125 predicate = "{}:{}".format(defaultPrefix, recordname) | |
| 126 | |
| 127 if context.get(recordname, predicate) != predicate: | |
| 128 raise SchemaException( | |
| 129 "Predicate collision on '{}', '{}' != '{}'".format( | |
| 130 recordname, context[recordname], predicate | |
| 131 ) | |
| 132 ) | |
| 133 | |
| 134 if not recordname: | |
| 135 raise SchemaException("Unable to find/derive recordname for {}".format(t)) | |
| 136 | |
| 137 _logger.debug( | |
| 138 "Adding to context '%s' %s (%s)", recordname, predicate, type(predicate) | |
| 139 ) | |
| 140 context[recordname] = predicate | |
| 141 | |
| 142 if t["type"] == "record": | |
| 143 for i in t.get("fields", []): | |
| 144 fieldname = i["name"] | |
| 145 | |
| 146 _logger.debug("Processing field %s", i) | |
| 147 | |
| 148 v = pred( | |
| 149 t, i, fieldname, context, defaultPrefix, namespaces | |
| 150 ) # type: Union[Dict[Any, Any], Text, None] | |
| 151 | |
| 152 if isinstance(v, six.string_types): | |
| 153 v = v if v[0] != "@" else None | |
| 154 elif v is not None: | |
| 155 v = v["_@id"] if v.get("_@id", "@")[0] != "@" else None | |
| 156 | |
| 157 if bool(v): | |
| 158 (ns, ln) = rdflib.namespace.split_uri(six.text_type(v)) | |
| 159 if ns[0:-1] in namespaces: | |
| 160 propnode = namespaces[ns[0:-1]][ln] | |
| 161 else: | |
| 162 propnode = URIRef(v) | |
| 163 | |
| 164 g.add((propnode, RDF.type, RDF.Property)) | |
| 165 g.add((propnode, RDFS.domain, classnode)) | |
| 166 | |
| 167 # TODO generate range from datatype. | |
| 168 | |
| 169 if isinstance(i["type"], MutableMapping): | |
| 170 process_type( | |
| 171 i["type"], g, context, defaultBase, namespaces, defaultPrefix | |
| 172 ) | |
| 173 | |
| 174 if "extends" in t: | |
| 175 for e in aslist(t["extends"]): | |
| 176 g.add((classnode, RDFS.subClassOf, URIRef(e))) | |
| 177 elif t["type"] == "enum": | |
| 178 _logger.debug("Processing enum %s", t.get("name")) | |
| 179 | |
| 180 for i in t["symbols"]: | |
| 181 pred(t, None, i, context, defaultBase, namespaces) | |
| 182 | |
| 183 | |
| 184 def salad_to_jsonld_context( | |
| 185 j, # type: Iterable[MutableMapping[Text, Any]] | |
| 186 schema_ctx, # type: MutableMapping[Text, Any] | |
| 187 ): # type: (...) -> Tuple[ContextType, Graph] | |
| 188 context = {} # type: ContextType | |
| 189 namespaces = {} | |
| 190 g = Graph() | |
| 191 defaultPrefix = "" | |
| 192 | |
| 193 for k, v in schema_ctx.items(): | |
| 194 context[k] = v | |
| 195 namespaces[k] = rdflib.namespace.Namespace(v) | |
| 196 | |
| 197 if "@base" in context: | |
| 198 defaultBase = cast(str, context["@base"]) | |
| 199 del context["@base"] | |
| 200 else: | |
| 201 defaultBase = "" | |
| 202 | |
| 203 for k, v in namespaces.items(): | |
| 204 g.bind(str(k), v) | |
| 205 | |
| 206 for t in j: | |
| 207 process_type(t, g, context, defaultBase, namespaces, defaultPrefix) | |
| 208 | |
| 209 return (context, g) | |
| 210 | |
| 211 | |
| 212 def fix_jsonld_ids( | |
| 213 obj, # type: Union[List[Dict[Text, Any]], MutableMapping[Text, Any]] | |
| 214 ids, # type: List[Text] | |
| 215 ): # type: (...) -> None | |
| 216 if isinstance(obj, MutableMapping): | |
| 217 for i in ids: | |
| 218 if i in obj: | |
| 219 obj["@id"] = obj[i] | |
| 220 for v in obj.values(): | |
| 221 fix_jsonld_ids(v, ids) | |
| 222 if isinstance(obj, MutableSequence): | |
| 223 for entry in obj: | |
| 224 fix_jsonld_ids(entry, ids) | |
| 225 | |
| 226 | |
| 227 def makerdf( | |
| 228 workflow, # type: Text | |
| 229 wf, # type: Union[List[Dict[Text, Any]], MutableMapping[Text, Any]] | |
| 230 ctx, # type: ContextType | |
| 231 graph=None, # type: Optional[Graph] | |
| 232 ): # type: (...) -> Graph | |
| 233 prefixes = {} | |
| 234 idfields = [] | |
| 235 for k, v in six.iteritems(ctx): | |
| 236 if isinstance(v, MutableMapping): | |
| 237 url = v["@id"] | |
| 238 else: | |
| 239 url = v | |
| 240 if url == "@id": | |
| 241 idfields.append(k) | |
| 242 doc_url, frg = urllib.parse.urldefrag(url) | |
| 243 if "/" in frg: | |
| 244 p = frg.split("/")[0] | |
| 245 prefixes[p] = u"{}#{}/".format(doc_url, p) | |
| 246 | |
| 247 fix_jsonld_ids(wf, idfields) | |
| 248 | |
| 249 if graph is None: | |
| 250 g = Graph() | |
| 251 else: | |
| 252 g = graph | |
| 253 | |
| 254 if isinstance(wf, MutableSequence): | |
| 255 for w in wf: | |
| 256 w["@context"] = ctx | |
| 257 g.parse(data=json_dumps(w), format="json-ld", publicID=str(workflow)) | |
| 258 else: | |
| 259 wf["@context"] = ctx | |
| 260 g.parse(data=json_dumps(wf), format="json-ld", publicID=str(workflow)) | |
| 261 | |
| 262 # Bug in json-ld loader causes @id fields to be added to the graph | |
| 263 for sub, pred, obj in g.triples((None, URIRef("@id"), None)): | |
| 264 g.remove((sub, pred, obj)) | |
| 265 | |
| 266 for k2, v2 in six.iteritems(prefixes): | |
| 267 g.namespace_manager.bind(k2, v2) | |
| 268 | |
| 269 return g |
