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,605 @@
1
+ from pylode.common import VERSION, STYLE_DIR, TEMPLATES_DIR
2
+ import collections
3
+ from os import path
4
+ from rdflib import URIRef, BNode, Literal
5
+ from rdflib.namespace import DC, DCTERMS, DOAP, OWL, PROV, RDF, RDFS, SDO, SKOS
6
+ import dateutil.parser
7
+ from itertools import chain
8
+ import markdown
9
+ from jinja2 import Environment, FileSystemLoader
10
+ from os.path import join
11
+ from pylode.profiles.base import BaseProfile
12
+
13
+
14
+ class VocPub(BaseProfile):
15
+ def __init__(self, g, source_info, outputformat="html", exclude_css=False, default_language="en", get_curies_online=False):
16
+ super().__init__(g, source_info, outputformat=outputformat, exclude_css=exclude_css, get_curies_online=False, default_language=default_language)
17
+ self.CONCEPTS = collections.OrderedDict()
18
+ self.COLLECTIONS = collections.OrderedDict()
19
+
20
+ def _load_template(self, template_file):
21
+ return Environment(loader=FileSystemLoader(join(TEMPLATES_DIR, "vocpub"))).get_template(template_file)
22
+
23
+ def _make_formatted_uri(self, uri, type=None):
24
+ # set display to CURIE
25
+ short = self._get_curie(uri)
26
+ # if the URI base is within the default namespace of this ontology
27
+ # use the fragment URI
28
+ # else
29
+ # use the given URI
30
+ uri_base = self._get_namespace_from_uri(uri)
31
+ link = None
32
+ if uri_base == self.METADATA.get("default_namespace"):
33
+ if self.CONCEPTS.get(uri):
34
+ link = "[{}]({})".format(self.CONCEPTS[uri]["default_prefLabel"], self.CONCEPTS[uri]["fid"]) \
35
+ if self.outputformat == "md" \
36
+ else '<a href="#{}">{}</a>'.format(self.CONCEPTS[uri]["fid"], self.CONCEPTS[uri]["default_prefLabel"])
37
+ elif self.COLLECTIONS.get(uri):
38
+ link = "[{}]({})".format(self.COLLECTIONS[uri]["default_prefLabel"], self.COLLECTIONS[uri]["fid"]) \
39
+ if self.outputformat == "md" \
40
+ else '<a href="#{}">{}</a>'.format(self.COLLECTIONS[uri]["fid"], self.COLLECTIONS[uri]["default_prefLabel"])
41
+
42
+ if link is None:
43
+ link = "[{}]({})".format(short, uri) \
44
+ if self.outputformat == "md" \
45
+ else '<a href="{}">{}</a>'.format(uri, short)
46
+
47
+ if type == "cp":
48
+ if self.outputformat == "md":
49
+ suffix = ' (cp)'
50
+ else:
51
+ suffix = '<sup class="sup-cp" title="concept">cp</sup>'
52
+ elif type == "cl":
53
+ if self.outputformat == "md":
54
+ suffix = ' (cl)'
55
+ else:
56
+ suffix = '<sup class="sup-cl" title="collection">cl</sup>'
57
+ # None
58
+ else:
59
+ suffix = ''
60
+
61
+ return link + suffix
62
+
63
+ def _expand_graph(self):
64
+ # name
65
+ for s, o in chain(
66
+ self.G.subject_objects(DC.title),
67
+ self.G.subject_objects(RDFS.label),
68
+ self.G.subject_objects(DCTERMS.title)
69
+ ):
70
+ self.G.add((s, SKOS.prefLabel, o))
71
+
72
+ # description
73
+ for s, o in chain(
74
+ self.G.subject_objects(DC.description),
75
+ self.G.subject_objects(DCTERMS.description),
76
+ self.G.subject_objects(RDFS.comment),
77
+ self.G.subject_objects(SDO.description)
78
+ ):
79
+ self.G.add((s, SKOS.definition, o))
80
+
81
+ # OWL -> SKOS
82
+ # classes as Concepts types
83
+ for s in chain(
84
+ self.G.subjects(RDF.type, RDFS.Class),
85
+ self.G.subjects(RDF.type, OWL.Class)
86
+ ):
87
+ self.G.add((s, RDF.type, SKOS.Concept))
88
+
89
+ # SKOS Concept Hierarchy from Class subsumption
90
+ for s, o in self.G.subject_objects(RDFS.subClassOf):
91
+ if type(o) != BNode: # stops restrictions being seen as broader/narrower
92
+ self.G.add((s, SKOS.broader, o))
93
+ self.G.add((o, SKOS.narrower, s))
94
+
95
+ for s, o in self.G.subject_objects(OWL.equivalentClass):
96
+ self.G.add((s, SKOS.exactMatch, o))
97
+ self.G.add((o, SKOS.exactMatch, s))
98
+
99
+ # the ontology is now a ConceptScheme
100
+ for s in self.G.subjects(RDF.type, OWL.Ontology):
101
+ self.G.add((s, RDF.type, SKOS.ConceptScheme))
102
+
103
+ # top concepts
104
+ # if the class (now typed a Concept) is declared here and
105
+ # has no subClassOf
106
+ # or is subClassOf skos:Concept
107
+ # or is only a subClassOf BNodes (restrictions)
108
+ for s2 in self.G.subjects(RDF.type, SKOS.Concept):
109
+ if (s2, RDFS.subClassOf, None) not in self.G:
110
+ self.G.add((s2, SKOS.topConceptOf, s))
111
+
112
+ only_bn = True
113
+ for o3 in self.G.objects(s2, RDFS.subClassOf):
114
+ if type(o3) != BNode:
115
+ only_bn = False
116
+ if only_bn:
117
+ self.G.add((s2, SKOS.topConceptOf, s))
118
+
119
+
120
+ # SKOS -> SKOS
121
+ # broader / narrower buildout
122
+ for s, o in self.G.subject_objects(SKOS.broader):
123
+ self.G.add((o, SKOS.narrower, s))
124
+
125
+ for s, o in self.G.subject_objects(SKOS.narrower):
126
+ self.G.add((o, SKOS.broader, s))
127
+
128
+ for s, o in self.G.subject_objects(SKOS.topConceptOf):
129
+ self.G.add((o, SKOS.hasTopConcept, s))
130
+
131
+ for s, o in self.G.subject_objects(SKOS.hasTopConcept):
132
+ self.G.add((o, SKOS.topConceptOf, s))
133
+
134
+ # Agents
135
+ # creator
136
+ for s, o in chain(
137
+ self.G.subject_objects(DC.creator),
138
+ self.G.subject_objects(SDO.creator),
139
+ self.G.subject_objects(SDO.author) # conflate SDO.author with DCTERMS.creator
140
+ ):
141
+ self.G.remove((s, DC.creator, o))
142
+ self.G.remove((s, SDO.creator, o))
143
+ self.G.remove((s, SDO.author, o))
144
+ self.G.add((s, DCTERMS.creator, o))
145
+
146
+ # contributor
147
+ for s, o in chain(
148
+ self.G.subject_objects(DC.contributor),
149
+ self.G.subject_objects(SDO.contributor)
150
+ ):
151
+ self.G.remove((s, DC.contributor, o))
152
+ self.G.remove((s, SDO.contributor, o))
153
+ self.G.add((s, DCTERMS.contributor, o))
154
+
155
+ # publisher
156
+ for s, o in chain(
157
+ self.G.subject_objects(DC.publisher),
158
+ self.G.subject_objects(SDO.publisher)
159
+ ):
160
+ self.G.remove((s, DC.publisher, o))
161
+ self.G.remove((s, SDO.publisher, o))
162
+ self.G.add((s, DCTERMS.publisher, o))
163
+
164
+ def _extract_collections(self):
165
+ """Extracts standard SKOS Collection metadata"""
166
+ collections = []
167
+ # TODO: handle OrderedCollections
168
+ for s in self.G.subjects(predicate=RDF.type, object=SKOS.Collection):
169
+ collections.append(str(s))
170
+
171
+ # keeping the OrderedDict ordered
172
+ for c in sorted(collections):
173
+ self.COLLECTIONS[c] = {}
174
+
175
+ # fill in each Collection's details from the graph
176
+ for c in self.COLLECTIONS.keys():
177
+ s = URIRef(c) # for use in Graph() loops
178
+
179
+ self.COLLECTIONS[c]["fid"] = None
180
+ self.COLLECTIONS[c]["default_prefLabel"] = None
181
+ self.COLLECTIONS[c]["prefLabels"] = set()
182
+ self.COLLECTIONS[c]["altLabels"] = set()
183
+ self.COLLECTIONS[c]["definitions"] = set()
184
+ self.COLLECTIONS[c]["scopeNotes"] = set()
185
+ self.COLLECTIONS[c]["source"] = None
186
+ self.COLLECTIONS[c]["members"] = set()
187
+
188
+ for p, o in self.G.predicate_objects(subject=s):
189
+ if p == SKOS.prefLabel:
190
+ self.COLLECTIONS[c]["prefLabels"].add((str(o), o.language)) # TODO: add in language
191
+ if o.language == self.default_language:
192
+ self.COLLECTIONS[c]["default_prefLabel"] = str(o)
193
+
194
+ elif p == SKOS.altLabel:
195
+ self.COLLECTIONS[c]["altLabels"].add(str(o)) # TODO: add in language
196
+
197
+ elif p == SKOS.definition:
198
+ self.COLLECTIONS[c]["definitions"].add(str(o)) # TODO: add in language
199
+
200
+ elif p == SKOS.scopeNote:
201
+ self.COLLECTIONS[c]["scopeNotes"].add(str(o)) # TODO: add in language
202
+
203
+ elif p == DCTERMS.source:
204
+ self.COLLECTIONS[c]["source"] = str(o)
205
+
206
+ elif p == SKOS.topConceptOf:
207
+ self.COLLECTIONS[c]["topConceptOfs"].add(str(o))
208
+
209
+ elif p == SKOS.member:
210
+ self.COLLECTIONS[c]["members"].add(str(o))
211
+ # TODO: handle members that are other Collections, not Concepts
212
+
213
+ # listify the sets
214
+ self.COLLECTIONS[c]["prefLabels"] = list(self.COLLECTIONS[c]["prefLabels"])
215
+ self.COLLECTIONS[c]["altLabels"] = list(self.COLLECTIONS[c]["altLabels"])
216
+ self.COLLECTIONS[c]["definitions"] = list(self.COLLECTIONS[c]["definitions"])
217
+ self.COLLECTIONS[c]["scopeNotes"] = list(self.COLLECTIONS[c]["scopeNotes"])
218
+ self.COLLECTIONS[c]["members"] = list(self.COLLECTIONS[c]["members"])
219
+
220
+ # make fid
221
+ # TODO: update to use default language label, not [0]
222
+ try:
223
+ pl = self.COLLECTIONS[c]["prefLabels"][0][0]
224
+
225
+ self.COLLECTIONS[c]["fid"] = self._make_fid(
226
+ pl, c
227
+ )
228
+ except Exception as e:
229
+ print(e)
230
+ raise Exception("You Collection {} doesn't have a label but it needs one!".format(c))
231
+
232
+ def _extract_concepts(self):
233
+ """Extracts standard SKOS Concepts and their metadata
234
+
235
+ Will interpret an owl:Class and rdfs:Class instances as skos:Concepts if run against an OWL document
236
+ """
237
+ concepts = []
238
+ for s in self.G.subjects(predicate=RDF.type, object=SKOS.Concept):
239
+ concepts.append(str(s))
240
+
241
+ # keeping the OrderedDict ordered
242
+ for c in sorted(concepts):
243
+ self.CONCEPTS[c] = {}
244
+
245
+ # fill in each Concept's details from the graph
246
+ for c in self.CONCEPTS.keys():
247
+ s = URIRef(c) # for use in Graph() loops
248
+
249
+ self.CONCEPTS[c]["fid"] = None
250
+ self.CONCEPTS[c]["prefLabels"] = set()
251
+ self.CONCEPTS[c]["default_prefLabel"] = None
252
+ self.CONCEPTS[c]["altLabels"] = set()
253
+ self.CONCEPTS[c]["definitions"] = set()
254
+ self.CONCEPTS[c]["scopeNotes"] = set()
255
+ self.CONCEPTS[c]["examples"] = set()
256
+ self.CONCEPTS[c]["inSchemes"] = set()
257
+ self.CONCEPTS[c]["topConceptOfs"] = set()
258
+ self.CONCEPTS[c]["source"] = None
259
+ self.CONCEPTS[c]["broaders"] = set()
260
+ self.CONCEPTS[c]["narrowers"] = set()
261
+ self.CONCEPTS[c]["exactMatches"] = set()
262
+ self.CONCEPTS[c]["closeMatches"] = set()
263
+ self.CONCEPTS[c]["broadMatches"] = set()
264
+ self.CONCEPTS[c]["narrowMatches"] = set()
265
+
266
+ for p, o in self.G.predicate_objects(subject=s):
267
+ if p == SKOS.prefLabel:
268
+ self.CONCEPTS[c]["prefLabels"].add((str(o), o.language)) # TODO: add in language
269
+ if o.language == self.default_language:
270
+ self.CONCEPTS[c]["default_prefLabel"] = str(o)
271
+
272
+ elif p == SKOS.altLabel:
273
+ self.CONCEPTS[c]["altLabels"].add(str(o)) # TODO: add in language
274
+
275
+ elif p == SKOS.definition:
276
+ self.CONCEPTS[c]["definitions"].add(str(o)) # TODO: add in language
277
+
278
+ elif p == SKOS.scopeNote:
279
+ self.CONCEPTS[c]["scopeNotes"].add(str(o)) # TODO: add in language
280
+
281
+ elif p == SKOS.example:
282
+ self.CONCEPTS[c]["examples"].add(str(o)) # TODO: add in language
283
+
284
+ elif p == SKOS.inScheme:
285
+ self.CONCEPTS[c]["inSchemes"].add(str(o))
286
+
287
+ elif p == DCTERMS.source:
288
+ self.CONCEPTS[c]["source"] = str(o)
289
+
290
+ elif p == SKOS.topConceptOf:
291
+ self.CONCEPTS[c]["topConceptOfs"].add(str(o))
292
+
293
+ elif p == SKOS.broader:
294
+ self.CONCEPTS[c]["broaders"].add(str(o))
295
+
296
+ elif p == SKOS.narrower:
297
+ self.CONCEPTS[c]["narrowers"].add(str(o))
298
+
299
+ elif p == SKOS.exactMatch:
300
+ self.CONCEPTS[c]["exactMatches"].add(str(o))
301
+
302
+ elif p == SKOS.closeMatch:
303
+ self.CONCEPTS[c]["closeMatches"].add(str(o))
304
+
305
+ elif p == SKOS.broadMatch:
306
+ self.CONCEPTS[c]["broadMatches"].add(str(o))
307
+
308
+ elif p == SKOS.narrowMatch:
309
+ self.CONCEPTS[c]["narrowMatches"].add(str(o))
310
+
311
+ # patch title from URI if we haven't got one
312
+ if len(self.CONCEPTS[c]["prefLabels"]) < 1:
313
+ pl = self._make_title_from_uri(c)
314
+ self.CONCEPTS[c]["prefLabels"].add((pl, 'en'))
315
+ self.CONCEPTS[c]["default_prefLabel"] = pl
316
+
317
+ # listify the sets
318
+ self.CONCEPTS[c]["prefLabels"] = list(self.CONCEPTS[c]["prefLabels"])
319
+ self.CONCEPTS[c]["altLabels"] = list(self.CONCEPTS[c]["altLabels"])
320
+ self.CONCEPTS[c]["definitions"] = list(self.CONCEPTS[c]["definitions"])
321
+ self.CONCEPTS[c]["scopeNotes"] = list(self.CONCEPTS[c]["scopeNotes"])
322
+ self.CONCEPTS[c]["examples"] = list(self.CONCEPTS[c]["examples"])
323
+ self.CONCEPTS[c]["inSchemes"] = list(self.CONCEPTS[c]["inSchemes"])
324
+ self.CONCEPTS[c]["topConceptOfs"] = list(self.CONCEPTS[c]["topConceptOfs"])
325
+ self.CONCEPTS[c]["broaders"] = list(self.CONCEPTS[c]["broaders"])
326
+ self.CONCEPTS[c]["narrowers"] = list(self.CONCEPTS[c]["narrowers"])
327
+ self.CONCEPTS[c]["exactMatches"] = list(self.CONCEPTS[c]["exactMatches"])
328
+ self.CONCEPTS[c]["closeMatches"] = list(self.CONCEPTS[c]["closeMatches"])
329
+ self.CONCEPTS[c]["broadMatches"] = list(self.CONCEPTS[c]["broadMatches"])
330
+ self.CONCEPTS[c]["narrowMatches"] = list(self.CONCEPTS[c]["narrowMatches"])
331
+
332
+ # make fid
333
+ # TODO: update to use default language label, not [0]
334
+ self.CONCEPTS[c]["fid"] = self._make_fid(
335
+ self.CONCEPTS[c]["default_prefLabel"], c
336
+ )
337
+
338
+ def _extract_concept_scheme(self):
339
+ """Extracts standard SKOS ConceptScheme metadata
340
+
341
+ Will interpret an owl:Ontology as a skos:ConceptScheme if run against an OWL document
342
+ """
343
+
344
+ self.METADATA["creators"] = set()
345
+ self.METADATA["contributors"] = set()
346
+ self.METADATA["publishers"] = set()
347
+ for s in self.G.subjects(predicate=RDF.type, object=SKOS.ConceptScheme):
348
+ self.METADATA["uri"] = str(s)
349
+ for p, o in self.G.predicate_objects(subject=s):
350
+ if p == SKOS.prefLabel:
351
+ self.METADATA["title"] = str(o)
352
+
353
+ if p == SKOS.definition:
354
+ self.METADATA["description"] = markdown.markdown(str(o))
355
+
356
+ if p == SKOS.historyNote:
357
+ self.METADATA["historyNote"] = markdown.markdown(str(o))
358
+
359
+ # dates
360
+ if p in [DCTERMS.created, DCTERMS.modified, DCTERMS.issued]:
361
+ date_type = p.split("/")[-1]
362
+ self.METADATA[date_type] = dateutil.parser.parse(str(o)).strftime(
363
+ "%Y-%m-%d"
364
+ )
365
+
366
+ if p == DCTERMS.source:
367
+ if str(o).startswith('http'):
368
+ self.METADATA["source"] = self._make_formatted_uri(o) # '<a href="{0}">{0}</a>'.format(str(o))
369
+ else:
370
+ self.METADATA["source"] = str(o)
371
+
372
+ if p == OWL.versionIRI:
373
+ self.METADATA["versionIRI"] = self._make_formatted_uri(o)
374
+
375
+ if p == OWL.versionInfo:
376
+ self.METADATA["versionInfo"] = str(o)
377
+
378
+ if p == URIRef("http://purl.org/vocab/vann/preferredNamespacePrefix"):
379
+ self.METADATA["preferredNamespacePrefix"] = str(o)
380
+
381
+ if p == URIRef("http://purl.org/vocab/vann/preferredNamespaceUri"):
382
+ self.METADATA["preferredNamespaceUri"] = str(o)
383
+
384
+ if p == DCTERMS.license:
385
+ self.METADATA["license"] = (
386
+ self._make_formatted_uri(o)
387
+ if str(o).startswith("http")
388
+ else str(o)
389
+ )
390
+
391
+ if p == DCTERMS.rights:
392
+ self.METADATA["rights"] = (
393
+ str(o)
394
+ .replace("Copyright", "&copy;")
395
+ .replace("copyright", "&copy;")
396
+ .replace("(c)", "&copy;")
397
+ )
398
+
399
+ # Agents
400
+ if p in [DCTERMS.creator, DCTERMS.contributor, DCTERMS.publisher]:
401
+ agent_type = p.split("/")[-1] + "s"
402
+ if type(o) == Literal:
403
+ self.METADATA[agent_type].add(str(o))
404
+ else: # Blank Node or URI
405
+ self.METADATA[agent_type].add(self._make_agent_html(o))
406
+
407
+ # TODO: cater for other Agent representations
408
+
409
+ if p == PROV.wasGeneratedBy:
410
+ for o2 in self.G.objects(subject=o, predicate=DOAP.repository):
411
+ self.METADATA["repository"] = self._make_formatted_uri(o2)
412
+
413
+ if p == SDO.codeRepository:
414
+ self.METADATA["repository"] = self._make_formatted_uri(o)
415
+
416
+ if self.METADATA.get("title") is None:
417
+ raise ValueError(
418
+ "Your taxonomy's ConceptScheme does not indicate any form of title. "
419
+ "You must declare one of the following for it: rdfs:label, dct:title, skos:prefLabel"
420
+ )
421
+
422
+ if len(self.COLLECTIONS.keys()) > 0:
423
+ self.METADATA["has_collections"] = True
424
+
425
+ if len(self.CONCEPTS.keys()) > 0:
426
+ self.METADATA["has_concepts"] = True
427
+
428
+ def _make_skos_concept_scheme(self):
429
+ return self._load_template("concept_scheme." + self.outputformat).render(
430
+ title=self.METADATA.get("title"),
431
+ uri=self.METADATA.get("uri"),
432
+ version_uri=self.METADATA.get("versionIRI"),
433
+ publishers=sorted(self.METADATA["publishers"]),
434
+ creators=sorted(self.METADATA["creators"]),
435
+ contributors=sorted(self.METADATA["contributors"]),
436
+ created=self.METADATA.get("created"),
437
+ modified=self.METADATA.get("modified"),
438
+ issued=self.METADATA.get("issued"),
439
+ source=self.METADATA.get("source"),
440
+ description=self.METADATA.get("description"),
441
+ historyNote=self.METADATA.get("historyNote"),
442
+ version_info=self.METADATA.get("versionInfo"),
443
+ license=self.METADATA.get("license"),
444
+ rights=self.METADATA.get("rights"),
445
+ repository=self.METADATA.get("repository"),
446
+ ont_rdf=self._make_source_file_link(),
447
+ has_collections=True if len(self.COLLECTIONS) > 0 else False,
448
+ has_concepts=True if len(self.CONCEPTS) > 0 else False,
449
+ )
450
+
451
+ def _make_skos_collection(self, collection):
452
+ return self._load_template("collection." + self.outputformat).render(
453
+ uri=collection[0],
454
+ fid=collection[1].get("fid"),
455
+ default_prefLabel=collection[1].get("default_prefLabel"),
456
+ prefLabels=collection[1].get("prefLabels"),
457
+ altLabels=collection[1].get("altLabels"),
458
+ definitions=collection[1].get("definitions"),
459
+ scopeNotes=collection[1].get("scopeNotes"),
460
+ source=collection[1].get("source"),
461
+ members=[self._make_formatted_uri(x, type="cp") for x in collection[1].get("members")],
462
+ )
463
+
464
+ def _make_skos_collections(self):
465
+ collections = []
466
+ for k, v in self.COLLECTIONS.items():
467
+ collections.append(
468
+ (
469
+ v["default_prefLabel"],
470
+ v["fid"],
471
+ self._make_skos_collection((k, v)),
472
+ )
473
+ )
474
+
475
+ return self._load_template("collections." + self.outputformat).render(collections=collections)
476
+
477
+ def _make_concept_hierarchy(self):
478
+ of = self.outputformat
479
+ # render concept
480
+ def _render(c, children, of, level=0):
481
+ if of == "md":
482
+ md = level*"\t" + "* [{}]({})\n".format(self.CONCEPTS.get(c).get("default_prefLabel"), c)
483
+ if len(children) > 0:
484
+ for ch in sorted(children):
485
+ md += _render(ch, self.CONCEPTS.get(ch).get("narrowers"), of, level=level + 1)
486
+ return md
487
+ else: # HTML
488
+ html = "<li>{}".format(self._make_formatted_uri(c))
489
+ if len(children) > 0:
490
+ for ch in sorted(children):
491
+ html += "\n<ul>" + \
492
+ _render(ch, self.CONCEPTS.get(ch).get("narrowers"), of, level=level + 1) + \
493
+ "</ul>"
494
+ html += "</li>"
495
+ return html
496
+
497
+ # start with a topConcept
498
+ tcs = []
499
+ for s, o in self.G.subject_objects(predicate=SKOS.hasTopConcept):
500
+ tcs.append(str(o))
501
+
502
+ txt = ""
503
+ for tc in sorted(tcs):
504
+ txt += _render(tc, self.CONCEPTS.get(tc).get("narrowers"), self.outputformat, 0)
505
+
506
+ if self.outputformat == "md":
507
+ return txt
508
+ else:
509
+ return "<ul class=\"hierarchy\">\n" + txt + "\n</ul>"
510
+
511
+ def _make_skos_concept(self, concept):
512
+ # handling Markdown formatting within a table
513
+ if self.outputformat == "md":
514
+ if len(concept[1].get("definitions")) > 0:
515
+ defs = [d.replace("\n", " ") for d in concept[1].get("definitions")]
516
+ else:
517
+ defs = None
518
+ if len(concept[1].get("examples")) > 0:
519
+ egs = [eg.strip().replace("\t", " ") for eg in concept[1].get("examples")]
520
+ egs2 = []
521
+ for ee in egs:
522
+ ee2 = ""
523
+ for line in ee.split("\n"):
524
+ ee2 += "`" + line + "`<br />"
525
+ egs2.append(ee2)
526
+ egs = egs2
527
+ else:
528
+ egs = []
529
+ else:
530
+ defs = concept[1].get("definitions")
531
+ if len(concept[1].get("examples")) > 1:
532
+ egs = [x.replace("<", "&lt;").replace(">", "&gt;") for x in concept[1].get("examples")]
533
+ else:
534
+ egs = []
535
+
536
+ return self._load_template("concept." + self.outputformat).render(
537
+ uri=concept[0],
538
+ fid=concept[1].get("fid"),
539
+ default_prefLabel=concept[1].get("default_prefLabel"),
540
+ prefLabels=concept[1].get("prefLabels"),
541
+ altLabels=concept[1].get("altLabels"),
542
+ definitions=defs,
543
+ scopeNotes=concept[1].get("scopeNotes"),
544
+ examples=egs,
545
+ source=concept[1].get("source"),
546
+ broaders=[self._make_formatted_uri(x, type="cp") for x in concept[1].get("broaders")],
547
+ narrowers=[self._make_formatted_uri(x, type="cp") for x in concept[1].get("narrowers")],
548
+ exactMatches=[self._make_formatted_uri(x, type="cp") for x in concept[1].get("exactMatches")],
549
+ closeMatches=[self._make_formatted_uri(x, type="cp") for x in concept[1].get("closeMatches")],
550
+ broadMatches=[self._make_formatted_uri(x, type="cp") for x in concept[1].get("broadMatches")],
551
+ narrowMatches=[self._make_formatted_uri(x, type="cp") for x in concept[1].get("narrowMatches")],
552
+ )
553
+
554
+ def _make_skos_concepts(self):
555
+ # TODO: make Concept hierarchy (URIs)
556
+
557
+ # TODO: list all Concepts, alphabetically by prefLabel
558
+ concepts = []
559
+ for k, v in self.CONCEPTS.items():
560
+ concepts.append(
561
+ (
562
+ v["default_prefLabel"],
563
+ v["fid"],
564
+ self._make_skos_concept((k, v)),
565
+ )
566
+ )
567
+
568
+ return self._load_template("concepts." + self.outputformat).render(
569
+ concept_hierarchy=self._make_concept_hierarchy(),
570
+ concepts=concepts
571
+ )
572
+
573
+ def _make_document(self):
574
+ css = None
575
+ if self.outputformat == "html":
576
+ if not self.exclude_css:
577
+ css = open(path.join(STYLE_DIR, "pylode.css")).read()
578
+
579
+ return self._load_template("taxonomy." + self.outputformat).render(
580
+ schemaorg=self._make_schemaorg_metadata(), # only does something for the HTML template
581
+ title=self.METADATA["title"],
582
+ concept_scheme=self._make_skos_concept_scheme(),
583
+ has_collections=True if len(self.COLLECTIONS) > 0 else False,
584
+ collections=self._make_skos_collections(),
585
+ has_concepts=True if len(self.CONCEPTS) > 0 else False,
586
+ concepts=self._make_skos_concepts(),
587
+ namespaces=self._make_namespaces(),
588
+ css=css,
589
+ pylode_version=VERSION
590
+ )
591
+
592
+ def generate_document(self):
593
+ # expand the graph using pre-defined rules to make querying easier (poor man's inference)
594
+ self._expand_graph()
595
+ # get all the namespaces using several methods
596
+ self._extract_namespaces()
597
+ # get the default namespace
598
+ self._get_default_namespace()
599
+ # extract all the SKOS things
600
+ self._extract_collections()
601
+ self._extract_concepts()
602
+ self._make_concept_hierarchy()
603
+ self._extract_concept_scheme()
604
+
605
+ return self._make_document()
pylode/server.py ADDED
@@ -0,0 +1,24 @@
1
+ import falcon
2
+ import subprocess
3
+
4
+
5
+ class DocResource:
6
+ def on_get(self, req, resp):
7
+ """Handles GET requests"""
8
+ url = req.get_param("url")
9
+ if url is not None:
10
+ cmd = "cd ./bin && ./pylode.sh -u {url} -c true".format(url=url)
11
+
12
+ # remove Overview image placeholder, if present
13
+ raw_html = subprocess.check_output(cmd, shell=True)
14
+ processed_html = raw_html.decode().replace(
15
+ '<section id="overview">', '<section id="overview" style="display:none;">').encode()
16
+ resp.body = processed_html
17
+
18
+ resp.set_header("Powered-By", "Falcon")
19
+ resp.set_header("content-type", "text/html")
20
+ resp.status = falcon.HTTP_200
21
+
22
+
23
+ api = falcon.API()
24
+ api.add_route("/lode", DocResource())