Skip to content

Commit c0c7a22

Browse files
authored
Recursive sub-sub-sections components (#112)
* 🚧♻️ Refactor creating and writing components streamlit files - writing of imports + component code (write python file) - component - fct map for generation - building list of components * 🎨 move static_dir to init, remove some old code * ✨ add components to streamlit home site - first section is home section recording it's compents - needs to be treated separately * ✨ Add section components - add overview page for each section showing components in main section folder * 🎨 pass relative section file paths on using Section dataclasses - Section could be used everywhere, Subsection is not really necessary. If a Section has subsections, the logic changes. Else nothing changes. * 🔥 remove old code (unused) * ♻️ move is_static_report to init for quarto - set based on report type, and this is set on init - avoid to pass on variable to all functions. * 🎨 move static_dir to quarto_report init - same as for streamlit_report to prepare refactoring of components parsing. * ♻️ factor out component creation - very similar to _combine_components in streamlit_report - imports has to be a list of list -> figure this out - added debug message for skipping components in static reports-> should maybe be a warining? * 🎨 remove blank line from logs * ✨ add components from main and section folders * 🎨 align import handling in streamlit and quarto reports * 🎨 cleaning up: remove some code/comments and add some docstrings * ✨ add sub-sub components to subsections, add max recursion - default still on subsection level - could consider to make subsubsections instead of adding components to subsection * 🐛 remove duplication due to merge * 🐛 stop per default on subsection folder * ✨ add maximum depth of search in reports folder to GUI
1 parent 16dac66 commit c0c7a22

File tree

6 files changed

+99
-17
lines changed

6 files changed

+99
-17
lines changed

gui/app.py

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,13 @@
110110

111111

112112
def create_run_vuegen(
113-
is_dir, config_path, report_type, run_streamlit, output_dir_entry, python_dir_entry
113+
is_dir,
114+
config_path,
115+
report_type,
116+
run_streamlit,
117+
output_dir_entry,
118+
python_dir_entry,
119+
max_depth: int,
114120
):
115121
def inner():
116122
kwargs = {}
@@ -125,6 +131,8 @@ def inner():
125131
print(f"{run_streamlit.get() = }")
126132
kwargs["streamlit_autorun"] = run_streamlit.get()
127133
kwargs["output_dir"] = output_dir_entry.get()
134+
if max_depth.get():
135+
kwargs["max_depth"] = int(max_depth.get())
128136
print("kwargs:")
129137
pprint(kwargs)
130138

@@ -146,6 +154,16 @@ def inner():
146154
messagebox.showwarning(
147155
"warning", "Running locally. Ignoring set Python Path"
148156
)
157+
if kwargs["max_depth"] < 2:
158+
messagebox.showwarning(
159+
"warning", "Maximum depth must be at least 2. Setting to 2."
160+
)
161+
kwargs["max_depth"] = 2
162+
elif kwargs["max_depth"] > 9:
163+
messagebox.showwarning(
164+
"warning", "Maximum depth must be at most 9. Setting to 9."
165+
)
166+
kwargs["max_depth"] = 9
149167
try:
150168
os.chdir(kwargs["output_dir"]) # Change the working directory
151169
# Define logger suffix based on report type and name
@@ -316,6 +334,40 @@ def select_directory():
316334
# output directory selection
317335
ctk_label_outdir = customtkinter.CTkLabel(app, text="Select output directory:")
318336
ctk_label_outdir.grid(row=row_count, column=0, columnspan=1, padx=10, pady=5)
337+
CTK_ENTRY_MAX_DEPTH_DEFAULT = 2
338+
# Maximum Depth input
339+
ctk_label_max_depth = customtkinter.CTkLabel(
340+
app,
341+
text=f"Maximum Depth: (default {CTK_ENTRY_MAX_DEPTH_DEFAULT})",
342+
)
343+
ctk_label_max_depth.grid(
344+
row=row_count, column=1, columnspan=1, padx=10, pady=5, sticky="e"
345+
)
346+
347+
348+
max_depth = tk.IntVar(value=CTK_ENTRY_MAX_DEPTH_DEFAULT)
349+
350+
351+
def slider_event(value):
352+
max_depth.set(value)
353+
ctk_label_max_depth.configure(text=f"Maximum Depth: {int(value)}")
354+
355+
356+
ctk_entry_max_depth = customtkinter.CTkSlider(
357+
app,
358+
from_=2,
359+
to=9,
360+
variable=max_depth,
361+
width=150,
362+
command=slider_event,
363+
number_of_steps=7,
364+
)
365+
ctk_entry_max_depth.set(
366+
CTK_ENTRY_MAX_DEPTH_DEFAULT
367+
) # Set the initial value of the slider
368+
ctk_entry_max_depth.grid(
369+
row=row_count, column=2, columnspan=1, padx=10, pady=5, sticky="w"
370+
)
319371
row_count += 1
320372
##########################################################################################
321373
output_dir_entry = tk.StringVar(value=str(output_dir))
@@ -380,6 +432,7 @@ def select_directory():
380432
run_streamlit=run_streamlit,
381433
output_dir_entry=output_dir_entry,
382434
python_dir_entry=python_dir_entry,
435+
max_depth=max_depth,
383436
)
384437
run_button = customtkinter.CTkButton(
385438
app,

src/vuegen/__main__.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,6 @@ def main():
1414
config_path = args.config
1515
dir_path = args.directory
1616
report_type = args.report_type
17-
output_dir = args.output_directory
18-
streamlit_autorun = args.streamlit_autorun
19-
quarto_cheks = args.quarto_checks
2017

2118
# Determine the report name for logger suffix
2219
if config_path:
@@ -47,9 +44,10 @@ def main():
4744
logger=logger,
4845
config_path=config_path,
4946
dir_path=dir_path,
50-
output_dir=output_dir,
51-
streamlit_autorun=streamlit_autorun,
52-
quarto_checks=quarto_cheks,
47+
output_dir=args.output_directory,
48+
streamlit_autorun=args.streamlit_autorun,
49+
quarto_checks=args.quarto_checks,
50+
max_depth=args.max_depth,
5351
)
5452

5553
# Print completion message

src/vuegen/config_manager.py

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,23 @@ class ConfigManager:
1313
Class for handling metadata of reports from YAML config file and creating report objects.
1414
"""
1515

16-
def __init__(self, logger: Optional[logging.Logger] = None):
16+
def __init__(self, logger: Optional[logging.Logger] = None, max_depth: int = 2):
1717
"""
1818
Initializes the ConfigManager with a logger.
1919
2020
Parameters
2121
----------
2222
logger : logging.Logger, optional
2323
A logger instance for the class. If not provided, a default logger will be created.
24+
max_depth : int, optional
25+
The maximum depth of the directory structure to consider when generating the report
26+
config from a directory.
27+
The default is 2, which means it will include sections and subsections.
2428
"""
2529
if logger is None:
2630
logger, _ = get_logger("report")
2731
self.logger = logger
32+
self.max_depth = max_depth
2833

2934
def _create_title_fromdir(self, file_dirname: str) -> str:
3035
"""
@@ -198,7 +203,7 @@ def _read_description_file(self, folder_path: Path) -> str:
198203
return ""
199204

200205
def _create_subsect_config_fromdir(
201-
self, subsection_dir_path: Path
206+
self, subsection_dir_path: Path, level: int = 2
202207
) -> Dict[str, Union[str, List[Dict]]]:
203208
"""
204209
Creates subsection config from a directory.
@@ -217,7 +222,6 @@ def _create_subsect_config_fromdir(
217222
sorted_files = self._sort_paths_by_numprefix(
218223
list(subsection_dir_path.iterdir())
219224
)
220-
221225
components = []
222226
for file in sorted_files:
223227
if file.is_file():
@@ -227,9 +231,17 @@ def _create_subsect_config_fromdir(
227231
continue
228232
# Add component config to list
229233
components.append(component_config)
230-
# ! if folder go into folder and pull files out?
231-
# nesting level already at point 2
232-
# loop of components in a folder
234+
elif file.is_dir():
235+
if level >= self.max_depth:
236+
self.logger.warning(
237+
"Subsection nesting level exceeded: %s. Skipping.", file.name
238+
)
239+
continue
240+
# components are added to subsection
241+
# ! Alternatively, one could add (sub-)sections to the subsection
242+
# ? Then one could remove differentiation between sections and subsections
243+
nested_components = self._create_subsect_config_fromdir(file, level + 1)
244+
components.extend(nested_components["components"])
233245

234246
subsection_config = {
235247
"title": self._create_title_fromdir(subsection_dir_path.name),
@@ -316,7 +328,7 @@ def create_yamlconfig_fromdir(
316328
sorted_sections = self._sort_paths_by_numprefix(list(base_dir_path.iterdir()))
317329

318330
main_section_config = {
319-
"title": "", # self._create_title_fromdir("home_components"),
331+
"title": "",
320332
"description": "Components added to homepage.",
321333
"components": [],
322334
}

src/vuegen/report_generator.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ def get_report(
1818
streamlit_autorun: bool = False,
1919
quarto_checks: bool = False,
2020
output_dir: Path = None,
21+
max_depth: int = 2, # section and subsection folders
2122
) -> tuple[str, str]:
2223
"""
2324
Generate and run a report based on the specified engine.
@@ -34,6 +35,16 @@ def get_report(
3435
Path to the directory from which to generate the configuration file.
3536
streamlit_autorun : bool, optional
3637
Whether to automatically run the Streamlit report after generation (default is False).
38+
quarto_checks : bool, optional
39+
Whether to perform checks for Quarto report generation for TeX and Chromium installation
40+
(default is False).
41+
output_dir : Path, optional
42+
The directory where the report folder will be generated.
43+
If not provided, the current directory will be used.
44+
max_depth : int, optional
45+
The maximum depth of the directory structure to consider when generating the report.
46+
The default is 2, which means it will include sections and subsections. The parater
47+
is only used when 'dir_path' is used.
3748
3849
Raises
3950
------
@@ -57,7 +68,7 @@ def get_report(
5768
logger, _ = get_logger("report", folder=_folder)
5869

5970
# Create the config manager object
60-
config_manager = ConfigManager(logger)
71+
config_manager = ConfigManager(logger, max_depth=max_depth)
6172

6273
if dir_path:
6374
# Generate configuration from the provided directory

src/vuegen/streamlit_reportview.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,6 @@ def _generate_sections(self, output_dir: str) -> None:
430430

431431
try:
432432
for section in self.report.sections[1:]:
433-
section_name_var = section.title.replace(" ", "_")
434433
self.report.logger.debug(
435434
f"Processing section '{section.id}': '{section.title}' - {len(section.subsections)} subsection(s)"
436435
)
@@ -465,7 +464,6 @@ def _generate_sections(self, output_dir: str) -> None:
465464
)
466465
try:
467466
# Create subsection file
468-
_subsection_name = make_valid_identifier(subsection.title)
469467
assert (
470468
subsection.file_path is not None
471469
), "Missing relative file path to subsection"

src/vuegen/utils/__init__.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,16 @@ def get_parser(prog_name: str, others: dict = {}) -> argparse.Namespace:
246246
default=False,
247247
help="Check if Quarto is installed and available for report generation.",
248248
)
249+
parser.add_argument(
250+
"-mdep",
251+
"--max_depth",
252+
type=int,
253+
default=2,
254+
help=(
255+
"Maximum depth for the recursive search of files in the input directory. "
256+
"Ignored if a config file is provided."
257+
),
258+
)
249259
# Parse arguments
250260
return parser
251261

0 commit comments

Comments
 (0)