pylode 3.4.4__tar.gz → 3.5.1__tar.gz

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 (48) hide show
  1. {pylode-3.4.4 → pylode-3.5.1}/PKG-INFO +28 -37
  2. {pylode-3.4.4 → pylode-3.5.1}/README.md +26 -35
  3. {pylode-3.4.4 → pylode-3.5.1}/pylode/cli.py +1 -1
  4. {pylode-3.4.4 → pylode-3.5.1}/pylode/profiles/ontpub.py +60 -2
  5. {pylode-3.4.4 → pylode-3.5.1}/pylode/profiles/vocpub.py +7 -49
  6. {pylode-3.4.4 → pylode-3.5.1}/pylode/pylode.css +76 -65
  7. {pylode-3.4.4 → pylode-3.5.1}/pylode/rdf/sdo-mod.ttl +1 -1
  8. {pylode-3.4.4 → pylode-3.5.1}/pylode/rdf_elements.py +9 -9
  9. {pylode-3.4.4 → pylode-3.5.1}/pylode/utils.py +163 -18
  10. {pylode-3.4.4 → pylode-3.5.1}/pyproject.toml +2 -2
  11. {pylode-3.4.4 → pylode-3.5.1}/pylode/__init__.py +0 -0
  12. {pylode-3.4.4 → pylode-3.5.1}/pylode/__main__.py +0 -0
  13. {pylode-3.4.4 → pylode-3.5.1}/pylode/profiles/__init__.py +0 -0
  14. {pylode-3.4.4 → pylode-3.5.1}/pylode/profiles/supermodel/__init__.py +0 -0
  15. {pylode-3.4.4 → pylode-3.5.1}/pylode/profiles/supermodel/component/__init__.py +0 -0
  16. {pylode-3.4.4 → pylode-3.5.1}/pylode/profiles/supermodel/component/example.py +0 -0
  17. {pylode-3.4.4 → pylode-3.5.1}/pylode/profiles/supermodel/component/heading.py +0 -0
  18. {pylode-3.4.4 → pylode-3.5.1}/pylode/profiles/supermodel/component/link.py +0 -0
  19. {pylode-3.4.4 → pylode-3.5.1}/pylode/profiles/supermodel/component/preamble.py +0 -0
  20. {pylode-3.4.4 → pylode-3.5.1}/pylode/profiles/supermodel/component/properties_table.py +0 -0
  21. {pylode-3.4.4 → pylode-3.5.1}/pylode/profiles/supermodel/component/tooltip.py +0 -0
  22. {pylode-3.4.4 → pylode-3.5.1}/pylode/profiles/supermodel/dataset.py +0 -0
  23. {pylode-3.4.4 → pylode-3.5.1}/pylode/profiles/supermodel/fragment.py +0 -0
  24. {pylode-3.4.4 → pylode-3.5.1}/pylode/profiles/supermodel/html.py +0 -0
  25. {pylode-3.4.4 → pylode-3.5.1}/pylode/profiles/supermodel/loader.py +0 -0
  26. {pylode-3.4.4 → pylode-3.5.1}/pylode/profiles/supermodel/model.py +0 -0
  27. {pylode-3.4.4 → pylode-3.5.1}/pylode/profiles/supermodel/namespace.py +0 -0
  28. {pylode-3.4.4 → pylode-3.5.1}/pylode/profiles/supermodel/query/__init__.py +0 -0
  29. {pylode-3.4.4 → pylode-3.5.1}/pylode/profiles/supermodel/query/common.py +0 -0
  30. {pylode-3.4.4 → pylode-3.5.1}/pylode/profiles/supermodel/query/property_shape.py +0 -0
  31. {pylode-3.4.4 → pylode-3.5.1}/pylode/profiles/supermodel/random.py +0 -0
  32. {pylode-3.4.4 → pylode-3.5.1}/pylode/profiles/supermodel/state.py +0 -0
  33. {pylode-3.4.4 → pylode-3.5.1}/pylode/rdf/dcterms-mod.ttl +0 -0
  34. {pylode-3.4.4 → pylode-3.5.1}/pylode/rdf/licenses.ttl +0 -0
  35. {pylode-3.4.4 → pylode-3.5.1}/pylode/rdf/ontdoc.ttl +0 -0
  36. {pylode-3.4.4 → pylode-3.5.1}/pylode/rdf/owl.ttl +0 -0
  37. {pylode-3.4.4 → pylode-3.5.1}/pylode/rdf/prov.ttl +0 -0
  38. {pylode-3.4.4 → pylode-3.5.1}/pylode/rdf/rdf.ttl +0 -0
  39. {pylode-3.4.4 → pylode-3.5.1}/pylode/rdf/rdfs-mod.ttl +0 -0
  40. {pylode-3.4.4 → pylode-3.5.1}/pylode/rdf/skos.ttl +0 -0
  41. {pylode-3.4.4 → pylode-3.5.1}/pylode/rdf/void.ttl +0 -0
  42. {pylode-3.4.4 → pylode-3.5.1}/pylode/server.py +0 -0
  43. {pylode-3.4.4 → pylode-3.5.1}/pylode/static/asciidoc.css +0 -0
  44. {pylode-3.4.4 → pylode-3.5.1}/pylode/static/hierarchy.css +0 -0
  45. {pylode-3.4.4 → pylode-3.5.1}/pylode/static/hierarchy.js +0 -0
  46. {pylode-3.4.4 → pylode-3.5.1}/pylode/static/property-table-row.js +0 -0
  47. {pylode-3.4.4 → pylode-3.5.1}/pylode/static/pylode.css +0 -0
  48. {pylode-3.4.4 → pylode-3.5.1}/pylode/version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pylode
3
- Version: 3.4.4
3
+ Version: 3.5.1
4
4
  Summary: An OWL ontology documentation tool using Python, based on LODE.
5
5
  Author: Nicholas Car
6
6
  Author-email: Nicholas Car <nick@kurrawong.ai>
@@ -13,7 +13,7 @@ Classifier: Programming Language :: Python :: 3.13
13
13
  Classifier: Programming Language :: Python :: 3.14
14
14
  Requires-Dist: rdflib>=7.6.0,<8.0.0
15
15
  Requires-Dist: markdown>=3.3.7,<4
16
- Requires-Dist: dominate>=2.6.0,<3
16
+ Requires-Dist: dominate>=2.9.1,<3
17
17
  Requires-Dist: html5lib>=1.1,<2
18
18
  Requires-Dist: httpx>=0.25.0,<1.0.0
19
19
  Requires-Dist: tqdm>=4.67.3
@@ -38,25 +38,7 @@ An OWL ontology documentation tool using Python, based on LODE.
38
38
 
39
39
  In addition to making web page, human-readable forms of ontologies, pyLODE encourages ontology annotation *best practice* by only producing good results for well documented inputs! pyLODE defines what it considers “well documented” in sections below, e.g. [What pyLODE understands](#what-pylode-understands).
40
40
 
41
- **New mode**: In v3.1.0, pyLODE now has a new mode called `supermodel`, in addition to the existing `ontpub` mode. This new mode allows for documenting **profiles and modules** of multipart models. See [supermodel.md](supermodel.md) for more information.
42
-
43
- ---
44
-
45
- ## A note on the v 3.x change
46
-
47
- This is pyLODE version 3.0.1 and it’s vastly different from pyLODE 2.x. It doesn’t yet handle all the various “profiles” that pyLODE 2.13.2 does, such as SKOS “vocabularies” & Profiles Vocabulary “profiles”, it only handles OWL “ontologies”, nor all the special data types, such as JSON literals.
48
-
49
- However, it generates HTML in a much more straightforward manner and the code is both more efficient and much more maintainable, which is why it’s been made.
50
-
51
- v 3.x will eventually catch up to all of v 2.13.2’s features.
52
-
53
- To access v 2.13.2 of pyLODE, either:
54
-
55
- - [Download it from PyPI](https://pypi.org/project/pylode/2.13.2/)
56
- - [Check it out from GitHub](https://github.com/RDFLib/pyLODE/releases/tag/2.13.2)
57
- - Access it via the [online service](http://tools.kurrawong.ai/pylode)
58
-
59
- ---
41
+ **New mode**: In v3.1.0, pyLODE now has a new mode called `supermodel`, in addition to the existing `ontpub` & `vocpub` modes. This new mode allows for documenting **profiles and modules** of multipart models. See [supermodel.md](supermodel.md) for more information.
60
42
 
61
43
  ## Contents
62
44
 
@@ -101,20 +83,23 @@ The tool can be used in multiple ways:
101
83
  ### Command line arguments
102
84
 
103
85
  ```text
104
- usage: cli.py [-h] [-v] [-o OUTPUTFILE] [-c {true,false}] input
86
+ usage: pylode [-h] [-v] [-o OUTPUTFILE] [-c {true,false}] [-s] [-p {ontpub,vocpub,supermodel}] [-l {error,warning,info,debug}] input
105
87
 
106
88
  positional arguments:
107
- input Input file location or URL
108
-
109
- optional arguments:
110
- -h, --help show this help message and exit
111
- -v, --version show program's version number and exit
112
- -o OUTPUTFILE,
113
- --outputfile OUTPUTFILE
114
- Output file name (postfixed with .html if needed)
115
- -c {true,false},
116
- --css {true,false}
117
- Include CSS in the output HTML
89
+ input Input file location or URL
90
+
91
+ options:
92
+ -h, --help show this help message and exit
93
+ -v, --version show program's version number and exit
94
+ -o, --outputfile OUTPUTFILE
95
+ A name you wish to assign to the output file. Will be postfixed with .html if not already added. If no output file is given, output will be printed to screen
96
+ -c, --css {true,false}
97
+ Whether (true) or not (false) to include CSS within an output HTML file.
98
+ -s, --sort Enables sorting of the subjects in the ontology in the output
99
+ -p, --profile {ontpub,vocpub,supermodel}
100
+ Which profile to use to generate HTML. Must be one of 'ontpub' (https://linked.data.gov.au/def/ontpub) - for ontologies, 'vocpub' (https://linked.data.gov.au/def/vocpub) - for SKOS vocabularies, 'supermodel' - for profiles of profiles
101
+ -l, --loglevel {error,warning,info,debug}
102
+ Set the logging level
118
103
  ```
119
104
 
120
105
  ### Basic Use
@@ -125,6 +110,12 @@ optional arguments:
125
110
  python pylode examples/ontpub/minimal.ttl -o minimal.html
126
111
  ```
127
112
 
113
+ for a SKOS vocabulary, indicate the `vocpub` profile:
114
+
115
+ ```bash
116
+ python pylode examples/vocpub/GeologicSpecimenMaterialTypes.ttl -o GeologicSpecimenMaterialTypes.html -p vocpub
117
+ ```
118
+
128
119
  #### As a Docker container
129
120
 
130
121
  ```bash
@@ -246,6 +237,9 @@ pyLODE deliberately does **not** translate everything in RDF to HTML, enforcing
246
237
  pyLODE supports simple and complex Agent objects, including ORCIDs, affiliations, and contact details.
247
238
 
248
239
  ```turtle
240
+ PREFIX schema: <https://schema.org/>
241
+ PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
242
+
249
243
  <ontology_x>
250
244
  schema:creator [
251
245
  schema:name "Nicholas J. Car" ;
@@ -302,9 +296,6 @@ Older releases
302
296
 
303
297
  | Version | Date | Description |
304
298
  |---------|------------------|---------------------------------------------------------------------------------|
305
- | 3.0.5 | 27 April 2023 | Minor patching |
306
- | 3.0.4 | 24 May 2022 | Use of Poetry |
307
- | 3.0.2 | 24 May 2022 | Support for preformatted skos:example literals |
308
299
  | 3.0.1 | 6 Jan 2022 | Direct HTML generation using dominate; easier to maintain and extend |
309
300
  | 2.13.2 | 21 December 2021 | Updated RDFlib to 6.1.1, improved test to properly use pytest |
310
301
  | 2.10.0 | 24 May 2021 | Update Windows EXE build process, simplified versioning |
@@ -355,7 +346,7 @@ Nicholas Car
355
346
  5. tag with version number: `$git tag ...`
356
347
  6. push changes `$ git push`
357
348
  7. push version `git push --tags`
358
- 8make a GitHub release
349
+ 8. make a GitHub release
359
350
  * PyPI release is automated from this in `pypi.yml`
360
351
  9. increment version number to next release alpha in `pyproject.toml`
361
352
  10. commit all changes `$ git commit -a "..."`
@@ -7,25 +7,7 @@ An OWL ontology documentation tool using Python, based on LODE.
7
7
 
8
8
  In addition to making web page, human-readable forms of ontologies, pyLODE encourages ontology annotation *best practice* by only producing good results for well documented inputs! pyLODE defines what it considers “well documented” in sections below, e.g. [What pyLODE understands](#what-pylode-understands).
9
9
 
10
- **New mode**: In v3.1.0, pyLODE now has a new mode called `supermodel`, in addition to the existing `ontpub` mode. This new mode allows for documenting **profiles and modules** of multipart models. See [supermodel.md](supermodel.md) for more information.
11
-
12
- ---
13
-
14
- ## A note on the v 3.x change
15
-
16
- This is pyLODE version 3.0.1 and it’s vastly different from pyLODE 2.x. It doesn’t yet handle all the various “profiles” that pyLODE 2.13.2 does, such as SKOS “vocabularies” & Profiles Vocabulary “profiles”, it only handles OWL “ontologies”, nor all the special data types, such as JSON literals.
17
-
18
- However, it generates HTML in a much more straightforward manner and the code is both more efficient and much more maintainable, which is why it’s been made.
19
-
20
- v 3.x will eventually catch up to all of v 2.13.2’s features.
21
-
22
- To access v 2.13.2 of pyLODE, either:
23
-
24
- - [Download it from PyPI](https://pypi.org/project/pylode/2.13.2/)
25
- - [Check it out from GitHub](https://github.com/RDFLib/pyLODE/releases/tag/2.13.2)
26
- - Access it via the [online service](http://tools.kurrawong.ai/pylode)
27
-
28
- ---
10
+ **New mode**: In v3.1.0, pyLODE now has a new mode called `supermodel`, in addition to the existing `ontpub` & `vocpub` modes. This new mode allows for documenting **profiles and modules** of multipart models. See [supermodel.md](supermodel.md) for more information.
29
11
 
30
12
  ## Contents
31
13
 
@@ -70,20 +52,23 @@ The tool can be used in multiple ways:
70
52
  ### Command line arguments
71
53
 
72
54
  ```text
73
- usage: cli.py [-h] [-v] [-o OUTPUTFILE] [-c {true,false}] input
55
+ usage: pylode [-h] [-v] [-o OUTPUTFILE] [-c {true,false}] [-s] [-p {ontpub,vocpub,supermodel}] [-l {error,warning,info,debug}] input
74
56
 
75
57
  positional arguments:
76
- input Input file location or URL
77
-
78
- optional arguments:
79
- -h, --help show this help message and exit
80
- -v, --version show program's version number and exit
81
- -o OUTPUTFILE,
82
- --outputfile OUTPUTFILE
83
- Output file name (postfixed with .html if needed)
84
- -c {true,false},
85
- --css {true,false}
86
- Include CSS in the output HTML
58
+ input Input file location or URL
59
+
60
+ options:
61
+ -h, --help show this help message and exit
62
+ -v, --version show program's version number and exit
63
+ -o, --outputfile OUTPUTFILE
64
+ A name you wish to assign to the output file. Will be postfixed with .html if not already added. If no output file is given, output will be printed to screen
65
+ -c, --css {true,false}
66
+ Whether (true) or not (false) to include CSS within an output HTML file.
67
+ -s, --sort Enables sorting of the subjects in the ontology in the output
68
+ -p, --profile {ontpub,vocpub,supermodel}
69
+ Which profile to use to generate HTML. Must be one of 'ontpub' (https://linked.data.gov.au/def/ontpub) - for ontologies, 'vocpub' (https://linked.data.gov.au/def/vocpub) - for SKOS vocabularies, 'supermodel' - for profiles of profiles
70
+ -l, --loglevel {error,warning,info,debug}
71
+ Set the logging level
87
72
  ```
88
73
 
89
74
  ### Basic Use
@@ -94,6 +79,12 @@ optional arguments:
94
79
  python pylode examples/ontpub/minimal.ttl -o minimal.html
95
80
  ```
96
81
 
82
+ for a SKOS vocabulary, indicate the `vocpub` profile:
83
+
84
+ ```bash
85
+ python pylode examples/vocpub/GeologicSpecimenMaterialTypes.ttl -o GeologicSpecimenMaterialTypes.html -p vocpub
86
+ ```
87
+
97
88
  #### As a Docker container
98
89
 
99
90
  ```bash
@@ -215,6 +206,9 @@ pyLODE deliberately does **not** translate everything in RDF to HTML, enforcing
215
206
  pyLODE supports simple and complex Agent objects, including ORCIDs, affiliations, and contact details.
216
207
 
217
208
  ```turtle
209
+ PREFIX schema: <https://schema.org/>
210
+ PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
211
+
218
212
  <ontology_x>
219
213
  schema:creator [
220
214
  schema:name "Nicholas J. Car" ;
@@ -271,9 +265,6 @@ Older releases
271
265
 
272
266
  | Version | Date | Description |
273
267
  |---------|------------------|---------------------------------------------------------------------------------|
274
- | 3.0.5 | 27 April 2023 | Minor patching |
275
- | 3.0.4 | 24 May 2022 | Use of Poetry |
276
- | 3.0.2 | 24 May 2022 | Support for preformatted skos:example literals |
277
268
  | 3.0.1 | 6 Jan 2022 | Direct HTML generation using dominate; easier to maintain and extend |
278
269
  | 2.13.2 | 21 December 2021 | Updated RDFlib to 6.1.1, improved test to properly use pytest |
279
270
  | 2.10.0 | 24 May 2021 | Update Windows EXE build process, simplified versioning |
@@ -324,7 +315,7 @@ Nicholas Car
324
315
  5. tag with version number: `$git tag ...`
325
316
  6. push changes `$ git push`
326
317
  7. push version `git push --tags`
327
- 8make a GitHub release
318
+ 8. make a GitHub release
328
319
  * PyPI release is automated from this in `pypi.yml`
329
320
  9. increment version number to next release alpha in `pyproject.toml`
330
321
  10. commit all changes `$ git commit -a "..."`
@@ -46,7 +46,7 @@ parser.add_argument(
46
46
  "--profile",
47
47
  help="Which profile to use to generate HTML. Must be one of "
48
48
  "'ontpub' (https://linked.data.gov.au/def/ontpub) - for ontologies, "
49
- "'vocpub' (https://linked.data.gov.au/def/vocpub) - for SKOS vocabularies"
49
+ "'vocpub' (https://linked.data.gov.au/def/vocpub) - for SKOS vocabularies, "
50
50
  "'supermodel' - for profiles of profiles",
51
51
  choices=["ontpub", "vocpub", "supermodel"],
52
52
  default="ontpub",
@@ -16,10 +16,12 @@ from dominate.tags import (
16
16
  h2,
17
17
  h3,
18
18
  h4,
19
+ h5,
19
20
  li,
20
21
  link,
21
22
  meta,
22
23
  script,
24
+ section,
23
25
  strong,
24
26
  style,
25
27
  sup,
@@ -132,6 +134,9 @@ class OntPub:
132
134
  for s_ in g.subjects(RDF.type, OWL.Class):
133
135
  g.add((s_, RDF.type, RDFS.Class))
134
136
 
137
+ for s_ in g.subjects(RDF.type, RDFS.Class):
138
+ g.add((s_, RDF.type, OWL.Class))
139
+
135
140
  # # property types
136
141
  # for s_ in chain(
137
142
  # g.subjects(RDF.type, OWL.ObjectProperty),
@@ -474,6 +479,21 @@ class OntPub:
474
479
  )
475
480
  d.render()
476
481
 
482
+ if (None, RDF.type, RDFS.Datatype) in self.ont:
483
+ d = section_html(
484
+ "Custom Datatypes",
485
+ self.ont,
486
+ self.back_onts,
487
+ self.ns,
488
+ RDFS.Datatype,
489
+ PROP_PROPS,
490
+ self.toc,
491
+ "datatypes",
492
+ self.fids,
493
+ self.props_labeled,
494
+ )
495
+ d.render()
496
+
477
497
  def _make_legend(self):
478
498
  with self.content:
479
499
  with div(id="legend"):
@@ -573,9 +593,23 @@ class OntPub:
573
593
  ):
574
594
  with li():
575
595
  h4(a("Classes", href="#classes"))
596
+ h5(
597
+ a(
598
+ "Class Hierarchy",
599
+ href="#class-hierarchy",
600
+ style="margin-left:10px;",
601
+ )
602
+ )
603
+ h5(
604
+ a(
605
+ "Class Definitions",
606
+ href="#class-definitions",
607
+ style="margin-left:10px;",
608
+ )
609
+ )
576
610
  with ul(_class="second"):
577
611
  for c in self.toc["classes"]:
578
- li(a(c[1], href=c[0]))
612
+ li(a(c[1], href=c[0]), style="margin-left:10px;")
579
613
 
580
614
  if (
581
615
  self.toc.get("properties") is not None
@@ -593,9 +627,23 @@ class OntPub:
593
627
  ):
594
628
  with li():
595
629
  h4(a("Object Properties", href="#objectproperties"))
630
+ h5(
631
+ a(
632
+ "Object Property Hierarchy",
633
+ href="#object-property-hierarchy",
634
+ style="margin-left:10px;",
635
+ )
636
+ )
637
+ h5(
638
+ a(
639
+ "Object Property Definitions",
640
+ href="#object-property-definitions",
641
+ style="margin-left:10px;",
642
+ )
643
+ )
596
644
  with ul(_class="second"):
597
645
  for c in self.toc["objectproperties"]:
598
- li(a(c[1], href=c[0]))
646
+ li(a(c[1], href=c[0]), style="margin-left:10px;")
599
647
 
600
648
  if (
601
649
  self.toc.get("datatypeproperties") is not None
@@ -627,6 +675,16 @@ class OntPub:
627
675
  for c in self.toc["functionalproperties"]:
628
676
  li(a(c[1], href=c[0]))
629
677
 
678
+ if (
679
+ self.toc.get("datatypes") is not None
680
+ and len(self.toc["datatypes"]) > 0
681
+ ):
682
+ with li():
683
+ h4(a("Custom Datatypes", href="#datatypes"))
684
+ with ul(_class="second"):
685
+ for c in self.toc["datatypes"]:
686
+ li(a(c[1], href=c[0]))
687
+
630
688
  with li():
631
689
  h4(a("Namespaces", href="#namespaces"))
632
690
  with ul(_class="second"):
@@ -50,7 +50,7 @@ from rdflib.namespace import (
50
50
 
51
51
  from pylode.rdf_elements import AGENT_PROPS, CONCEPT_SCHEME_PROPS, ONT_PROPS, ONTDOC
52
52
  from pylode.utils import (
53
- PylodeError,
53
+ _make_hierarchy_html,
54
54
  back_onts_label_props,
55
55
  get_ns,
56
56
  load_background_onts,
@@ -302,7 +302,7 @@ class VocPub:
302
302
  )
303
303
 
304
304
  def _make_body(self):
305
- """Healper function for make_html(). Makes <body>???</body> content.
305
+ """Helper function for make_html(). Makes <body>???</body> content.
306
306
 
307
307
  Just calls other helper functions in order"""
308
308
  make_pylode_logo(
@@ -450,53 +450,11 @@ class VocPub:
450
450
  def _make_concept_hierarchy(self):
451
451
  with self.content:
452
452
  if (None, RDF.type, SKOS.Concept) in self.ont:
453
- concepts = []
454
- for s in self.ont.subjects(RDF.type, SKOS.Concept):
455
- for o in self.ont.objects(s, SDO.name):
456
- c = {"iri": str(s), "prefLabel": str(o)}
457
- for o2 in self.ont.objects(s, SKOS.broader):
458
- c["broader"] = str(o2)
459
- concepts.append(c)
460
-
461
- def build_html_tree(items):
462
- # Index items by id
463
- by_id = {item["iri"]: dict(item, children=[]) for item in items}
464
-
465
- roots = []
466
-
467
- # Build tree structure
468
- for item in by_id.values():
469
- broader = item.get("broader")
470
- if broader and broader in by_id:
471
- by_id[broader]["children"].append(item)
472
- else:
473
- roots.append(item)
474
-
475
- # Recursive renderer
476
- def render_nodes(nodes):
477
- container = ul()
478
- for node in nodes:
479
- node_li = li(
480
- a(
481
- node["prefLabel"],
482
- href="#"
483
- + str(
484
- self.ont.namespace_manager.qname(
485
- node["iri"]
486
- ).replace(":", "_")
487
- ),
488
- )
489
- )
490
- if node["children"]:
491
- node_li.add(render_nodes(node["children"]))
492
- container.add(node_li)
493
- return container
494
-
495
- return render_nodes(roots)
496
-
497
453
  d = div(
498
454
  h2("Concept Hierarchy"),
499
- build_html_tree(concepts),
455
+ _make_hierarchy_html(
456
+ self.ont, SKOS.Concept, SKOS.broader, self.fids
457
+ ),
500
458
  id="concept-hierarchy",
501
459
  )
502
460
 
@@ -516,7 +474,7 @@ class VocPub:
516
474
  remaining = {k: v for k, v in d.items() if k not in ordered}
517
475
  return ordered | remaining
518
476
 
519
- d = div(h2("Concepts"), id="concepts")
477
+ d = div(h2("Concept Definitions"), id="concepts")
520
478
  for iri, concept in dict(
521
479
  sorted(concepts.items(), key=lambda item: item[1][SDO.name])
522
480
  ).items(): # order by Concept's label
@@ -601,7 +559,7 @@ class VocPub:
601
559
 
602
560
  li(h4(a("Concepts Hierarchy", href="#concept-hierarchy")))
603
561
 
604
- li(h4(a("Concepts", href="#concepts")))
562
+ li(h4(a("Concept Definitions", href="#concepts")))
605
563
 
606
564
  with li():
607
565
  h4(a("Namespaces", href="#namespaces"))
@@ -51,17 +51,18 @@
51
51
  padding-top: 7px;
52
52
  }
53
53
 
54
- #metadata ul,
55
- #classes ul {
54
+ ul.pylodelitlist {
56
55
  list-style-type: none;
56
+ margin-left: -40px;
57
57
  }
58
58
 
59
59
  #metadata ul li,
60
60
  #classes ul li {
61
- margin-left: -40px;
61
+ margin-bottom: 10px;
62
62
  }
63
+
63
64
  #metadata dd p {
64
- margin: 0;
65
+ margin: 0 0 1em 0;
65
66
  }
66
67
 
67
68
  ul.hlist {
@@ -71,12 +72,19 @@
71
72
  background-color: #F4FFFF;
72
73
  }
73
74
 
74
- ul.hierarchy {
75
- border: 1px solid navy;
76
- padding: 5px 25px 5px 25px;
75
+ div.hierarchy {
76
+ margin-bottom: 30px;
77
77
  background-color: #F4FFFF;
78
+ padding: 10px;
78
79
  }
79
80
 
81
+ ul.hierarchy {=
82
+ padding: 5px 25px 5px 25px;
83
+ }
84
+
85
+ ul.hierarchy li {
86
+ margin-top:5px;
87
+ }
80
88
 
81
89
  ul.hlist li {
82
90
  display: inline;
@@ -128,7 +136,10 @@
128
136
  border-bottom: 1px solid navy;
129
137
  }
130
138
  h4 { font: bold 100% sans-serif }
131
- h5 { font: italic 100% sans-serif }
139
+ h5 {
140
+ font: italic 100% sans-serif;
141
+ margin-bottom: 10px;
142
+ }
132
143
  h6 { font: small-caps 100% sans-serif }
133
144
 
134
145
  body {
@@ -217,60 +228,60 @@
217
228
  text-decoration: none;
218
229
  }
219
230
 
220
- #toc {
221
- position: fixed;
222
- top: 0;
223
- right: 0;
224
- z-index: 2;
225
- height: 100%;
226
- overflow-y: auto;
227
- padding: 10px;
228
- border: solid 1px navy;
229
- font-size: small;
230
- width: 180px;
231
- }
232
- #toc h3 {
233
- margin-top: 5px;
234
- }
235
-
236
- #toc ul {
237
- list-style: none;
238
- padding-left: 0;
239
- }
240
-
241
- #toc .first > li {
242
- margin-top: 5px;
243
- }
244
-
245
- #toc .second,
246
- #toc .third {
247
- padding-left: 10px;
248
- }
249
-
250
- #content {
251
- width: calc(100% - 150px);
252
- }
253
-
254
- .hover_property {
255
- cursor: help;
256
- text-decoration: none;
257
- border-bottom: dashed 1px;
258
- }
259
-
260
- .setclass {
261
- list-style-type: none;
262
- }
263
-
264
- code{
265
- word-wrap: break-word;
266
- }
267
- table {
268
- table-layout: fixed;
269
- width: 100%;
270
- }
271
- td {
272
- word-wrap: break-word;
273
- }
274
- #legend table.entity {
275
- width: 25%;
276
- }
231
+ #toc {
232
+ position: fixed;
233
+ top: 0;
234
+ right: 0;
235
+ z-index: 2;
236
+ height: 100%;
237
+ overflow-y: auto;
238
+ padding: 10px;
239
+ border: solid 1px navy;
240
+ font-size: small;
241
+ width: 180px;
242
+ }
243
+ #toc h3 {
244
+ margin-top: 5px;
245
+ }
246
+
247
+ #toc ul {
248
+ list-style: none;
249
+ padding-left: 0;
250
+ }
251
+
252
+ #toc .first > li {
253
+ margin-top: 5px;
254
+ }
255
+
256
+ #toc .second,
257
+ #toc .third {
258
+ padding-left: 10px;
259
+ }
260
+
261
+ #content {
262
+ width: calc(100% - 150px);
263
+ }
264
+
265
+ .hover_property {
266
+ cursor: help;
267
+ text-decoration: none;
268
+ border-bottom: dashed 1px;
269
+ }
270
+
271
+ .setclass {
272
+ list-style-type: none;
273
+ }
274
+
275
+ code{
276
+ word-wrap: break-word;
277
+ }
278
+ table {
279
+ table-layout: fixed;
280
+ width: 100%;
281
+ }
282
+ td {
283
+ word-wrap: break-word;
284
+ }
285
+ #legend table.entity {
286
+ width: 25%;
287
+ }
@@ -1715,7 +1715,7 @@ schema:code a rdf:Property ;
1715
1715
  schema:rangeIncludes schema:MedicalCode .
1716
1716
 
1717
1717
  schema:codeRepository a rdf:Property ;
1718
- rdfs:label "codeRepository" ;
1718
+ rdfs:label "code repository" ;
1719
1719
  rdfs:comment "Link to the repository where the un-compiled, human readable code and related code is located (SVN, github, CodePlex)." ;
1720
1720
  schema:domainIncludes schema:SoftwareSourceCode ;
1721
1721
  schema:rangeIncludes schema:URL .
@@ -1,13 +1,5 @@
1
1
  from rdflib import Namespace
2
- from rdflib.namespace import (
3
- DCTERMS,
4
- OWL,
5
- RDF,
6
- RDFS,
7
- SDO,
8
- SKOS,
9
- VANN,
10
- )
2
+ from rdflib.namespace import DCTERMS, OWL, RDF, RDFS, SDO, SKOS, VANN, XSD
11
3
 
12
4
  ONTDOC = Namespace("https://w3id.org/profile/ontdoc/")
13
5
 
@@ -160,6 +152,7 @@ ONT_TYPES = {
160
152
  OWL.AnnotationProperty: ("ap", "OWL Annotation Property"),
161
153
  OWL.FunctionalProperty: ("fp", "OWL Functional Property"),
162
154
  OWL.InverseFunctionalProperty: ("ifp", "OWL Inverse Functional Property"),
155
+ RDFS.Datatype: ("dt", "RDFS Datatypes"),
163
156
  OWL.NamedIndividual: ("ni", "OWL Named Individual"),
164
157
  }
165
158
 
@@ -176,3 +169,10 @@ RESTRICTION_TYPES = [
176
169
  ]
177
170
 
178
171
  OWL_SET_TYPES = [OWL.unionOf, OWL.intersectionOf]
172
+
173
+ DATATYPE_CARDINALITIES = {
174
+ XSD.minInclusive: ">=",
175
+ XSD.minExclusive: ">",
176
+ XSD.maxInclusive: "<=",
177
+ XSD.maxExclusive: "<",
178
+ }
@@ -36,6 +36,7 @@ from rdflib.paths import ZeroOrMore
36
36
  try:
37
37
  from .rdf_elements import (
38
38
  AGENT_PROPS,
39
+ DATATYPE_CARDINALITIES,
39
40
  ONT_TYPES,
40
41
  ONTDOC,
41
42
  OWL_SET_TYPES,
@@ -410,9 +411,9 @@ def rdf_obj_html(
410
411
  ):
411
412
  def _hyperlink_html(
412
413
  ont__: Graph,
414
+ iri__: URIRef,
413
415
  back_onts__: Graph,
414
416
  ns__: Tuple[str, str],
415
- iri__: URIRef,
416
417
  fids__,
417
418
  rdf_type__: Optional[URIRef] = None,
418
419
  ):
@@ -470,8 +471,6 @@ def rdf_obj_html(
470
471
  else:
471
472
  try:
472
473
  qname = ont__.compute_qname(iri__, False)
473
- if "ASGS" in qname[2]:
474
- print(qname)
475
474
  except Exception:
476
475
  qname = iri__
477
476
  prefix = "" if qname[0] == "" else f"{qname[0]}:"
@@ -503,11 +502,13 @@ def rdf_obj_html(
503
502
  def _literal_html(obj__):
504
503
  if str(obj__).startswith("http"):
505
504
  return _hyperlink_html(
506
- ont_, back_onts_, ns_, cast(URIRef, obj__), fids_
505
+ ont_, cast(URIRef, obj__), back_onts_, ns_, fids_
507
506
  )
508
507
  else:
509
508
  if prop == SKOS.example:
510
509
  return pre(str(obj__))
510
+ elif obj__.datatype == RDF.HTML:
511
+ return raw(str(obj__))
511
512
  else:
512
513
  return raw(markdown.markdown(str(obj__)))
513
514
 
@@ -559,18 +560,47 @@ def rdf_obj_html(
559
560
  </g>
560
561
  </svg>"""
561
562
 
563
+ def _ror_logo_svg(id):
564
+ id = id if ("http" in id) else f"https://ror.org/{id}"
565
+ return f"""
566
+ <svg
567
+ width="20px"
568
+ height="20px"
569
+ viewBox="0 0 164 118"
570
+ version="1.1"
571
+ xmlns="http://www.w3.org/2000/svg"
572
+ xmlns:xlink="http://www.w3.org/1999/xlink"
573
+ xml:space="preserve"
574
+ xmlns:serif="http://www.serif.com/"
575
+ style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
576
+ <title>{id}</title>
577
+ <g transform="matrix(0.994301,0,0,0.989352,0,0)">
578
+ <rect x="0" y="0" width="164.94" height="119.27" style="fill:white;"/>
579
+ </g>
580
+ <g transform="matrix(1,0,0,1,-0.945,-0.815)">
581
+ <path d="M68.65,4.16L56.52,22.74L44.38,4.16L68.65,4.16Z" style="fill:rgb(83,186,161);fill-rule:nonzero;"/>
582
+ <path d="M119.41,4.16L107.28,22.74L95.14,4.16L119.41,4.16Z" style="fill:rgb(83,186,161);fill-rule:nonzero;"/>
583
+ <path d="M44.38,115.47L56.52,96.88L68.65,115.47L44.38,115.47Z" style="fill:rgb(83,186,161);fill-rule:nonzero;"/>
584
+ <path d="M95.14,115.47L107.28,96.88L119.41,115.47L95.14,115.47Z" style="fill:rgb(83,186,161);fill-rule:nonzero;"/>
585
+ <path d="M145.53,63.71C149.83,62.91 153.1,61 155.33,57.99C157.57,54.98 158.68,51.32 158.68,47.03C158.68,43.47 158.06,40.51 156.83,38.13C155.6,35.75 153.93,33.86 151.84,32.45C149.75,31.05 147.31,30.04 144.53,29.44C141.75,28.84 138.81,28.54 135.72,28.54L112.16,28.54L112.16,47.37C111.97,46.82 111.77,46.28 111.55,45.74C109.92,41.79 107.64,38.42 104.71,35.64C101.78,32.86 98.32,30.72 94.3,29.23C90.29,27.74 85.9,26.99 81.14,26.99C76.38,26.99 72,27.74 67.98,29.23C63.97,30.72 60.5,32.86 57.57,35.64C54.95,38.13 52.85,41.1 51.27,44.54C51.04,42.07 50.46,39.93 49.53,38.13C48.3,35.75 46.63,33.86 44.54,32.45C42.45,31.05 40.01,30.04 37.23,29.44C34.45,28.84 31.51,28.54 28.42,28.54L4.87,28.54L4.87,89.42L18.28,89.42L18.28,65.08L24.9,65.08L37.63,89.42L53.71,89.42L38.24,63.71C42.54,62.91 45.81,61 48.04,57.99C48.14,57.85 48.23,57.7 48.33,57.56C48.31,58.03 48.3,58.5 48.3,58.98C48.3,63.85 49.12,68.27 50.75,72.22C52.38,76.17 54.66,79.54 57.59,82.32C60.51,85.1 63.98,87.24 68,88.73C72.01,90.22 76.4,90.97 81.16,90.97C85.92,90.97 90.3,90.22 94.32,88.73C98.33,87.24 101.8,85.1 104.73,82.32C107.65,79.54 109.93,76.17 111.57,72.22C111.79,71.69 111.99,71.14 112.18,70.59L112.18,89.42L125.59,89.42L125.59,65.08L132.21,65.08L144.94,89.42L161.02,89.42L145.53,63.71ZM36.39,50.81C35.67,51.73 34.77,52.4 33.68,52.83C32.59,53.26 31.37,53.52 30.03,53.6C28.68,53.69 27.41,53.73 26.2,53.73L18.29,53.73L18.29,39.89L27.06,39.89C28.26,39.89 29.5,39.98 30.76,40.15C32.02,40.32 33.14,40.65 34.11,41.14C35.08,41.63 35.89,42.33 36.52,43.25C37.15,44.17 37.47,45.4 37.47,46.95C37.47,48.6 37.11,49.89 36.39,50.81ZM98.74,66.85C97.85,69.23 96.58,71.29 94.91,73.04C93.25,74.79 91.26,76.15 88.93,77.13C86.61,78.11 84.01,78.59 81.15,78.59C78.28,78.59 75.69,78.1 73.37,77.13C71.05,76.16 69.06,74.79 67.39,73.04C65.73,71.29 64.45,69.23 63.56,66.85C62.67,64.47 62.23,61.85 62.23,58.98C62.23,56.17 62.67,53.56 63.56,51.15C64.45,48.74 65.72,46.67 67.39,44.92C69.05,43.17 71.04,41.81 73.37,40.83C75.69,39.86 78.28,39.37 81.15,39.37C84.02,39.37 86.61,39.86 88.93,40.83C91.25,41.8 93.24,43.17 94.91,44.92C96.57,46.67 97.85,48.75 98.74,51.15C99.63,53.56 100.07,56.17 100.07,58.98C100.07,61.85 99.63,64.47 98.74,66.85ZM143.68,50.81C142.96,51.73 142.06,52.4 140.97,52.83C139.88,53.26 138.66,53.52 137.32,53.6C135.97,53.69 134.7,53.73 133.49,53.73L125.58,53.73L125.58,39.89L134.35,39.89C135.55,39.89 136.79,39.98 138.05,40.15C139.31,40.32 140.43,40.65 141.4,41.14C142.37,41.63 143.18,42.33 143.81,43.25C144.44,44.17 144.76,45.4 144.76,46.95C144.76,48.6 144.4,49.89 143.68,50.81Z" style="fill:rgb(32,40,38);fill-rule:nonzero;"/>
586
+ </g>
587
+ </svg>"""
588
+
562
589
  if isinstance(obj__, Literal):
563
590
  return span(str(obj__))
564
591
  honorific_prefix = None
565
592
  name = None
566
593
  identifier = None
567
594
  orcid = None
595
+ ror = None
568
596
  url = None
569
597
  email = None
570
598
  affiliation = None
571
599
 
572
600
  if "orcid.org" in str(obj__):
573
601
  orcid = True
602
+ elif "ror.org" in str(obj__):
603
+ ror = True
574
604
 
575
605
  for px, o in ont__.predicate_objects(obj__):
576
606
  if px in AGENT_PROPS:
@@ -607,6 +637,13 @@ def rdf_obj_html(
607
637
  sp.appendChild(
608
638
  a(raw(_orcid_logo_svg(identifier)), href=identifier)
609
639
  )
640
+ elif ror:
641
+ if "ror.org" in obj__:
642
+ sp.appendChild(a(raw(_ror_logo_svg(obj__)), href=obj__))
643
+ else:
644
+ sp.appendChild(
645
+ a(raw(_ror_logo_svg(identifier)), href=identifier)
646
+ )
610
647
  elif identifier is not None:
611
648
  sp.appendChild(a(identifier, href=identifier))
612
649
  if email is not None:
@@ -629,10 +666,15 @@ def rdf_obj_html(
629
666
  for px, o in ont__.predicate_objects(obj__):
630
667
  if px != RDF.type:
631
668
  if px == OWL.onProperty:
632
- prop = _hyperlink_html(ont__, back_onts_, ns__, o, fids_)
633
- #Added the onClass restriction otherwise the class name is ignored in the HTML output.
669
+ prop = _hyperlink_html(ont__, o, back_onts_, ns__, fids_)
670
+ # Added the onClass restriction otherwise the class name is ignored in the HTML output.
634
671
  elif px == OWL.onClass:
635
- cls = _hyperlink_html(ont__, back_onts_, ns__, o, fids_, OWL.Class)
672
+ if (o, OWL.unionOf | OWL.intersectionOf, None) in ont__:
673
+ cls = _setclass_html(ont__, o, back_onts_, ns__, fids_)
674
+ else:
675
+ cls = _hyperlink_html(
676
+ ont__, o, back_onts_, ns__, fids_, OWL.Class
677
+ )
636
678
  elif px in RESTRICTION_TYPES + OWL_SET_TYPES:
637
679
  if px in [
638
680
  OWL.minCardinality,
@@ -674,12 +716,35 @@ def rdf_obj_html(
674
716
  span(card, _class="cardinality"),
675
717
  span(
676
718
  _hyperlink_html(
677
- ont__, back_onts_, ns__, o, fids_, OWL.Class
719
+ ont__, o, back_onts_, ns__, fids_, OWL.Class
678
720
  )
679
721
  ),
680
722
  )
681
-
682
- #Combined the check for card and cls so that only one br is added.
723
+ elif px == ONTDOC.inRangeOf: # rdfs:range
724
+ for o2 in ont__.objects(o, RDFS.range):
725
+ for rp, ro in ont__.predicate_objects(o2):
726
+ if rp == OWL.onDatatype:
727
+ prop = _hyperlink_html(
728
+ ont__, ro, back_onts_, ns__, fids_
729
+ )
730
+ if rp == OWL.withRestrictions:
731
+ cards = []
732
+ for ro2 in ont__.objects(o2, OWL.withRestrictions):
733
+ for ro3 in ont__.objects(
734
+ ro2, RDF.rest * ZeroOrMore / RDF.first
735
+ ):
736
+ for rp4, ro4 in ont__.predicate_objects(
737
+ ro3
738
+ ):
739
+ if rp4 in DATATYPE_CARDINALITIES.keys():
740
+ cards.append(
741
+ f"{DATATYPE_CARDINALITIES[rp4]}{ro4}"
742
+ )
743
+ if len(cards) > 0:
744
+ card = " [" + ", ".join(cards) + "]"
745
+ # XXX
746
+
747
+ # Combined the check for card and cls so that only one br is added.
683
748
  if card is not None and cls is not None:
684
749
  restriction = span(prop, card, cls, br())
685
750
  elif card is not None:
@@ -699,18 +764,18 @@ def rdf_obj_html(
699
764
  else:
700
765
  joining_word = span(",", _class="cardinality")
701
766
 
702
- class_set = set()
767
+ class_set = []
703
768
  for o in ont__.objects(obj__, OWL.unionOf | OWL.intersectionOf):
704
769
  for o2 in ont__.objects(o, RDF.rest * ZeroOrMore / RDF.first):
705
- class_set.add(
770
+ class_set.append(
706
771
  _rdf_obj_single_html(
707
- ont__, back_onts__, ns__, o2, fids__, OWL.Class
772
+ ont__, back_onts__, ns__, o2, fids__, OWL.Class, prop=prop
708
773
  )
709
774
  )
710
775
 
711
776
  return intersperse(class_set, joining_word)
712
777
 
713
- def _bn_html(ont__, back_onts__, ns__, fids__, obj__: BNode):
778
+ def _bn_html(ont__, obj__, back_onts__, ns__, fids__: BNode):
714
779
  # TODO: remove back_onts and fids if not needed by subfunctions
715
780
  # What kind of BN is it?
716
781
  # An Agent, a Restriction or a Set Class (union/intersection)
@@ -719,15 +784,17 @@ def rdf_obj_html(
719
784
  return _agent_html(ont__, obj__)
720
785
  elif (obj__, RDF.type, OWL.Restriction) in ont__:
721
786
  return _restriction_html(ont__, obj__, ns__)
787
+ elif (obj__, RDF.type, RDFS.Datatype) in ont__:
788
+ return _restriction_html(ont__, obj__, ns__)
722
789
  else: # (obj, RDF.type, OWL.Class) in ont: # Set Class
723
790
  return _setclass_html(ont__, obj__, back_onts__, ns__, fids__)
724
791
 
725
792
  if isinstance(obj_, Tuple) or isinstance(obj_, URIRef):
726
793
  ret = _hyperlink_html(
727
- ont_, back_onts_, ns_, obj_, fids_, rdf_type__=rdf_type_
794
+ ont_, obj_, back_onts_, ns_, fids_, rdf_type__=rdf_type_
728
795
  )
729
796
  elif isinstance(obj_, BNode):
730
- ret = _bn_html(ont_, back_onts_, ns_, fids_, obj_)
797
+ ret = _bn_html(ont_, obj_, back_onts_, ns_, fids_)
731
798
  else: # isinstance(obj, Literal):
732
799
  ret = _literal_html(obj_)
733
800
 
@@ -738,7 +805,7 @@ def rdf_obj_html(
738
805
  ont, back_onts, ns, obj[0], fids, rdf_type_=rdf_type, prop=prop
739
806
  )
740
807
  else:
741
- u_ = ul()
808
+ u_ = ul(_class="pylodelitlist")
742
809
  for x in obj:
743
810
  u_.appendChild(
744
811
  li(
@@ -781,6 +848,68 @@ def prop_obj_pair_html(
781
848
  return t
782
849
 
783
850
 
851
+ def _make_hierarchy_html(
852
+ ont: Graph, obj_class: URIRef, parent_indicator: URIRef, fids: dict
853
+ ):
854
+ if (None, RDF.type, obj_class) in ont:
855
+ items = []
856
+ for s in ont.subjects(RDF.type, obj_class):
857
+ if not isinstance(s, BNode):
858
+ name = ont.value(s, DCTERMS.title | SDO.name)
859
+ if name is None:
860
+ name = make_title_from_iri(s)
861
+ c = {
862
+ "iri": str(s),
863
+ "name": str(
864
+ name
865
+ ), # need 2 x for OntPub (title) and VocPub (prefLabel) profiles
866
+ }
867
+ for o2 in ont.objects(s, parent_indicator):
868
+ if not isinstance(o2, BNode):
869
+ c["parent"] = str(o2)
870
+ items.append(c)
871
+
872
+ def build_html_tree(items):
873
+ # Index items by id
874
+ by_id = {item["iri"]: dict(item, children=[]) for item in items}
875
+
876
+ roots = []
877
+
878
+ # Build tree structure
879
+ for item in by_id.values():
880
+ parent = item.get("parent")
881
+ if parent and parent in by_id:
882
+ by_id[parent]["children"].append(item)
883
+ else:
884
+ roots.append(item)
885
+
886
+ # Recursive renderer
887
+ def render_nodes(nodes):
888
+ container = ul(_class="hierarchy")
889
+ for node in sorted(
890
+ nodes,
891
+ key=lambda node: (
892
+ node["name"].casefold(),
893
+ node["name"],
894
+ node["iri"],
895
+ ),
896
+ ):
897
+ node_li = li(
898
+ a(
899
+ node["name"],
900
+ href="#" + generate_fid(None, node["iri"], fids),
901
+ )
902
+ )
903
+ if node["children"]:
904
+ node_li.add(render_nodes(node["children"]))
905
+ container.add(node_li)
906
+ return container
907
+
908
+ return render_nodes(roots)
909
+
910
+ return div(build_html_tree(items), _class="hierarchy")
911
+
912
+
784
913
  def section_html(
785
914
  section_title: str,
786
915
  ont: Graph,
@@ -853,6 +982,22 @@ def section_html(
853
982
 
854
983
  elems = div(id=toc_ul_id, _class="section")
855
984
  elems.appendChild(h2(section_title))
985
+
986
+ if obj_class == OWL.Class:
987
+ elems.appendChild(h3("Class Hierarchy", id="class-hierarchy"))
988
+ elems.appendChild(_make_hierarchy_html(ont, OWL.Class, RDFS.subClassOf, fids))
989
+ elems.appendChild(h3("Class Definitions", id="class-definitions"))
990
+ elif obj_class == OWL.ObjectProperty:
991
+ elems.appendChild(
992
+ h3("Object Property Hierarchy", id="object-property-hierarchy")
993
+ )
994
+ elems.appendChild(
995
+ _make_hierarchy_html(ont, OWL.ObjectProperty, RDFS.subPropertyOf, fids)
996
+ )
997
+ elems.appendChild(
998
+ h3("Object Property Definitions", id="object-property-definitions")
999
+ )
1000
+
856
1001
  # get all objects of this class
857
1002
  for s_ in ont.subjects(predicate=RDF.type, object=obj_class):
858
1003
  if obj_class == RDF.Property:
@@ -918,7 +1063,7 @@ def de_space_html(html):
918
1063
  # s = "".join([l_.strip().replace("\n", " ") for l_ in html.split("\n")])
919
1064
  # return re.sub(" +", " ", s)
920
1065
  s = re.sub(r">\s+<", "><", html)
921
- return re.sub(r"\s+", " ", s)
1066
+ return re.sub(r"\s+", " ", s).strip()
922
1067
 
923
1068
 
924
1069
  def make_pylode_logo(doc, version, profile_name, profile_iri):
@@ -7,7 +7,7 @@ Changelog = "https://github.com/RDFLib/pyLODE/releases"
7
7
 
8
8
  [project]
9
9
  name = "pylode"
10
- version = "3.4.4"
10
+ version = "3.5.1"
11
11
  description = "An OWL ontology documentation tool using Python, based on LODE."
12
12
  authors = [{ name = "Nicholas Car", email = "nick@kurrawong.ai" }]
13
13
  requires-python = ">=3.12,<4"
@@ -24,7 +24,7 @@ classifiers = [
24
24
  dependencies = [
25
25
  "rdflib>=7.6.0,<8.0.0",
26
26
  "Markdown>=3.3.7,<4",
27
- "dominate>=2.6.0,<3",
27
+ "dominate>=2.9.1,<3",
28
28
  "html5lib>=1.1,<2",
29
29
  "httpx>=0.25.0,<1.0.0",
30
30
  "tqdm>=4.67.3",
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes