diff --git a/.github/workflows/cdci.yml b/.github/workflows/cdci.yml index 1a9b233..3801537 100644 --- a/.github/workflows/cdci.yml +++ b/.github/workflows/cdci.yml @@ -10,7 +10,7 @@ on: jobs: test: - name: Test + name: Unittests+streamlit runs-on: ubuntu-latest strategy: matrix: @@ -30,4 +30,59 @@ jobs: pip install -e . - name: Run tests run: python -m pytest tests + - name: Execute streamlit report + run: | + cd docs + vuegen --config example_data/MicW2Graph/report_config_micw2graph.yaml + other-reports: + name: Integration tests + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] + steps: + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + cache: 'pip' # caching pip dependencies + cache-dependency-path: '**/pyproject.toml' + - name: Install dependencies + run: | + pip install --upgrade pip + pip install -e ".[quarto]" + - name: Execute streamlit report (to check) + run: | + cd docs + vuegen -c example_data/MicW2Graph/report_config_micw2graph.yaml + # repeat for easier inspection on GitHub: + - name: quarto html report + run: | + cd docs + vuegen -c example_data/MicW2Graph/report_config_micw2graph.yaml -rt html + - name: quarto pdf report + run: | + cd docs + vuegen -c example_data/MicW2Graph/report_config_micw2graph.yaml -rt pdf + - name: quarto docx report + run: | + cd docs + vuegen -c example_data/MicW2Graph/report_config_micw2graph.yaml -rt docx + - name: quarto odt report + run: | + cd docs + vuegen -c example_data/MicW2Graph/report_config_micw2graph.yaml -rt odt + - name: quarto revealjs report + run: | + cd docs + vuegen -c example_data/MicW2Graph/report_config_micw2graph.yaml -rt revealjs + - name: quarto pptx report + run: | + cd docs + vuegen -c example_data/MicW2Graph/report_config_micw2graph.yaml -rt pptx + - name: quarto jupyter report + run: | + cd docs + vuegen -c example_data/MicW2Graph/report_config_micw2graph.yaml -rt jupyter diff --git a/.gitignore b/.gitignore index cc38bf7..b05281e 100644 --- a/.gitignore +++ b/.gitignore @@ -123,7 +123,8 @@ quarto_report/ .DS_Store # Extra info -UML_diagrams/ -Graphical_abstract/ +docs/images/UML_diagrams/ +docs/images/Graphical_abstract/ +docs/images/Nfcore_module_figure docs/presentations/ test.py \ No newline at end of file diff --git a/README.md b/README.md index 97ac14d..f1c5d96 100644 --- a/README.md +++ b/README.md @@ -70,15 +70,18 @@ quarto check Run VueGen using a configuration file with the following command: ```bash -python vuegen/main.py --config docs/example_data/MicW2Graph/report_config_micw2graph.yaml --report_type streamlit +cd docs +vuegen --config example_data/MicW2Graph/report_config_micw2graph.yaml --report_type streamlit ``` +> 💡 If `vuegen` does not work, try `python -m vuegen` instead. + By default, the `streamlit_autorun` argument is set to False, but you can use it in case you want to automatically run the streamlit app. It's also possible to provide a directory instead of a configuration file: ```bash -python vuegen/main.py --directory docs/example_data/MicW2Graph/ --report_type streamlit +vuegen --directory docs/example_data/MicW2Graph/ --report_type streamlit ``` The current report types are streamlit, html, pdf, docx, odt, revealjs, pptx, and jupyter. diff --git a/docs/images/vuegen_classdiagram_noattmeth.png b/docs/images/vuegen_classdiagram_noattmeth.png index 7d1f63e..e9eeaa0 100644 Binary files a/docs/images/vuegen_classdiagram_noattmeth.png and b/docs/images/vuegen_classdiagram_noattmeth.png differ diff --git a/pyproject.toml b/pyproject.toml index 2e71d3d..b5270bd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,6 +27,7 @@ kaleido = "0.2.0" vl-convert-python = "^1.7.0" dataframe-image = "^0.2.6" strenum = { version = "^0.4.15", python = "<3.11" } + # optional doc depencencies, follow approach as described here: # https://github.com/python-poetry/poetry/issues/2567#issuecomment-646766059 sphinx = {version="*", optional=true} @@ -35,13 +36,12 @@ myst-nb = {version="*", optional=true} ipywidgets = {version="*", optional=true} sphinx-new-tab-link = {version = "!=0.2.2", optional=true} jupytext = {version="*", optional=true} -# quartro +# quarto quarto-cli = {version="*", optional=true} [tool.poetry.group.dev.dependencies] ipykernel = {version="^6.29.5", optional=true} - [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" @@ -52,5 +52,6 @@ streamlit = ["streamlit"] quarto = ["quarto-cli", "ipykernel"] docs = ["sphinx", "sphinx-book-theme", "myst-nb", "ipywidgets", "sphinx-new-tab-link", "jupytext"] -# [project.scripts] -# my-script = "vuegen.main:main" +[tool.poetry.scripts] +# https://python-poetry.org/docs/pyproject/#scripts +vuegen = "vuegen.__main__:main" diff --git a/tests/test_module_imports.py b/tests/test_module_imports.py index 64657e4..e632558 100644 --- a/tests/test_module_imports.py +++ b/tests/test_module_imports.py @@ -1,7 +1,7 @@ def test_imports(): import vuegen - import vuegen.main + import vuegen.__main__ import vuegen.quarto_reportview import vuegen.report import vuegen.report_generator diff --git a/vuegen/__main__.py b/vuegen/__main__.py new file mode 100644 index 0000000..1304235 --- /dev/null +++ b/vuegen/__main__.py @@ -0,0 +1,49 @@ +import sys +from pathlib import Path + +from vuegen import report_generator +from vuegen.utils import get_logger, get_parser + + +def main(): + # Parse command-line arguments + parser = get_parser(prog_name="VueGen") + args = parser.parse_args() + + # Determine the vuegen arguments + config_path = args.config + dir_path = args.directory + report_type = args.report_type + streamlit_autorun = args.streamlit_autorun + + # Determine the report name for logger suffix + if config_path: + report_name = Path(config_path).stem + elif dir_path: + report_name = Path(dir_path).name + else: + print("Please provide a configuration file or directory path:\n") + # https://docs.python.org/3/library/argparse.html#printing-help + parser.print_help() + sys.exit(1) + + if config_path and dir_path: + print("Please provide only one of configuration file or directory path:\n") + parser.print_help() + sys.exit(1) # otherwise could resort to either or ? + + # Define logger suffix based on report type and name + logger_suffix = f"{report_type}_report_{str(report_name)}" + + # Initialize logger + logger = get_logger(f"{logger_suffix}") + + # Generate the report + report_generator.get_report(report_type=report_type, + logger=logger, + config_path=config_path, + dir_path=dir_path, + streamlit_autorun=streamlit_autorun) + +if __name__ == '__main__': + main() diff --git a/vuegen/main.py b/vuegen/main.py deleted file mode 100644 index 1bd593a..0000000 --- a/vuegen/main.py +++ /dev/null @@ -1,33 +0,0 @@ -from pathlib import Path - -from vuegen import report_generator -from vuegen.utils import get_args, get_logger - -if __name__ == '__main__': - # Parse command-line arguments - args = get_args(prog_name="VueGen") - - # Determine the vuegen arguments - config_path = args.config - dir_path = args.directory - report_type = args.report_type - streamlit_autorun = args.streamlit_autorun - - # Determine the report name for logger suffix - if config_path: - report_name = Path(config_path).stem - else: - report_name = Path(dir_path).name - - # Define logger suffix based on report type and name - logger_suffix = f"{report_type}_report_{str(report_name)}" - - # Initialize logger - logger = get_logger(f"{logger_suffix}") - - # Generate the report - report_generator.get_report(report_type = report_type, - logger = logger, - config_path = config_path, - dir_path = dir_path, - streamlit_autorun = streamlit_autorun) \ No newline at end of file diff --git a/vuegen/quarto_reportview.py b/vuegen/quarto_reportview.py index 3dd5623..29a8d7e 100644 --- a/vuegen/quarto_reportview.py +++ b/vuegen/quarto_reportview.py @@ -117,6 +117,8 @@ def run_report(self, output_dir: str = BASE_DIR) -> None: """ try: subprocess.run(["quarto", "render", os.path.join(output_dir, f"{self.BASE_DIR}.qmd")], check=True) + if self.report_type == r.ReportType.JUPYTER: + subprocess.run(["quarto", "convert", os.path.join(output_dir, f"{self.BASE_DIR}.qmd")], check=True) self.report.logger.info(f"'{self.report.title}' '{self.report_type}' report rendered") except subprocess.CalledProcessError as e: self.report.logger.error(f"Error running '{self.report.title}' {self.report_type} report: {str(e)}") @@ -174,8 +176,12 @@ def _create_yaml_header(self) -> str: toc: false output: true""", r.ReportType.JUPYTER: """ - jupyter: - kernel: python3""" + html: + toc: true + toc-location: left + toc-depth: 3 + page-layout: full + self-contained: true""" } # Create a key based on the report type and format diff --git a/vuegen/streamlit_reportview.py b/vuegen/streamlit_reportview.py index c54dad1..b258bbe 100644 --- a/vuegen/streamlit_reportview.py +++ b/vuegen/streamlit_reportview.py @@ -127,7 +127,13 @@ def run_report(self, output_dir: str = SECTIONS_DIR) -> None: # If autorun is False, print instructions for manual execution self.report.logger.info(f"All the scripts to build the Streamlit app are available at {output_dir}") self.report.logger.info(f"To run the Streamlit app, use the following command:") - self.report.logger.info(f"streamlit run {os.path.join(output_dir, self.REPORT_MANAG_SCRIPT)}") + self.report.logger.info(f"streamlit run {os.path.join(output_dir, self.REPORT_MANAG_SCRIPT)}") + msg = ( + f"\nAll the scripts to build the Streamlit app are available at: {output_dir}\n\n" + f"To run the Streamlit app, use the following command:\n\n" + f"\tstreamlit run {os.path.join(output_dir, self.REPORT_MANAG_SCRIPT)}" + ) + print(msg) def _format_text(self, text: str, type: str, level: int = 1, color: str = '#000000', text_align: str = 'center') -> str: """ diff --git a/vuegen/utils.py b/vuegen/utils.py index c8a9471..99f1283 100644 --- a/vuegen/utils.py +++ b/vuegen/utils.py @@ -13,9 +13,9 @@ from strenum import StrEnum from io import StringIO +from pathlib import Path from typing import Type from urllib.parse import urlparse -from pathlib import Path import networkx as nx import requests @@ -145,7 +145,7 @@ def create_folder(directory_path: str, is_nested: bool = False) -> bool: except OSError as e: raise OSError(f"Error creating directory '{directory_path}': {e}") -def get_args(prog_name: str, others: dict = {}) -> argparse.Namespace: +def get_parser(prog_name: str, others: dict = {}) -> argparse.Namespace: """ Initiates argparse.ArgumentParser() and adds common arguments. @@ -193,7 +193,7 @@ def get_args(prog_name: str, others: dict = {}) -> argparse.Namespace: "-rt", "--report_type", type = str, - default = None, + default = 'streamlit', help = "Type of the report to generate (streamlit, html, pdf, docx, odt, revealjs, pptx, or jupyter)." ) parser.add_argument( @@ -205,7 +205,7 @@ def get_args(prog_name: str, others: dict = {}) -> argparse.Namespace: ) # Parse arguments - return parser.parse_args() + return parser def fetch_file_stream(file_path: str) -> StringIO: """