Loading src/saref_pypeline/docgen/site_manager.py +28 −6 Original line number Diff line number Diff line Loading @@ -166,19 +166,41 @@ class SiteManager: ) # variants graph = self.dataset.graph(doc.version_iri) # graph = self.dataset.graph(doc.version_iri) # for format, ext in [ # ("json-ld", ".jsonld"), # ("n3", ".n3"), # ("nt", ".nt"), # ("xml", ".rdf"), # ]: # graph.serialize( # os.path.join(self.site_dir, doc.site_path + ext), # format=format, # encoding="utf-8", # ) metadata = self.pipeline.dataset.graph(METADATA) pv_dir = Path(self.site_dir, project_version.project.path, str(project_version.version)) for term in metadata.subjects(RDFS.isDefinedBy, project_version.ontology.version_iri): html, graph = docgen.render_term_documentation(project_version, term) local_name = term[len(project_version.project.namespace):] Path(pv_dir, local_name + ".html").write_text(html) # variants for format, ext in [ ("json-ld", ".jsonld"), ("n3", ".n3"), ("nt", ".nt"), ("xml", ".rdf"), ("ttl", ".ttl"), # ("json-ld", ".jsonld"), # ("n3", ".n3"), # ("nt", ".nt"), # ("xml", ".rdf"), ]: graph.serialize( os.path.join(self.site_dir, doc.site_path + ext), os.path.join(pv_dir, local_name + ext), format=format, encoding="utf-8", ) def generate_ts(self, project_version: SAREFProjectVersion): project = project_version.project version = project_version.version Loading src/saref_pypeline/docgen/website_generator.py +186 −6 Original line number Diff line number Diff line Loading @@ -3,6 +3,7 @@ import os from pathlib import Path import logging from re import Match import dominate from dominate.tags import * from dominate.util import text, raw from markdown import markdown Loading @@ -25,7 +26,7 @@ from saref_pypeline.etsi import WK_FIELD from saref_pypeline.utils import filter_document from saref_pypeline.vocabs import SCHEMA from saref_pypeline.docgen.utils import * from typing import List, TypeVar, Callable, TYPE_CHECKING, Dict, TypeVar from typing import List, Set, TypeVar, Callable, TYPE_CHECKING, Dict, TypeVar T = TypeVar("T") Loading @@ -45,6 +46,10 @@ class HTMLEntityDescription(EntityDescription): def __init__(self, uri: URIRef, graph: Graph, namespace_manager=None): super().__init__(uri, graph, namespace_manager) @cached_property def local_name(self): return re.search(r"([^/#]+)$", self.uri).group(1) def sup(self): if OWL.Class in self.types: return sup("c", title="class", _class="type-c") Loading Loading @@ -83,6 +88,7 @@ class WebsiteGenerator: self.project = project_version.project self.version = project_version.version self.context = site_manager.context(project_version) self.metadata = self.dataset.graph(METADATA) self.nm = NamespaceManager(Graph(), bind_namespaces="none") for prefix, ns in self.project_version.ontology.namespaces: Loading Loading @@ -152,6 +158,9 @@ class WebsiteGenerator: template = jinja2_env.get_template("html.j2") g = self.dataset.graph(document.version_iri) with h1() as doc_h1: a(self.title(document), href=f"{document.declaration_iri}") with dl() as dls: dt("Latest version") Loading Loading @@ -312,7 +321,7 @@ class WebsiteGenerator: "ETSI IPR Policy", href="https://www.etsi.org/intellectual-property-rights", ), class_= "alert-warning" class_="alert-warning", ) toc = ol() Loading Loading @@ -470,6 +479,7 @@ class WebsiteGenerator: "project_version": self.project_version, "version": self.version, "document": document, "h1": doc_h1, "dls": dls, "content": contents, "my_tab_content": my_tab_content, Loading @@ -489,6 +499,9 @@ class WebsiteGenerator: template = jinja2_env.get_template("html.j2") g = self.dataset.graph(document.version_iri) with h1() as doc_h1: a(self.title(document), href=f"{document.declaration_iri}") with dl() as dls: dt(f"IRI") Loading Loading @@ -607,11 +620,153 @@ class WebsiteGenerator: "version": self.version, "document": document, "dls": dls, "h1": doc_h1, "content": contents, } return template.render(context) def render_term_documentation( self, project_version: SAREFProjectVersion, term: URIRef ): logger.debug(f"Printing html documentation for {term.n3(self.dataset.namespace_manager)}") ontology = project_version.ontology g = Graph(bind_namespaces="none") for prefix, namespace in ontology.namespaces: g.bind(prefix, namespace) onto_graph = self.dataset.graph(ontology.version_iri_with_imports) self.describe(term, onto_graph, g) self.describe(term, self.metadata, g) describe_funcs = [ ("Description as a Class", OWL.Class, self.describe_class), ("Description as an Object Property", OWL.ObjectProperty, self.describe_object_property), ("Definition as a Datatype Property", OWL.DatatypeProperty, self.describe_data_property), ("Definition as an Annotation Property", OWL.AnnotationProperty, self.describe_annotation_property), ("Definition as a Named Individual", OWL.Thing, self.describe_named_individual), ] template = jinja2_env.get_template("html.j2") desc = self.description(term, onto_graph) with h1() as doc_h1: a(desc.curie, href=term) text(f" — {desc.label}") with div() as dls: with dl(): dt(f"IRI") dd(a(term, href=term)) objects = sorted(g.objects(term, RDFS.isDefinedBy)) if objects: dt("Is defined by") with dd(): raw("<br/>".join([f'<a href="{o}">{o}</a>' for o in objects]) ) objects = sorted(g.objects(term, DCTERMS.isReferencedBy)) if objects: dt("Is referenced by") with dd(): raw("<br/>".join([f'<a href="{o}">{o}</a>' for o in objects]) ) dt("Download serialization") dd( span( a( img( src="https://img.shields.io/badge/Format-JSON--LD-blue.svg" ), href=f"{ desc.local_name }.jsonld", target="_blank", ) ), span( a( img(src="https://img.shields.io/badge/Format-N3-blue.svg"), href=f"{ desc.local_name }.n3", target="_blank", ) ), span( a( img( src="https://img.shields.io/badge/Format-N--Triples-blue.svg" ), href=f"{ desc.local_name }.nt", target="_blank", ) ), span( a( img(src="https://img.shields.io/badge/Format-RDF/XML-blue.svg"), href=f"{ desc.local_name }.rdf", target="_blank", ) ), span( a( img(src="https://img.shields.io/badge/Format-Turtle-blue.svg"), href=f"{ desc.local_name }.ttl", target="_blank", ) ), ) dt("License") dd( span( a( img( src="https://img.shields.io/badge/License-https://forge.etsi.org/etsi--software--license-blue.svg" ), href="https://forge.etsi.org/etsi-software-license", target="_blank", ) ) ) with div(_class="container") as contents: h2("Definition") with p(): with em(): text("Defined in ") a(ontology.declaration_iri, href=ontology.declaration_iri) text(" as:") q(strong(desc.comment)) for title, typ, describe_func in describe_funcs: if (term, RDF.type, typ) not in g: continue h2(title) describe_func(ontology, term) h2("Turtle") pre( g.serialize(), _class="codehilite", ) context = { "title": f"{desc.curie} — {desc.label}", "pathToStatic": "../../static", "isOntologyDocumentation": False, "project": self.project, "project_version": self.project_version, "version": self.version, "h1": doc_h1, "dls": dls, "content": contents, } return template.render(context), g def title(self, document: SAREFGraphDocument): g = self.dataset.graph(document.version_iri) return ( Loading Loading @@ -781,6 +936,28 @@ class WebsiteGenerator: for organization in g.objects(person, SCHEMA.affiliation): self.organization(document, organization) def describe( self, t: IdentifiedNode, source: Graph, target: Graph, visited: Set | None = set() ) -> Graph: visited = visited or set() if t in visited: return visited.add(t) for s, p in source.subject_predicates(t): target.add((s, p, t)) if isinstance(s, BNode): self.describe(s, source, target, visited) for p, o in source.predicate_objects(t): target.add((t, p, o)) if isinstance(o, BNode): self.describe(o, source, target, visited) for s, o in source.subject_objects(t): target.add((s, t, o)) if isinstance(s, BNode): self.describe(s, source, target, visited) if isinstance(o, BNode): self.describe(o, source, target, visited) def describe_entities( self, document: SAREFGraphDocument, Loading Loading @@ -1278,7 +1455,9 @@ class WebsiteGenerator: elif ( path := Path(self.project.directory, document.doc_folder, f"{file_name}.md") ).exists(): creators = markdown(path.read_text(), extensions=["extra", "admonition", "codehilite"]) creators = markdown( path.read_text(), extensions=["extra", "admonition", "codehilite"] ) if creators.strip(): dt(title) dd(raw(creators)) Loading Loading @@ -1620,6 +1799,7 @@ class WebsiteGenerator: else: logger.error(f"Field {field} not implemented") return "" return re.sub(r"\{\{(.*?)\}\}", replace_field, html) def replace_table_1(self, document: SAREFGraphDocument): Loading @@ -1632,4 +1812,4 @@ class WebsiteGenerator: td(prefix) td(ns) caption("Table 1: Namespace Declarations", id="Table_1") return el.render(indent=' ') return el.render(indent=" ") src/saref_pypeline/resources/docgen/html.j2 +1 −1 Original line number Diff line number Diff line Loading @@ -60,7 +60,7 @@ <div class="g-container"> <div class="g-block size-100"> <div class="g-content g-particle"> <h1><a href="{{ document.declaration_iri }}">{{ title }}</a></h1> {{ h1 }} {{ dls }} </div> </div> Loading Loading
src/saref_pypeline/docgen/site_manager.py +28 −6 Original line number Diff line number Diff line Loading @@ -166,19 +166,41 @@ class SiteManager: ) # variants graph = self.dataset.graph(doc.version_iri) # graph = self.dataset.graph(doc.version_iri) # for format, ext in [ # ("json-ld", ".jsonld"), # ("n3", ".n3"), # ("nt", ".nt"), # ("xml", ".rdf"), # ]: # graph.serialize( # os.path.join(self.site_dir, doc.site_path + ext), # format=format, # encoding="utf-8", # ) metadata = self.pipeline.dataset.graph(METADATA) pv_dir = Path(self.site_dir, project_version.project.path, str(project_version.version)) for term in metadata.subjects(RDFS.isDefinedBy, project_version.ontology.version_iri): html, graph = docgen.render_term_documentation(project_version, term) local_name = term[len(project_version.project.namespace):] Path(pv_dir, local_name + ".html").write_text(html) # variants for format, ext in [ ("json-ld", ".jsonld"), ("n3", ".n3"), ("nt", ".nt"), ("xml", ".rdf"), ("ttl", ".ttl"), # ("json-ld", ".jsonld"), # ("n3", ".n3"), # ("nt", ".nt"), # ("xml", ".rdf"), ]: graph.serialize( os.path.join(self.site_dir, doc.site_path + ext), os.path.join(pv_dir, local_name + ext), format=format, encoding="utf-8", ) def generate_ts(self, project_version: SAREFProjectVersion): project = project_version.project version = project_version.version Loading
src/saref_pypeline/docgen/website_generator.py +186 −6 Original line number Diff line number Diff line Loading @@ -3,6 +3,7 @@ import os from pathlib import Path import logging from re import Match import dominate from dominate.tags import * from dominate.util import text, raw from markdown import markdown Loading @@ -25,7 +26,7 @@ from saref_pypeline.etsi import WK_FIELD from saref_pypeline.utils import filter_document from saref_pypeline.vocabs import SCHEMA from saref_pypeline.docgen.utils import * from typing import List, TypeVar, Callable, TYPE_CHECKING, Dict, TypeVar from typing import List, Set, TypeVar, Callable, TYPE_CHECKING, Dict, TypeVar T = TypeVar("T") Loading @@ -45,6 +46,10 @@ class HTMLEntityDescription(EntityDescription): def __init__(self, uri: URIRef, graph: Graph, namespace_manager=None): super().__init__(uri, graph, namespace_manager) @cached_property def local_name(self): return re.search(r"([^/#]+)$", self.uri).group(1) def sup(self): if OWL.Class in self.types: return sup("c", title="class", _class="type-c") Loading Loading @@ -83,6 +88,7 @@ class WebsiteGenerator: self.project = project_version.project self.version = project_version.version self.context = site_manager.context(project_version) self.metadata = self.dataset.graph(METADATA) self.nm = NamespaceManager(Graph(), bind_namespaces="none") for prefix, ns in self.project_version.ontology.namespaces: Loading Loading @@ -152,6 +158,9 @@ class WebsiteGenerator: template = jinja2_env.get_template("html.j2") g = self.dataset.graph(document.version_iri) with h1() as doc_h1: a(self.title(document), href=f"{document.declaration_iri}") with dl() as dls: dt("Latest version") Loading Loading @@ -312,7 +321,7 @@ class WebsiteGenerator: "ETSI IPR Policy", href="https://www.etsi.org/intellectual-property-rights", ), class_= "alert-warning" class_="alert-warning", ) toc = ol() Loading Loading @@ -470,6 +479,7 @@ class WebsiteGenerator: "project_version": self.project_version, "version": self.version, "document": document, "h1": doc_h1, "dls": dls, "content": contents, "my_tab_content": my_tab_content, Loading @@ -489,6 +499,9 @@ class WebsiteGenerator: template = jinja2_env.get_template("html.j2") g = self.dataset.graph(document.version_iri) with h1() as doc_h1: a(self.title(document), href=f"{document.declaration_iri}") with dl() as dls: dt(f"IRI") Loading Loading @@ -607,11 +620,153 @@ class WebsiteGenerator: "version": self.version, "document": document, "dls": dls, "h1": doc_h1, "content": contents, } return template.render(context) def render_term_documentation( self, project_version: SAREFProjectVersion, term: URIRef ): logger.debug(f"Printing html documentation for {term.n3(self.dataset.namespace_manager)}") ontology = project_version.ontology g = Graph(bind_namespaces="none") for prefix, namespace in ontology.namespaces: g.bind(prefix, namespace) onto_graph = self.dataset.graph(ontology.version_iri_with_imports) self.describe(term, onto_graph, g) self.describe(term, self.metadata, g) describe_funcs = [ ("Description as a Class", OWL.Class, self.describe_class), ("Description as an Object Property", OWL.ObjectProperty, self.describe_object_property), ("Definition as a Datatype Property", OWL.DatatypeProperty, self.describe_data_property), ("Definition as an Annotation Property", OWL.AnnotationProperty, self.describe_annotation_property), ("Definition as a Named Individual", OWL.Thing, self.describe_named_individual), ] template = jinja2_env.get_template("html.j2") desc = self.description(term, onto_graph) with h1() as doc_h1: a(desc.curie, href=term) text(f" — {desc.label}") with div() as dls: with dl(): dt(f"IRI") dd(a(term, href=term)) objects = sorted(g.objects(term, RDFS.isDefinedBy)) if objects: dt("Is defined by") with dd(): raw("<br/>".join([f'<a href="{o}">{o}</a>' for o in objects]) ) objects = sorted(g.objects(term, DCTERMS.isReferencedBy)) if objects: dt("Is referenced by") with dd(): raw("<br/>".join([f'<a href="{o}">{o}</a>' for o in objects]) ) dt("Download serialization") dd( span( a( img( src="https://img.shields.io/badge/Format-JSON--LD-blue.svg" ), href=f"{ desc.local_name }.jsonld", target="_blank", ) ), span( a( img(src="https://img.shields.io/badge/Format-N3-blue.svg"), href=f"{ desc.local_name }.n3", target="_blank", ) ), span( a( img( src="https://img.shields.io/badge/Format-N--Triples-blue.svg" ), href=f"{ desc.local_name }.nt", target="_blank", ) ), span( a( img(src="https://img.shields.io/badge/Format-RDF/XML-blue.svg"), href=f"{ desc.local_name }.rdf", target="_blank", ) ), span( a( img(src="https://img.shields.io/badge/Format-Turtle-blue.svg"), href=f"{ desc.local_name }.ttl", target="_blank", ) ), ) dt("License") dd( span( a( img( src="https://img.shields.io/badge/License-https://forge.etsi.org/etsi--software--license-blue.svg" ), href="https://forge.etsi.org/etsi-software-license", target="_blank", ) ) ) with div(_class="container") as contents: h2("Definition") with p(): with em(): text("Defined in ") a(ontology.declaration_iri, href=ontology.declaration_iri) text(" as:") q(strong(desc.comment)) for title, typ, describe_func in describe_funcs: if (term, RDF.type, typ) not in g: continue h2(title) describe_func(ontology, term) h2("Turtle") pre( g.serialize(), _class="codehilite", ) context = { "title": f"{desc.curie} — {desc.label}", "pathToStatic": "../../static", "isOntologyDocumentation": False, "project": self.project, "project_version": self.project_version, "version": self.version, "h1": doc_h1, "dls": dls, "content": contents, } return template.render(context), g def title(self, document: SAREFGraphDocument): g = self.dataset.graph(document.version_iri) return ( Loading Loading @@ -781,6 +936,28 @@ class WebsiteGenerator: for organization in g.objects(person, SCHEMA.affiliation): self.organization(document, organization) def describe( self, t: IdentifiedNode, source: Graph, target: Graph, visited: Set | None = set() ) -> Graph: visited = visited or set() if t in visited: return visited.add(t) for s, p in source.subject_predicates(t): target.add((s, p, t)) if isinstance(s, BNode): self.describe(s, source, target, visited) for p, o in source.predicate_objects(t): target.add((t, p, o)) if isinstance(o, BNode): self.describe(o, source, target, visited) for s, o in source.subject_objects(t): target.add((s, t, o)) if isinstance(s, BNode): self.describe(s, source, target, visited) if isinstance(o, BNode): self.describe(o, source, target, visited) def describe_entities( self, document: SAREFGraphDocument, Loading Loading @@ -1278,7 +1455,9 @@ class WebsiteGenerator: elif ( path := Path(self.project.directory, document.doc_folder, f"{file_name}.md") ).exists(): creators = markdown(path.read_text(), extensions=["extra", "admonition", "codehilite"]) creators = markdown( path.read_text(), extensions=["extra", "admonition", "codehilite"] ) if creators.strip(): dt(title) dd(raw(creators)) Loading Loading @@ -1620,6 +1799,7 @@ class WebsiteGenerator: else: logger.error(f"Field {field} not implemented") return "" return re.sub(r"\{\{(.*?)\}\}", replace_field, html) def replace_table_1(self, document: SAREFGraphDocument): Loading @@ -1632,4 +1812,4 @@ class WebsiteGenerator: td(prefix) td(ns) caption("Table 1: Namespace Declarations", id="Table_1") return el.render(indent=' ') return el.render(indent=" ")
src/saref_pypeline/resources/docgen/html.j2 +1 −1 Original line number Diff line number Diff line Loading @@ -60,7 +60,7 @@ <div class="g-container"> <div class="g-block size-100"> <div class="g-content g-particle"> <h1><a href="{{ document.declaration_iri }}">{{ title }}</a></h1> {{ h1 }} {{ dls }} </div> </div> Loading