Skip to content

Commit 0db77f5

Browse files
committed
feat: support for AsciiDoc template generation
The AsciiDoc template generation system mirrors the HTML system. We reuse existing infrastructure, using the same data source builder and validation as the HTML templates, ensuring consistency. - Template environment setup in `dqgen/services/__init__.py` - New AsciiDoc generator service `asciidoc_generator.py` - New AsciiDoc template generator service `asciidoc_templates_generator.py` - New CLI entrypoint for the new service - New Make target The generator reuses `template_builder.build_html_template()` (same parameters, different template content). Example run, for the OWL AP: ```sh make generate_asciidoc_templates ap=dqgen/resources/aps/owl-core.csv output=./output ``` Thanks to CursorAI.
1 parent be97e6f commit 0db77f5

File tree

5 files changed

+206
-0
lines changed

5 files changed

+206
-0
lines changed

Makefile

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,10 @@ generate_queries:
3232
# example: make generate_html_templates ap=dqgen/resources/aps/owl-core.csv output=./output
3333
generate_html_templates:
3434
@ python -m dqgen.entrypoints.cli.generate_html_template $(ap) $(output)
35+
36+
#-----------------------------------------------------------------------------
37+
# Generator commands
38+
#-----------------------------------------------------------------------------
39+
# example: make generate_asciidoc_templates ap=dqgen/resources/aps/owl-core.csv output=./output
40+
generate_asciidoc_templates:
41+
@ python -m dqgen.entrypoints.cli.generate_asciidoc_template $(ap) $(output)
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#!/usr/bin/python3
2+
3+
# Date: 2024
4+
# Author: Generated for AsciiDoc template support
5+
import pathlib
6+
7+
import click
8+
9+
from dqgen.services.asciidoc_templates_generator import generate_asciidoc_templates_from_csv
10+
11+
12+
@click.command()
13+
@click.argument("file_path", type=click.Path(exists=True, dir_okay=False))
14+
@click.argument("output_folder", type=click.Path(dir_okay=True, file_okay=False))
15+
def generate_asciidoc_templates(file_path, output_folder):
16+
generate_asciidoc_templates_from_csv(pathlib.Path(file_path), pathlib.Path(output_folder))
17+
18+
19+
if __name__ == '__main__':
20+
generate_asciidoc_templates()
21+

dqgen/services/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@
1313
from jinja2 import Environment, PackageLoader
1414

1515
HTML_TEMPLATES = Environment(loader=PackageLoader("dqgen.resources", "html_templates"))
16+
ASCII_DOC_TEMPLATES = Environment(loader=PackageLoader("dqgen.resources", "asciidoc_templates"))
1617
QUERIES_TEMPLATES = Environment(loader=PackageLoader("dqgen.resources", "query_templates"))
1718
PATH_TO_STATIC_FOLDER = pathlib.Path(__file__).parent.parent / "resources" / "html_templates" / "static"
19+
PATH_TO_ASCIIDOC_STATIC_FOLDER = pathlib.Path(__file__).parent.parent / "resources" / "asciidoc_templates" / "static"
1820

1921
CLASSES_OPERATION_TEMPLATE_MAPPING = {
2022
"added_instance": QUERIES_TEMPLATES.get_template("instance_additions.rq"),
@@ -82,6 +84,9 @@
8284
TEMPLATE_AND_HTML_FILE_NAME_MAPPING = {"main.html": HTML_TEMPLATES.get_template("main.jinja2"),
8385
"statistics.html": HTML_TEMPLATES.get_template("statistics.jinja2")}
8486

87+
TEMPLATE_AND_ASCIIDOC_FILE_NAME_MAPPING = {"main.adoc": ASCII_DOC_TEMPLATES.get_template("main.jinja2"),
88+
"statistics.adoc": ASCII_DOC_TEMPLATES.get_template("statistics.jinja2")}
89+
8590
MULTI_LANGUAGES = ["en", "fr", "de", "es"]
8691
SINGLE_LANGUAGE = ["en"]
8792

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
from jinja2 import Template
2+
3+
from dqgen.adapters import template_builder
4+
from dqgen.services import QUERY_FALLBACK_LANGUAGES
5+
from dqgen.services.base_generator import BaseGenerator
6+
7+
8+
class AsciiDocGenerator(BaseGenerator):
9+
"""
10+
This class will generate an AsciiDoc template file from an AsciiDoc meta-template
11+
"""
12+
def __init__(self, cls: str, operation: str, output_folder_path: str, template: Template, prop: str = None,
13+
object_property: str = None, new_version_graph: str = None, old_version_graph: str = None,
14+
version_history_graph: str = None, languages: list = QUERY_FALLBACK_LANGUAGES, class_name: str = "", prop_name: str = "",
15+
obj_prop_name: str = ""):
16+
super().__init__(cls, operation, output_folder_path, template, prop, object_property, new_version_graph,
17+
old_version_graph, version_history_graph, languages)
18+
self.file_extension = "adoc"
19+
self.class_name = class_name
20+
self.prop_name = prop_name
21+
self.obj_prop_name = obj_prop_name
22+
23+
def build_template(self):
24+
"""
25+
This method builds a desired AsciiDoc template from the a meta-template
26+
:return: AsciiDoc template
27+
"""
28+
query_file = self.build_file_name(file_extension='rq')
29+
operation = self.operation.split("_")[0]
30+
return template_builder.build_html_template(jinja2_template=self.template, query_file=query_file,
31+
operation=operation, cls=self.cls, prop=self.prop,
32+
obj_prop=self.object_property, class_name=self.class_name,
33+
prop_name=self.prop_name,
34+
obj_prop_name=self.obj_prop_name)
35+
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
#!/usr/bin/python3
2+
3+
# asciidoc_templates_generator.py
4+
# Date: 2024
5+
# Author: Generated for AsciiDoc template support
6+
import logging
7+
import pathlib
8+
from distutils.dir_util import copy_tree
9+
from pathlib import Path
10+
from shutil import copyfile
11+
12+
import numpy as np
13+
import pandas as pd
14+
from dqgen.adapters.ap_reader import read_ap_from_csv
15+
16+
from dqgen.services import INSTANCE_OPERATIONS, PROPERTIES_OPERATIONS, REIFIED_PROPERTIES_OPERATIONS, ASCII_DOC_TEMPLATES, \
17+
PATH_TO_ASCIIDOC_STATIC_FOLDER, TEMPLATE_AND_ASCIIDOC_FILE_NAME_MAPPING
18+
from dqgen.services.asciidoc_generator import AsciiDocGenerator
19+
from dqgen.services.html_templates_data_source_builder import build_datasource_for_html_template, camel_case_to_words
20+
from dqgen.services.validate_application_profile import validate_application_profile
21+
22+
23+
def generate_class_level_asciidoc_templates(processed_csv_file: pd.DataFrame, asciidoc_output_folder_path):
24+
"""
25+
generate AsciiDoc templates for each class in the configuration CSV.
26+
"""
27+
28+
for cls in processed_csv_file["class"].unique():
29+
for operation in INSTANCE_OPERATIONS:
30+
class_name = cls.split(":")[1]
31+
class_folder_name = class_name.lower()
32+
output_folder_path = asciidoc_output_folder_path + "/" + class_folder_name
33+
pathlib.Path(output_folder_path).mkdir(parents=True, exist_ok=True)
34+
AsciiDocGenerator(cls=cls, operation=operation,
35+
class_name=camel_case_to_words(class_name).title(),
36+
output_folder_path=output_folder_path,
37+
template=ASCII_DOC_TEMPLATES.get_template("instance.jinja2")).to_file()
38+
logging.info("Generated instance AsciiDoc templates ...")
39+
40+
41+
def generate_property_level_asciidoc_templates(processed_csv_file: pd.DataFrame, asciidoc_output_folder_path):
42+
"""
43+
generate AsciiDoc template for data properties and their values for each instance in the configuration CSV
44+
"""
45+
for index, row in processed_csv_file.iterrows():
46+
47+
if not row["object property"]:
48+
for operation in PROPERTIES_OPERATIONS:
49+
class_folder_name = row["class"].split(":")[1].lower()
50+
if row["property group"] and row["property group"] is not np.NaN:
51+
property_group_folder = row["property group"].replace(" ", "_")
52+
output_folder_path = asciidoc_output_folder_path + "/" + class_folder_name + "/" + property_group_folder
53+
else:
54+
output_folder_path = asciidoc_output_folder_path + "/" + class_folder_name
55+
pathlib.Path(output_folder_path).mkdir(parents=True, exist_ok=True)
56+
AsciiDocGenerator(cls=row["class"],
57+
prop=row["property"],
58+
prop_name=camel_case_to_words(row["property"].split(":")[1]).lower(),
59+
operation=operation,
60+
output_folder_path=output_folder_path,
61+
template=ASCII_DOC_TEMPLATES.get_template("property.jinja2")).to_file()
62+
63+
logging.info("Generated property AsciiDoc templates ...")
64+
65+
66+
def generate_reified_property_level_asciidoc_templates(processed_csv_file: pd.DataFrame, asciidoc_output_folder_path):
67+
"""
68+
generate AsciiDoc template of reified structures for each instance in the configuration CSV
69+
"""
70+
for index, row in processed_csv_file.iterrows():
71+
if row["object property"]:
72+
for operation in REIFIED_PROPERTIES_OPERATIONS:
73+
class_folder_name = row["class"].split(":")[1].lower()
74+
if row["property group"] and row["property group"] is not np.NaN:
75+
property_group_folder = row["property group"].replace(" ", "_")
76+
output_folder_path = asciidoc_output_folder_path + "/" + class_folder_name + "/" + property_group_folder
77+
else:
78+
output_folder_path = asciidoc_output_folder_path + "/" + class_folder_name
79+
pathlib.Path(output_folder_path).mkdir(parents=True, exist_ok=True)
80+
AsciiDocGenerator(cls=row["class"],
81+
prop=row["property"],
82+
object_property=row["object property"],
83+
prop_name=camel_case_to_words(row["property"].split(":")[1]).lower(),
84+
operation=operation,
85+
output_folder_path=output_folder_path,
86+
template=ASCII_DOC_TEMPLATES.get_template("reified_property.jinja2")).to_file()
87+
88+
logging.info("Generated reified property AsciiDoc templates ...")
89+
90+
91+
def generate_asciidoc_template(processed_csv_file: pd.DataFrame, asciidoc_output_folder_path, template, file_name):
92+
"""
93+
Builds an AsciiDoc page and puts into a specified folder
94+
:param file_name:
95+
:param template:
96+
:param processed_csv_file:
97+
:param asciidoc_output_folder_path:
98+
:return:
99+
"""
100+
101+
data_source = build_datasource_for_html_template(processed_csv_file=processed_csv_file)
102+
build_template = template.stream(data_source=data_source)
103+
build_template.dump(asciidoc_output_folder_path + "/" + file_name)
104+
105+
106+
def copy_files_from_static_folder(file_list: list, destination_folder: str):
107+
"""
108+
Copy the files from the static folder to a specified destination
109+
:param file_list:
110+
:param destination_folder:
111+
"""
112+
for file in file_list:
113+
file_name = file.name
114+
copyfile(file, destination_folder + "/" + file_name)
115+
116+
117+
def generate_asciidoc_templates_from_csv(ap_file_path: pathlib.Path, output_base_dir: pathlib.Path):
118+
"""
119+
generates a set of AsciiDoc templates from the configuration CSV
120+
"""
121+
processed_csv_file = read_ap_from_csv(ap_file_path)
122+
validate_application_profile(application_profile_df=processed_csv_file)
123+
output = Path(output_base_dir) / ap_file_path.stem
124+
asciidoc_output = output / "asciidoc"
125+
asciidoc_output.mkdir(parents=True, exist_ok=True)
126+
127+
generate_class_level_asciidoc_templates(processed_csv_file=processed_csv_file, asciidoc_output_folder_path=str(asciidoc_output))
128+
generate_property_level_asciidoc_templates(processed_csv_file=processed_csv_file,
129+
asciidoc_output_folder_path=str(asciidoc_output))
130+
generate_reified_property_level_asciidoc_templates(processed_csv_file=processed_csv_file,
131+
asciidoc_output_folder_path=str(asciidoc_output))
132+
133+
for file_name, template in TEMPLATE_AND_ASCIIDOC_FILE_NAME_MAPPING.items():
134+
generate_asciidoc_template(processed_csv_file=processed_csv_file,
135+
asciidoc_output_folder_path=str(asciidoc_output), template=template, file_name=file_name)
136+
137+
copy_tree(PATH_TO_ASCIIDOC_STATIC_FOLDER, str(asciidoc_output))
138+

0 commit comments

Comments
 (0)