Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use Jinja2 to generate Markdown #67

Merged
merged 1 commit into from
Sep 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/python
{
"name": "Python 3",
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
"image": "mcr.microsoft.com/devcontainers/python:1-3.11-bullseye",

// Features to add to the dev container. More info: https://containers.dev/features.
// "features": {},

// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],

// Use 'postCreateCommand' to run commands after the container is created.
"postCreateCommand": "pip3 install --user -r requirements.txt",

// Configure tool-specific properties.
// "customizations": {},

// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
// "remoteUser": "root"
}
14 changes: 4 additions & 10 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
#
# These requirements were autogenerated by pipenv
# To regenerate from the project's Pipfile, run:
#
# pipenv lock --requirements
#

-i https://pypi.org/simple
isodate==0.6.0
pyparsing==2.4.7; python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'
Jinja2==3.1.2
MarkupSafe==2.1.3
pyparsing==2.4.7
rdflib==6.0.0
six==1.16.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
six==1.16.0
sly==0.4
48 changes: 48 additions & 0 deletions spec_parser/templates/default/class.md.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<!-- Auto generated markdown by Spec-parser {{__version__}} -->

<!-- SPDX-License-Identifier: {{license_name}} -->

# {{name}}

## Summary
{{summary}}

## Description
{{description}}

## Metadata
{% for name, vals in metadata | dictsort %}
- {{name}}: {{vals | list | join(" ")}}
{% endfor %}

## Properties
{% if args.get("use_table", False) %}
header_list = ["type", "minCount", "maxCount"]

# print the header
f.write("|" + "|".join(["property"] + header_list) + "|\n")
f.write("|" + "---|" * (len(header_list) + 1) + "\n")

{% for name, subprops in properties | dictsort %}
|{{name}}
{% for subprop in header_list %}
|{{subprops.get(subprop, ["NA"]) | join(" ")}}
{% endfor %}
|
{% endfor %}
{% else %}
{% for name, subprops in properties | dictsort %}
- {{name}}
{% for _key, subprop in subprops | dictsort %}
- {{_key}}: {{subprop | list | join(" ")}}
{% endfor %}
{% endfor %}
{% endif %}

{% if format_pattern %}
## Format

{% for name, vals in format_pattern | dictsort %}
- {{name}}: {{vals | list | join(" ")}}
{% endfor %}
{% endif %}
23 changes: 23 additions & 0 deletions spec_parser/templates/default/property.md.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<!-- Auto generated markdown by Spec-parser v{{__version__}} -->

<!-- SPDX-License-Identifier: {{license_name}} -->

# {{name}}

## Summary
{{summary}}

## Description
{{description}}

## Metadata
{% for name, vals in metadata | dictsort %}
- {{name}}: {{vals | list | join(" ")}}
{% endfor %}

{% if args.get("gen_refs", False) %}
## References
{% for name in spec.dataprop_refs.get(self.name, []) | dictsort %}
- {{name}}
{% endfor %}
{% endif %}
21 changes: 21 additions & 0 deletions spec_parser/templates/default/vocab.md.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!-- Auto generated markdown by Spec-parser v{{__version__}} -->

<!-- SPDX-License-Identifier: {{license_name}} -->

# {{name}}

## Summary
{{summary}}

## Description
{{description}}

## Metadata
{% for name, vals in metadata | dictsort %}
- {{name}}: {{vals | list | join(" ")}}
{% endfor %}

## Entries
{% for name, val in entries | dictsort %}
- {{name}}: {{val}}
{% endfor %}
172 changes: 25 additions & 147 deletions spec_parser/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
from os import path
from typing import List

from jinja2 import Environment, PackageLoader, select_autoescape

import rdflib
from rdflib import URIRef, Literal, BNode, SH
from rdflib.extras.infixowl import EnumeratedClass
Expand Down Expand Up @@ -115,20 +117,25 @@ def gen_md(self) -> None:
if path.isdir(self.args["out_dir"]):
self.logger.warning(f"Overwriting out_dir `{self.args['out_dir']}`")

env = Environment(
loader=PackageLoader("spec_parser", package_path="templates/default"),
autoescape=False
)

for namespace in self.namespaces.values():

classes = namespace["classes"]
properties = namespace["properties"]
vocabs = namespace["vocabs"]

for class_obj in classes.values():
class_obj._gen_md(self.args)
class_obj._gen_md(env, self.args)

for prop_obj in properties.values():
prop_obj._gen_md(self.args)
prop_obj._gen_md(env, self.args)

for vocab_obj in vocabs.values():
vocab_obj._gen_md(self.args)
vocab_obj._gen_md(env, self.args)

def _get_defined_class_types(self) -> List[URIRef]:
class_types = []
Expand Down Expand Up @@ -386,78 +393,17 @@ def _extract_format(self, format_list):

self.format_pattern[_key] = _values

def _gen_md(self, args: dict) -> None:
def _gen_md(self, env, args: dict) -> None:

fname = path.join(
args["out_dir"], self.namespace_name, "Classes", f"{self.name}.md"
)

with safe_open(fname, "w") as f:

# write the header
f.write(
f"<!-- Auto generated markdown by Spec-parser v{__version__} -->\n\n"
)

# write the license name
f.write(
f"<!-- SPDX-License-Identifier: {self.license_name} -->\n\n"
)
template = env.get_template("class.md.j2")
result = template.render({'__version__': __version__, 'args': args} | vars(self))

# write the topheadline
f.write(f"# {self.name}\n\n")

# write the summary
f.write("## Summary\n\n")
f.write(f"{self.summary}\n")
f.write("\n")

# write the description
f.write("## Description\n\n")
f.write(f"{self.description}\n")
f.write("\n")

# write the metadata
f.write("## Metadata\n\n")
for name, vals in sorted(self.metadata.items()):
if isinstance(vals, list):
f.write(f'- {name}: {" ".join(vals)}\n')
else:
f.write(f'- {name}: {vals}\n')
f.write("\n")

# write the data_props
f.write("## Properties\n\n")
if args.get("use_table", False):
# generate markdown-table from properties
header_list = ["type", "minCount", "maxCount"]

# print the header
f.write("|" + "|".join(["property"] + header_list) + "|\n")
f.write("|" + "---|" * (len(header_list) + 1) + "\n")

for name, subprops in sorted(self.properties.items()):
f.write(f"|{name}")
for subprop in header_list:
f.write(f'|{" ".join(subprops.get(subprop, ["NA"]))}')
f.write("|\n")
else:
# generate markdown-list from properties
for name, subprops in sorted(self.properties.items()):
f.write(f"- {name}\n")
for _key, subprop in sorted(subprops.items()):
if isinstance(subprop, list):
f.write(f' - {_key}: {" ".join(subprop)}\n')
else:
f.write(f' - {_key}: {subprop}\n')
f.write("\n")
if self.format_pattern:
f.write("## Format\n\n")
for name, vals in sorted(self.format_pattern.items()):
if isinstance(vals, list):
f.write(f'- {name}: {" ".join(vals)}\n')
else:
f.write(f'- {name}: {vals}\n')
with safe_open(fname, "w") as f:
f.write(result)


def _gen_rdf(self, g: rdflib.Graph, class_types: List[URIRef]) -> None:
Expand Down Expand Up @@ -540,51 +486,17 @@ def __init__(
self.logger = logging.getLogger(self.__class__.__name__)
self._extract_metadata(metadata)

def _gen_md(self, args: dict) -> None:
def _gen_md(self, env, args: dict) -> None:

fname = path.join(
args["out_dir"], self.namespace_name, "Properties", f"{self.name}.md"
)

with safe_open(fname, "w") as f:
template = env.get_template("property.md.j2")
result = template.render({'__version__': __version__, 'args': args} | vars(self))

# write the header
f.write(
f"<!-- Auto generated markdown by Spec-parser v{__version__} -->\n\n"
)

# write the license name
f.write(
f"<!-- SPDX-License-Identifier: {self.license_name} -->\n\n"
)

# write the topheadline
f.write(f"# {self.name}\n\n")

# write the summary
f.write("## Summary\n\n")
f.write(f"{self.summary}\n")
f.write("\n")

# write the description
f.write("## Description\n\n")
f.write(f"{self.description}\n")
f.write("\n")

# write the metadata
f.write("## Metadata\n\n")
for name, vals in sorted(self.metadata.items()):
if isinstance(vals, list):
f.write(f'- {name}: {" ".join(vals)}\n')
else:
f.write(f'- {name}: {vals}\n')
f.write("\n")

if args.get("gen_refs", False):
# Class references
f.write("## References\n\n")
for name in sorted(self.spec.dataprop_refs.get(self.name, [])):
f.write(f"- {name}\n")
with safe_open(fname, "w") as f:
f.write(result)


def _gen_rdf(self, g: rdflib.Graph) -> None:
Expand Down Expand Up @@ -662,51 +574,17 @@ def __init__(
self._extract_metadata(metadata)
self._extract_entries(entries)

def _gen_md(self, args: dict) -> None:
def _gen_md(self, env, args: dict) -> None:

fname = path.join(
args["out_dir"], self.namespace_name, "Vocabularies", f"{self.name}.md"
)

with safe_open(fname, "w") as f:

# write the header
f.write(
f"<!-- Auto generated markdown by Spec-parser v{__version__} -->\n\n"
)

# write the license name
f.write(
f"<!-- SPDX-License-Identifier: {self.license_name} -->\n\n"
)

# write the topheadline
f.write(f"# {self.name}\n\n")

# write the summary
f.write("## Summary\n\n")
f.write(f"{self.summary}\n")
f.write("\n")

# write the description
f.write("## Description\n\n")
f.write(f"{self.description}\n")
f.write("\n")

# write the metadata
f.write("## Metadata\n\n")
for name, vals in sorted(self.metadata.items()):
if isinstance(vals, list):
f.write(f'- {name}: {" ".join(vals)}\n')
else:
f.write(f'- {name}: {vals}\n')
f.write("\n")

# write the entries
f.write("## Entries\n\n")
for name, val in sorted(self.entries.items()):
f.write(f"- {name}: {val}\n")
template = env.get_template("vocab.md.j2")
result = template.render({'__version__': __version__, 'args': args} | vars(self))

with safe_open(fname, "w") as f:
f.write(result)

def _gen_rdf(self, g: rdflib.Graph):

Expand Down