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.
Files changed (63) hide show
  1. pyLODE-2.8.3.dist-info/LICENSE +643 -0
  2. pyLODE-2.8.3.dist-info/METADATA +772 -0
  3. pyLODE-2.8.3.dist-info/RECORD +63 -0
  4. pyLODE-2.8.3.dist-info/WHEEL +6 -0
  5. pyLODE-2.8.3.dist-info/entry_points.txt +3 -0
  6. pyLODE-2.8.3.dist-info/top_level.txt +1 -0
  7. pylode/__init__.py +5 -0
  8. pylode/cli.py +191 -0
  9. pylode/common.py +191 -0
  10. pylode/curies.py +2208 -0
  11. pylode/profiles/__init__.py +4 -0
  12. pylode/profiles/base.py +463 -0
  13. pylode/profiles/ontdoc.py +1283 -0
  14. pylode/profiles/prof.py +276 -0
  15. pylode/profiles/profile.py +135 -0
  16. pylode/profiles/vocpub.py +605 -0
  17. pylode/server.py +24 -0
  18. pylode/style/pylode.css +179 -0
  19. pylode/templates/agent.html +11 -0
  20. pylode/templates/agent.md +11 -0
  21. pylode/templates/namespaces.html +9 -0
  22. pylode/templates/namespaces.md +5 -0
  23. pylode/templates/ontdoc/class.html +121 -0
  24. pylode/templates/ontdoc/class.md +40 -0
  25. pylode/templates/ontdoc/classes.html +11 -0
  26. pylode/templates/ontdoc/classes.md +7 -0
  27. pylode/templates/ontdoc/document.html +42 -0
  28. pylode/templates/ontdoc/document.md +16 -0
  29. pylode/templates/ontdoc/metadata.html +127 -0
  30. pylode/templates/ontdoc/metadata.md +111 -0
  31. pylode/templates/ontdoc/named_individual.html +65 -0
  32. pylode/templates/ontdoc/named_individual.md +25 -0
  33. pylode/templates/ontdoc/named_individuals.html +11 -0
  34. pylode/templates/ontdoc/named_individuals.md +7 -0
  35. pylode/templates/ontdoc/properties.html +65 -0
  36. pylode/templates/ontdoc/properties.md +45 -0
  37. pylode/templates/ontdoc/property.html +129 -0
  38. pylode/templates/ontdoc/property.md +37 -0
  39. pylode/templates/orcid.html +19 -0
  40. pylode/templates/orcid.md +1 -0
  41. pylode/templates/prof/prof.html +38 -0
  42. pylode/templates/prof/prof.md +16 -0
  43. pylode/templates/prof/profile.html +90 -0
  44. pylode/templates/prof/profile.md +85 -0
  45. pylode/templates/prof/resource_descriptor.html +49 -0
  46. pylode/templates/prof/resource_descriptor.md +21 -0
  47. pylode/templates/vocpub/collection.html +51 -0
  48. pylode/templates/vocpub/collection.md +23 -0
  49. pylode/templates/vocpub/collections.html +11 -0
  50. pylode/templates/vocpub/collections.md +7 -0
  51. pylode/templates/vocpub/concept.html +115 -0
  52. pylode/templates/vocpub/concept.md +40 -0
  53. pylode/templates/vocpub/concept_hierarchy.html +80 -0
  54. pylode/templates/vocpub/concept_scheme.html +105 -0
  55. pylode/templates/vocpub/concept_scheme.md +102 -0
  56. pylode/templates/vocpub/concepts.html +9 -0
  57. pylode/templates/vocpub/concepts.md +7 -0
  58. pylode/templates/vocpub/taxonomy.html +42 -0
  59. pylode/templates/vocpub/taxonomy.md +17 -0
  60. pylode/tests/__init__.py +0 -0
  61. pylode/tests/test_ont_html_description.py +7 -0
  62. pylode/tests/test_skos.py +8 -0
  63. pylode/tests/tests_all.py +50 -0
@@ -0,0 +1,1283 @@
1
+ from os import path
2
+ from pylode.common import VERSION, TEMPLATES_DIR, STYLE_DIR
3
+ import collections
4
+ from os import path
5
+ import dateutil.parser
6
+ from itertools import chain
7
+ import markdown
8
+ from jinja2 import Environment, FileSystemLoader
9
+ from os.path import join
10
+ from rdflib import URIRef, BNode, Literal
11
+ from rdflib.namespace import DC, DCTERMS, DOAP, OWL, PROV, RDF, RDFS, SDO, SKOS
12
+ from pylode.profiles.base import BaseProfile
13
+
14
+
15
+ class OntDoc(BaseProfile):
16
+ def __init__(self, g, source_info, outputformat="html", exclude_css=False, default_language="en", get_curies_online=False):
17
+ super().__init__(g, source_info, outputformat=outputformat, exclude_css=exclude_css, get_curies_online=False, default_language=default_language)
18
+ self.G.bind("prov", PROV)
19
+ self.CLASSES = collections.OrderedDict()
20
+ self.PROPERTIES = collections.OrderedDict()
21
+ self.NAMED_INDIVIDUALS = collections.OrderedDict()
22
+
23
+ def _make_collection_class_html(self, col_type, col_members):
24
+ if col_type == "owl:unionOf":
25
+ j = " or "
26
+ elif col_type == "owl:intersectionOf":
27
+ j = " and "
28
+ else:
29
+ j = " ? "
30
+ # others...
31
+ return "({})".format(
32
+ j.join([self._make_formatted_uri(x, type="c") for x in col_members])
33
+ )
34
+
35
+ def _make_restriction_html(self, subject, restriction_bn):
36
+ prop = None
37
+ card = None
38
+ cls = None
39
+
40
+ for p2, o2 in self.G.predicate_objects(subject=restriction_bn):
41
+ if p2 != RDF.type:
42
+ if p2 == OWL.onProperty:
43
+ # TODO: add the property type for HTML
44
+ t = None
45
+ if str(o2) in self.PROPERTIES.keys():
46
+ t = self.PROPERTIES[str(o2)]["prop_type"]
47
+ prop = self._make_formatted_uri(str(o2), t)
48
+ elif p2 == OWL.onClass:
49
+ """
50
+ domains = []
51
+ for o in self.G.objects(subject=s, predicate=RDFS.domain):
52
+ if type(o) != BNode:
53
+ domains.append(str(o)) # domains that are just classes
54
+ else:
55
+ # domain collections (unionOf | intersectionOf
56
+ q = '''
57
+ PREFIX owl: <http://www.w3.org/2002/07/owl#>
58
+ PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
59
+
60
+ SELECT ?col_type ?col_member
61
+ WHERE {{
62
+ <{}> rdfs:domain ?domain .
63
+ ?domain owl:unionOf|owl:intersectionOf ?collection .
64
+ ?domain ?col_type ?collection .
65
+ ?collection rdf:rest*/rdf:first ?col_member .
66
+ }}
67
+ '''.format(s)
68
+ collection_type = None
69
+ collection_members = []
70
+ for r in self.G.query(q):
71
+ collection_type = self._get_curie(str(r.col_type))
72
+ collection_members.append(str(r.col_member))
73
+ domains.append((collection_type, collection_members))
74
+ self.PROPERTIES[prop]['domains'] = domains
75
+
76
+ """
77
+ if type(o2) == BNode:
78
+ # onClass collections (unionOf | intersectionOf
79
+ q = """
80
+ PREFIX owl: <http://www.w3.org/2002/07/owl#>
81
+ PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
82
+
83
+ SELECT ?col_type ?col_member
84
+ WHERE {{
85
+ <{}> owl:onClass ?onClass .
86
+ ?onClass owl:unionOf|owl:intersectionOf ?collection .
87
+ ?onClass ?col_type ?collection .
88
+ ?collection rdf:rest*/rdf:first ?col_member .
89
+ }}
90
+ """.format(
91
+ str(subject)
92
+ )
93
+ collection_type = None
94
+ collection_members = []
95
+ for r in self.G.query(q):
96
+ collection_type = self._get_curie(str(r.col_type))
97
+ collection_members.append(str(r.col_member))
98
+
99
+ cls = self._make_collection_class_html(
100
+ collection_type, collection_members
101
+ )
102
+ else:
103
+ cls = self._make_formatted_uri(str(o2), type="c")
104
+ elif p2 in [
105
+ OWL.cardinality,
106
+ OWL.qualifiedCardinality,
107
+ OWL.minCardinality,
108
+ OWL.minQualifiedCardinality,
109
+ OWL.maxCardinality,
110
+ OWL.maxQualifiedCardinality,
111
+ ]:
112
+ if p2 in [OWL.minCardinality, OWL.minQualifiedCardinality]:
113
+ card = "min"
114
+ elif p2 in [OWL.maxCardinality, OWL.maxQualifiedCardinality]:
115
+ card = "max"
116
+ elif p2 in [OWL.cardinality, OWL.qualifiedCardinality]:
117
+ card = "exactly"
118
+
119
+ if self.outputformat == "md":
120
+ card = '**{}** {}'.format(
121
+ card, str(o2)
122
+ )
123
+ else:
124
+ card = '<span class="cardinality">{}</span> {}'.format(
125
+ card, str(o2)
126
+ )
127
+ elif p2 in [OWL.allValuesFrom, OWL.someValuesFrom]:
128
+ if p2 == OWL.allValuesFrom:
129
+ card = "only"
130
+ else: # p2 == OWL.someValuesFrom
131
+ card = "some"
132
+
133
+ if type(o2) == BNode:
134
+ # someValuesFrom collections (unionOf | intersectionOf
135
+ q = """
136
+ PREFIX owl: <http://www.w3.org/2002/07/owl#>
137
+ PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
138
+
139
+ SELECT ?col_type ?col_member
140
+ WHERE {{
141
+ <{0}> ?x _:{1} .
142
+ _:{1} owl:someValuesFrom|owl:allValuesFrom ?bn2 .
143
+ ?bn2 owl:unionOf|owl:intersectionOf ?collection .
144
+ ?s ?col_type ?collection .
145
+ ?collection rdf:rest*/rdf:first ?col_member .
146
+ }}
147
+ """.format(
148
+ str(subject), str(o2)
149
+ )
150
+ collection_type = None
151
+ collection_members = []
152
+ for r in self.G.query(q):
153
+ collection_type = self._get_curie(str(r.col_type))
154
+ collection_members.append(str(r.col_member))
155
+
156
+ c = self._make_collection_class_html(
157
+ collection_type, collection_members
158
+ )
159
+ else:
160
+ c = self._make_formatted_uri(str(o2), type="c")
161
+
162
+ if self.outputformat == "md":
163
+ card = '**{}** {}'.format(card, c)
164
+ else:
165
+ card = '<span class="cardinality">{}</span> {}'.format(card, c)
166
+ elif p2 == OWL.hasValue:
167
+ if self.outputformat == "md":
168
+ card = '**value** {}'.format(
169
+ self._make_formatted_uri(str(o2), type="c")
170
+ )
171
+ else:
172
+ card = '<span class="cardinality">value</span> {}'.format(
173
+ self._make_formatted_uri(str(o2), type="c")
174
+ )
175
+
176
+ restriction = prop + " " + card if card is not None else prop
177
+ restriction = restriction + " " + cls if cls is not None else restriction
178
+
179
+ return restriction
180
+
181
+ def _load_template(self, template_file):
182
+ return Environment(loader=FileSystemLoader(join(TEMPLATES_DIR, "ontdoc"))).get_template(template_file)
183
+
184
+ def _make_formatted_uri(self, uri, type=None):
185
+ # set display to CURIE
186
+ short = self._get_curie(uri)
187
+ # if the URI base is within the default namespace of this ontology
188
+ # use the fragment URI
189
+ # else
190
+ # use the given URI
191
+ uri_base = self._get_namespace_from_uri(uri)
192
+ link = None
193
+ if uri_base == self.METADATA.get("default_namespace"):
194
+ if self.PROPERTIES.get(uri):
195
+ link = "[{}]({})".format(short, self.PROPERTIES[uri]["fid"]) \
196
+ if self.outputformat == "md" \
197
+ else '<a href="#{}">{}</a>'.format(self.PROPERTIES[uri]["fid"], short)
198
+ elif self.CLASSES.get(uri):
199
+ link = "[{}]({})".format(short, self.CLASSES[uri]["fid"]) \
200
+ if self.outputformat == "md" \
201
+ else '<a href="#{}">{}</a>'.format(self.CLASSES[uri]["fid"], short)
202
+ else:
203
+ link = "[{}]({})".format(short, uri) \
204
+ if self.outputformat == "md" \
205
+ else '<a href="{}">{}</a>'.format(uri, short)
206
+
207
+ if link is None:
208
+ link = "[{}]({})".format(short, uri) \
209
+ if self.outputformat == "md" \
210
+ else '<a href="{}">{}</a>'.format(uri, short)
211
+
212
+ if type == "c":
213
+ if self.outputformat == "md":
214
+ suffix = ' (c)'
215
+ else:
216
+ suffix = '<sup class="sup-c" title="class">c</sup>'
217
+ elif type == "op":
218
+ if self.outputformat == "md":
219
+ suffix = ' (op)'
220
+ else:
221
+ suffix = '<sup class="sup-op" title="object property">op</sup>'
222
+ elif type == "fp":
223
+ if self.outputformat == "md":
224
+ suffix = ' (fp)'
225
+ else:
226
+ suffix = '<sup class="sup-fp" title="functional property">fp</sup>'
227
+ elif type == "dp":
228
+ if self.outputformat == "md":
229
+ suffix = ' (dp)'
230
+ else:
231
+ suffix = '<sup class="sup-dp" title="datatype property">dp</sup>'
232
+ elif type == "ap":
233
+ if self.outputformat == "md":
234
+ suffix = ' (ap)'
235
+ else:
236
+ suffix = '<sup class="sup-ap" title="annotation property">ap</sup>'
237
+ elif type == "ni":
238
+ if self.outputformat == "md":
239
+ suffix = ' (ni)'
240
+ else:
241
+ suffix = '<sup class="sup-ni" title="named individual">ni</sup>'
242
+ else:
243
+ suffix = ''
244
+
245
+ return link + suffix
246
+
247
+ def _expand_graph(self):
248
+ # name
249
+ for s, o in chain(
250
+ self.G.subject_objects(DC.title),
251
+ self.G.subject_objects(RDFS.label),
252
+ self.G.subject_objects(SKOS.prefLabel),
253
+ self.G.subject_objects(SDO.name)
254
+ ):
255
+ self.G.add((s, DCTERMS.title, o))
256
+
257
+ # description
258
+ for s, o in chain(
259
+ self.G.subject_objects(DC.description),
260
+ self.G.subject_objects(RDFS.comment),
261
+ self.G.subject_objects(SKOS.definition),
262
+ self.G.subject_objects(SDO.description)
263
+ ):
264
+ self.G.add((s, DCTERMS.description, o))
265
+
266
+ # property types
267
+ for s in chain(
268
+ self.G.subjects(RDF.type, OWL.ObjectProperty),
269
+ self.G.subjects(RDF.type, OWL.FunctionalProperty),
270
+ self.G.subjects(RDF.type, OWL.DatatypeProperty),
271
+ self.G.subjects(RDF.type, OWL.AnnotationProperty)
272
+ ):
273
+ self.G.add((s, RDF.type, RDF.Property))
274
+
275
+ # class types
276
+ for s in self.G.subjects(RDF.type, OWL.Class):
277
+ self.G.add((s, RDF.type, RDFS.Class))
278
+
279
+ # owl:Restrictions from Blank Nodes
280
+ for s in self.G.subjects(OWL.onProperty):
281
+ self.G.add((s, RDF.type, OWL.Restriction))
282
+
283
+ # Agents
284
+ # creator
285
+ for s, o in chain(
286
+ self.G.subject_objects(DC.creator),
287
+ self.G.subject_objects(SDO.creator),
288
+ self.G.subject_objects(SDO.author) # conflate SDO.author with DCTERMS.creator
289
+ ):
290
+ self.G.remove((s, DC.creator, o))
291
+ self.G.remove((s, SDO.creator, o))
292
+ self.G.remove((s, SDO.author, o))
293
+ self.G.add((s, DCTERMS.creator, o))
294
+
295
+ # contributor
296
+ for s, o in chain(
297
+ self.G.subject_objects(DC.contributor),
298
+ self.G.subject_objects(SDO.contributor)
299
+ ):
300
+ self.G.remove((s, DC.contributor, o))
301
+ self.G.remove((s, SDO.contributor, o))
302
+ self.G.add((s, DCTERMS.contributor, o))
303
+
304
+ # publisher
305
+ for s, o in chain(
306
+ self.G.subject_objects(DC.publisher),
307
+ self.G.subject_objects(SDO.publisher)
308
+ ):
309
+ self.G.remove((s, DC.publisher, o))
310
+ self.G.remove((s, SDO.publisher, o))
311
+ self.G.add((s, DCTERMS.publisher, o))
312
+
313
+ def _extract_metadata(self):
314
+ if len(self.CLASSES.keys()) > 0:
315
+ self.METADATA["has_classes"] = True
316
+
317
+ self.METADATA["has_ops"] = False
318
+ self.METADATA["has_fps"] = False
319
+ self.METADATA["has_dps"] = False
320
+ self.METADATA["has_aps"] = False
321
+ self.METADATA["has_ps"] = False
322
+
323
+ if len(self.NAMED_INDIVIDUALS.keys()) > 0:
324
+ self.METADATA["has_nis"] = True
325
+
326
+ for k, v in self.PROPERTIES.items():
327
+ if v.get("prop_type") == "op":
328
+ self.METADATA["has_ops"] = True
329
+ if v.get("prop_type") == "fp":
330
+ self.METADATA["has_fps"] = True
331
+ if v.get("prop_type") == "dp":
332
+ self.METADATA["has_dps"] = True
333
+ if v.get("prop_type") == "ap":
334
+ self.METADATA["has_aps"] = True
335
+ if v.get("prop_type") == "p":
336
+ self.METADATA["has_ps"] = True
337
+
338
+ s_str = None
339
+ self.METADATA["imports"] = set()
340
+ self.METADATA["creators"] = set()
341
+ self.METADATA["contributors"] = set()
342
+ self.METADATA["publishers"] = set()
343
+ self.METADATA["editors"] = set()
344
+ self.METADATA["funders"] = set()
345
+ self.METADATA["translators"] = set()
346
+ for s in self.G.subjects(predicate=RDF.type, object=OWL.Ontology):
347
+ s_str = str(s) # this is the Ontology's URI
348
+ self.METADATA["uri"] = s_str
349
+
350
+ for p, o in self.G.predicate_objects(subject=s):
351
+ if p == OWL.imports:
352
+ self.METADATA["imports"].add(self._make_formatted_uri(o))
353
+
354
+ if p == DCTERMS.title:
355
+ self.METADATA["title"] = str(o)
356
+
357
+ if p == DCTERMS.description:
358
+ self.METADATA["description"] = markdown.markdown(str(o))
359
+
360
+ if p == SKOS.historyNote:
361
+ self.METADATA["historyNote"] = markdown.markdown(str(o))
362
+
363
+ # dates
364
+ if p in [DCTERMS.created, DCTERMS.modified, DCTERMS.issued]:
365
+ date_type = p.split("/")[-1]
366
+ self.METADATA[date_type] = dateutil.parser.parse(str(o)).strftime(
367
+ "%Y-%m-%d"
368
+ )
369
+
370
+ if p == DCTERMS.source:
371
+ if str(o).startswith('http'):
372
+ self.METADATA["source"] = self._make_formatted_uri(o)
373
+ else:
374
+ self.METADATA["source"] = str(o)
375
+
376
+ if p == OWL.versionIRI:
377
+ self.METADATA["versionIRI"] = self._make_formatted_uri(o)
378
+
379
+ if p == OWL.versionInfo:
380
+ self.METADATA["versionInfo"] = str(o)
381
+
382
+ if p == URIRef("http://purl.org/vocab/vann/preferredNamespacePrefix"):
383
+ self.METADATA["preferredNamespacePrefix"] = str(o)
384
+
385
+ if p == URIRef("http://purl.org/vocab/vann/preferredNamespaceUri"):
386
+ self.METADATA["preferredNamespaceUri"] = str(o)
387
+
388
+ if p == DCTERMS.license:
389
+ self.METADATA["license"] = (
390
+ self._make_formatted_uri(o)
391
+ if str(o).startswith("http")
392
+ else str(o)
393
+ )
394
+
395
+ if p == DCTERMS.rights:
396
+ self.METADATA["rights"] = (
397
+ str(o)
398
+ .replace("Copyright", "&copy;")
399
+ .replace("copyright", "&copy;")
400
+ .replace("(c)", "&copy;")
401
+ )
402
+
403
+ # Agents
404
+ if p in [
405
+ DCTERMS.creator, DCTERMS.contributor, DCTERMS.publisher, SDO.editor, SDO.funder, SDO.translator
406
+ ]:
407
+ agent_type = p.split("/")[-1] + "s"
408
+ if type(o) == Literal:
409
+ self.METADATA[agent_type].add(str(o))
410
+ else: # Blank Node or URI
411
+ self.METADATA[agent_type].add(self._make_agent_html(o))
412
+
413
+ if p == PROV.wasGeneratedBy:
414
+ for o2 in self.G.objects(subject=o, predicate=DOAP.repository):
415
+ self.METADATA["repository"] = self._make_formatted_uri(o2)
416
+
417
+ if p == SDO.codeRepository:
418
+ self.METADATA["repository"] = self._make_formatted_uri(o)
419
+
420
+ if self.METADATA.get("title") is None:
421
+ self.METADATA["title"] = "{no title found}"
422
+ # raise ValueError(
423
+ # "Your ontology does not indicate any form of label or title. "
424
+ # "You must declare one of the following for your ontology: rdfs:label, dct:title, skos:prefLabel"
425
+ # )
426
+
427
+ if s_str is None:
428
+ raise Exception(
429
+ "Your RDF file does not define an ontology. "
430
+ "It must contains a declaration such as <...> rdf:type owl:Ontology ."
431
+ )
432
+
433
+ def _extract_classes_uris(self):
434
+ classes = []
435
+ for s in self.G.subjects(predicate=RDF.type, object=RDFS.Class):
436
+ # ignore blank nodes for things like [ owl:unionOf ( ... ) ]
437
+ if type(s) == BNode:
438
+ pass
439
+ else:
440
+ classes.append(str(s))
441
+
442
+ for p in sorted(classes):
443
+ self.CLASSES[p] = {}
444
+
445
+ def _extract_classes(self):
446
+ for cls in self.CLASSES.keys():
447
+ s = URIRef(cls)
448
+ # create Python dict for each class
449
+ self.CLASSES[cls] = {}
450
+
451
+ # basic class properties
452
+ self.CLASSES[cls]["title"] = None
453
+ self.CLASSES[cls]["description"] = None
454
+ self.CLASSES[cls]["scopeNote"] = None
455
+ self.CLASSES[cls]["example"] = None
456
+ self.CLASSES[cls]["isDefinedBy"] = None
457
+ self.CLASSES[cls]["source"] = None
458
+
459
+ for p, o in self.G.predicate_objects(subject=s):
460
+ if p == DCTERMS.title:
461
+ self.CLASSES[cls]["title"] = str(o)
462
+
463
+ if p == DCTERMS.description:
464
+ self.CLASSES[cls]["description"] = markdown.markdown(str(o))
465
+
466
+ if p == SKOS.scopeNote:
467
+ self.CLASSES[cls]["scopeNote"] = str(o)
468
+
469
+ if p == SKOS.example:
470
+ self.CLASSES[cls]["example"] = str(o)
471
+
472
+ if p == RDFS.isDefinedBy:
473
+ self.CLASSES[cls]["isDefinedBy"] = str(o)
474
+
475
+ if p == DCTERMS.source or p == DC.source:
476
+ if str(o).startswith('http'):
477
+ self.CLASSES[cls]["source"] = self._make_formatted_uri(o) # '<a href="{0}">{0}</a>'.format(str(o))
478
+ else:
479
+ self.CLASSES[cls]["source"] = str(o)
480
+
481
+ # patch title from URI if we haven't got one
482
+ if self.CLASSES[cls]["title"] is None:
483
+ self.CLASSES[cls]["title"] = self._make_title_from_uri(cls)
484
+
485
+ # make fid
486
+ self.CLASSES[cls]["fid"] = self._make_fid(self.CLASSES[cls]["title"], cls)
487
+
488
+ # equivalent classes
489
+ equivalent_classes = []
490
+ for o in self.G.objects(subject=s, predicate=OWL.equivalentClass):
491
+ if type(o) != BNode:
492
+ equivalent_classes.append(
493
+ self._get_curie(str(o))
494
+ ) # ranges that are just classes
495
+ else:
496
+ # equivalent classes collections (unionOf | intersectionOf
497
+ q = """
498
+ PREFIX owl: <http://www.w3.org/2002/07/owl#>
499
+ PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
500
+
501
+ SELECT ?col_type ?col_member
502
+ WHERE {{
503
+ <{}> owl:equivalentClass ?eq .
504
+ ?eq owl:unionOf|owl:intersectionOf ?collection .
505
+ ?eq ?col_type ?collection .
506
+ ?collection rdf:rest*/rdf:first ?col_member .
507
+ }}
508
+ """.format(
509
+ s
510
+ )
511
+ collection_type = None
512
+ collection_members = []
513
+ for r in self.G.query(q):
514
+ collection_type = self._get_curie(str(r.col_type))
515
+ collection_members.append(self._get_curie(str(r.col_member)))
516
+ equivalent_classes.append((collection_type, collection_members))
517
+ self.CLASSES[cls]["equivalents"] = equivalent_classes
518
+
519
+ # super classes
520
+ supers = []
521
+ restrictions = []
522
+ for o in self.G.objects(subject=s, predicate=RDFS.subClassOf):
523
+ if (o, RDF.type, OWL.Restriction) not in self.G:
524
+ if type(o) != BNode:
525
+ supers.append(str(o)) # supers that are just classes
526
+ else:
527
+ # super collections (unionOf | intersectionOf
528
+ q = """
529
+ PREFIX owl: <http://www.w3.org/2002/07/owl#>
530
+ PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
531
+
532
+ SELECT ?col_type ?col_member
533
+ WHERE {{
534
+ <{}> rdfs:subClassOf ?sup .
535
+ ?sup owl:unionOf|owl:intersectionOf ?collection .
536
+ ?sup ?col_type ?collection .
537
+ ?collection rdf:rest*/rdf:first ?col_member .
538
+ }}
539
+ """.format(
540
+ s
541
+ )
542
+ collection_type = None
543
+ collection_members = []
544
+ for r in self.G.query(q):
545
+ collection_type = self._get_curie(str(r.col_type))
546
+ collection_members.append(str(r.col_member))
547
+ supers.append((collection_type, collection_members))
548
+ else:
549
+ restrictions.append(o)
550
+
551
+ self.CLASSES[cls]["supers"] = supers
552
+ self.CLASSES[cls]["restrictions"] = restrictions
553
+
554
+ # sub classes
555
+ subs = []
556
+ for o in self.G.subjects(predicate=RDFS.subClassOf, object=s):
557
+ if type(o) != BNode:
558
+ subs.append(str(o))
559
+ else:
560
+ # sub classes collections (unionOf | intersectionOf
561
+ q = """
562
+ PREFIX owl: <http://www.w3.org/2002/07/owl#>
563
+ PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
564
+
565
+ SELECT ?col_type ?col_member
566
+ WHERE {{
567
+ ?sub rdfs:subClassOf <{}> .
568
+ ?sub owl:unionOf|owl:intersectionOf ?collection .
569
+ ?sub ?col_type ?collection .
570
+ ?collection rdf:rest*/rdf:first ?col_member .
571
+ }}
572
+ """.format(
573
+ s
574
+ )
575
+ collection_type = None
576
+ collection_members = []
577
+ for r in self.G.query(q):
578
+ collection_type = self._get_curie(str(r.col_type))
579
+ collection_members.append(self._get_curie(str(r.col_member)))
580
+ subs.append((collection_type, collection_members))
581
+ self.CLASSES[cls]["subs"] = subs
582
+
583
+ in_domain_of = []
584
+ for o in self.G.subjects(predicate=RDFS.domain, object=s):
585
+ in_domain_of.append(str(o))
586
+ self.CLASSES[cls]["in_domain_of"] = in_domain_of
587
+
588
+ in_domain_includes_of = []
589
+ for o in self.G.subjects(predicate=SDO.domainIncludes, object=s):
590
+ in_domain_includes_of.append(str(o))
591
+ self.CLASSES[cls]["in_domain_includes_of"] = in_domain_includes_of
592
+
593
+ in_range_of = []
594
+ for o in self.G.subjects(predicate=RDFS.range, object=s):
595
+ in_range_of.append(str(o))
596
+ self.CLASSES[cls]["in_range_of"] = in_range_of
597
+
598
+ in_range_includes_of = []
599
+ for o in self.G.subjects(predicate=SDO.rangeIncludes, object=s):
600
+ in_range_includes_of.append(str(o))
601
+ self.CLASSES[cls]["in_range_includes_of"] = in_range_includes_of
602
+
603
+ # TODO: cater for Named Individuals of this class - "has members"
604
+
605
+ # # sort properties by title
606
+ # x = sorted([(k, v) for k, v in classes.items()], key=lambda tup: tup[1]['title'])
607
+ # y = collections.OrderedDict()
608
+ # for n in x:
609
+ # y[n[0]] = n[1]
610
+ #
611
+ # return y
612
+
613
+ def _extract_properties_uris(self):
614
+ properties = []
615
+ for s in self.G.subjects(predicate=RDF.type, object=RDF.Property):
616
+ properties.append(str(s))
617
+
618
+ for p in sorted(properties):
619
+ self.PROPERTIES[p] = {}
620
+
621
+ def _extract_properties(self):
622
+ for prop in self.PROPERTIES.keys():
623
+ s = URIRef(prop)
624
+ # property type
625
+ if (s, RDF.type, OWL.ObjectProperty) in self.G:
626
+ self.PROPERTIES[prop]["prop_type"] = "op"
627
+ elif (s, RDF.type, OWL.FunctionalProperty) in self.G:
628
+ self.PROPERTIES[prop]["prop_type"] = "fp"
629
+ elif (s, RDF.type, OWL.DatatypeProperty) in self.G:
630
+ self.PROPERTIES[prop]["prop_type"] = "dp"
631
+ elif (s, RDF.type, OWL.AnnotationProperty) in self.G:
632
+ self.PROPERTIES[prop]["prop_type"] = "ap"
633
+ else:
634
+ self.PROPERTIES[prop]["prop_type"] = "p"
635
+
636
+ self.PROPERTIES[prop]["title"] = None
637
+ self.PROPERTIES[prop]["description"] = None
638
+ self.PROPERTIES[prop]["scopeNote"] = None
639
+ self.PROPERTIES[prop]["example"] = None
640
+ self.PROPERTIES[prop]["isDefinedBy"] = None
641
+ self.PROPERTIES[prop]["source"] = None
642
+ self.PROPERTIES[prop]["supers"] = []
643
+ self.PROPERTIES[prop]["subs"] = []
644
+ self.PROPERTIES[prop]["equivs"] = []
645
+ self.PROPERTIES[prop]["invs"] = []
646
+ self.PROPERTIES[prop]["domains"] = []
647
+ self.PROPERTIES[prop]["domainIncludes"] = []
648
+ self.PROPERTIES[prop]["ranges"] = []
649
+ self.PROPERTIES[prop]["rangeIncludes"] = []
650
+
651
+ for p, o in self.G.predicate_objects(subject=s):
652
+ if p == RDFS.label:
653
+ self.PROPERTIES[prop]["title"] = str(o)
654
+
655
+ if p == RDFS.comment:
656
+ self.PROPERTIES[prop]["description"] = str(o)
657
+
658
+ if p == SKOS.scopeNote:
659
+ self.PROPERTIES[prop]["scopeNote"] = str(o)
660
+
661
+ if p == SKOS.example:
662
+ self.PROPERTIES[prop]["example"] = str(o)
663
+
664
+ if p == RDFS.isDefinedBy:
665
+ self.PROPERTIES[prop]["isDefinedBy"] = str(o)
666
+
667
+ if p == DCTERMS.source or p == DC.source:
668
+ if str(o).startswith('http'):
669
+ self.PROPERTIES[prop]["source"] = self._make_formatted_uri(
670
+ o) # '<a href="{0}">{0}</a>'.format(str(o))
671
+ else:
672
+ self.PROPERTIES[prop]["source"] = str(o)
673
+
674
+ # patch title from URI if we haven't got one
675
+ if self.PROPERTIES[prop]["title"] is None:
676
+ self.PROPERTIES[prop]["title"] = self._make_title_from_uri(prop)
677
+
678
+ # make fid
679
+ self.PROPERTIES[prop]["fid"] = self._make_fid(
680
+ self.PROPERTIES[prop]["title"], prop
681
+ )
682
+
683
+ # super properties
684
+ for o in self.G.objects(subject=s, predicate=RDFS.subPropertyOf):
685
+ if type(o) != BNode:
686
+ self.PROPERTIES[prop]["supers"].append(str(o)) # self._make_uri_html
687
+
688
+ # sub properties
689
+ for o in self.G.subjects(predicate=RDFS.subPropertyOf, object=s):
690
+ if type(o) != BNode:
691
+ self.PROPERTIES[prop]["subs"].append(str(o))
692
+
693
+ # equivalent properties
694
+ for o in self.G.objects(subject=s, predicate=OWL.equivalentProperty):
695
+ if type(o) != BNode:
696
+ self.PROPERTIES[prop]["equivs"].append(str(o))
697
+
698
+ # inverse properties
699
+ for o in self.G.objects(subject=s, predicate=OWL.inverseOf):
700
+ if type(o) != BNode:
701
+ self.PROPERTIES[prop]["invs"].append(str(o))
702
+
703
+ # domains
704
+ for o in self.G.objects(subject=s, predicate=RDFS.domain):
705
+ if type(o) != BNode:
706
+ self.PROPERTIES[prop]["domains"].append(str(o)) # domains that are just classes
707
+ else:
708
+ # domain collections (unionOf | intersectionOf
709
+ q = """
710
+ PREFIX owl: <http://www.w3.org/2002/07/owl#>
711
+ PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
712
+
713
+ SELECT ?col_type ?col_member
714
+ WHERE {{
715
+ <{}> rdfs:domain ?domain .
716
+ ?domain owl:unionOf|owl:intersectionOf ?collection .
717
+ ?domain ?col_type ?collection .
718
+ ?collection rdf:rest*/rdf:first ?col_member .
719
+ }}
720
+ """.format(
721
+ s
722
+ )
723
+ collection_type = None
724
+ collection_members = []
725
+ for r in self.G.query(q):
726
+ collection_type = self._get_curie(str(r.col_type))
727
+ collection_members.append(str(r.col_member))
728
+ self.PROPERTIES[prop]["domains"].append((collection_type, collection_members))
729
+
730
+ # domainIncludes
731
+ for o in self.G.objects(subject=s, predicate=SDO.domainIncludes):
732
+ if type(o) != BNode:
733
+ self.PROPERTIES[prop]["domainIncludes"].append(
734
+ str(o)
735
+ ) # domainIncludes that are just classes
736
+ else:
737
+ # domainIncludes collections (unionOf | intersectionOf
738
+ q = """
739
+ PREFIX owl: <http://www.w3.org/2002/07/owl#>
740
+ PREFIX sdo: <https://schema.org/>
741
+
742
+ SELECT ?col_type ?col_member
743
+ WHERE {{
744
+ <{}> sdo:domainIncludes ?domainIncludes .
745
+ ?domainIncludes owl:unionOf|owl:intersectionOf ?collection .
746
+ ?domainIncludes ?col_type ?collection .
747
+ ?collection rdf:rest*/rdf:first ?col_member .
748
+ }}
749
+ """.format(
750
+ s
751
+ )
752
+ collection_type = None
753
+ collection_members = []
754
+ for r in self.G.query(q):
755
+ collection_type = self._get_curie(str(r.col_type))
756
+ collection_members.append(str(r.col_member))
757
+ self.PROPERTIES[prop]["domainIncludes"].append((collection_type, collection_members))
758
+
759
+ # ranges
760
+ for o in self.G.objects(subject=s, predicate=RDFS.range):
761
+ if type(o) != BNode:
762
+ self.PROPERTIES[prop]["ranges"].append(str(o)) # ranges that are just classes
763
+ else:
764
+ # range collections (unionOf | intersectionOf
765
+ q = """
766
+ PREFIX owl: <http://www.w3.org/2002/07/owl#>
767
+ PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
768
+
769
+ SELECT ?col_type ?col_member
770
+ WHERE {{
771
+ <{}> rdfs:range ?range .
772
+ ?range owl:unionOf|owl:intersectionOf ?collection .
773
+ ?range ?col_type ?collection .
774
+ ?collection rdf:rest*/rdf:first ?col_member .
775
+ }}
776
+ """.format(
777
+ s
778
+ )
779
+ collection_type = None
780
+ collection_members = []
781
+ for r in self.G.query(q):
782
+ collection_type = self._get_curie(str(r.col_type))
783
+ collection_members.append(str(r.col_member))
784
+ self.PROPERTIES[prop]["ranges"].append((collection_type, collection_members))
785
+
786
+ # rangeIncludes
787
+ for o in self.G.objects(subject=s, predicate=SDO.rangeIncludes):
788
+ if type(o) != BNode:
789
+ self.PROPERTIES[prop]["rangeIncludes"].append(str(o)) # rangeIncludes that are just classes
790
+ else:
791
+ # rangeIncludes collections (unionOf | intersectionOf
792
+ q = """
793
+ PREFIX owl: <http://www.w3.org/2002/07/owl#>
794
+ PREFIX sdo: <https://schema.org/>
795
+
796
+ SELECT ?col_type ?col_member
797
+ WHERE {{
798
+ <{}> sdo:rangeIncludes ?rangeIncludes .
799
+ ?rangeIncludes owl:unionOf|owl:intersectionOf ?collection .
800
+ ?rangeIncludes ?col_type ?collection .
801
+ ?collection rdf:rest*/rdf:first ?col_member .
802
+ }}
803
+ """.format(
804
+ s
805
+ )
806
+ collection_type = None
807
+ collection_members = []
808
+ for r in self.G.query(q):
809
+ collection_type = self._get_curie(str(r.col_type))
810
+ collection_members.append(str(r.col_member))
811
+ self.PROPERTIES[prop]["rangeIncludes"].append((collection_type, collection_members))
812
+
813
+ # TODO: cater for sub property chains
814
+
815
+ # # sort properties by title
816
+ # x = sorted([(k, v) for k, v in self.PROPERTIES.items()], key=lambda tup: tup[1]['title'])
817
+ # y = collections.OrderedDict()
818
+ # for n in x:
819
+ # y[n[0]] = n[1]
820
+ #
821
+ # return y
822
+
823
+ def _extract_named_individuals_uris(self):
824
+ named_individuals = []
825
+ for s in self.G.subjects(predicate=RDF.type, object=OWL.NamedIndividual):
826
+ named_individuals.append(str(s))
827
+
828
+ for ni in sorted(named_individuals):
829
+ self.NAMED_INDIVIDUALS[ni] = {}
830
+
831
+ def _extract_named_individuals(self):
832
+ for ni in self.NAMED_INDIVIDUALS.keys():
833
+ if ni.startswith("http"):
834
+ s = URIRef(ni)
835
+ else:
836
+ s = BNode(ni)
837
+ # create Python dict for each NI
838
+ self.NAMED_INDIVIDUALS[ni] = {}
839
+
840
+ # basic NI properties
841
+ self.NAMED_INDIVIDUALS[ni]["classes"] = set()
842
+ self.NAMED_INDIVIDUALS[ni]["title"] = None
843
+ self.NAMED_INDIVIDUALS[ni]["description"] = None
844
+ self.NAMED_INDIVIDUALS[ni]["isDefinedBy"] = None
845
+ self.NAMED_INDIVIDUALS[ni]["source"] = None
846
+ self.NAMED_INDIVIDUALS[ni]["seeAlso"] = None
847
+ self.NAMED_INDIVIDUALS[ni]["sameAs"] = None
848
+
849
+ for p, o in self.G.predicate_objects(subject=s):
850
+ # list all the other classes of this NI
851
+ if p == RDF.type:
852
+ if o != OWL.NamedIndividual:
853
+ self.NAMED_INDIVIDUALS[ni]["classes"].add(self._make_formatted_uri(o))
854
+
855
+ if p == RDFS.label:
856
+ self.NAMED_INDIVIDUALS[ni]["title"] = str(o)
857
+
858
+ if p == RDFS.comment:
859
+ self.NAMED_INDIVIDUALS[ni]["description"] = str(o)
860
+
861
+ if p == RDFS.isDefinedBy:
862
+ self.NAMED_INDIVIDUALS[ni]["isDefinedBy"] = str(o)
863
+
864
+ if p == DCTERMS.source or p == DC.source:
865
+ if str(o).startswith('http'):
866
+ self.NAMED_INDIVIDUALS[ni]["source"] = self._make_formatted_uri(
867
+ o) # '<a href="{0}">{0}</a>'.format(str(o))
868
+ else:
869
+ self.NAMED_INDIVIDUALS[ni]["source"] = str(o)
870
+
871
+ if p == RDFS.seeAlso:
872
+ self.NAMED_INDIVIDUALS[ni]["seeAlso"] = self._make_formatted_uri(o)
873
+
874
+ if p == OWL.sameAs:
875
+ self.NAMED_INDIVIDUALS[ni]["sameAs"] = self._make_formatted_uri(o)
876
+
877
+ # patch title from URI if we haven't got one
878
+ if self.NAMED_INDIVIDUALS[ni].get("title") is None:
879
+ self.NAMED_INDIVIDUALS[ni]["title"] = self._make_title_from_uri(ni)
880
+
881
+ # make fid
882
+ self.NAMED_INDIVIDUALS[ni]["fid"] = self._make_fid(self.NAMED_INDIVIDUALS[ni]["title"], ni)
883
+
884
+ def _make_metadata(self):
885
+ return self._load_template("metadata." + self.outputformat).render(
886
+ imports=sorted(self.METADATA["imports"]),
887
+ title=self.METADATA.get("title"),
888
+ uri=self.METADATA.get("uri"),
889
+ version_uri=self.METADATA.get("versionIRI"),
890
+ publishers=sorted(self.METADATA["publishers"]),
891
+ creators=sorted(self.METADATA["creators"]),
892
+ contributors=sorted(self.METADATA["contributors"]),
893
+ created=self.METADATA.get("created"), # TODO: auto-detect format
894
+ modified=self.METADATA.get("modified"),
895
+ issued=self.METADATA.get("issued"),
896
+ source=self.METADATA.get("source"),
897
+ description=self.METADATA.get("description"),
898
+ historyNote=self.METADATA.get("historyNote"),
899
+ version_info=self.METADATA.get("versionInfo"),
900
+ license=self.METADATA.get("license"),
901
+ rights=self.METADATA.get("rights"),
902
+ repository=self.METADATA.get("repository"),
903
+ ont_rdf=self._make_source_file_link(),
904
+ has_classes=self.METADATA.get("has_classes"),
905
+ has_ops=self.METADATA.get("has_ops"),
906
+ has_fps=self.METADATA.get("has_fps"),
907
+ has_dps=self.METADATA.get("has_dps"),
908
+ has_aps=self.METADATA.get("has_aps"),
909
+ has_ps=self.METADATA.get("has_ps"),
910
+ has_nis=self.METADATA.get("has_nis"),
911
+ )
912
+
913
+ def _make_classes(self):
914
+ # make all Classes
915
+ class_template = self._load_template("class." + self.outputformat)
916
+ classes_list = []
917
+ for k, v in self.CLASSES.items():
918
+ # handling Markdown formatting within a table
919
+ if self.outputformat == "md":
920
+ desc = v["description"].replace("\n", " ") if v.get("description") is not None else None
921
+ if v.get("example") is not None:
922
+ eg = v["example"].strip().replace("\t", " ").split("\n")
923
+ eg2 = ""
924
+ for line in eg:
925
+ eg2 += "`" + line + "`<br />"
926
+ eg = eg2
927
+ else:
928
+ eg = None
929
+ else:
930
+ desc = v["description"]
931
+ eg = v["example"].replace("<", "&lt;").replace(">", "&gt;") if v.get("example") is not None else None
932
+
933
+ classes_list.append(
934
+ class_template.render(
935
+ uri=k,
936
+ fid=v["fid"],
937
+ title=v["title"],
938
+ description=desc,
939
+ supers=v["supers"],
940
+ restrictions=v["restrictions"],
941
+ scopeNote=v["scopeNote"],
942
+ example=eg,
943
+ is_defined_by=v["isDefinedBy"],
944
+ source=v["source"],
945
+ subs=v["subs"],
946
+ in_domain_of=v["in_domain_of"],
947
+ in_domain_includes_of=v["in_domain_includes_of"],
948
+ in_range_of=v["in_range_of"],
949
+ in_range_includes_of=v["in_range_includes_of"],
950
+ )
951
+ )
952
+
953
+ # make the template for all Classes
954
+ classes_template = self._load_template("classes." + self.outputformat)
955
+ # add in Class index
956
+ fids = sorted(
957
+ [(v.get("fid"), v.get("title")) for k, v in self.CLASSES.items()],
958
+ key=lambda tup: tup[1],
959
+ )
960
+ return classes_template.render(fids=fids, classes=classes_list, )
961
+
962
+ def _make_property(self, property):
963
+ # handling Markdown formatting within a table
964
+ if self.outputformat == "md":
965
+ desc = property[1].get("description").replace("\n", " ") \
966
+ if property[1].get("description") is not None else None
967
+ if property[1].get("example") is not None:
968
+ eg = property[1].get("example").strip().replace("\t", " ").split("\n")
969
+ eg2 = ""
970
+ for line in eg:
971
+ eg2 += "`" + line + "`<br />"
972
+ eg = eg2
973
+ else:
974
+ eg = None
975
+ else:
976
+ desc = property[1].get("description")
977
+ eg = property[1].get("example")
978
+
979
+ return self._load_template("property." + self.outputformat).render(
980
+ uri=property[0],
981
+ fid=property[1].get("fid"),
982
+ property_type=property[1].get("prop_type"),
983
+ title=property[1].get("title"),
984
+ description=desc,
985
+ scopeNote=property[1].get("scopeNote"),
986
+ example=eg,
987
+ is_defined_by=property[1].get("isDefinedBy"),
988
+ source=property[1].get("source"),
989
+ supers=property[1].get("supers"),
990
+ subs=property[1].get("subs"),
991
+ equivs=property[1].get("equivs"),
992
+ invs=property[1].get("invs"),
993
+ domains=property[1]["domains"],
994
+ domainIncludes=property[1]["domainIncludes"],
995
+ ranges=property[1]["ranges"],
996
+ rangeIncludes=property[1]["rangeIncludes"],
997
+ )
998
+
999
+ def _make_properties(self):
1000
+ # make all properties, grouped by OWL type
1001
+ op_instances = []
1002
+ fp_instances = []
1003
+ dp_instances = []
1004
+ ap_instances = []
1005
+ p_instances = []
1006
+
1007
+ for k, v in self.PROPERTIES.items():
1008
+ if v.get("prop_type") == "op":
1009
+ op_instances.append(
1010
+ (
1011
+ v["title"],
1012
+ v["fid"],
1013
+ self._make_property((k, v)),
1014
+ )
1015
+ )
1016
+ elif v.get("prop_type") == "fp":
1017
+ fp_instances.append(
1018
+ (
1019
+ v["title"],
1020
+ v["fid"],
1021
+ self._make_property((k, v)),
1022
+ )
1023
+ )
1024
+ elif v.get("prop_type") == "dp":
1025
+ dp_instances.append(
1026
+ (
1027
+ v["title"],
1028
+ v["fid"],
1029
+ self._make_property((k, v)),
1030
+ )
1031
+ )
1032
+ elif v.get("prop_type") == "ap":
1033
+ ap_instances.append(
1034
+ (
1035
+ v["title"],
1036
+ v["fid"],
1037
+ self._make_property((k, v)),
1038
+ )
1039
+ )
1040
+ elif v.get("prop_type") == "p":
1041
+ p_instances.append(
1042
+ (
1043
+ v["title"],
1044
+ v["fid"],
1045
+ self._make_property((k, v)),
1046
+ )
1047
+ )
1048
+
1049
+ # make the template for all properties
1050
+ return self._load_template("properties." + self.outputformat).render(
1051
+ op_instances=op_instances,
1052
+ fp_instances=fp_instances,
1053
+ dp_instances=dp_instances,
1054
+ ap_instances=ap_instances,
1055
+ p_instances=p_instances,
1056
+ )
1057
+
1058
+ def _make_named_individual(self, named_individual):
1059
+ return self._load_template("named_individual." + self.outputformat).render(
1060
+ uri=named_individual[0],
1061
+ fid=named_individual[1].get("fid"),
1062
+ classes=named_individual[1].get("classes"),
1063
+ title=named_individual[1].get("title"),
1064
+ description=named_individual[1].get("description"),
1065
+ is_defined_by=named_individual[1].get("isDefinedBy"),
1066
+ source=named_individual[1].get("source"),
1067
+ see_also=named_individual[1].get("seeAlso"),
1068
+ same_as=named_individual[1].get("sameAs")
1069
+ )
1070
+
1071
+ def _make_named_individuals(self):
1072
+ # make all NIs
1073
+ named_individuals_list = []
1074
+ for k, v in self.NAMED_INDIVIDUALS.items():
1075
+ named_individuals_list.append(
1076
+ self._make_named_individual((k, v))
1077
+ )
1078
+
1079
+ # add in NIs index
1080
+ fids = []
1081
+ for k, v in self.NAMED_INDIVIDUALS.items():
1082
+ if v.get("fid") is not None: # ensure BNodes not added
1083
+ fids.append((v.get("fid"), v.get("title")))
1084
+ fids = sorted(fids, key=lambda tup: tup[1])
1085
+ return self._load_template("named_individuals." + self.outputformat).render(
1086
+ fids=fids,
1087
+ named_individuals=named_individuals_list
1088
+ )
1089
+
1090
+ def _make_document(self):
1091
+ css = None
1092
+ if self.outputformat == "html":
1093
+ if not self.exclude_css:
1094
+ css = open(path.join(STYLE_DIR, "pylode.css")).read()
1095
+
1096
+ return self._load_template("document." + self.outputformat).render(
1097
+ schemaorg=self._make_schemaorg_metadata(), # only does something for the HTML templates
1098
+ title=self.METADATA["title"],
1099
+ metadata=self._make_metadata(),
1100
+ classes=self._make_classes(),
1101
+ properties=self._make_properties(),
1102
+ named_individuals=self._make_named_individuals(),
1103
+ default_namespace=self.METADATA["default_namespace"],
1104
+ namespaces=self._make_namespaces(),
1105
+ css=css,
1106
+ pylode_version=VERSION
1107
+ )
1108
+
1109
+ def generate_document(self):
1110
+ # expand the graph using pre-defined rules to make querying easier (poor man's inference)
1111
+ self._expand_graph()
1112
+ # get all the namespaces using several methods
1113
+ self._extract_namespaces()
1114
+ # get the default namespace
1115
+ self._get_default_namespace()
1116
+ # get the IDs (URIs) of all properties -> self.PROPERTIES
1117
+ self._extract_properties_uris()
1118
+ # get the IDs (URIs) of all classes -> CLASSES
1119
+ self._extract_classes_uris()
1120
+ # get the IDs (URIs) of all Named Individuals -> NAMED_INDIVIDUALS
1121
+ self._extract_named_individuals_uris()
1122
+ # get all the properties' details
1123
+ self._extract_properties()
1124
+ # get all the classes' details
1125
+ self._extract_classes()
1126
+ # get all the Named Individuals' details
1127
+ self._extract_named_individuals()
1128
+ # get the ontology's metadata
1129
+ self._extract_metadata()
1130
+ # create fragment URIs for default namespace classes & properties
1131
+ # for each CURIE, if it's in the default namespace, i.e. this ontology, use its fragment URI
1132
+
1133
+ # crosslinking properties
1134
+ for uri, prop in self.PROPERTIES.items():
1135
+ html = []
1136
+ for p in prop["supers"]:
1137
+ prop_type = (
1138
+ self.PROPERTIES.get(p).get("prop_type")
1139
+ if self.PROPERTIES.get(p)
1140
+ else None
1141
+ )
1142
+ html.append(self._make_formatted_uri(p, type=prop_type))
1143
+ self.PROPERTIES[uri]["supers"] = html
1144
+
1145
+ html = []
1146
+ for p in prop["subs"]:
1147
+ prop_type = (
1148
+ self.PROPERTIES.get(p).get("prop_type")
1149
+ if self.PROPERTIES.get(p)
1150
+ else None
1151
+ )
1152
+ html.append(self._make_formatted_uri(p, type=prop_type))
1153
+ self.PROPERTIES[uri]["subs"] = html
1154
+
1155
+ html = []
1156
+ for p in prop["equivs"]:
1157
+ prop_type = (
1158
+ self.PROPERTIES.get(p).get("prop_type")
1159
+ if self.PROPERTIES.get(p)
1160
+ else None
1161
+ )
1162
+ html.append(self._make_formatted_uri(p, type=prop_type))
1163
+ self.PROPERTIES[uri]["equivs"] = html
1164
+
1165
+ html = []
1166
+ for p in prop["invs"]:
1167
+ prop_type = (
1168
+ self.PROPERTIES.get(p).get("prop_type")
1169
+ if self.PROPERTIES.get(p)
1170
+ else None
1171
+ )
1172
+ html.append(self._make_formatted_uri(p, type=prop_type))
1173
+ self.PROPERTIES[uri]["invs"] = html
1174
+
1175
+ html = []
1176
+ for d in prop["domains"]:
1177
+ if type(d) == tuple:
1178
+ html.append(self._make_collection_class_html(d[0], d[1]))
1179
+ else:
1180
+ html.append(self._make_formatted_uri(d, type="c"))
1181
+ self.PROPERTIES[uri]["domains"] = html
1182
+
1183
+ html = []
1184
+ for d in prop["domainIncludes"]:
1185
+ if type(d) == tuple:
1186
+ for m in d[1]:
1187
+ html.append(self._make_formatted_uri(m, type="c"))
1188
+ else:
1189
+ html.append(self._make_formatted_uri(d, type="c"))
1190
+ self.PROPERTIES[uri]["domainIncludes"] = html
1191
+
1192
+ html = []
1193
+ for d in prop["ranges"]:
1194
+ if type(d) == tuple:
1195
+ for m in d[1]:
1196
+ html.append(self._make_formatted_uri(m, type="c"))
1197
+ else:
1198
+ html.append(self._make_formatted_uri(d, type="c"))
1199
+ self.PROPERTIES[uri]["ranges"] = html
1200
+
1201
+ html = []
1202
+ for d in prop["rangeIncludes"]:
1203
+ if type(d) == tuple:
1204
+ for m in d[1]:
1205
+ html.append(self._make_formatted_uri(m, type="c"))
1206
+ else:
1207
+ html.append(self._make_formatted_uri(d, type="c"))
1208
+ self.PROPERTIES[uri]["rangeIncludes"] = html
1209
+
1210
+ # crosslinking classes
1211
+ for uri, cls in self.CLASSES.items():
1212
+ html = []
1213
+ for d in cls["equivalents"]:
1214
+ if type(d) == tuple:
1215
+ for m in d[1]:
1216
+ html.append(self._make_formatted_uri(m, type="c"))
1217
+ else:
1218
+ html.append(self._make_formatted_uri(d, type="c"))
1219
+ self.CLASSES[uri]["equivalents"] = html
1220
+
1221
+ html = []
1222
+ for d in cls["supers"]:
1223
+ if type(d) == tuple:
1224
+ html.append(self._make_collection_class_html(d[0], d[1]))
1225
+ else:
1226
+ html.append(self._make_formatted_uri(d, type="c"))
1227
+ self.CLASSES[uri]["supers"] = html
1228
+
1229
+ html = []
1230
+ for d in cls["restrictions"]:
1231
+ html.append(self._make_restriction_html(uri, d))
1232
+ self.CLASSES[uri]["restrictions"] = html
1233
+
1234
+ html = []
1235
+ for d in cls["subs"]:
1236
+ if type(d) == tuple:
1237
+ for m in d[1]:
1238
+ html.append(self._make_formatted_uri(m, type="c"))
1239
+ else:
1240
+ html.append(self._make_formatted_uri(d, type="c"))
1241
+ self.CLASSES[uri]["subs"] = html
1242
+
1243
+ html = []
1244
+ for p in cls["in_domain_of"]:
1245
+ prop_type = (
1246
+ self.PROPERTIES.get(p).get("prop_type")
1247
+ if self.PROPERTIES.get(p)
1248
+ else None
1249
+ )
1250
+ html.append(self._make_formatted_uri(p, type=prop_type))
1251
+ self.CLASSES[uri]["in_domain_of"] = html
1252
+
1253
+ html = []
1254
+ for p in cls["in_domain_includes_of"]:
1255
+ prop_type = (
1256
+ self.PROPERTIES.get(p).get("prop_type")
1257
+ if self.PROPERTIES.get(p)
1258
+ else None
1259
+ )
1260
+ html.append(self._make_formatted_uri(p, type=prop_type))
1261
+ self.CLASSES[uri]["in_domain_includes_of"] = html
1262
+
1263
+ html = []
1264
+ for p in cls["in_range_of"]:
1265
+ prop_type = (
1266
+ self.PROPERTIES.get(p).get("prop_type")
1267
+ if self.PROPERTIES.get(p)
1268
+ else None
1269
+ )
1270
+ html.append(self._make_formatted_uri(p, type=prop_type))
1271
+ self.CLASSES[uri]["in_range_of"] = html
1272
+
1273
+ html = []
1274
+ for p in cls["in_range_includes_of"]:
1275
+ prop_type = (
1276
+ self.PROPERTIES.get(p).get("prop_type")
1277
+ if self.PROPERTIES.get(p)
1278
+ else None
1279
+ )
1280
+ html.append(self._make_formatted_uri(p, type=prop_type))
1281
+ self.CLASSES[uri]["in_range_includes_of"] = html
1282
+
1283
+ return self._make_document()