Skip to content

Commit 3af33ee

Browse files
authored
Merge branch 'main' into docs_update
2 parents 06fb961 + 515886c commit 3af33ee

File tree

9 files changed

+115
-30
lines changed

9 files changed

+115
-30
lines changed

.github/workflows/cdci.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,13 @@ jobs:
2727
python-version: ${{ matrix.python-version }}
2828
cache: "pip" # caching pip dependencies
2929
cache-dependency-path: "**/pyproject.toml"
30-
- name: Install dependencies
30+
- name: lint package code with ruff
3131
run: |
3232
python -m pip install --upgrade pip
33+
pip install ruff
34+
ruff check src
35+
- name: Install dependencies for testing
36+
run: |
3337
pip install pytest
3438
pip install -e .
3539
- name: Run tests

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/quarto_reportview.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -578,7 +578,7 @@ def _generate_plot_content(self, plot) -> List[str]:
578578
)
579579
plot_content.append(self._generate_image_content(static_plot_path))
580580
else:
581-
plot_content.append(f"""fig_plotly.show()\n```\n""")
581+
plot_content.append("""fig_plotly.show()\n```\n""")
582582
elif plot.plot_type == r.PlotType.ALTAIR:
583583
plot_content.append(self._generate_plot_code(plot))
584584
if self.is_report_static:
@@ -587,15 +587,15 @@ def _generate_plot_content(self, plot) -> List[str]:
587587
)
588588
plot_content.append(self._generate_image_content(static_plot_path))
589589
else:
590-
plot_content.append(f"""fig_altair\n```\n""")
590+
plot_content.append("""fig_altair\n```\n""")
591591
elif plot.plot_type == r.PlotType.INTERACTIVE_NETWORK:
592592
networkx_graph = plot.read_network()
593593
if isinstance(networkx_graph, tuple):
594594
# If network_data is a tuple, separate the network and html file path
595595
networkx_graph, html_plot_file = networkx_graph
596596
elif isinstance(networkx_graph, nx.Graph) and not self.is_report_static:
597597
# Get the pyvis object and create html
598-
pyvis_graph = plot.create_and_save_pyvis_network(
598+
_ = plot.create_and_save_pyvis_network(
599599
networkx_graph, html_plot_file
600600
)
601601

@@ -805,7 +805,7 @@ def _generate_markdown_content(self, markdown) -> List[str]:
805805
)
806806

807807
# Code to display md content
808-
markdown_content.append(f"""display.Markdown(markdown_content)\n```\n""")
808+
markdown_content.append("""display.Markdown(markdown_content)\n```\n""")
809809

810810
except Exception as e:
811811
self.report.logger.error(
@@ -926,7 +926,7 @@ def _show_dataframe(self, dataframe) -> List[str]:
926926
else:
927927
# Append code to display the DataFrame interactively
928928
dataframe_content.append(
929-
f"""show(df, classes="display nowrap compact", lengthMenu=[3, 5, 10])\n```\n"""
929+
"""show(df, classes="display nowrap compact", lengthMenu=[3, 5, 10])\n```\n"""
930930
)
931931

932932
return dataframe_content

src/vuegen/report.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import json
21
import logging
32
import os
43
from abc import ABC, abstractmethod

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: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ def run_report(self, output_dir: str = SECTIONS_DIR) -> None:
296296
f"All the scripts to build the Streamlit app are available at {output_dir}"
297297
)
298298
self.report.logger.info(
299-
f"To run the Streamlit app, use the following command:"
299+
"To run the Streamlit app, use the following command:"
300300
)
301301
self.report.logger.info(
302302
f"streamlit run {Path(output_dir) / self.REPORT_MANAG_SCRIPT}"
@@ -381,7 +381,7 @@ def _generate_home_section(
381381

382382
# Create the home page content
383383
home_content = []
384-
home_content.append(f"import streamlit as st")
384+
home_content.append("import streamlit as st")
385385
if subsection_imports:
386386
home_content.extend(subsection_imports)
387387
if self.report.description:
@@ -409,9 +409,9 @@ def _generate_home_section(
409409

410410
# Add the home page to the report manager content
411411
report_manag_content.append(
412-
f"homepage = st.Page('Home/Homepage.py', title='Homepage')" # ! here Posix Path is hardcoded
412+
"homepage = st.Page('Home/Homepage.py', title='Homepage')" # ! here Posix Path is hardcoded
413413
)
414-
report_manag_content.append(f"sections_pages['Home'] = [homepage]\n")
414+
report_manag_content.append("sections_pages['Home'] = [homepage]\n")
415415
self.report.logger.info("Home page added to the report manager content.")
416416
except Exception as e:
417417
self.report.logger.error(f"Error generating the home section: {str(e)}")
@@ -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"
@@ -604,11 +602,11 @@ def _generate_plot_content(self, plot) -> List[str]:
604602
html_plot_file = (
605603
Path(self.static_dir) / f"{plot.title.replace(' ', '_')}.html"
606604
)
607-
pyvis_graph = plot.create_and_save_pyvis_network(
605+
_ = plot.create_and_save_pyvis_network(
608606
networkx_graph, html_plot_file
609607
)
610608

611-
# Add number of nodes and edges to the plor conetnt
609+
# Add number of nodes and edges to the plot content
612610
num_nodes = networkx_graph.number_of_nodes()
613611
num_edges = networkx_graph.number_of_edges()
614612

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)