pylode 2.8.3__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyLODE-2.8.3.dist-info/LICENSE +643 -0
- pyLODE-2.8.3.dist-info/METADATA +772 -0
- pyLODE-2.8.3.dist-info/RECORD +63 -0
- pyLODE-2.8.3.dist-info/WHEEL +6 -0
- pyLODE-2.8.3.dist-info/entry_points.txt +3 -0
- pyLODE-2.8.3.dist-info/top_level.txt +1 -0
- pylode/__init__.py +5 -0
- pylode/cli.py +191 -0
- pylode/common.py +191 -0
- pylode/curies.py +2208 -0
- pylode/profiles/__init__.py +4 -0
- pylode/profiles/base.py +463 -0
- pylode/profiles/ontdoc.py +1283 -0
- pylode/profiles/prof.py +276 -0
- pylode/profiles/profile.py +135 -0
- pylode/profiles/vocpub.py +605 -0
- pylode/server.py +24 -0
- pylode/style/pylode.css +179 -0
- pylode/templates/agent.html +11 -0
- pylode/templates/agent.md +11 -0
- pylode/templates/namespaces.html +9 -0
- pylode/templates/namespaces.md +5 -0
- pylode/templates/ontdoc/class.html +121 -0
- pylode/templates/ontdoc/class.md +40 -0
- pylode/templates/ontdoc/classes.html +11 -0
- pylode/templates/ontdoc/classes.md +7 -0
- pylode/templates/ontdoc/document.html +42 -0
- pylode/templates/ontdoc/document.md +16 -0
- pylode/templates/ontdoc/metadata.html +127 -0
- pylode/templates/ontdoc/metadata.md +111 -0
- pylode/templates/ontdoc/named_individual.html +65 -0
- pylode/templates/ontdoc/named_individual.md +25 -0
- pylode/templates/ontdoc/named_individuals.html +11 -0
- pylode/templates/ontdoc/named_individuals.md +7 -0
- pylode/templates/ontdoc/properties.html +65 -0
- pylode/templates/ontdoc/properties.md +45 -0
- pylode/templates/ontdoc/property.html +129 -0
- pylode/templates/ontdoc/property.md +37 -0
- pylode/templates/orcid.html +19 -0
- pylode/templates/orcid.md +1 -0
- pylode/templates/prof/prof.html +38 -0
- pylode/templates/prof/prof.md +16 -0
- pylode/templates/prof/profile.html +90 -0
- pylode/templates/prof/profile.md +85 -0
- pylode/templates/prof/resource_descriptor.html +49 -0
- pylode/templates/prof/resource_descriptor.md +21 -0
- pylode/templates/vocpub/collection.html +51 -0
- pylode/templates/vocpub/collection.md +23 -0
- pylode/templates/vocpub/collections.html +11 -0
- pylode/templates/vocpub/collections.md +7 -0
- pylode/templates/vocpub/concept.html +115 -0
- pylode/templates/vocpub/concept.md +40 -0
- pylode/templates/vocpub/concept_hierarchy.html +80 -0
- pylode/templates/vocpub/concept_scheme.html +105 -0
- pylode/templates/vocpub/concept_scheme.md +102 -0
- pylode/templates/vocpub/concepts.html +9 -0
- pylode/templates/vocpub/concepts.md +7 -0
- pylode/templates/vocpub/taxonomy.html +42 -0
- pylode/templates/vocpub/taxonomy.md +17 -0
- pylode/tests/__init__.py +0 -0
- pylode/tests/test_ont_html_description.py +7 -0
- pylode/tests/test_skos.py +8 -0
- pylode/tests/tests_all.py +50 -0
pylode/profiles/base.py
ADDED
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
import collections
|
|
2
|
+
|
|
3
|
+
from jinja2 import Environment, FileSystemLoader
|
|
4
|
+
from rdflib import SDO, SKOS, OWL, URIRef, RDF, PROF, Literal, XSD, Graph, Namespace, FOAF
|
|
5
|
+
|
|
6
|
+
import pylode.profiles.profile
|
|
7
|
+
from pylode.common import TEMPLATES_DIR
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class BaseProfile:
|
|
11
|
+
def __init__(self, g, source_info, outputformat="html", exclude_css=False, default_language="en", get_curies_online=False):
|
|
12
|
+
self.outputformat = outputformat
|
|
13
|
+
self.exclude_css = exclude_css
|
|
14
|
+
self.default_language = default_language
|
|
15
|
+
self.get_curies_online = get_curies_online
|
|
16
|
+
self.default_namespace = None
|
|
17
|
+
self.G = g
|
|
18
|
+
self.source_info = source_info
|
|
19
|
+
self.G.bind("sdo", SDO)
|
|
20
|
+
self.G.bind("skos", SKOS)
|
|
21
|
+
self.NAMESPACES = collections.OrderedDict()
|
|
22
|
+
self.FIDS = {}
|
|
23
|
+
self.METADATA = {}
|
|
24
|
+
|
|
25
|
+
def _load_template(self, template_file):
|
|
26
|
+
return Environment(loader=FileSystemLoader(TEMPLATES_DIR)).get_template(template_file)
|
|
27
|
+
|
|
28
|
+
def _expand_graph(self):
|
|
29
|
+
"""Abstract method"""
|
|
30
|
+
|
|
31
|
+
# TODO: replace this with rdflib native method
|
|
32
|
+
def _get_namespace_from_uri(self, uri):
|
|
33
|
+
# split on hash
|
|
34
|
+
segments = uri.split("#")
|
|
35
|
+
if len(segments) == 2:
|
|
36
|
+
return segments[0] + "#"
|
|
37
|
+
else:
|
|
38
|
+
segments = uri.split("/")
|
|
39
|
+
if len(segments) > 1:
|
|
40
|
+
return "/".join(segments[0:-1]) + "/"
|
|
41
|
+
else:
|
|
42
|
+
return None
|
|
43
|
+
|
|
44
|
+
def _get_uri_id(self, uri):
|
|
45
|
+
# split on hash
|
|
46
|
+
segments = uri.split("#")
|
|
47
|
+
if len(segments) == 2:
|
|
48
|
+
return segments[1]
|
|
49
|
+
else:
|
|
50
|
+
return uri.split("/")[-1] # could return None if URI ends in /
|
|
51
|
+
|
|
52
|
+
def _make_formatted_uri(self, uri):
|
|
53
|
+
"""Abstract method"""
|
|
54
|
+
|
|
55
|
+
def _get_curie(self, uri):
|
|
56
|
+
n = self._get_namespace_from_uri(str(uri))
|
|
57
|
+
for k, v in self.NAMESPACES.items():
|
|
58
|
+
if v == n or v.strip("/#") == n:
|
|
59
|
+
if k == ":":
|
|
60
|
+
return "{}".format(self._get_uri_id(uri))
|
|
61
|
+
else:
|
|
62
|
+
return "{}:{}".format(k, self._get_uri_id(uri))
|
|
63
|
+
|
|
64
|
+
# if no match, return the original URI
|
|
65
|
+
return uri
|
|
66
|
+
|
|
67
|
+
def _get_curie_prefix(uself, uri, existing_curies):
|
|
68
|
+
ns_count = 0
|
|
69
|
+
|
|
70
|
+
from curies import CURIES
|
|
71
|
+
|
|
72
|
+
# TODO: replace this with a once-per run update CURIES function
|
|
73
|
+
def get_curie_online(uri):
|
|
74
|
+
import requests
|
|
75
|
+
try:
|
|
76
|
+
r = requests.get(
|
|
77
|
+
"http://prefix.cc/reverse", params={"uri": uri, "format": "txt"}
|
|
78
|
+
)
|
|
79
|
+
if r.status_code == 200:
|
|
80
|
+
# primitive check to see if it really is prefix.cc replying with a text/plain response
|
|
81
|
+
if r.headers["Content-Type"] == "text/plain":
|
|
82
|
+
return r.text.split("\t")[0]
|
|
83
|
+
else:
|
|
84
|
+
return None
|
|
85
|
+
else:
|
|
86
|
+
return None
|
|
87
|
+
except requests.exceptions.ConnectionError:
|
|
88
|
+
# presumably this module can't access the internet or prefix.cc is down
|
|
89
|
+
return None
|
|
90
|
+
|
|
91
|
+
def get_curie_from_namespace(uri, existing_curies, ns_count):
|
|
92
|
+
# strip off trailing hash or slash and return last path segment
|
|
93
|
+
c = uri.rstrip("#/").split("/")[-1]
|
|
94
|
+
|
|
95
|
+
# prevent CURIE collision = return nsX (X int) if we already have this one
|
|
96
|
+
if c in existing_curies:
|
|
97
|
+
ns_count += 1
|
|
98
|
+
return "ns" + str(ns_count)
|
|
99
|
+
|
|
100
|
+
return c
|
|
101
|
+
|
|
102
|
+
# attempt to look up the well-known curie for this Namespace in http://prefix.cc dump
|
|
103
|
+
for k, v in CURIES.items():
|
|
104
|
+
if v == uri:
|
|
105
|
+
return k
|
|
106
|
+
|
|
107
|
+
# attempt to look up the well-known CURIE for this Namespace using http://prefix.cc online (more up-to-date)
|
|
108
|
+
c = get_curie_online(uri)
|
|
109
|
+
if c is not None:
|
|
110
|
+
return c
|
|
111
|
+
|
|
112
|
+
# can't find CURIE online so make up one
|
|
113
|
+
c = get_curie_from_namespace(uri, existing_curies, ns_count)
|
|
114
|
+
return c if c is not None else ""
|
|
115
|
+
|
|
116
|
+
def _make_title_from_uri(self, uri):
|
|
117
|
+
# can't tolerate any URI faults so return None if anything is wrong
|
|
118
|
+
|
|
119
|
+
# URIs with no path segments or ending in slash
|
|
120
|
+
segments = uri.split("/")
|
|
121
|
+
if len(segments[-1]) < 1:
|
|
122
|
+
return None
|
|
123
|
+
|
|
124
|
+
# URIs with only a domain - no path segments
|
|
125
|
+
if len(segments) < 4:
|
|
126
|
+
return None
|
|
127
|
+
|
|
128
|
+
# URIs ending in hash
|
|
129
|
+
if segments[-1].endswith("#"):
|
|
130
|
+
return None
|
|
131
|
+
|
|
132
|
+
return (
|
|
133
|
+
segments[-1].split("#")[-1]
|
|
134
|
+
if segments[-1].split("#")[-1] != ""
|
|
135
|
+
else segments[-1].split("#")[-2]
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
def _extract_namespaces(self):
|
|
139
|
+
"""
|
|
140
|
+
First we get the namespaces from rdflib
|
|
141
|
+
|
|
142
|
+
Then we cycle through all the URIs in the graph (all s, p & o),
|
|
143
|
+
create a set of them,
|
|
144
|
+
extract their base URIS (i.e. a non-duplicative list of them)
|
|
145
|
+
see if they are in the namespaces,
|
|
146
|
+
if not, generate their CURIE and add them to namespaces
|
|
147
|
+
"""
|
|
148
|
+
# get declared namespaces, keyed by URI
|
|
149
|
+
ns = {}
|
|
150
|
+
uri_bases = set()
|
|
151
|
+
for k, v in self.G.namespaces():
|
|
152
|
+
ns[str(v)] = k
|
|
153
|
+
|
|
154
|
+
# get other namespaces by extracting base URIs from all URIs
|
|
155
|
+
for s, p, o in self.G:
|
|
156
|
+
# exclude certain annotation URIs
|
|
157
|
+
# and individuals (SDO.identifier)
|
|
158
|
+
# exclude known annoying URIs (ORCID)
|
|
159
|
+
if (
|
|
160
|
+
p == OWL.versionIRI
|
|
161
|
+
or p == OWL.imports
|
|
162
|
+
or p == SDO.identifier
|
|
163
|
+
or str(o).startswith("https://orcid")
|
|
164
|
+
):
|
|
165
|
+
pass
|
|
166
|
+
else:
|
|
167
|
+
# add only URI subjects (not Blank Nodes)
|
|
168
|
+
if type(s) == URIRef:
|
|
169
|
+
uri_bases.add(self._get_namespace_from_uri(str(s)))
|
|
170
|
+
|
|
171
|
+
# predicates are always URIs so add them all
|
|
172
|
+
uri_bases.add(self._get_namespace_from_uri(str(p)))
|
|
173
|
+
|
|
174
|
+
# add only URI objects (not Blank Nodes or Literals), exclude emails
|
|
175
|
+
if type(o) == URIRef and "@" not in str(o):
|
|
176
|
+
uri_bases.add(self._get_namespace_from_uri(str(o)))
|
|
177
|
+
|
|
178
|
+
# for the de-duplicated URIs, if the uri_base is not in namespaces, get CURIE and add it
|
|
179
|
+
for uri_base in uri_bases:
|
|
180
|
+
if ns.get(uri_base) is None:
|
|
181
|
+
if self.get_curies_online:
|
|
182
|
+
uri_prefix = self._get_curie_prefix(uri_base, [x for x in ns.values()])
|
|
183
|
+
ns[uri_base] = uri_prefix
|
|
184
|
+
|
|
185
|
+
# invert the key/values in instances
|
|
186
|
+
for k, v in sorted(ns.items(), key=lambda x: x[1]):
|
|
187
|
+
if v == "": # can't use empty dict keys in Python
|
|
188
|
+
self.NAMESPACES[":"] = k
|
|
189
|
+
else:
|
|
190
|
+
self.NAMESPACES[v] = k
|
|
191
|
+
|
|
192
|
+
del(self.NAMESPACES["xml"]) # that bloody XML namespace has to go!
|
|
193
|
+
|
|
194
|
+
def _get_default_namespace(self):
|
|
195
|
+
self.METADATA["default_namespace"] = None
|
|
196
|
+
|
|
197
|
+
# if this ontology declares a preferred URI, use that
|
|
198
|
+
if self.METADATA.get("preferredNamespaceUri"):
|
|
199
|
+
self.METADATA["default_namespace"] = self.METADATA.get(
|
|
200
|
+
"preferredNamespaceUri"
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
ont_uri = ""
|
|
204
|
+
|
|
205
|
+
# if not, try the URI of the ontology compared to all prefixes
|
|
206
|
+
for s in self.G.subjects(predicate=RDF.type, object=OWL.Ontology):
|
|
207
|
+
ont_uri = str(s)
|
|
208
|
+
|
|
209
|
+
for s in self.G.subjects(predicate=RDF.type, object=SKOS.ConceptScheme):
|
|
210
|
+
ont_uri = str(s)
|
|
211
|
+
|
|
212
|
+
for s in self.G.subjects(predicate=RDF.type, object=PROF.Profile):
|
|
213
|
+
ont_uri = str(s)
|
|
214
|
+
|
|
215
|
+
for k, v in self.NAMESPACES.items():
|
|
216
|
+
# i.e. the ontology URI is the same as the default namespace + / or #
|
|
217
|
+
if v == ont_uri + "/" or v == ont_uri + "#":
|
|
218
|
+
self.METADATA["default_namespace"] = v
|
|
219
|
+
|
|
220
|
+
if self.NAMESPACES.get("") is not None:
|
|
221
|
+
del self.NAMESPACES[""]
|
|
222
|
+
|
|
223
|
+
def _make_namespaces(self):
|
|
224
|
+
return BaseProfile._load_template(self, "namespaces." + self.outputformat).render(
|
|
225
|
+
namespaces=self.NAMESPACES,
|
|
226
|
+
default_namespace=self.METADATA["default_namespace"],
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
# makes the fragment ID for a class, property, Named Individual (any entity) based on URI or name
|
|
230
|
+
def _make_fid(self, title, uri):
|
|
231
|
+
# does this URI already have a fid?
|
|
232
|
+
existing_fid = self.FIDS.get(uri)
|
|
233
|
+
if existing_fid is not None:
|
|
234
|
+
return existing_fid
|
|
235
|
+
|
|
236
|
+
# if we get here, there is no fid, so make one
|
|
237
|
+
def _remove_non_ascii_chars(s):
|
|
238
|
+
return "".join(j for j in s if ord(j) < 128).replace("&", "")
|
|
239
|
+
|
|
240
|
+
# try creating an ID from label
|
|
241
|
+
# lowercase, remove spaces, escape all non-ASCII chars
|
|
242
|
+
if title is not None:
|
|
243
|
+
fid = _remove_non_ascii_chars(title.replace(" ", ""))
|
|
244
|
+
|
|
245
|
+
# do not return fid if it's already in use
|
|
246
|
+
if fid not in self.FIDS.values():
|
|
247
|
+
self.FIDS[uri] = fid
|
|
248
|
+
return fid
|
|
249
|
+
|
|
250
|
+
# this fid is already present so generate a new one from the URI instead
|
|
251
|
+
|
|
252
|
+
# split URI for last slash segment
|
|
253
|
+
segments = uri.split("/")
|
|
254
|
+
# return None for empty string - URI ends in slash
|
|
255
|
+
if len(segments[-1]) < 1:
|
|
256
|
+
return None
|
|
257
|
+
|
|
258
|
+
# return None for domains, i.e. ['http:', '', '{domain}'] - no path segments
|
|
259
|
+
if len(segments) < 4:
|
|
260
|
+
return None
|
|
261
|
+
|
|
262
|
+
# split out hash URIs
|
|
263
|
+
# remove any training hashes
|
|
264
|
+
if segments[-1].endswith("#"):
|
|
265
|
+
return None
|
|
266
|
+
|
|
267
|
+
fid = (
|
|
268
|
+
segments[-1].split("#")[-1]
|
|
269
|
+
if segments[-1].split("#")[-1] != ""
|
|
270
|
+
else segments[-1].split("#")[-2]
|
|
271
|
+
)
|
|
272
|
+
# fid = fid.lower()
|
|
273
|
+
|
|
274
|
+
# do not return fid if it's already in use
|
|
275
|
+
if fid not in self.FIDS.values():
|
|
276
|
+
self.FIDS[uri] = fid
|
|
277
|
+
return fid
|
|
278
|
+
else:
|
|
279
|
+
# since it's in use but we've exhausted generation options, just add 1 to existing fid name
|
|
280
|
+
self.FIDS[uri] = fid + "1"
|
|
281
|
+
return fid + "1" # yeah yeah, there could be more than one but unlikely
|
|
282
|
+
|
|
283
|
+
def _make_schemaorg_metadata(self):
|
|
284
|
+
uri = URIRef(self.METADATA.get("uri"))
|
|
285
|
+
name = Literal(self.METADATA.get("title"))
|
|
286
|
+
publishers = ""
|
|
287
|
+
creators = ""
|
|
288
|
+
if self.METADATA.get("created") is not None:
|
|
289
|
+
date_created = Literal(self.METADATA.get("created"), datatype=XSD.date)
|
|
290
|
+
if self.METADATA.get("modified") is not None:
|
|
291
|
+
date_modified = Literal(self.METADATA.get("modified"), datatype=XSD.date)
|
|
292
|
+
if self.METADATA.get("description") is not None:
|
|
293
|
+
description = Literal(self.METADATA.get("description"))
|
|
294
|
+
if self.METADATA.get("license") is not None:
|
|
295
|
+
if self.outputformat == "md":
|
|
296
|
+
license = URIRef(self.METADATA.get("license").split('(')[0].strip('[]'))
|
|
297
|
+
else:
|
|
298
|
+
license = URIRef(self.METADATA.get("license").split('"')[1])
|
|
299
|
+
else:
|
|
300
|
+
license = None
|
|
301
|
+
if self.METADATA.get("rights") is not None:
|
|
302
|
+
rights = Literal(self.METADATA.get("rights"))
|
|
303
|
+
copyright_holder = ""
|
|
304
|
+
if self.METADATA.get("created") is not None:
|
|
305
|
+
copyright_year = Literal(
|
|
306
|
+
self.METADATA.get("created").split("-")[0], datatype=XSD.int
|
|
307
|
+
)
|
|
308
|
+
if self.METADATA.get("repository") is not None:
|
|
309
|
+
if self.outputformat == "md":
|
|
310
|
+
repository = URIRef(self.METADATA.get("repository").split('(')[0].strip('[]'))
|
|
311
|
+
else:
|
|
312
|
+
repository = URIRef(self.METADATA.get("repository").split('"')[1])
|
|
313
|
+
|
|
314
|
+
"""
|
|
315
|
+
@prefix sdo: <https://schema.org/> .
|
|
316
|
+
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
|
|
317
|
+
|
|
318
|
+
<http://linked.data.gov.au/def/crs> a sdo:DigitalDocument ;
|
|
319
|
+
sdo:name "CRS Ontology" ;
|
|
320
|
+
sdo:publisher <http://catalogue.linked.data.gov.au/org/naa> ;
|
|
321
|
+
sdo:creator [
|
|
322
|
+
sdo:name "Nicholas J. Car" ;
|
|
323
|
+
sdo:identifier <http://orcid.org/0000-0002-8742-7730> ;
|
|
324
|
+
sdo:email <nicholas.car@csiro.au> ;
|
|
325
|
+
sdo:memberOf [
|
|
326
|
+
sdo:name "CSIRO" ;
|
|
327
|
+
sdo:identifier <http://catalogue.linked.data.gov.au/org/csiro> ;
|
|
328
|
+
] ;
|
|
329
|
+
] ;
|
|
330
|
+
sdo:date_created "2018-09-10"^^xsd:date ;
|
|
331
|
+
sdo:date_modified "2019-05-31"^^xsd:date ;
|
|
332
|
+
sdo:license <https://creativecommons.org/licenses/by/4.0/> ;
|
|
333
|
+
|
|
334
|
+
sdo:copyrightHolder <http://catalogue.linked.data.gov.au/org/naa> ;
|
|
335
|
+
sdo:copyright_year "2019"^^xsd:gYear ;
|
|
336
|
+
sdo:encodingFormat <https://w3id.org/mediatype/text/html> ;
|
|
337
|
+
.
|
|
338
|
+
|
|
339
|
+
<http://catalogue.linked.data.gov.au/org/naa>
|
|
340
|
+
a sdo:Organization ;
|
|
341
|
+
sdo:name "National Archives of Australia" ;
|
|
342
|
+
sdo:identifier <http://catalogue.linked.data.gov.au/org/naa> ;
|
|
343
|
+
.
|
|
344
|
+
"""
|
|
345
|
+
g = Graph()
|
|
346
|
+
SDO = Namespace("https://schema.org/")
|
|
347
|
+
g.bind("sdo", SDO)
|
|
348
|
+
g.bind("xsd", XSD)
|
|
349
|
+
|
|
350
|
+
g.add((uri, RDF.type, SDO.DefinedTermSet))
|
|
351
|
+
g.add((uri, SDO.name, name))
|
|
352
|
+
# g.add((uri, SDO.publishers, SDO.DigitalDocument))
|
|
353
|
+
# g.add((uri, SDO.creators, SDO.DigitalDocument))
|
|
354
|
+
if self.METADATA.get("date_created") is not None:
|
|
355
|
+
g.add((uri, SDO.dateCreated, date_created))
|
|
356
|
+
if self.METADATA.get("date_modified") is not None:
|
|
357
|
+
g.add((uri, SDO.dateModified, date_modified))
|
|
358
|
+
if self.METADATA.get("description") is not None:
|
|
359
|
+
g.add((uri, SDO.description, description))
|
|
360
|
+
if license is not None:
|
|
361
|
+
g.add((uri, SDO.license, license))
|
|
362
|
+
if self.METADATA.get("rights") is not None:
|
|
363
|
+
g.add((uri, SDO.rights, rights))
|
|
364
|
+
# g.add((uri, SDO.copyrightHolder, copyrightHolder))
|
|
365
|
+
if self.METADATA.get("copyright_year") is not None:
|
|
366
|
+
g.add((uri, SDO.copyrightYear, copyright_year))
|
|
367
|
+
if self.METADATA.get("repository") is not None:
|
|
368
|
+
g.add((uri, SDO.codeRepository, repository))
|
|
369
|
+
|
|
370
|
+
return g.serialize(format="json-ld").decode("utf-8")
|
|
371
|
+
|
|
372
|
+
def _make_agent_link(self, name, url=None, email=None, affiliation=None):
|
|
373
|
+
if self.outputformat == "md":
|
|
374
|
+
orcid = None
|
|
375
|
+
if url is not None:
|
|
376
|
+
if "orcid.org" in url:
|
|
377
|
+
orcid = BaseProfile._load_template(self, "orcid.md").render()
|
|
378
|
+
|
|
379
|
+
return BaseProfile._load_template(self, "agent.md").render(
|
|
380
|
+
url=url,
|
|
381
|
+
name=name,
|
|
382
|
+
orcid=orcid,
|
|
383
|
+
email=email.replace("mailto:", "") if email is not None else None,
|
|
384
|
+
affiliation=affiliation
|
|
385
|
+
)
|
|
386
|
+
else: # self.outputformat == "html":
|
|
387
|
+
orcid = None
|
|
388
|
+
if url is not None:
|
|
389
|
+
if "orcid.org" in url:
|
|
390
|
+
orcid = BaseProfile._load_template(self, "orcid.html").render()
|
|
391
|
+
|
|
392
|
+
return BaseProfile._load_template(self, "agent.html").render(
|
|
393
|
+
url=url,
|
|
394
|
+
name=name,
|
|
395
|
+
orcid=orcid,
|
|
396
|
+
email=email.replace("mailto:", "") if email is not None else None,
|
|
397
|
+
affiliation=affiliation
|
|
398
|
+
)
|
|
399
|
+
|
|
400
|
+
def _make_agent_html(self, agent_node):
|
|
401
|
+
# we understand foaf:name, foaf:homepage & sdo:name & sdo:identifier & sdo:email (as a URI)
|
|
402
|
+
# TODO: cater for other Agent representations
|
|
403
|
+
|
|
404
|
+
name = None
|
|
405
|
+
url = None
|
|
406
|
+
email = None
|
|
407
|
+
org_name = None
|
|
408
|
+
org_url = None
|
|
409
|
+
org_email = None
|
|
410
|
+
for p, o in self.G.predicate_objects(subject=agent_node):
|
|
411
|
+
if p in [FOAF.homepage, SDO.identifier]:
|
|
412
|
+
url = str(o)
|
|
413
|
+
elif p in [FOAF.name, SDO.name]:
|
|
414
|
+
name = str(o)
|
|
415
|
+
elif p in [FOAF.mbox, SDO.email]:
|
|
416
|
+
email = str(o).split("/")[-1].split("#")[-1] # remove base URI leaving only email address
|
|
417
|
+
elif p in [SDO.memberOf, SDO.affiliation]:
|
|
418
|
+
for p2, o2 in self.G.predicate_objects(subject=o):
|
|
419
|
+
if p2 in [FOAF.homepage, SDO.identifier, SDO.url]: # TODO: split homepage form IDs, cater for rdfs:seeAlso
|
|
420
|
+
org_url = str(o2)
|
|
421
|
+
elif p2 in [FOAF.name, SDO.name]:
|
|
422
|
+
org_name = str(o2)
|
|
423
|
+
elif p in [FOAF.mbox, SDO.email]:
|
|
424
|
+
org_email = str(o2).split("/")[-1].split("#")[-1] # remove base URI leaving only email address
|
|
425
|
+
|
|
426
|
+
# use the URI of the Agent for its URL if no FOAF.homepage or SDO.identifier has been set
|
|
427
|
+
if url is None and type(agent_node) == URIRef:
|
|
428
|
+
url = str(agent_node)
|
|
429
|
+
|
|
430
|
+
agent = self._make_agent_link(name, url=url, email=email)
|
|
431
|
+
|
|
432
|
+
if org_name is not None:
|
|
433
|
+
org = self._make_agent_link(org_name, url=org_url, email=org_email)
|
|
434
|
+
agent += " of " + org
|
|
435
|
+
|
|
436
|
+
return agent
|
|
437
|
+
|
|
438
|
+
def _make_source_file_link(self):
|
|
439
|
+
# if the source is a URI, use that
|
|
440
|
+
# if it's a file path, only use the file name
|
|
441
|
+
if self.source_info[0].startswith("http"):
|
|
442
|
+
uri_of_rdf = self.source_info[0]
|
|
443
|
+
else:
|
|
444
|
+
uri_of_rdf = self.source_info[0].split("/")[-1]
|
|
445
|
+
if self.outputformat == "md":
|
|
446
|
+
return 'RDF ([{}]({}))'.format(
|
|
447
|
+
uri_of_rdf, self.source_info[1]
|
|
448
|
+
)
|
|
449
|
+
else:
|
|
450
|
+
return '<a href="{}">RDF ({})</a>'.format(
|
|
451
|
+
uri_of_rdf, self.source_info[1]
|
|
452
|
+
)
|
|
453
|
+
|
|
454
|
+
def generate_document(self):
|
|
455
|
+
if self.ouputformat == "md":
|
|
456
|
+
return """# Empty pyLODE output (Markdown)"""
|
|
457
|
+
else: # HTML
|
|
458
|
+
return """<!DOCTYPE html>""" \
|
|
459
|
+
"""<html lang="en">""" \
|
|
460
|
+
""" <body>""" \
|
|
461
|
+
""" <h1>Empty pyLODE output (HTML)</h1>""" \
|
|
462
|
+
"""</body>""" \
|
|
463
|
+
"""</html>"""
|