diff --git a/gui/app.py b/gui/app.py index f0a68c0..3755a09 100644 --- a/gui/app.py +++ b/gui/app.py @@ -110,7 +110,13 @@ def create_run_vuegen( - is_dir, config_path, report_type, run_streamlit, output_dir_entry, python_dir_entry + is_dir, + config_path, + report_type, + run_streamlit, + output_dir_entry, + python_dir_entry, + max_depth: int, ): def inner(): kwargs = {} @@ -125,6 +131,8 @@ def inner(): print(f"{run_streamlit.get() = }") kwargs["streamlit_autorun"] = run_streamlit.get() kwargs["output_dir"] = output_dir_entry.get() + if max_depth.get(): + kwargs["max_depth"] = int(max_depth.get()) print("kwargs:") pprint(kwargs) @@ -146,6 +154,16 @@ def inner(): messagebox.showwarning( "warning", "Running locally. Ignoring set Python Path" ) + if kwargs["max_depth"] < 2: + messagebox.showwarning( + "warning", "Maximum depth must be at least 2. Setting to 2." + ) + kwargs["max_depth"] = 2 + elif kwargs["max_depth"] > 9: + messagebox.showwarning( + "warning", "Maximum depth must be at most 9. Setting to 9." + ) + kwargs["max_depth"] = 9 try: os.chdir(kwargs["output_dir"]) # Change the working directory # Define logger suffix based on report type and name @@ -316,6 +334,40 @@ def select_directory(): # output directory selection ctk_label_outdir = customtkinter.CTkLabel(app, text="Select output directory:") ctk_label_outdir.grid(row=row_count, column=0, columnspan=1, padx=10, pady=5) +CTK_ENTRY_MAX_DEPTH_DEFAULT = 2 +# Maximum Depth input +ctk_label_max_depth = customtkinter.CTkLabel( + app, + text=f"Maximum Depth: (default {CTK_ENTRY_MAX_DEPTH_DEFAULT})", +) +ctk_label_max_depth.grid( + row=row_count, column=1, columnspan=1, padx=10, pady=5, sticky="e" +) + + +max_depth = tk.IntVar(value=CTK_ENTRY_MAX_DEPTH_DEFAULT) + + +def slider_event(value): + max_depth.set(value) + ctk_label_max_depth.configure(text=f"Maximum Depth: {int(value)}") + + +ctk_entry_max_depth = customtkinter.CTkSlider( + app, + from_=2, + to=9, + variable=max_depth, + width=150, + command=slider_event, + number_of_steps=7, +) +ctk_entry_max_depth.set( + CTK_ENTRY_MAX_DEPTH_DEFAULT +) # Set the initial value of the slider +ctk_entry_max_depth.grid( + row=row_count, column=2, columnspan=1, padx=10, pady=5, sticky="w" +) row_count += 1 ########################################################################################## output_dir_entry = tk.StringVar(value=str(output_dir)) @@ -380,6 +432,7 @@ def select_directory(): run_streamlit=run_streamlit, output_dir_entry=output_dir_entry, python_dir_entry=python_dir_entry, + max_depth=max_depth, ) run_button = customtkinter.CTkButton( app, diff --git a/src/vuegen/__main__.py b/src/vuegen/__main__.py index 3027556..be29fde 100644 --- a/src/vuegen/__main__.py +++ b/src/vuegen/__main__.py @@ -14,9 +14,6 @@ def main(): config_path = args.config dir_path = args.directory report_type = args.report_type - output_dir = args.output_directory - streamlit_autorun = args.streamlit_autorun - quarto_cheks = args.quarto_checks # Determine the report name for logger suffix if config_path: @@ -47,9 +44,10 @@ def main(): logger=logger, config_path=config_path, dir_path=dir_path, - output_dir=output_dir, - streamlit_autorun=streamlit_autorun, - quarto_checks=quarto_cheks, + output_dir=args.output_directory, + streamlit_autorun=args.streamlit_autorun, + quarto_checks=args.quarto_checks, + max_depth=args.max_depth, ) # Print completion message diff --git a/src/vuegen/config_manager.py b/src/vuegen/config_manager.py index 25fa86a..26180ba 100644 --- a/src/vuegen/config_manager.py +++ b/src/vuegen/config_manager.py @@ -13,7 +13,7 @@ class ConfigManager: Class for handling metadata of reports from YAML config file and creating report objects. """ - def __init__(self, logger: Optional[logging.Logger] = None): + def __init__(self, logger: Optional[logging.Logger] = None, max_depth: int = 2): """ Initializes the ConfigManager with a logger. @@ -21,10 +21,15 @@ def __init__(self, logger: Optional[logging.Logger] = None): ---------- logger : logging.Logger, optional A logger instance for the class. If not provided, a default logger will be created. + max_depth : int, optional + The maximum depth of the directory structure to consider when generating the report + config from a directory. + The default is 2, which means it will include sections and subsections. """ if logger is None: logger, _ = get_logger("report") self.logger = logger + self.max_depth = max_depth def _create_title_fromdir(self, file_dirname: str) -> str: """ @@ -198,7 +203,7 @@ def _read_description_file(self, folder_path: Path) -> str: return "" def _create_subsect_config_fromdir( - self, subsection_dir_path: Path + self, subsection_dir_path: Path, level: int = 2 ) -> Dict[str, Union[str, List[Dict]]]: """ Creates subsection config from a directory. @@ -217,7 +222,6 @@ def _create_subsect_config_fromdir( sorted_files = self._sort_paths_by_numprefix( list(subsection_dir_path.iterdir()) ) - components = [] for file in sorted_files: if file.is_file(): @@ -227,9 +231,17 @@ def _create_subsect_config_fromdir( continue # Add component config to list components.append(component_config) - # ! if folder go into folder and pull files out? - # nesting level already at point 2 - # loop of components in a folder + elif file.is_dir(): + if level >= self.max_depth: + self.logger.warning( + "Subsection nesting level exceeded: %s. Skipping.", file.name + ) + continue + # components are added to subsection + # ! Alternatively, one could add (sub-)sections to the subsection + # ? Then one could remove differentiation between sections and subsections + nested_components = self._create_subsect_config_fromdir(file, level + 1) + components.extend(nested_components["components"]) subsection_config = { "title": self._create_title_fromdir(subsection_dir_path.name), @@ -316,7 +328,7 @@ def create_yamlconfig_fromdir( sorted_sections = self._sort_paths_by_numprefix(list(base_dir_path.iterdir())) main_section_config = { - "title": "", # self._create_title_fromdir("home_components"), + "title": "", "description": "Components added to homepage.", "components": [], } diff --git a/src/vuegen/report_generator.py b/src/vuegen/report_generator.py index 7708571..261336b 100644 --- a/src/vuegen/report_generator.py +++ b/src/vuegen/report_generator.py @@ -18,6 +18,7 @@ def get_report( streamlit_autorun: bool = False, quarto_checks: bool = False, output_dir: Path = None, + max_depth: int = 2, # section and subsection folders ) -> tuple[str, str]: """ Generate and run a report based on the specified engine. @@ -34,6 +35,16 @@ def get_report( Path to the directory from which to generate the configuration file. streamlit_autorun : bool, optional Whether to automatically run the Streamlit report after generation (default is False). + quarto_checks : bool, optional + Whether to perform checks for Quarto report generation for TeX and Chromium installation + (default is False). + output_dir : Path, optional + The directory where the report folder will be generated. + If not provided, the current directory will be used. + max_depth : int, optional + The maximum depth of the directory structure to consider when generating the report. + The default is 2, which means it will include sections and subsections. The parater + is only used when 'dir_path' is used. Raises ------ @@ -57,7 +68,7 @@ def get_report( logger, _ = get_logger("report", folder=_folder) # Create the config manager object - config_manager = ConfigManager(logger) + config_manager = ConfigManager(logger, max_depth=max_depth) if dir_path: # Generate configuration from the provided directory diff --git a/src/vuegen/streamlit_reportview.py b/src/vuegen/streamlit_reportview.py index 2e568fe..91169b9 100644 --- a/src/vuegen/streamlit_reportview.py +++ b/src/vuegen/streamlit_reportview.py @@ -430,7 +430,6 @@ def _generate_sections(self, output_dir: str) -> None: try: for section in self.report.sections[1:]: - section_name_var = section.title.replace(" ", "_") self.report.logger.debug( f"Processing section '{section.id}': '{section.title}' - {len(section.subsections)} subsection(s)" ) @@ -465,7 +464,6 @@ def _generate_sections(self, output_dir: str) -> None: ) try: # Create subsection file - _subsection_name = make_valid_identifier(subsection.title) assert ( subsection.file_path is not None ), "Missing relative file path to subsection" diff --git a/src/vuegen/utils/__init__.py b/src/vuegen/utils/__init__.py index e028974..0084208 100644 --- a/src/vuegen/utils/__init__.py +++ b/src/vuegen/utils/__init__.py @@ -246,6 +246,16 @@ def get_parser(prog_name: str, others: dict = {}) -> argparse.Namespace: default=False, help="Check if Quarto is installed and available for report generation.", ) + parser.add_argument( + "-mdep", + "--max_depth", + type=int, + default=2, + help=( + "Maximum depth for the recursive search of files in the input directory. " + "Ignored if a config file is provided." + ), + ) # Parse arguments return parser