From e5c723a47f36a613d2765af673465bade514413b Mon Sep 17 00:00:00 2001 From: Sujay Patil Date: Tue, 14 Nov 2023 01:55:41 -0800 Subject: [PATCH] Add ability to customize documentation pages using jinja templates (#333) Resolves #332 This PR added in the relevant/necessary base jinja templates (as is) from the linkml library: https://github.com/linkml/linkml/tree/main/linkml/generators/docgen in a folder called `src/doc-templates`. Now the jinja templates can be edited here, in order to customize the class/slot/index, etc. documentation pages to look like how we want them to look. The downstream makefile target which uses linkml `gen-doc` is `make gendoc` and that has been updated too. - [ ] `docs/` have been added/updated if necessary - [x] `make test` has been run locally - [ ] tests have been added/updated (if applicable) - [ ] [CHANGELOG.md](https://github.com/mapping-commons/sssom/blob/master/CHANGELOG.md) has been updated. --- Makefile | 3 +- src/doc-templates/class.md.jinja2 | 123 ++++++++++++++++++++ src/doc-templates/class_diagram.md.jinja2 | 59 ++++++++++ src/doc-templates/common_metadata.md.jinja2 | 78 +++++++++++++ src/doc-templates/index.md.jinja2 | 62 ++++++++++ src/doc-templates/slot.md.jinja2 | 96 +++++++++++++++ 6 files changed, 420 insertions(+), 1 deletion(-) create mode 100644 src/doc-templates/class.md.jinja2 create mode 100644 src/doc-templates/class_diagram.md.jinja2 create mode 100644 src/doc-templates/common_metadata.md.jinja2 create mode 100644 src/doc-templates/index.md.jinja2 create mode 100644 src/doc-templates/slot.md.jinja2 diff --git a/Makefile b/Makefile index 23f0d235..621e77e5 100644 --- a/Makefile +++ b/Makefile @@ -14,6 +14,7 @@ SRC = src DEST = project PYMODEL = $(SRC)/$(SCHEMA_NAME)/datamodel DOCDIR = docs +TEMPLATE_DIR = $(SRC)/doc-templates # basename of a YAML file in model/ .PHONY: all clean @@ -84,7 +85,7 @@ $(DOCDIR): gendoc: $(DOCDIR) cp $(SRC)/docs/*md $(DOCDIR) ; \ - $(RUN) gen-doc -d $(DOCDIR) $(SOURCE_SCHEMA_PATH) + $(RUN) gen-doc -d $(DOCDIR) $(SOURCE_SCHEMA_PATH) --template-directory $(TEMPLATE_DIR) testdoc: gendoc serve diff --git a/src/doc-templates/class.md.jinja2 b/src/doc-templates/class.md.jinja2 new file mode 100644 index 00000000..390f5923 --- /dev/null +++ b/src/doc-templates/class.md.jinja2 @@ -0,0 +1,123 @@ +# Class: {{ gen.name(element) }} + +{%- if header -%} +{{header}} +{%- endif -%} + + +{% if element.description %} +{% set element_description_lines = element.description.split('\n') %} +{% for element_description_line in element_description_lines %} +_{{ element_description_line }}_ +{% endfor %} +{% endif %} + +{% if element.abstract %} +* __NOTE__: this is an abstract class and should not be instantiated directly +{% endif %} + +URI: {{ gen.uri_link(element) }} + + +{% if diagram_type == "er_diagram" %} +```{{ gen.mermaid_directive() }} +{{ gen.mermaid_diagram([element.name]) }} +``` +{% else %} +{% include "class_diagram.md.jinja2" %} +{% endif %} + +{% if schemaview.class_parents(element.name) or schemaview.class_children(element.name, mixins=False) %} + +## Inheritance +{{ gen.inheritance_tree(element, mixins=True) }} +{% else %} + +{% endif %} + +## Slots + +| Name | Cardinality and Range | Description | Inheritance | +| --- | --- | --- | --- | +{% if gen.get_direct_slots(element)|length > 0 %} +{%- for slot in gen.get_direct_slots(element) -%} +| {{ gen.link(slot) }} | {{ gen.cardinality(slot) }}
{{ gen.link(slot.range) }} | {{ slot.description|enshorten }} | direct | +{% endfor -%} +{% endif -%} +{% if gen.get_indirect_slots(element)|length > 0 %} +{%- for slot in gen.get_indirect_slots(element) -%} +| {{ gen.link(slot) }} | {{ gen.cardinality(slot) }}
{{ gen.link(slot.range) }} | {{ slot.description|enshorten }} | {{ gen.links(gen.get_slot_inherited_from(element.name, slot.name))|join(', ') }} | +{% endfor -%} +{% endif %} + +{% if schemaview.is_mixin(element.name) %} +## Mixin Usage + +| mixed into | description | +| --- | --- | +{% for c in schemaview.class_children(element.name, is_a=False) -%} +| {{ gen.link(c) }} | {{ schemaview.get_class(c).description|enshorten }} | +{% endfor %} +{% endif %} + +{% if schemaview.usage_index().get(element.name) %} +## Usages + +| used by | used in | type | used | +| --- | --- | --- | --- | +{% for usage in schemaview.usage_index().get(element.name) -%} +| {{gen.link(usage.used_by)}} | {{gen.link(usage.slot)}} | {{usage.metaslot}} | {{ gen.link(usage.used) }} | +{% endfor %} +{% endif %} + +{% include "common_metadata.md.jinja2" %} + + +{% if schemaview.get_mappings(element.name).items() -%} +## Mappings + +| Mapping Type | Mapped Value | +| --- | --- | +{% for m, mt in schemaview.get_mappings(element.name).items() -%} +{% if mt|length > 0 -%} +| {{ m }} | {{ mt|join(', ') }} | +{% endif -%} +{% endfor %} + +{% endif -%} + +{% if gen.example_object_blobs(element.name) -%} +## Examples +{% for name, blob in gen.example_object_blobs(element.name) -%} +### Example: {{name}} + +```yaml +{{ blob }} +``` +{% endfor %} +{% endif %} + + +## LinkML Source + + + +### Direct + +
+```yaml +{{gen.yaml(element)}} +``` +
+ +### Induced + +
+```yaml +{{gen.yaml(element, inferred=True)}} +``` +
+ +{%- if footer -%} +{{footer}} +{%- endif -%} \ No newline at end of file diff --git a/src/doc-templates/class_diagram.md.jinja2 b/src/doc-templates/class_diagram.md.jinja2 new file mode 100644 index 00000000..a636caa3 --- /dev/null +++ b/src/doc-templates/class_diagram.md.jinja2 @@ -0,0 +1,59 @@ +{% if schemaview.class_parents(element.name) and schemaview.class_children(element.name) %} +```{{ gen.mermaid_directive() }} + classDiagram + class {{ gen.name(element) }} + {% for s in schemaview.class_parents(element.name)|sort(attribute='name') -%} + {{ gen.name(schemaview.get_element(s)) }} <|-- {{ gen.name(element) }} + {% endfor %} + + {% for s in schemaview.class_children(element.name)|sort(attribute='name') -%} + {{ gen.name(element) }} <|-- {{ gen.name(schemaview.get_element(s)) }} + {% endfor %} + + {% for s in schemaview.class_induced_slots(element.name)|sort(attribute='name') -%} + {{ gen.name(element) }} : {{gen.name(s)}} + {% if s.range not in gen.all_type_object_names() %} + {{ gen.name(element) }} --|> {{ s.range }} : {{ gen.name(s) }} + {% endif %} + {% endfor %} +``` +{% elif schemaview.class_parents(element.name) %} +```{{ gen.mermaid_directive() }} + classDiagram + class {{ gen.name(element) }} + {% for s in schemaview.class_parents(element.name)|sort(attribute='name') -%} + {{ gen.name(schemaview.get_element(s)) }} <|-- {{ gen.name(element) }} + {% endfor %} + {% for s in schemaview.class_induced_slots(element.name)|sort(attribute='name') -%} + {{ gen.name(element) }} : {{gen.name(s)}} + {% if s.range not in gen.all_type_object_names() %} + {{ gen.name(element) }} --|> {{ s.range }} : {{ gen.name(s) }} + {% endif %} + {% endfor %} +``` +{% elif schemaview.class_children(element.name) %} +```{{ gen.mermaid_directive() }} + classDiagram + class {{ gen.name(element) }} + {% for s in schemaview.class_children(element.name)|sort(attribute='name') -%} + {{ gen.name(element) }} <|-- {{ gen.name(schemaview.get_element(s)) }} + {% endfor %} + {% for s in schemaview.class_induced_slots(element.name)|sort(attribute='name') -%} + {{ gen.name(element) }} : {{gen.name(s)}} + {% if s.range not in gen.all_type_object_names() %} + {{ gen.name(element) }} --|> {{ s.range }} : {{ gen.name(s) }} + {% endif %} + {% endfor %} +``` +{% else %} +```{{ gen.mermaid_directive() }} + classDiagram + class {{ gen.name(element) }} + {% for s in schemaview.class_induced_slots(element.name)|sort(attribute='name') -%} + {{ gen.name(element) }} : {{gen.name(s)}} + {% if s.range not in gen.all_type_object_names() %} + {{ gen.name(element) }} --|> {{ s.range }} : {{ gen.name(s) }} + {% endif %} + {% endfor %} +``` +{% endif %} \ No newline at end of file diff --git a/src/doc-templates/common_metadata.md.jinja2 b/src/doc-templates/common_metadata.md.jinja2 new file mode 100644 index 00000000..de147a50 --- /dev/null +++ b/src/doc-templates/common_metadata.md.jinja2 @@ -0,0 +1,78 @@ +{% if element.aliases %} +## Aliases + +{% for alias in element.aliases %} +* {{ alias }} +{%- endfor %} +{% endif %} + + +{% if element.examples %} +## Examples + +| Value | +| --- | +{% for x in element.examples -%} +| {{ x.value }} | +{% endfor %} +{% endif -%} + +{% if element.comments -%} +## Comments + +{% for x in element.comments -%} +* {{x}} +{% endfor %} +{% endif -%} + +{% if element.todos -%} +## TODOs + +{% for x in element.todos -%} +* {{x}} +{% endfor %} +{% endif -%} + +{% if element.see_also -%} +## See Also + +{% for x in element.see_also -%} +* {{ gen.uri_link(x) }} +{% endfor %} +{% endif -%} + +## Identifier and Mapping Information + +{% if element.id_prefixes %} +### Valid ID Prefixes + +Instances of this class *should* have identifiers with one of the following prefixes: +{% for p in element.id_prefixes %} +* {{p}} +{% endfor %} + +{% endif %} + + +{% if element.annotations %} +### Annotations + +| property | value | +| --- | --- | +{% for a in element.annotations -%} +{%- if a|string|first != '_' -%} +| {{ a }} | {{ element.annotations[a].value }} | +{%- endif -%} +{% endfor %} +{% endif %} + +{% if element.from_schema or element.imported_from %} +### Schema Source + +{% if element.from_schema %} +* from schema: {{ element.from_schema }} +{% endif %} +{% if element.imported_from %} +* imported from: {{ element.imported_from }} +{% endif %} +{% endif %} \ No newline at end of file diff --git a/src/doc-templates/index.md.jinja2 b/src/doc-templates/index.md.jinja2 new file mode 100644 index 00000000..2a78e4c4 --- /dev/null +++ b/src/doc-templates/index.md.jinja2 @@ -0,0 +1,62 @@ +# {% if schema.title %}{{ schema.title }}{% else %}{{ schema.name }}{% endif %} + +{% if schema.description %}{{ schema.description }}{% endif %} + +URI: {{ schema.id }} + +Name: {{ schema.name }} + +{% if include_top_level_diagram %} + +## Schema Diagram + +```{{ gen.mermaid_directive() }} +{{ gen.mermaid_diagram() }} +``` +{% endif %} + +## Classes + +| Class | Description | +| --- | --- | +{% if gen.hierarchical_class_view -%} +{% for u, v in gen.class_hierarchy_as_tuples() -%} +| {{ " "|safe*u*8 }}{{ gen.link(schemaview.get_class(v)) }} | {{ schemaview.get_class(v).description }} | +{% endfor %} +{% else -%} +{% for c in gen.all_class_objects()|sort(attribute=sort_by) -%} +| {{gen.link(c)}} | {{c.description|enshorten}} | +{% endfor %} +{% endif %} + +## Slots + +| Slot | Description | +| --- | --- | +{% for s in gen.all_slot_objects()|sort(attribute=sort_by) -%} +| {{gen.link(s)}} | {{s.description|enshorten}} | +{% endfor %} + +## Enumerations + +| Enumeration | Description | +| --- | --- | +{% for e in gen.all_enum_objects()|sort(attribute=sort_by) -%} +| {{gen.link(e)}} | {{e.description|enshorten}} | +{% endfor %} + +## Types + +| Type | Description | +| --- | --- | +{% for t in gen.all_type_objects()|sort(attribute=sort_by) -%} +| {{gen.link(t)}} | {{t.description|enshorten}} | +{% endfor %} + +## Subsets + +| Subset | Description | +| --- | --- | +{% for ss in schemaview.all_subsets().values()|sort(attribute='name') -%} +| {{gen.link(ss)}} | {{ss.description|enshorten}} | +{% endfor %} diff --git a/src/doc-templates/slot.md.jinja2 b/src/doc-templates/slot.md.jinja2 new file mode 100644 index 00000000..8bd9890d --- /dev/null +++ b/src/doc-templates/slot.md.jinja2 @@ -0,0 +1,96 @@ +# Slot: {{ gen.name(element) }} + +{%- if header -%} +{{header}} +{%- endif -%} + +{% if element.description %} +{% set element_description_lines = element.description.split('\n') %} +{% for element_description_line in element_description_lines %} +_{{ element_description_line }}_ +{% endfor %} +{% endif %} + +URI: {{ gen.uri_link(element) }} + + +{% if schemaview.slot_parents(element.name) or schemaview.slot_children(element.name, mixins=False) %} + +## Inheritance + +{{ gen.inheritance_tree(element, mixins=True) }} +{% else %} + +{% endif %} + +{% if schemaview.get_classes_by_slot(element, include_induced=True) %} + +## Applicable Classes + +| Name | Description | Modifies Slot | +| --- | --- | --- | +{% for c in schemaview.get_classes_by_slot(element, include_induced=True) -%} +{{ gen.link(c) }} | {{ schemaview.get_class(c).description|enshorten }} | {% if c in schemaview.get_classes_modifying_slot(element) %} yes {% else %} no {% endif %} | +{% endfor %} + +{% endif %} + + +{% if schemaview.is_mixin(element.name) %} +## Mixin Usage + +| mixed into | description | range | domain | +| --- | --- | --- | --- | +{% for s in schemaview.slot_children(element.name, is_a=False) -%} +| {{ gen.link(s) }} | {{ schemaview.get_slot(s).description|enshorten }} | {{ schemaview.get_slot(s).range }} | {{ schemaview.get_classes_by_slot(schemaview.get_slot(s))|join(', ') }} | +{% endfor %} +{% endif %} + +## Properties + +* Range: {{gen.link(element.range)}} +{% if element.multivalued %} +* Multivalued: {{ element.multivalued }} +{% endif -%} +{% if element.required %} +* Required: {{ element.required }} +{% elif element.recommended %} +* Recommended: {{ element.recommended }} +{% endif -%} +{% if element.minimum_value is not none %} +* Minimum Value: {{ element.minimum_value|int }} +{% endif -%} +{% if element.maximum_value is not none %} +* Maximum Value: {{ element.maximum_value|int }} +{% endif -%} +{% if element.pattern %} +* Regex pattern: {{ '`' }}{{ element.pattern }}{{ '`' }} +{% endif -%} +{% if schemaview.is_mixin(element.name) %} +* Mixin: {{ element.mixin }} +{% endif -%} + + +{% if schemaview.usage_index().get(element.name) %} +## Usages + +| used by | used in | type | used | +| --- | --- | --- | --- | +{% for usage in schemaview.usage_index().get(element.name) -%} +| {{gen.link(usage.used_by)}} | {{gen.link(usage.slot)}} | {{usage.metaslot}} | {{ gen.link(usage.used) }} | +{% endfor %} +{% endif %} + +{% include "common_metadata.md.jinja2" %} + +## LinkML Source + +
+```yaml +{{ gen.yaml(element) }} +``` +
+ +{%- if footer -%} +{{footer}} +{%- endif -%} \ No newline at end of file